llvoiceclient.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:192k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. }
  2. }
  3. void LLVoiceClient::clearAllLists()
  4. {
  5. // FOR TESTING ONLY
  6. // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
  7. buddyListMap::iterator buddy_it;
  8. for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
  9. {
  10. buddyListEntry *buddy = buddy_it->second;
  11. buddy_it++;
  12. std::ostringstream stream;
  13. if(buddy->mInVivoxBuddies)
  14. {
  15. // delete this entry from the vivox buddy list
  16. buddy->mInVivoxBuddies = false;
  17. LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
  18. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.BuddyDelete.1">"
  19. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  20. << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
  21. << "</Request>nnn";
  22. }
  23. if(buddy->mHasBlockListEntry)
  24. {
  25. // Delete the associated block list entry (so the block list doesn't fill up with junk)
  26. buddy->mHasBlockListEntry = false;
  27. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteBlockRule.1">"
  28. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  29. << "<BlockMask>" << buddy->mURI << "</BlockMask>"
  30. << "</Request>nnn";
  31. }
  32. if(buddy->mHasAutoAcceptListEntry)
  33. {
  34. // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
  35. buddy->mHasAutoAcceptListEntry = false;
  36. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteAutoAcceptRule.1">"
  37. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  38. << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
  39. << "</Request>nnn";
  40. }
  41. writeString(stream.str());
  42. }
  43. }
  44. void LLVoiceClient::sendFriendsListUpdates()
  45. {
  46. if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
  47. {
  48. mFriendsListDirty = false;
  49. if(0)
  50. {
  51. // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
  52. clearAllLists();
  53. return;
  54. }
  55. LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
  56. buddyListMap::iterator buddy_it;
  57. for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
  58. {
  59. // reset the temp flags in the local buddy list
  60. buddy_it->second->mInSLFriends = false;
  61. }
  62. // correlate with the friends list
  63. {
  64. LLCollectAllBuddies collect;
  65. LLAvatarTracker::instance().applyFunctor(collect);
  66. LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
  67. LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
  68. for ( ; it != end; ++it)
  69. {
  70. checkFriend(it->second);
  71. }
  72. it = collect.mOffline.begin();
  73. end = collect.mOffline.end();
  74. for ( ; it != end; ++it)
  75. {
  76. checkFriend(it->second);
  77. }
  78. }
  79. LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
  80. for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
  81. {
  82. buddyListEntry *buddy = buddy_it->second;
  83. buddy_it++;
  84. // Ignore entries that aren't resolved yet.
  85. if(buddy->mNameResolved)
  86. {
  87. std::ostringstream stream;
  88. if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
  89. {
  90. if(mNumberOfAliases > 0)
  91. {
  92. // Add (or update) this entry in the vivox buddy list
  93. buddy->mInVivoxBuddies = true;
  94. buddy->mNeedsNameUpdate = false;
  95. LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
  96. stream 
  97. << "<Request requestId="" << mCommandCookie++ << "" action="Account.BuddySet.1">"
  98. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  99. << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
  100. << "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
  101. << "<BuddyData></BuddyData>" // Without this, SLVoice doesn't seem to parse the command.
  102. << "<GroupID>0</GroupID>"
  103. << "</Request>nnn";
  104. }
  105. }
  106. else if(!buddy->mInSLFriends)
  107. {
  108. // This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list.
  109.   if(buddy->mInVivoxBuddies)
  110. {
  111. // delete this entry from the vivox buddy list
  112. buddy->mInVivoxBuddies = false;
  113. LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
  114. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.BuddyDelete.1">"
  115. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  116. << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
  117. << "</Request>nnn";
  118. }
  119. if(buddy->mHasBlockListEntry)
  120. {
  121. // Delete the associated block list entry, if any
  122. buddy->mHasBlockListEntry = false;
  123. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteBlockRule.1">"
  124. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  125. << "<BlockMask>" << buddy->mURI << "</BlockMask>"
  126. << "</Request>nnn";
  127. }
  128. if(buddy->mHasAutoAcceptListEntry)
  129. {
  130. // Delete the associated auto-accept list entry, if any
  131. buddy->mHasAutoAcceptListEntry = false;
  132. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteAutoAcceptRule.1">"
  133. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  134. << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
  135. << "</Request>nnn";
  136. }
  137. }
  138. if(buddy->mInSLFriends)
  139. {
  140. if(buddy->mCanSeeMeOnline)
  141. {
  142. // Buddy should not be blocked.
  143. // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
  144. // If the buddy has a block list entry, delete it.
  145. if(buddy->mHasBlockListEntry)
  146. {
  147. buddy->mHasBlockListEntry = false;
  148. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteBlockRule.1">"
  149. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  150. << "<BlockMask>" << buddy->mURI << "</BlockMask>"
  151. << "</Request>nnn";
  152. // If we just deleted a block list entry, add an auto-accept entry.
  153. if(!buddy->mHasAutoAcceptListEntry)
  154. {
  155. buddy->mHasAutoAcceptListEntry = true;
  156. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.CreateAutoAcceptRule.1">"
  157. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  158. << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
  159. << "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
  160. << "</Request>nnn";
  161. }
  162. }
  163. }
  164. else
  165. {
  166. // Buddy should be blocked.
  167. // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
  168. // If this buddy has an autoaccept entry, delete it
  169. if(buddy->mHasAutoAcceptListEntry)
  170. {
  171. buddy->mHasAutoAcceptListEntry = false;
  172. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.DeleteAutoAcceptRule.1">"
  173. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  174. << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
  175. << "</Request>nnn";
  176. // If we just deleted an auto-accept entry, add a block list entry.
  177. if(!buddy->mHasBlockListEntry)
  178. {
  179. buddy->mHasBlockListEntry = true;
  180. stream << "<Request requestId="" << mCommandCookie++ << "" action="Account.CreateBlockRule.1">"
  181. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  182. << "<BlockMask>" << buddy->mURI << "</BlockMask>"
  183. << "<PresenceOnly>1</PresenceOnly>"
  184. << "</Request>nnn";
  185. }
  186. }
  187. }
  188. if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
  189. {
  190. // Delete this entry from the local buddy list.  This should NOT invalidate the iterator,
  191. // since it has already been incremented to the next entry.
  192. deleteBuddy(buddy->mURI);
  193. }
  194. }
  195. writeString(stream.str());
  196. }
  197. }
  198. }
  199. }
  200. /////////////////////////////
  201. // Response/Event handlers
  202. void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
  203. {
  204. if(statusCode != 0)
  205. {
  206. LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
  207. setState(stateConnectorFailed);
  208. }
  209. else
  210. {
  211. // Connector created, move forward.
  212. LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
  213. mAPIVersion = versionID;
  214. mConnectorHandle = connectorHandle;
  215. if(getState() == stateConnectorStarting)
  216. {
  217. setState(stateConnectorStarted);
  218. }
  219. }
  220. }
  221. void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
  222. LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
  223. // Status code of 20200 means "bad password".  We may want to special-case that at some point.
  224. if ( statusCode == 401 )
  225. {
  226. // Login failure which is probably caused by the delay after a user's password being updated.
  227. LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
  228. setState(stateLoginRetry);
  229. }
  230. else if(statusCode != 0)
  231. {
  232. LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
  233. setState(stateLoginFailed);
  234. }
  235. else
  236. {
  237. // Login succeeded, move forward.
  238. mAccountHandle = accountHandle;
  239. mNumberOfAliases = numberOfAliases;
  240. // This needs to wait until the AccountLoginStateChangeEvent is received.
  241. // if(getState() == stateLoggingIn)
  242. // {
  243. // setState(stateLoggedIn);
  244. // }
  245. }
  246. }
  247. void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
  248. {
  249. sessionState *session = findSessionBeingCreatedByURI(requestId);
  250. if(session)
  251. {
  252. session->mCreateInProgress = false;
  253. }
  254. if(statusCode != 0)
  255. {
  256. LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
  257. if(session)
  258. {
  259. session->mErrorStatusCode = statusCode;
  260. session->mErrorStatusString = statusString;
  261. if(session == mAudioSession)
  262. {
  263. setState(stateJoinSessionFailed);
  264. }
  265. else
  266. {
  267. reapSession(session);
  268. }
  269. }
  270. }
  271. else
  272. {
  273. LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
  274. if(session)
  275. {
  276. setSessionHandle(session, sessionHandle);
  277. }
  278. }
  279. }
  280. void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
  281. {
  282. sessionState *session = findSessionBeingCreatedByURI(requestId);
  283. if(session)
  284. {
  285. session->mCreateInProgress = false;
  286. }
  287. if(statusCode != 0)
  288. {
  289. LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
  290. if(session)
  291. {
  292. session->mErrorStatusCode = statusCode;
  293. session->mErrorStatusString = statusString;
  294. if(session == mAudioSession)
  295. {
  296. setState(stateJoinSessionFailed);
  297. }
  298. else
  299. {
  300. reapSession(session);
  301. }
  302. }
  303. }
  304. else
  305. {
  306. LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
  307. if(session)
  308. {
  309. setSessionHandle(session, sessionHandle);
  310. }
  311. }
  312. }
  313. void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
  314. {
  315. sessionState *session = findSession(requestId);
  316. if(statusCode != 0)
  317. {
  318. LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
  319. if(session)
  320. {
  321. session->mMediaConnectInProgress = false;
  322. session->mErrorStatusCode = statusCode;
  323. session->mErrorStatusString = statusString;
  324. if(session == mAudioSession)
  325. setState(stateJoinSessionFailed);
  326. }
  327. }
  328. else
  329. {
  330. LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL;
  331. }
  332. }
  333. void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
  334. {
  335. if(statusCode != 0)
  336. {
  337. LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
  338. // Should this ever fail?  do we care if it does?
  339. }
  340. }
  341. void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
  342. {
  343. if(statusCode != 0)
  344. {
  345. LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
  346. // Should this ever fail?  do we care if it does?
  347. }
  348. mConnected = false;
  349. if(getState() == stateConnectorStopping)
  350. {
  351. setState(stateConnectorStopped);
  352. }
  353. }
  354. void LLVoiceClient::sessionAddedEvent(
  355. std::string &uriString, 
  356. std::string &alias, 
  357. std::string &sessionHandle, 
  358. std::string &sessionGroupHandle, 
  359. bool isChannel, 
  360. bool incoming,
  361. std::string &nameString,
  362. std::string &applicationString)
  363. {
  364. sessionState *session = NULL;
  365. LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
  366. session = addSession(uriString, sessionHandle);
  367. if(session)
  368. {
  369. session->mGroupHandle = sessionGroupHandle;
  370. session->mIsChannel = isChannel;
  371. session->mIncoming = incoming;
  372. session->mAlias = alias;
  373. // Generate a caller UUID -- don't need to do this for channels
  374. if(!session->mIsChannel)
  375. {
  376. if(IDFromName(session->mSIPURI, session->mCallerID))
  377. {
  378. // Normal URI(base64-encoded UUID) 
  379. }
  380. else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
  381. {
  382. // Wrong URI, but an alias is available.  Stash the incoming URI as an alternate
  383. session->mAlternateSIPURI = session->mSIPURI;
  384. // and generate a proper URI from the ID.
  385. setSessionURI(session, sipURIFromID(session->mCallerID));
  386. }
  387. else
  388. {
  389. LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
  390. setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
  391. session->mSynthesizedCallerID = true;
  392. // Can't look up the name in this case -- we have to extract it from the URI.
  393. std::string namePortion = nameFromsipURI(session->mSIPURI);
  394. if(namePortion.empty())
  395. {
  396. // Didn't seem to be a SIP URI, just use the whole provided name.
  397. namePortion = nameString;
  398. }
  399. // Some incoming names may be separated with an underscore instead of a space.  Fix this.
  400. LLStringUtil::replaceChar(namePortion, '_', ' ');
  401. // Act like we just finished resolving the name (this stores it in all the right places)
  402. avatarNameResolved(session->mCallerID, namePortion);
  403. }
  404. LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
  405. if(!session->mSynthesizedCallerID)
  406. {
  407. // If we got here, we don't have a proper name.  Initiate a lookup.
  408. lookupName(session->mCallerID);
  409. }
  410. }
  411. }
  412. }
  413. void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
  414. {
  415. LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
  416. #if USE_SESSION_GROUPS
  417. if(mMainSessionGroupHandle.empty())
  418. {
  419. // This is the first (i.e. "main") session group.  Save its handle.
  420. mMainSessionGroupHandle = sessionGroupHandle;
  421. }
  422. else
  423. {
  424. LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
  425. }
  426. #endif
  427. }
  428. void LLVoiceClient::joinedAudioSession(sessionState *session)
  429. {
  430. if(mAudioSession != session)
  431. {
  432. sessionState *oldSession = mAudioSession;
  433. mAudioSession = session;
  434. mAudioSessionChanged = true;
  435. // The old session may now need to be deleted.
  436. reapSession(oldSession);
  437. }
  438. // This is the session we're joining.
  439. if(getState() == stateJoiningSession)
  440. {
  441. setState(stateSessionJoined);
  442. // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
  443. // Add the current user as a participant here.
  444. participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
  445. if(participant)
  446. {
  447. participant->mIsSelf = true;
  448. lookupName(participant->mAvatarID);
  449. LL_INFOS("Voice") << "added self as participant "" << participant->mAccountName 
  450. << "" (" << participant->mAvatarID << ")"<< LL_ENDL;
  451. }
  452. if(!session->mIsChannel)
  453. {
  454. // this is a p2p session.  Make sure the other end is added as a participant.
  455. participantState *participant = session->addParticipant(session->mSIPURI);
  456. if(participant)
  457. {
  458. if(participant->mAvatarIDValid)
  459. {
  460. lookupName(participant->mAvatarID);
  461. }
  462. else if(!session->mName.empty())
  463. {
  464. participant->mDisplayName = session->mName;
  465. avatarNameResolved(participant->mAvatarID, session->mName);
  466. }
  467. // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
  468. LL_INFOS("Voice") << "added caller as participant "" << participant->mAccountName 
  469. << "" (" << participant->mAvatarID << ")"<< LL_ENDL;
  470. }
  471. }
  472. }
  473. }
  474. void LLVoiceClient::sessionRemovedEvent(
  475. std::string &sessionHandle, 
  476. std::string &sessionGroupHandle)
  477. {
  478. LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
  479. sessionState *session = findSession(sessionHandle);
  480. if(session)
  481. {
  482. leftAudioSession(session);
  483. // This message invalidates the session's handle.  Set it to empty.
  484. setSessionHandle(session);
  485. // This also means that the session's session group is now empty.
  486. // Terminate the session group so it doesn't leak.
  487. sessionGroupTerminateSendMessage(session);
  488. // Reset the media state (we now have no info)
  489. session->mMediaStreamState = streamStateUnknown;
  490. session->mTextStreamState = streamStateUnknown;
  491. // Conditionally delete the session
  492. reapSession(session);
  493. }
  494. else
  495. {
  496. LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
  497. }
  498. }
  499. void LLVoiceClient::reapSession(sessionState *session)
  500. {
  501. if(session)
  502. {
  503. if(!session->mHandle.empty())
  504. {
  505. LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
  506. }
  507. else if(session->mCreateInProgress)
  508. {
  509. LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
  510. }
  511. else if(session->mMediaConnectInProgress)
  512. {
  513. LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
  514. }
  515. else if(session == mAudioSession)
  516. {
  517. LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
  518. }
  519. else if(session == mNextAudioSession)
  520. {
  521. LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
  522. }
  523. else
  524. {
  525. // TODO: Question: Should we check for queued text messages here?
  526. // We don't have a reason to keep tracking this session, so just delete it.
  527. LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
  528. deleteSession(session);
  529. session = NULL;
  530. }
  531. }
  532. else
  533. {
  534. // LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
  535. }
  536. }
  537. // Returns true if the session seems to indicate we've moved to a region on a different voice server
  538. bool LLVoiceClient::sessionNeedsRelog(sessionState *session)
  539. {
  540. bool result = false;
  541. if(session != NULL)
  542. {
  543. // Only make this check for spatial channels (so it won't happen for group or p2p calls)
  544. if(session->mIsSpatial)
  545. {
  546. std::string::size_type atsign;
  547. atsign = session->mSIPURI.find("@");
  548. if(atsign != std::string::npos)
  549. {
  550. std::string urihost = session->mSIPURI.substr(atsign + 1);
  551. if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
  552. {
  553. // The hostname in this URI is different from what we expect.  This probably means we need to relog.
  554. // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
  555. // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
  556. result = true;
  557. }
  558. }
  559. }
  560. }
  561. return result;
  562. }
  563. void LLVoiceClient::leftAudioSession(
  564. sessionState *session)
  565. {
  566. if(mAudioSession == session)
  567. {
  568. switch(getState())
  569. {
  570. case stateJoiningSession:
  571. case stateSessionJoined:
  572. case stateRunning:
  573. case stateLeavingSession:
  574. case stateJoinSessionFailed:
  575. case stateJoinSessionFailedWaiting:
  576. // normal transition
  577. LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
  578. setState(stateSessionTerminated);
  579. break;
  580. case stateSessionTerminated:
  581. // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
  582. LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
  583. break;
  584. default:
  585. LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
  586. setState(stateSessionTerminated);
  587. break;
  588. }
  589. }
  590. }
  591. void LLVoiceClient::accountLoginStateChangeEvent(
  592. std::string &accountHandle, 
  593. int statusCode, 
  594. std::string &statusString, 
  595. int state)
  596. {
  597. LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
  598. /*
  599. According to Mike S., status codes for this event are:
  600. login_state_logged_out=0,
  601.         login_state_logged_in = 1,
  602.         login_state_logging_in = 2,
  603.         login_state_logging_out = 3,
  604.         login_state_resetting = 4,
  605.         login_state_error=100
  606. */
  607. switch(state)
  608. {
  609. case 1:
  610. if(getState() == stateLoggingIn)
  611. {
  612. setState(stateLoggedIn);
  613. }
  614. break;
  615. case 3:
  616. // The user is in the process of logging out.
  617. setState(stateLoggingOut);
  618. break;
  619. case 0:
  620. // The user has been logged out.  
  621. setState(stateLoggedOut);
  622. break;
  623. default:
  624. //Used to be a commented out warning
  625. LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
  626. break;
  627. }
  628. }
  629. void LLVoiceClient::mediaStreamUpdatedEvent(
  630. std::string &sessionHandle, 
  631. std::string &sessionGroupHandle, 
  632. int statusCode, 
  633. std::string &statusString, 
  634. int state, 
  635. bool incoming)
  636. {
  637. sessionState *session = findSession(sessionHandle);
  638. LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string "" << statusString << """ << LL_ENDL;
  639. if(session)
  640. {
  641. // We know about this session
  642. // Save the state for later use
  643. session->mMediaStreamState = state;
  644. switch(statusCode)
  645. {
  646. case 0:
  647. case 200:
  648. // generic success
  649. // Don't change the saved error code (it may have been set elsewhere)
  650. break;
  651. default:
  652. // save the status code for later
  653. session->mErrorStatusCode = statusCode;
  654. break;
  655. }
  656. switch(state)
  657. {
  658. case streamStateIdle:
  659. // Standard "left audio session"
  660. session->mVoiceEnabled = false;
  661. session->mMediaConnectInProgress = false;
  662. leftAudioSession(session);
  663. break;
  664. case streamStateConnected:
  665. session->mVoiceEnabled = true;
  666. session->mMediaConnectInProgress = false;
  667. joinedAudioSession(session);
  668. break;
  669. case streamStateRinging:
  670. if(incoming)
  671. {
  672. // Send the voice chat invite to the GUI layer
  673. // *TODO: Question: Should we correlate with the mute list here?
  674. session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
  675. session->mVoiceInvitePending = true;
  676. if(session->mName.empty())
  677. {
  678. lookupName(session->mCallerID);
  679. }
  680. else
  681. {
  682. // Act like we just finished resolving the name
  683. avatarNameResolved(session->mCallerID, session->mName);
  684. }
  685. }
  686. break;
  687. default:
  688. LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
  689. break;
  690. }
  691. }
  692. else
  693. {
  694. LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
  695. }
  696. }
  697. void LLVoiceClient::textStreamUpdatedEvent(
  698. std::string &sessionHandle, 
  699. std::string &sessionGroupHandle, 
  700. bool enabled,
  701. int state, 
  702. bool incoming)
  703. {
  704. sessionState *session = findSession(sessionHandle);
  705. if(session)
  706. {
  707. // Save the state for later use
  708. session->mTextStreamState = state;
  709. // We know about this session
  710. switch(state)
  711. {
  712. case 0: // We see this when the text stream closes
  713. LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
  714. break;
  715. case 1: // We see this on an incoming call from the Connector
  716. // Try to send any text messages queued for this session.
  717. sendQueuedTextMessages(session);
  718. // Send the text chat invite to the GUI layer
  719. // TODO: Question: Should we correlate with the mute list here?
  720. session->mTextInvitePending = true;
  721. if(session->mName.empty())
  722. {
  723. lookupName(session->mCallerID);
  724. }
  725. else
  726. {
  727. // Act like we just finished resolving the name
  728. avatarNameResolved(session->mCallerID, session->mName);
  729. }
  730. break;
  731. default:
  732. LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
  733. break;
  734. }
  735. }
  736. }
  737. void LLVoiceClient::participantAddedEvent(
  738. std::string &sessionHandle, 
  739. std::string &sessionGroupHandle, 
  740. std::string &uriString, 
  741. std::string &alias, 
  742. std::string &nameString, 
  743. std::string &displayNameString, 
  744. int participantType)
  745. {
  746. sessionState *session = findSession(sessionHandle);
  747. if(session)
  748. {
  749. participantState *participant = session->addParticipant(uriString);
  750. if(participant)
  751. {
  752. participant->mAccountName = nameString;
  753. LL_DEBUGS("Voice") << "added participant "" << participant->mAccountName 
  754. << "" (" << participant->mAvatarID << ")"<< LL_ENDL;
  755. if(participant->mAvatarIDValid)
  756. {
  757. // Initiate a lookup
  758. lookupName(participant->mAvatarID);
  759. }
  760. else
  761. {
  762. // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
  763. std::string namePortion = nameFromsipURI(uriString);
  764. if(namePortion.empty())
  765. {
  766. // Problem with the SIP URI, fall back to the display name
  767. namePortion = displayNameString;
  768. }
  769. if(namePortion.empty())
  770. {
  771. // Problems with both of the above, fall back to the account name
  772. namePortion = nameString;
  773. }
  774. // Set the display name (which is a hint to the active speakers window not to do its own lookup)
  775. participant->mDisplayName = namePortion;
  776. avatarNameResolved(participant->mAvatarID, namePortion);
  777. }
  778. }
  779. }
  780. }
  781. void LLVoiceClient::participantRemovedEvent(
  782. std::string &sessionHandle, 
  783. std::string &sessionGroupHandle, 
  784. std::string &uriString, 
  785. std::string &alias, 
  786. std::string &nameString)
  787. {
  788. sessionState *session = findSession(sessionHandle);
  789. if(session)
  790. {
  791. participantState *participant = session->findParticipant(uriString);
  792. if(participant)
  793. {
  794. session->removeParticipant(participant);
  795. }
  796. else
  797. {
  798. LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
  799. }
  800. }
  801. else
  802. {
  803. LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
  804. }
  805. }
  806. void LLVoiceClient::participantUpdatedEvent(
  807. std::string &sessionHandle, 
  808. std::string &sessionGroupHandle, 
  809. std::string &uriString, 
  810. std::string &alias, 
  811. bool isModeratorMuted, 
  812. bool isSpeaking, 
  813. int volume, 
  814. F32 energy)
  815. {
  816. sessionState *session = findSession(sessionHandle);
  817. if(session)
  818. {
  819. participantState *participant = session->findParticipant(uriString);
  820. if(participant)
  821. {
  822. participant->mIsSpeaking = isSpeaking;
  823. participant->mIsModeratorMuted = isModeratorMuted;
  824. // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
  825. if (isSpeaking)
  826. {
  827. participant->mSpeakingTimeout.reset();
  828. participant->mPower = energy;
  829. }
  830. else
  831. {
  832. participant->mPower = 0.0f;
  833. }
  834. participant->mVolume = volume;
  835. // *HACK: mantipov: added while working on EXT-3544
  836. /*
  837. Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE 
  838. LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.
  839. participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted
  840. Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.
  841. Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.
  842. But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()
  843. voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
  844. and event is not fired.
  845. So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it
  846. in LLCallFloater::draw()
  847. */
  848. LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
  849. // ignore session ID of local chat
  850. if (voice_cnl && voice_cnl->getSessionID().notNull())
  851. {
  852. LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID());
  853. if (speaker_manager)
  854. {
  855. speaker_manager->update(true);
  856. }
  857. }
  858. }
  859. else
  860. {
  861. LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
  862. }
  863. }
  864. else
  865. {
  866. LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
  867. }
  868. }
  869. void LLVoiceClient::buddyPresenceEvent(
  870. std::string &uriString, 
  871. std::string &alias, 
  872. std::string &statusString,
  873. std::string &applicationString)
  874. {
  875. buddyListEntry *buddy = findBuddy(uriString);
  876. if(buddy)
  877. {
  878. LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status "" << statusString << "", application "" << applicationString << """<< LL_ENDL;
  879. LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
  880. if(applicationString.empty())
  881. {
  882. // This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way.
  883. // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
  884. if ( stricmp("Unknown", statusString.c_str())== 0) 
  885. {
  886. // User went offline with a non-SLim-enabled viewer.
  887. buddy->mOnlineSL = false;
  888. }
  889. else if ( stricmp("Online", statusString.c_str())== 0) 
  890. {
  891. // User came online with a non-SLim-enabled viewer.
  892. buddy->mOnlineSL = true;
  893. }
  894. else
  895. {
  896. // If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
  897. // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string.
  898. buddy->mOnlineSLim = true;
  899. }
  900. else if(applicationString.find("SecondLifeViewer") != std::string::npos)
  901. {
  902. // This presence event is from a viewer that sets the application string
  903. if ( stricmp("Unknown", statusString.c_str())== 0) 
  904. {
  905. // Viewer says they're offline
  906. buddy->mOnlineSL = false;
  907. }
  908. else
  909. {
  910. // Viewer says they're online
  911. buddy->mOnlineSL = true;
  912. }
  913. }
  914. else
  915. {
  916. // This presence event is from something which is NOT the SL viewer (assume it's SLim).
  917. if ( stricmp("Unknown", statusString.c_str())== 0) 
  918. {
  919. // SLim says they're offline
  920. buddy->mOnlineSLim = false;
  921. }
  922. else
  923. {
  924. // SLim says they're online
  925. buddy->mOnlineSLim = true;
  926. }
  927. LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
  928. // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
  929. LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
  930. notifyFriendObservers();
  931. }
  932. else
  933. {
  934. LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
  935. }
  936. }
  937. void LLVoiceClient::messageEvent(
  938. std::string &sessionHandle, 
  939. std::string &uriString, 
  940. std::string &alias, 
  941. std::string &messageHeader, 
  942. std::string &messageBody,
  943. std::string &applicationString)
  944. {
  945. LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
  946. // LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: n" << messageBody << LL_ENDL;
  947. if(messageHeader.find("text/html") != std::string::npos)
  948. {
  949. std::string message;
  950. {
  951. const std::string startMarker = "<body";
  952. const std::string startMarker2 = ">";
  953. const std::string endMarker = "</body>";
  954. const std::string startSpan = "<span";
  955. const std::string endSpan = "</span>";
  956. std::string::size_type start;
  957. std::string::size_type end;
  958. // Default to displaying the raw string, so the message gets through.
  959. message = messageBody;
  960. // Find the actual message text within the XML fragment
  961. start = messageBody.find(startMarker);
  962. start = messageBody.find(startMarker2, start);
  963. end = messageBody.find(endMarker);
  964. if(start != std::string::npos)
  965. {
  966. start += startMarker2.size();
  967. if(end != std::string::npos)
  968. end -= start;
  969. message.assign(messageBody, start, end);
  970. }
  971. else 
  972. {
  973. // Didn't find a <body>, try looking for a <span> instead.
  974. start = messageBody.find(startSpan);
  975. start = messageBody.find(startMarker2, start);
  976. end = messageBody.find(endSpan);
  977. if(start != std::string::npos)
  978. {
  979. start += startMarker2.size();
  980. if(end != std::string::npos)
  981. end -= start;
  982. message.assign(messageBody, start, end);
  983. }
  984. }
  985. }
  986. // LL_DEBUGS("Voice") << "    raw message = n" << message << LL_ENDL;
  987. // strip formatting tags
  988. {
  989. std::string::size_type start;
  990. std::string::size_type end;
  991. while((start = message.find('<')) != std::string::npos)
  992. {
  993. if((end = message.find('>', start + 1)) != std::string::npos)
  994. {
  995. // Strip out the tag
  996. message.erase(start, (end + 1) - start);
  997. }
  998. else
  999. {
  1000. // Avoid an infinite loop
  1001. break;
  1002. }
  1003. }
  1004. }
  1005. // Decode ampersand-escaped chars
  1006. {
  1007. std::string::size_type mark = 0;
  1008. // The text may contain text encoded with &lt;, &gt;, and &amp;
  1009. mark = 0;
  1010. while((mark = message.find("&lt;", mark)) != std::string::npos)
  1011. {
  1012. message.replace(mark, 4, "<");
  1013. mark += 1;
  1014. }
  1015. mark = 0;
  1016. while((mark = message.find("&gt;", mark)) != std::string::npos)
  1017. {
  1018. message.replace(mark, 4, ">");
  1019. mark += 1;
  1020. }
  1021. mark = 0;
  1022. while((mark = message.find("&amp;", mark)) != std::string::npos)
  1023. {
  1024. message.replace(mark, 5, "&");
  1025. mark += 1;
  1026. }
  1027. }
  1028. // strip leading/trailing whitespace (since we always seem to get a couple newlines)
  1029. LLStringUtil::trim(message);
  1030. // LL_DEBUGS("Voice") << "    stripped message = n" << message << LL_ENDL;
  1031. sessionState *session = findSession(sessionHandle);
  1032. if(session)
  1033. {
  1034. bool is_busy = gAgent.getBusy();
  1035. bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
  1036. bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
  1037. bool quiet_chat = false;
  1038. LLChat chat;
  1039. chat.mMuted = is_muted && !is_linden;
  1040. if(!chat.mMuted)
  1041. {
  1042. chat.mFromID = session->mCallerID;
  1043. chat.mFromName = session->mName;
  1044. chat.mSourceType = CHAT_SOURCE_AGENT;
  1045. if(is_busy && !is_linden)
  1046. {
  1047. quiet_chat = true;
  1048. // TODO: Question: Return busy mode response here?  Or maybe when session is started instead?
  1049. }
  1050. LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
  1051. gIMMgr->addMessage(session->mIMSessionID,
  1052. session->mCallerID,
  1053. session->mName.c_str(),
  1054. message.c_str(),
  1055. LLStringUtil::null, // default arg
  1056. IM_NOTHING_SPECIAL, // default arg
  1057. 0, // default arg
  1058. LLUUID::null, // default arg
  1059. LLVector3::zero, // default arg
  1060. true); // prepend name and make it a link to the user's profile
  1061. }
  1062. }
  1063. }
  1064. }
  1065. void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
  1066. {
  1067. sessionState *session = findSession(sessionHandle);
  1068. if(session)
  1069. {
  1070. participantState *participant = session->findParticipant(uriString);
  1071. if(participant)
  1072. {
  1073. if (!stricmp(notificationType.c_str(), "Typing"))
  1074. {
  1075. // Other end started typing
  1076. // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
  1077. // It requires an LLIMInfo for the message, which we don't have here.
  1078. }
  1079. else if (!stricmp(notificationType.c_str(), "NotTyping"))
  1080. {
  1081. // Other end stopped typing
  1082. // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
  1083. // It requires an LLIMInfo for the message, which we don't have here.
  1084. }
  1085. else
  1086. {
  1087. LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
  1088. }
  1089. }
  1090. else
  1091. {
  1092. LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
  1093. }
  1094. }
  1095. else
  1096. {
  1097. LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
  1098. }
  1099. }
  1100. void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
  1101. {
  1102. buddyListEntry *buddy = findBuddy(buddyURI);
  1103. if(!buddy)
  1104. {
  1105. // Couldn't find buddy by URI, try converting the alias...
  1106. if(!alias.empty())
  1107. {
  1108. LLUUID id;
  1109. if(IDFromName(alias, id))
  1110. {
  1111. buddy = findBuddy(id);
  1112. }
  1113. }
  1114. }
  1115. if(buddy)
  1116. {
  1117. std::ostringstream stream;
  1118. if(buddy->mCanSeeMeOnline)
  1119. {
  1120. // Sending the response will create an auto-accept rule
  1121. buddy->mHasAutoAcceptListEntry = true;
  1122. }
  1123. else
  1124. {
  1125. // Sending the response will create a block rule
  1126. buddy->mHasBlockListEntry = true;
  1127. }
  1128. if(buddy->mInSLFriends)
  1129. {
  1130. buddy->mInVivoxBuddies = true;
  1131. }
  1132. stream
  1133. << "<Request requestId="" << mCommandCookie++ << "" action="Account.SendSubscriptionReply.1">"
  1134. << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
  1135. << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
  1136. << "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
  1137. << "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
  1138. << "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
  1139. << "</Request>"
  1140. << "nnn";
  1141. writeString(stream.str());
  1142. }
  1143. }
  1144. void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
  1145. {
  1146. LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL;
  1147. mTuningEnergy = energy;
  1148. }
  1149. void LLVoiceClient::buddyListChanged()
  1150. {
  1151. // This is called after we receive a BuddyAndGroupListChangedEvent.
  1152. mBuddyListMapPopulated = true;
  1153. mFriendsListDirty = true;
  1154. }
  1155. void LLVoiceClient::muteListChanged()
  1156. {
  1157. // The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
  1158. if(mAudioSession)
  1159. {
  1160. participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
  1161. for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
  1162. {
  1163. participantState *p = iter->second;
  1164. // Check to see if this participant is on the mute list already
  1165. if(p->updateMuteState())
  1166. mAudioSession->mVolumeDirty = true;
  1167. }
  1168. }
  1169. }
  1170. void LLVoiceClient::updateFriends(U32 mask)
  1171. {
  1172. if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
  1173. {
  1174. // Just resend the whole friend list to the daemon
  1175. mFriendsListDirty = true;
  1176. }
  1177. }
  1178. /////////////////////////////
  1179. // Managing list of participants
  1180. LLVoiceClient::participantState::participantState(const std::string &uri) : 
  1181.  mURI(uri), 
  1182.  mPTT(false), 
  1183.  mIsSpeaking(false), 
  1184.  mIsModeratorMuted(false), 
  1185.  mLastSpokeTimestamp(0.f), 
  1186.  mPower(0.f), 
  1187.  mVolume(-1), 
  1188.  mOnMuteList(false), 
  1189.  mUserVolume(-1), 
  1190.  mVolumeDirty(false), 
  1191.  mAvatarIDValid(false),
  1192.  mIsSelf(false)
  1193. {
  1194. }
  1195. LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri)
  1196. {
  1197. participantState *result = NULL;
  1198. bool useAlternateURI = false;
  1199. // Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it
  1200. // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
  1201. {
  1202. participantMap::iterator iter = mParticipantsByURI.find(&uri);
  1203. if(iter == mParticipantsByURI.end())
  1204. {
  1205. if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
  1206. {
  1207. // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
  1208. // Use mSIPURI instead, since it will be properly encoded.
  1209. iter = mParticipantsByURI.find(&(mSIPURI));
  1210. useAlternateURI = true;
  1211. }
  1212. }
  1213. if(iter != mParticipantsByURI.end())
  1214. {
  1215. result = iter->second;
  1216. }
  1217. }
  1218. if(!result)
  1219. {
  1220. // participant isn't already in one list or the other.
  1221. result = new participantState(useAlternateURI?mSIPURI:uri);
  1222. mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result));
  1223. mParticipantsChanged = true;
  1224. // Try to do a reverse transform on the URI to get the GUID back.
  1225. {
  1226. LLUUID id;
  1227. if(IDFromName(result->mURI, id))
  1228. {
  1229. result->mAvatarIDValid = true;
  1230. result->mAvatarID = id;
  1231. if(result->updateMuteState())
  1232. mVolumeDirty = true;
  1233. }
  1234. else
  1235. {
  1236. // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
  1237. // This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
  1238. setUUIDFromStringHash(result->mAvatarID, uri);
  1239. }
  1240. }
  1241. mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
  1242. result->mUserVolume = LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID);
  1243. if (result->mUserVolume != -1)
  1244. {
  1245. result->mVolumeDirty = true;
  1246. mVolumeDirty = true;
  1247. }
  1248. LL_DEBUGS("Voice") << "participant "" << result->mURI << "" added." << LL_ENDL;
  1249. }
  1250. return result;
  1251. }
  1252. bool LLVoiceClient::participantState::updateMuteState()
  1253. {
  1254. bool result = false;
  1255. if(mAvatarIDValid)
  1256. {
  1257. bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
  1258. if(mOnMuteList != isMuted)
  1259. {
  1260. mOnMuteList = isMuted;
  1261. mVolumeDirty = true;
  1262. result = true;
  1263. }
  1264. }
  1265. return result;
  1266. }
  1267. bool LLVoiceClient::participantState::isAvatar()
  1268. {
  1269. return mAvatarIDValid;
  1270. }
  1271. void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant)
  1272. {
  1273. if(participant)
  1274. {
  1275. participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI));
  1276. participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID));
  1277. LL_DEBUGS("Voice") << "participant "" << participant->mURI <<  "" (" << participant->mAvatarID << ") removed." << LL_ENDL;
  1278. if(iter == mParticipantsByURI.end())
  1279. {
  1280. LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
  1281. }
  1282. else if(iter2 == mParticipantsByUUID.end())
  1283. {
  1284. LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
  1285. }
  1286. else if(iter->second != iter2->second)
  1287. {
  1288. LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
  1289. }
  1290. else
  1291. {
  1292. mParticipantsByURI.erase(iter);
  1293. mParticipantsByUUID.erase(iter2);
  1294. delete participant;
  1295. mParticipantsChanged = true;
  1296. }
  1297. }
  1298. }
  1299. void LLVoiceClient::sessionState::removeAllParticipants()
  1300. {
  1301. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  1302. while(!mParticipantsByURI.empty())
  1303. {
  1304. removeParticipant(mParticipantsByURI.begin()->second);
  1305. }
  1306. if(!mParticipantsByUUID.empty())
  1307. {
  1308. LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL;
  1309. }
  1310. }
  1311. LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
  1312. {
  1313. participantMap *result = NULL;
  1314. if(mAudioSession)
  1315. {
  1316. result = &(mAudioSession->mParticipantsByURI);
  1317. }
  1318. return result;
  1319. }
  1320. void LLVoiceClient::getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids)
  1321. {
  1322. if (NULL == mAudioSession) return;
  1323. participantUUIDMap::const_iterator it = mAudioSession->mParticipantsByUUID.begin(),
  1324. it_end = mAudioSession->mParticipantsByUUID.end();
  1325. for (; it != it_end; ++it)
  1326. {
  1327. participant_uuids.insert((*(*it).first));
  1328. }
  1329. }
  1330. LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
  1331. {
  1332. participantState *result = NULL;
  1333. participantMap::iterator iter = mParticipantsByURI.find(&uri);
  1334. if(iter == mParticipantsByURI.end())
  1335. {
  1336. if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
  1337. {
  1338. // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
  1339. // Look up the other URI
  1340. iter = mParticipantsByURI.find(&(mSIPURI));
  1341. }
  1342. }
  1343. if(iter != mParticipantsByURI.end())
  1344. {
  1345. result = iter->second;
  1346. }
  1347. return result;
  1348. }
  1349. LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
  1350. {
  1351. participantState * result = NULL;
  1352. participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id);
  1353. if(iter != mParticipantsByUUID.end())
  1354. {
  1355. result = iter->second;
  1356. }
  1357. return result;
  1358. }
  1359. LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
  1360. {
  1361. participantState * result = NULL;
  1362. if(mAudioSession)
  1363. {
  1364. result = mAudioSession->findParticipantByID(id);
  1365. }
  1366. return result;
  1367. }
  1368. void LLVoiceClient::parcelChanged()
  1369. {
  1370. if(getState() >= stateNoChannel)
  1371. {
  1372. // If the user is logged in, start a channel lookup.
  1373. LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
  1374. std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
  1375. LLSD data;
  1376. LLHTTPClient::post(
  1377. url,
  1378. data,
  1379. new LLVoiceClientCapResponder);
  1380. }
  1381. else
  1382. {
  1383. // The transition to stateNoChannel needs to kick this off again.
  1384. LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
  1385. }
  1386. }
  1387. void LLVoiceClient::switchChannel(
  1388. std::string uri,
  1389. bool spatial,
  1390. bool no_reconnect,
  1391. bool is_p2p,
  1392. std::string hash)
  1393. {
  1394. bool needsSwitch = false;
  1395. LL_DEBUGS("Voice") 
  1396. << "called in state " << state2string(getState()) 
  1397. << " with uri "" << uri << """ 
  1398. << (spatial?", spatial is true":", spatial is false")
  1399. << LL_ENDL;
  1400. switch(getState())
  1401. {
  1402. case stateJoinSessionFailed:
  1403. case stateJoinSessionFailedWaiting:
  1404. case stateNoChannel:
  1405. // Always switch to the new URI from these states.
  1406. needsSwitch = true;
  1407. break;
  1408. default:
  1409. if(mSessionTerminateRequested)
  1410. {
  1411. // If a terminate has been requested, we need to compare against where the URI we're already headed to.
  1412. if(mNextAudioSession)
  1413. {
  1414. if(mNextAudioSession->mSIPURI != uri)
  1415. needsSwitch = true;
  1416. }
  1417. else
  1418. {
  1419. // mNextAudioSession is null -- this probably means we're on our way back to spatial.
  1420. if(!uri.empty())
  1421. {
  1422. // We do want to process a switch in this case.
  1423. needsSwitch = true;
  1424. }
  1425. }
  1426. }
  1427. else
  1428. {
  1429. // Otherwise, compare against the URI we're in now.
  1430. if(mAudioSession)
  1431. {
  1432. if(mAudioSession->mSIPURI != uri)
  1433. {
  1434. needsSwitch = true;
  1435. }
  1436. }
  1437. else
  1438. {
  1439. if(!uri.empty())
  1440. {
  1441. // mAudioSession is null -- it's not clear what case would cause this.
  1442. // For now, log it as a warning and see if it ever crops up.
  1443. LL_WARNS("Voice") << "No current audio session." << LL_ENDL;
  1444. }
  1445. }
  1446. }
  1447. break;
  1448. }
  1449. if(needsSwitch)
  1450. {
  1451. if(uri.empty())
  1452. {
  1453. // Leave any channel we may be in
  1454. LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
  1455. sessionState *oldSession = mNextAudioSession;
  1456. mNextAudioSession = NULL;
  1457. // The old session may now need to be deleted.
  1458. reapSession(oldSession);
  1459. notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
  1460. }
  1461. else
  1462. {
  1463. LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
  1464. mNextAudioSession = addSession(uri);
  1465. mNextAudioSession->mHash = hash;
  1466. mNextAudioSession->mIsSpatial = spatial;
  1467. mNextAudioSession->mReconnect = !no_reconnect;
  1468. mNextAudioSession->mIsP2P = is_p2p;
  1469. }
  1470. if(getState() <= stateNoChannel)
  1471. {
  1472. // We're already set up to join a channel, just needed to fill in the session URI
  1473. }
  1474. else
  1475. {
  1476. // State machine will come around and rejoin if uri/handle is not empty.
  1477. sessionTerminate();
  1478. }
  1479. }
  1480. }
  1481. void LLVoiceClient::joinSession(sessionState *session)
  1482. {
  1483. mNextAudioSession = session;
  1484. if(getState() <= stateNoChannel)
  1485. {
  1486. // We're already set up to join a channel, just needed to fill in the session handle
  1487. }
  1488. else
  1489. {
  1490. // State machine will come around and rejoin if uri/handle is not empty.
  1491. sessionTerminate();
  1492. }
  1493. }
  1494. void LLVoiceClient::setNonSpatialChannel(
  1495. const std::string &uri,
  1496. const std::string &credentials)
  1497. {
  1498. switchChannel(uri, false, false, false, credentials);
  1499. }
  1500. void LLVoiceClient::setSpatialChannel(
  1501. const std::string &uri,
  1502. const std::string &credentials)
  1503. {
  1504. mSpatialSessionURI = uri;
  1505. mSpatialSessionCredentials = credentials;
  1506. mAreaVoiceDisabled = mSpatialSessionURI.empty();
  1507. LL_DEBUGS("Voice") << "got spatial channel uri: "" << uri << """ << LL_ENDL;
  1508. if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
  1509. {
  1510. // User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
  1511. LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
  1512. }
  1513. else
  1514. {
  1515. switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
  1516. }
  1517. }
  1518. void LLVoiceClient::callUser(const LLUUID &uuid)
  1519. {
  1520. std::string userURI = sipURIFromID(uuid);
  1521. switchChannel(userURI, false, true, true);
  1522. }
  1523. LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid)
  1524. {
  1525. // Figure out if a session with the user already exists
  1526. sessionState *session = findSession(uuid);
  1527. if(!session)
  1528. {
  1529. // No session with user, need to start one.
  1530. std::string uri = sipURIFromID(uuid);
  1531. session = addSession(uri);
  1532. llassert(session);
  1533. if (!session) return NULL;
  1534. session->mIsSpatial = false;
  1535. session->mReconnect = false;
  1536. session->mIsP2P = true;
  1537. session->mCallerID = uuid;
  1538. }
  1539. if(session->mHandle.empty())
  1540. {
  1541. // Session isn't active -- start it up.
  1542. sessionCreateSendMessage(session, false, true);
  1543. }
  1544. else
  1545. {
  1546. // Session is already active -- start up text.
  1547. sessionTextConnectSendMessage(session);
  1548. }
  1549. return session;
  1550. }
  1551. bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
  1552. {
  1553. bool result = false;
  1554. // Attempt to locate the indicated session
  1555. sessionState *session = startUserIMSession(participant_id);
  1556. if(session)
  1557. {
  1558. // found the session, attempt to send the message
  1559. session->mTextMsgQueue.push(message);
  1560. // Try to send queued messages (will do nothing if the session is not open yet)
  1561. sendQueuedTextMessages(session);
  1562. // The message is queued, so we succeed.
  1563. result = true;
  1564. }
  1565. else
  1566. {
  1567. LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
  1568. }
  1569. return result;
  1570. }
  1571. void LLVoiceClient::sendQueuedTextMessages(sessionState *session)
  1572. {
  1573. if(session->mTextStreamState == 1)
  1574. {
  1575. if(!session->mTextMsgQueue.empty())
  1576. {
  1577. std::ostringstream stream;
  1578. while(!session->mTextMsgQueue.empty())
  1579. {
  1580. std::string message = session->mTextMsgQueue.front();
  1581. session->mTextMsgQueue.pop();
  1582. stream
  1583. << "<Request requestId="" << mCommandCookie++ << "" action="Session.SendMessage.1">"
  1584. << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
  1585. << "<MessageHeader>text/HTML</MessageHeader>"
  1586. << "<MessageBody>" << message << "</MessageBody>"
  1587. << "</Request>"
  1588. << "nnn";
  1589. }
  1590. writeString(stream.str());
  1591. }
  1592. }
  1593. else
  1594. {
  1595. // Session isn't connected yet, defer until later.
  1596. }
  1597. }
  1598. void LLVoiceClient::endUserIMSession(const LLUUID &uuid)
  1599. {
  1600. // Figure out if a session with the user exists
  1601. sessionState *session = findSession(uuid);
  1602. if(session)
  1603. {
  1604. // found the session
  1605. if(!session->mHandle.empty())
  1606. {
  1607. sessionTextDisconnectSendMessage(session);
  1608. }
  1609. }
  1610. else
  1611. {
  1612. LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
  1613. }
  1614. }
  1615. bool LLVoiceClient::answerInvite(std::string &sessionHandle)
  1616. {
  1617. // this is only ever used to answer incoming p2p call invites.
  1618. sessionState *session = findSession(sessionHandle);
  1619. if(session)
  1620. {
  1621. session->mIsSpatial = false;
  1622. session->mReconnect = false;
  1623. session->mIsP2P = true;
  1624. joinSession(session);
  1625. return true;
  1626. }
  1627. return false;
  1628. }
  1629. bool LLVoiceClient::isOnlineSIP(const LLUUID &id)
  1630. {
  1631. bool result = false;
  1632. buddyListEntry *buddy = findBuddy(id);
  1633. if(buddy)
  1634. {
  1635. result = buddy->mOnlineSLim;
  1636. LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
  1637. }
  1638. if(!result)
  1639. {
  1640. // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM.
  1641. sessionState *session = findSession(id);
  1642. if(session && !session->mHandle.empty())
  1643. {
  1644. if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
  1645. {
  1646. LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
  1647. // we have a p2p text session open with this user, so by definition they're online.
  1648. result = true;
  1649. }
  1650. }
  1651. }
  1652. return result;
  1653. }
  1654. // Returns true if the indicated participant in the current audio session is really an SL avatar.
  1655. // Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls.
  1656. bool LLVoiceClient::isParticipantAvatar(const LLUUID &id)
  1657. {
  1658. bool result = true; 
  1659. sessionState *session = findSession(id);
  1660. if(session != NULL)
  1661. {
  1662. // this is a p2p session with the indicated caller, or the session with the specified UUID.
  1663. if(session->mSynthesizedCallerID)
  1664. result = false;
  1665. }
  1666. else
  1667. {
  1668. // Didn't find a matching session -- check the current audio session for a matching participant
  1669. if(mAudioSession != NULL)
  1670. {
  1671. participantState *participant = findParticipantByID(id);
  1672. if(participant != NULL)
  1673. {
  1674. result = participant->isAvatar();
  1675. }
  1676. }
  1677. }
  1678. return result;
  1679. }
  1680. // Returns true if calling back the session URI after the session has closed is possible.
  1681. // Currently this will be false only for PSTN P2P calls.
  1682. bool LLVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
  1683. {
  1684. bool result = true; 
  1685. sessionState *session = findSession(session_id);
  1686. if(session != NULL)
  1687. {
  1688. result = session->isCallBackPossible();
  1689. }
  1690. return result;
  1691. }
  1692. // Returns true if the session can accepte text IM's.
  1693. // Currently this will be false only for PSTN P2P calls.
  1694. bool LLVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
  1695. {
  1696. bool result = true; 
  1697. sessionState *session = findSession(session_id);
  1698. if(session != NULL)
  1699. {
  1700. result = session->isTextIMPossible();
  1701. }
  1702. return result;
  1703. }
  1704. void LLVoiceClient::declineInvite(std::string &sessionHandle)
  1705. {
  1706. sessionState *session = findSession(sessionHandle);
  1707. if(session)
  1708. {
  1709. sessionMediaDisconnectSendMessage(session);
  1710. }
  1711. }
  1712. void LLVoiceClient::leaveNonSpatialChannel()
  1713. {
  1714. LL_DEBUGS("Voice") 
  1715. << "called in state " << state2string(getState()) 
  1716. << LL_ENDL;
  1717. // Make sure we don't rejoin the current session.
  1718. sessionState *oldNextSession = mNextAudioSession;
  1719. mNextAudioSession = NULL;
  1720. // Most likely this will still be the current session at this point, but check it anyway.
  1721. reapSession(oldNextSession);
  1722. verifySessionState();
  1723. sessionTerminate();
  1724. }
  1725. std::string LLVoiceClient::getCurrentChannel()
  1726. {
  1727. std::string result;
  1728. if((getState() == stateRunning) && !mSessionTerminateRequested)
  1729. {
  1730. result = getAudioSessionURI();
  1731. }
  1732. return result;
  1733. }
  1734. bool LLVoiceClient::inProximalChannel()
  1735. {
  1736. bool result = false;
  1737. if((getState() == stateRunning) && !mSessionTerminateRequested)
  1738. {
  1739. result = inSpatialChannel();
  1740. }
  1741. return result;
  1742. }
  1743. std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
  1744. {
  1745. std::string result;
  1746. result = "sip:";
  1747. result += nameFromID(id);
  1748. result += "@";
  1749. result += mVoiceSIPURIHostName;
  1750. return result;
  1751. }
  1752. std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
  1753. {
  1754. std::string result;
  1755. if(avatar)
  1756. {
  1757. result = "sip:";
  1758. result += nameFromID(avatar->getID());
  1759. result += "@";
  1760. result += mVoiceSIPURIHostName;
  1761. }
  1762. return result;
  1763. }
  1764. std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
  1765. {
  1766. std::string result;
  1767. if(avatar)
  1768. {
  1769. result = nameFromID(avatar->getID());
  1770. }
  1771. return result;
  1772. }
  1773. std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
  1774. {
  1775. std::string result;
  1776. if (uuid.isNull()) {
  1777. //VIVOX, the uuid emtpy look for the mURIString and return that instead.
  1778. //result.assign(uuid.mURIStringName);
  1779. LLStringUtil::replaceChar(result, '_', ' ');
  1780. return result;
  1781. }
  1782. // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
  1783. result = "x";
  1784. // Base64 encode and replace the pieces of base64 that are less compatible 
  1785. // with e-mail local-parts.
  1786. // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
  1787. result += LLBase64::encode(uuid.mData, UUID_BYTES);
  1788. LLStringUtil::replaceChar(result, '+', '-');
  1789. LLStringUtil::replaceChar(result, '/', '_');
  1790. // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
  1791. // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
  1792. // The reverse transform can be done with:
  1793. // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
  1794. return result;
  1795. }
  1796. bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
  1797. {
  1798. bool result = false;
  1799. // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
  1800. // If it is, convert to a bare name before doing the transform.
  1801. std::string name = nameFromsipURI(inName);
  1802. // Doesn't look like a SIP URI, assume it's an actual name.
  1803. if(name.empty())
  1804. name = inName;
  1805. // This will only work if the name is of the proper form.
  1806. // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
  1807. // "xFnPP04IpREWNkuw1cOXlhw=="
  1808. if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
  1809. {
  1810. // The name appears to have the right form.
  1811. // Reverse the transforms done by nameFromID
  1812. std::string temp = name;
  1813. LLStringUtil::replaceChar(temp, '-', '+');
  1814. LLStringUtil::replaceChar(temp, '_', '/');
  1815. U8 rawuuid[UUID_BYTES + 1]; 
  1816. int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
  1817. if(len == UUID_BYTES)
  1818. {
  1819. // The decode succeeded.  Stuff the bits into the result's UUID
  1820. memcpy(uuid.mData, rawuuid, UUID_BYTES);
  1821. result = true;
  1822. }
  1823. if(!result)
  1824. {
  1825. // VIVOX:  not a standard account name, just copy the URI name mURIString field
  1826. // and hope for the best.  bpj
  1827. uuid.setNull();  // VIVOX, set the uuid field to nulls
  1828. }
  1829. return result;
  1830. }
  1831. std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
  1832. {
  1833. return avatar->getFullname();
  1834. }
  1835. std::string LLVoiceClient::sipURIFromName(std::string &name)
  1836. {
  1837. std::string result;
  1838. result = "sip:";
  1839. result += name;
  1840. result += "@";
  1841. result += mVoiceSIPURIHostName;
  1842. // LLStringUtil::toLower(result);
  1843. return result;
  1844. }
  1845. std::string LLVoiceClient::nameFromsipURI(const std::string &uri)
  1846. {
  1847. std::string result;
  1848. std::string::size_type sipOffset, atOffset;
  1849. sipOffset = uri.find("sip:");
  1850. atOffset = uri.find("@");
  1851. if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
  1852. {
  1853. result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
  1854. }
  1855. return result;
  1856. }
  1857. bool LLVoiceClient::inSpatialChannel(void)
  1858. {
  1859. bool result = false;
  1860. if(mAudioSession)
  1861. result = mAudioSession->mIsSpatial;
  1862. return result;
  1863. }
  1864. std::string LLVoiceClient::getAudioSessionURI()
  1865. {
  1866. std::string result;
  1867. if(mAudioSession)
  1868. result = mAudioSession->mSIPURI;
  1869. return result;
  1870. }
  1871. std::string LLVoiceClient::getAudioSessionHandle()
  1872. {
  1873. std::string result;
  1874. if(mAudioSession)
  1875. result = mAudioSession->mHandle;
  1876. return result;
  1877. }
  1878. /////////////////////////////
  1879. // Sending updates of current state
  1880. void LLVoiceClient::enforceTether(void)
  1881. {
  1882. LLVector3d tethered = mCameraRequestedPosition;
  1883. // constrain 'tethered' to within 50m of mAvatarPosition.
  1884. {
  1885. F32 max_dist = 50.0f;
  1886. LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
  1887. F32 camera_distance = (F32)camera_offset.magVec();
  1888. if(camera_distance > max_dist)
  1889. {
  1890. tethered = mAvatarPosition + 
  1891. (max_dist / camera_distance) * camera_offset;
  1892. }
  1893. }
  1894. if(dist_vec(mCameraPosition, tethered) > 0.1)
  1895. {
  1896. mCameraPosition = tethered;
  1897. mSpatialCoordsDirty = true;
  1898. }
  1899. }
  1900. void LLVoiceClient::updatePosition(void)
  1901. {
  1902. if(gVoiceClient)
  1903. {
  1904. LLVOAvatar *agent = gAgent.getAvatarObject();
  1905. LLViewerRegion *region = gAgent.getRegion();
  1906. if(region && agent)
  1907. {
  1908. LLMatrix3 rot;
  1909. LLVector3d pos;
  1910. // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
  1911. // They're currently always set to zero.
  1912. // Send the current camera position to the voice code
  1913. rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());
  1914. pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin());
  1915. gVoiceClient->setCameraPosition(
  1916. pos, // position
  1917. LLVector3::zero,  // velocity
  1918. rot); // rotation matrix
  1919. // Send the current avatar position to the voice code
  1920. rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
  1921. pos = agent->getPositionGlobal();
  1922. // TODO: Can we get the head offset from outside the LLVOAvatar?
  1923. // pos += LLVector3d(mHeadOffset);
  1924. pos += LLVector3d(0.f, 0.f, 1.f);
  1925. gVoiceClient->setAvatarPosition(
  1926. pos, // position
  1927. LLVector3::zero,  // velocity
  1928. rot); // rotation matrix
  1929. }
  1930. }
  1931. }
  1932. void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
  1933. {
  1934. mCameraRequestedPosition = position;
  1935. if(mCameraVelocity != velocity)
  1936. {
  1937. mCameraVelocity = velocity;
  1938. mSpatialCoordsDirty = true;
  1939. }
  1940. if(mCameraRot != rot)
  1941. {
  1942. mCameraRot = rot;
  1943. mSpatialCoordsDirty = true;
  1944. }
  1945. }
  1946. void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
  1947. {
  1948. if(dist_vec(mAvatarPosition, position) > 0.1)
  1949. {
  1950. mAvatarPosition = position;
  1951. mSpatialCoordsDirty = true;
  1952. }
  1953. if(mAvatarVelocity != velocity)
  1954. {
  1955. mAvatarVelocity = velocity;
  1956. mSpatialCoordsDirty = true;
  1957. }
  1958. if(mAvatarRot != rot)
  1959. {
  1960. mAvatarRot = rot;
  1961. mSpatialCoordsDirty = true;
  1962. }
  1963. }
  1964. bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
  1965. {
  1966. bool result = false;
  1967. if(region)
  1968. {
  1969. name = region->getName();
  1970. }
  1971. if(!name.empty())
  1972. result = true;
  1973. return result;
  1974. }
  1975. void LLVoiceClient::leaveChannel(void)
  1976. {
  1977. if(getState() == stateRunning)
  1978. {
  1979. LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;
  1980. mChannelName.clear();
  1981. sessionTerminate();
  1982. }
  1983. }
  1984. void LLVoiceClient::setMuteMic(bool muted)
  1985. {
  1986. mMuteMic = muted;
  1987. }
  1988. bool LLVoiceClient::getMuteMic() const
  1989. {
  1990. return mMuteMic;
  1991. }
  1992. void LLVoiceClient::setUserPTTState(bool ptt)
  1993. {
  1994. mUserPTTState = ptt;
  1995. }
  1996. bool LLVoiceClient::getUserPTTState()
  1997. {
  1998. return mUserPTTState;
  1999. }
  2000. void LLVoiceClient::toggleUserPTTState(void)
  2001. {
  2002. mUserPTTState = !mUserPTTState;
  2003. }
  2004. void LLVoiceClient::setVoiceEnabled(bool enabled)
  2005. {
  2006. if (enabled != mVoiceEnabled)
  2007. {
  2008. mVoiceEnabled = enabled;
  2009. LLVoiceClientStatusObserver::EStatusType status;
  2010. if (enabled)
  2011. {
  2012. LLVoiceChannel::getCurrentVoiceChannel()->activate();
  2013. status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
  2014. }
  2015. else
  2016. {
  2017. // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it.
  2018. LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
  2019. status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
  2020. }
  2021. notifyStatusObservers(status);
  2022. }
  2023. }
  2024. bool LLVoiceClient::voiceEnabled()
  2025. {
  2026. return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
  2027. }
  2028. //AD *TODO: investigate possible merge of voiceWorking() and voiceEnabled() into one non-static method
  2029. bool LLVoiceClient::voiceWorking()
  2030. {
  2031. //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758)
  2032. return (stateLoggedIn <= mState) && (mState <= stateSessionTerminated);
  2033. }
  2034. void LLVoiceClient::setLipSyncEnabled(BOOL enabled)
  2035. {
  2036. mLipSyncEnabled = enabled;
  2037. }
  2038. BOOL LLVoiceClient::lipSyncEnabled()
  2039. {
  2040.    
  2041. if ( mVoiceEnabled && stateDisabled != getState() )
  2042. {
  2043. return mLipSyncEnabled;
  2044. }
  2045. else
  2046. {
  2047. return FALSE;
  2048. }
  2049. }
  2050. void LLVoiceClient::setUsePTT(bool usePTT)
  2051. {
  2052. if(usePTT && !mUsePTT)
  2053. {
  2054. // When the user turns on PTT, reset the current state.
  2055. mUserPTTState = false;
  2056. }
  2057. mUsePTT = usePTT;
  2058. }
  2059. void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
  2060. {
  2061. if(!PTTIsToggle && mPTTIsToggle)
  2062. {
  2063. // When the user turns off toggle, reset the current state.
  2064. mUserPTTState = false;
  2065. }
  2066. mPTTIsToggle = PTTIsToggle;
  2067. }
  2068. bool LLVoiceClient::getPTTIsToggle()
  2069. {
  2070. return mPTTIsToggle;
  2071. }
  2072. void LLVoiceClient::setPTTKey(std::string &key)
  2073. {
  2074. if(key == "MiddleMouse")
  2075. {
  2076. mPTTIsMiddleMouse = true;
  2077. }
  2078. else
  2079. {
  2080. mPTTIsMiddleMouse = false;
  2081. if(!LLKeyboard::keyFromString(key, &mPTTKey))
  2082. {
  2083. // If the call failed, don't match any key.
  2084. key = KEY_NONE;
  2085. }
  2086. }
  2087. }
  2088. void LLVoiceClient::setEarLocation(S32 loc)
  2089. {
  2090. if(mEarLocation != loc)
  2091. {
  2092. LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
  2093. mEarLocation = loc;
  2094. mSpatialCoordsDirty = true;
  2095. }
  2096. }
  2097. void LLVoiceClient::setVoiceVolume(F32 volume)
  2098. {
  2099. int scaled_volume = scale_speaker_volume(volume);
  2100. if(scaled_volume != mSpeakerVolume)
  2101. {
  2102. int min_volume = scale_speaker_volume(0);
  2103. if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume))
  2104. {
  2105. mSpeakerMuteDirty = true;
  2106. }
  2107. mSpeakerVolume = scaled_volume;
  2108. mSpeakerVolumeDirty = true;
  2109. }
  2110. }
  2111. void LLVoiceClient::setMicGain(F32 volume)
  2112. {
  2113. int scaled_volume = scale_mic_volume(volume);
  2114. if(scaled_volume != mMicVolume)
  2115. {
  2116. mMicVolume = scaled_volume;
  2117. mMicVolumeDirty = true;
  2118. }
  2119. }
  2120. void LLVoiceClient::keyDown(KEY key, MASK mask)
  2121. {
  2122. if (gKeyboard->getKeyRepeated(key))
  2123. {
  2124. // ignore auto-repeat keys
  2125. return;
  2126. }
  2127. if(!mPTTIsMiddleMouse)
  2128. {
  2129. bool down = (mPTTKey != KEY_NONE)
  2130. && gKeyboard->getKeyDown(mPTTKey);
  2131. inputUserControlState(down);
  2132. }
  2133. }
  2134. void LLVoiceClient::keyUp(KEY key, MASK mask)
  2135. {
  2136. if(!mPTTIsMiddleMouse)
  2137. {
  2138. bool down = (mPTTKey != KEY_NONE)
  2139. && gKeyboard->getKeyDown(mPTTKey);
  2140. inputUserControlState(down);
  2141. }
  2142. }
  2143. void LLVoiceClient::inputUserControlState(bool down)
  2144. {
  2145. if(mPTTIsToggle)
  2146. {
  2147. if(down) // toggle open-mic state on 'down'
  2148. {
  2149. toggleUserPTTState();
  2150. }
  2151. }
  2152. else // set open-mic state as an absolute
  2153. {
  2154. setUserPTTState(down);
  2155. }
  2156. }
  2157. void LLVoiceClient::middleMouseState(bool down)
  2158. {
  2159. if(mPTTIsMiddleMouse)
  2160. {
  2161. inputUserControlState(down);
  2162. }
  2163. }
  2164. /////////////////////////////
  2165. // Accessors for data related to nearby speakers
  2166. BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
  2167. {
  2168. BOOL result = FALSE;
  2169. participantState *participant = findParticipantByID(id);
  2170. if(participant)
  2171. {
  2172. // I'm not sure what the semantics of this should be.
  2173. // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
  2174. result = TRUE;
  2175. }
  2176. return result;
  2177. }
  2178. BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
  2179. {
  2180. BOOL result = FALSE;
  2181. participantState *participant = findParticipantByID(id);
  2182. if(participant)
  2183. {
  2184. if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
  2185. {
  2186. participant->mIsSpeaking = FALSE;
  2187. }
  2188. result = participant->mIsSpeaking;
  2189. }
  2190. return result;
  2191. }
  2192. BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
  2193. {
  2194. BOOL result = FALSE;
  2195. participantState *participant = findParticipantByID(id);
  2196. if(participant)
  2197. {
  2198. result = participant->mIsModeratorMuted;
  2199. }
  2200. return result;
  2201. }
  2202. F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
  2203. {
  2204. F32 result = 0;
  2205. participantState *participant = findParticipantByID(id);
  2206. if(participant)
  2207. {
  2208. result = participant->mPower;
  2209. }
  2210. return result;
  2211. }
  2212. std::string LLVoiceClient::getDisplayName(const LLUUID& id)
  2213. {
  2214. std::string result;
  2215. participantState *participant = findParticipantByID(id);
  2216. if(participant)
  2217. {
  2218. result = participant->mDisplayName;
  2219. }
  2220. return result;
  2221. }
  2222. BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
  2223. {
  2224. BOOL result = FALSE;
  2225. participantState *participant = findParticipantByID(id);
  2226. if(participant)
  2227. {
  2228. // I'm not sure what the semantics of this should be.
  2229. // Does "using PTT" mean they're configured with a push-to-talk button?
  2230. // For now, we know there's no PTT mechanism in place, so nobody is using it.
  2231. }
  2232. return result;
  2233. }
  2234. BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
  2235. {
  2236. BOOL result = FALSE;
  2237. participantState *participant = findParticipantByID(id);
  2238. if(participant)
  2239. {
  2240. result = participant->mOnMuteList;
  2241. }
  2242. return result;
  2243. }
  2244. // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
  2245. // internal = 400 * external^2
  2246. F32 LLVoiceClient::getUserVolume(const LLUUID& id)
  2247. {
  2248. F32 result = 0.0f;
  2249. participantState *participant = findParticipantByID(id);
  2250. if(participant)
  2251. {
  2252. S32 ires = 100; // nominal default volume
  2253. if(participant->mIsSelf)
  2254. {
  2255. // Always make it look like the user's own volume is set at the default.
  2256. }
  2257. else if(participant->mUserVolume != -1)
  2258. {
  2259. // Use the internal volume
  2260. ires = participant->mUserVolume;
  2261. // Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
  2262. // LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL;
  2263. }
  2264. else if(participant->mVolume != -1)
  2265. {
  2266. // Map backwards from vivox volume 
  2267. // Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
  2268. // LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL;
  2269. if(participant->mVolume < 56)
  2270. {
  2271. ires = (participant->mVolume * 100) / 56;
  2272. }
  2273. else
  2274. {
  2275. ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100;
  2276. }
  2277. }
  2278. result = sqrtf(((F32)ires) / 400.f);
  2279. }
  2280. // Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
  2281. // LL_DEBUGS("Voice") << "returning " << result << LL_ENDL;
  2282. return result;
  2283. }
  2284. void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
  2285. {
  2286. if(mAudioSession)
  2287. {
  2288. participantState *participant = findParticipantByID(id);
  2289. if (participant)
  2290. {
  2291. // store this volume setting for future sessions
  2292. LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume);
  2293. // volume can amplify by as much as 4x!
  2294. S32 ivol = (S32)(400.f * volume * volume);
  2295. participant->mUserVolume = llclamp(ivol, 0, 400);
  2296. participant->mVolumeDirty = TRUE;
  2297. mAudioSession->mVolumeDirty = TRUE;
  2298. }
  2299. }
  2300. }
  2301. std::string LLVoiceClient::getGroupID(const LLUUID& id)
  2302. {
  2303. std::string result;
  2304. participantState *participant = findParticipantByID(id);
  2305. if(participant)
  2306. {
  2307. result = participant->mGroupID;
  2308. }
  2309. return result;
  2310. }
  2311. BOOL LLVoiceClient::getAreaVoiceDisabled()
  2312. {
  2313. return mAreaVoiceDisabled;
  2314. }
  2315. void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
  2316. {
  2317. // LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
  2318. if(!mMainSessionGroupHandle.empty())
  2319. {
  2320. std::ostringstream stream;
  2321. stream
  2322. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.ControlRecording.1">"
  2323. << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
  2324. << "<RecordingControlType>Start</RecordingControlType>" 
  2325. << "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
  2326. << "<Filename>" << "" << "</Filename>"
  2327. << "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
  2328. << "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
  2329. << "</Request>nnn";
  2330. writeString(stream.str());
  2331. }
  2332. }
  2333. void LLVoiceClient::recordingLoopSave(const std::string& filename)
  2334. {
  2335. // LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
  2336. if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
  2337. {
  2338. std::ostringstream stream;
  2339. stream
  2340. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.ControlRecording.1">"
  2341. << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
  2342. << "<RecordingControlType>Flush</RecordingControlType>" 
  2343. << "<Filename>" << filename << "</Filename>"
  2344. << "</Request>nnn";
  2345. writeString(stream.str());
  2346. }
  2347. }
  2348. void LLVoiceClient::recordingStop()
  2349. {
  2350. // LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
  2351. if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
  2352. {
  2353. std::ostringstream stream;
  2354. stream
  2355. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.ControlRecording.1">"
  2356. << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
  2357. << "<RecordingControlType>Stop</RecordingControlType>" 
  2358. << "</Request>nnn";
  2359. writeString(stream.str());
  2360. }
  2361. }
  2362. void LLVoiceClient::filePlaybackStart(const std::string& filename)
  2363. {
  2364. // LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
  2365. if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
  2366. {
  2367. std::ostringstream stream;
  2368. stream
  2369. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.ControlPlayback.1">"
  2370. << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
  2371. << "<RecordingControlType>Start</RecordingControlType>" 
  2372. << "<Filename>" << filename << "</Filename>"
  2373. << "</Request>nnn";
  2374. writeString(stream.str());
  2375. }
  2376. }
  2377. void LLVoiceClient::filePlaybackStop()
  2378. {
  2379. // LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
  2380. if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
  2381. {
  2382. std::ostringstream stream;
  2383. stream
  2384. << "<Request requestId="" << mCommandCookie++ << "" action="SessionGroup.ControlPlayback.1">"
  2385. << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
  2386. << "<RecordingControlType>Stop</RecordingControlType>" 
  2387. << "</Request>nnn";
  2388. writeString(stream.str());
  2389. }
  2390. }
  2391. void LLVoiceClient::filePlaybackSetPaused(bool paused)
  2392. {
  2393. // TODO: Implement once Vivox gives me a sample
  2394. }
  2395. void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
  2396. {
  2397. // TODO: Implement once Vivox gives me a sample
  2398. }
  2399. LLVoiceClient::sessionState::sessionState() :
  2400. mErrorStatusCode(0),
  2401. mMediaStreamState(streamStateUnknown),
  2402. mTextStreamState(streamStateUnknown),
  2403. mCreateInProgress(false),
  2404. mMediaConnectInProgress(false),
  2405. mVoiceInvitePending(false),
  2406. mTextInvitePending(false),
  2407. mSynthesizedCallerID(false),
  2408. mIsChannel(false),
  2409. mIsSpatial(false),
  2410. mIsP2P(false),
  2411. mIncoming(false),
  2412. mVoiceEnabled(false),
  2413. mReconnect(false),
  2414. mVolumeDirty(false),
  2415. mParticipantsChanged(false)
  2416. {
  2417. }
  2418. LLVoiceClient::sessionState::~sessionState()
  2419. {
  2420. removeAllParticipants();
  2421. }
  2422. bool LLVoiceClient::sessionState::isCallBackPossible()
  2423. {
  2424. // This may change to be explicitly specified by vivox in the future...
  2425. // Currently, only PSTN P2P calls cannot be returned.
  2426. // Conveniently, this is also the only case where we synthesize a caller UUID.
  2427. return !mSynthesizedCallerID;
  2428. }
  2429. bool LLVoiceClient::sessionState::isTextIMPossible()
  2430. {
  2431. // This may change to be explicitly specified by vivox in the future...
  2432. return !mSynthesizedCallerID;
  2433. }
  2434. LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void)
  2435. {
  2436. return mSessions.begin();
  2437. }
  2438. LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void)
  2439. {
  2440. return mSessions.end();
  2441. }
  2442. LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle)
  2443. {
  2444. sessionState *result = NULL;
  2445. sessionMap::iterator iter = mSessionsByHandle.find(&handle);
  2446. if(iter != mSessionsByHandle.end())
  2447. {
  2448. result = iter->second;
  2449. }
  2450. return result;
  2451. }
  2452. LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
  2453. {
  2454. sessionState *result = NULL;
  2455. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
  2456. {
  2457. sessionState *session = *iter;
  2458. if(session->mCreateInProgress && (session->mSIPURI == uri))
  2459. {
  2460. result = session;
  2461. break;
  2462. }
  2463. }
  2464. return result;
  2465. }
  2466. LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id)
  2467. {
  2468. sessionState *result = NULL;
  2469. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
  2470. {
  2471. sessionState *session = *iter;
  2472. if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id))
  2473. {
  2474. result = session;
  2475. break;
  2476. }
  2477. }
  2478. return result;
  2479. }
  2480. LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle)
  2481. {
  2482. sessionState *result = NULL;
  2483. if(handle.empty())
  2484. {
  2485. // No handle supplied.
  2486. // Check whether there's already a session with this URI
  2487. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
  2488. {
  2489. sessionState *s = *iter;
  2490. if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
  2491. {
  2492. // TODO: I need to think about this logic... it's possible that this case should raise an internal error.
  2493. result = s;
  2494. break;
  2495. }
  2496. }
  2497. }
  2498. else // (!handle.empty())
  2499. {
  2500. // Check for an existing session with this handle
  2501. sessionMap::iterator iter = mSessionsByHandle.find(&handle);
  2502. if(iter != mSessionsByHandle.end())
  2503. {
  2504. result = iter->second;
  2505. }
  2506. }
  2507. if(!result)
  2508. {
  2509. // No existing session found.
  2510. LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
  2511. result = new sessionState();
  2512. result->mSIPURI = uri;
  2513. result->mHandle = handle;
  2514. mSessions.insert(result);
  2515. if(!result->mHandle.empty())
  2516. {
  2517. mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result));
  2518. }
  2519. }
  2520. else
  2521. {
  2522. // Found an existing session
  2523. if(uri != result->mSIPURI)
  2524. {
  2525. // TODO: Should this be an internal error?
  2526. LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
  2527. setSessionURI(result, uri);
  2528. }
  2529. if(handle != result->mHandle)
  2530. {
  2531. if(handle.empty())
  2532. {
  2533. // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break.
  2534. LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL;
  2535. }
  2536. else
  2537. {
  2538. // TODO: Should this be an internal error?
  2539. LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
  2540. setSessionHandle(result, handle);
  2541. }
  2542. }
  2543. LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
  2544. }
  2545. verifySessionState();
  2546. return result;
  2547. }
  2548. void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
  2549. {
  2550. // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
  2551. if(!session->mHandle.empty())
  2552. {
  2553. // Remove session from the map if it should have been there.
  2554. sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
  2555. if(iter != mSessionsByHandle.end())
  2556. {
  2557. if(iter->second != session)
  2558. {
  2559. LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
  2560. }
  2561. mSessionsByHandle.erase(iter);
  2562. }
  2563. else
  2564. {
  2565. LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
  2566. }
  2567. }
  2568. session->mHandle = handle;
  2569. if(!handle.empty())
  2570. {
  2571. mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session));
  2572. }
  2573. verifySessionState();
  2574. }
  2575. void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
  2576. {
  2577. // There used to be a map of session URIs to sessions, which made this complex....
  2578. session->mSIPURI = uri;
  2579. verifySessionState();
  2580. }
  2581. void LLVoiceClient::deleteSession(sessionState *session)
  2582. {
  2583. // Remove the session from the handle map
  2584. if(!session->mHandle.empty())
  2585. {
  2586. sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
  2587. if(iter != mSessionsByHandle.end())
  2588. {
  2589. if(iter->second != session)
  2590. {
  2591. LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL;
  2592. }
  2593. mSessionsByHandle.erase(iter);
  2594. }
  2595. }
  2596. // Remove the session from the URI map
  2597. mSessions.erase(session);
  2598. // At this point, the session should be unhooked from all lists and all state should be consistent.
  2599. verifySessionState();
  2600. // If this is the current audio session, clean up the pointer which will soon be dangling.
  2601. if(mAudioSession == session)
  2602. {
  2603. mAudioSession = NULL;
  2604. mAudioSessionChanged = true;
  2605. }
  2606. // ditto for the next audio session
  2607. if(mNextAudioSession == session)
  2608. {
  2609. mNextAudioSession = NULL;
  2610. }
  2611. // delete the session
  2612. delete session;
  2613. }
  2614. void LLVoiceClient::deleteAllSessions()
  2615. {
  2616. LL_DEBUGS("Voice") << "called" << LL_ENDL;
  2617. while(!mSessions.empty())
  2618. {
  2619. deleteSession(*(sessionsBegin()));
  2620. }
  2621. if(!mSessionsByHandle.empty())
  2622. {
  2623. LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL;
  2624. }
  2625. }
  2626. void LLVoiceClient::verifySessionState(void)
  2627. {
  2628. // This is mostly intended for debugging problems with session state management.
  2629. LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
  2630. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
  2631. {
  2632. sessionState *session = *iter;
  2633. LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
  2634. if(!session->mHandle.empty())
  2635. {
  2636. // every session with a non-empty handle needs to be in the handle map
  2637. sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle));
  2638. if(i2 == mSessionsByHandle.end())
  2639. {
  2640. LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
  2641. }
  2642. else
  2643. {
  2644. if(i2->second != session)
  2645. {
  2646. LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
  2647. }
  2648. }
  2649. }
  2650. }
  2651. // check that every entry in the handle map points to a valid session in the session set
  2652. for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
  2653. {
  2654. sessionState *session = iter->second;
  2655. sessionIterator i2 = mSessions.find(session);
  2656. if(i2 == mSessions.end())
  2657. {
  2658. LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
  2659. }
  2660. else
  2661. {
  2662. if(session->mHandle != (*i2)->mHandle)
  2663. {
  2664. LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
  2665. }
  2666. }
  2667. }
  2668. }
  2669. LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
  2670. mURI(uri)
  2671. {
  2672. mOnlineSL = false;
  2673. mOnlineSLim = false;
  2674. mCanSeeMeOnline = true;
  2675. mHasBlockListEntry = false;
  2676. mHasAutoAcceptListEntry = false;
  2677. mNameResolved = false;
  2678. mInVivoxBuddies = false;
  2679. mInSLFriends = false;
  2680. mNeedsNameUpdate = false;
  2681. }
  2682. void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
  2683. {
  2684. buddyListEntry *buddy = addBuddy(uri, displayName);
  2685. buddy->mInVivoxBuddies = true;
  2686. }
  2687. LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri)
  2688. {
  2689. std::string empty;
  2690. buddyListEntry *buddy = addBuddy(uri, empty);
  2691. if(buddy->mDisplayName.empty())
  2692. {
  2693. buddy->mNameResolved = false;
  2694. }
  2695. return buddy;
  2696. }
  2697. LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
  2698. {
  2699. buddyListEntry *result = NULL;
  2700. buddyListMap::iterator iter = mBuddyListMap.find(&uri);
  2701. if(iter != mBuddyListMap.end())
  2702. {
  2703. // Found a matching buddy already in the map.
  2704. LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
  2705. result = iter->second;
  2706. }
  2707. if(!result)
  2708. {
  2709. // participant isn't already in one list or the other.
  2710. LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
  2711. result = new buddyListEntry(uri);
  2712. result->mDisplayName = displayName;
  2713. if(IDFromName(uri, result->mUUID)) 
  2714. {
  2715. // Extracted UUID from name successfully.
  2716. }
  2717. else
  2718. {
  2719. LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " ("" << displayName << "")" << LL_ENDL;
  2720. }
  2721. mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result));
  2722. }
  2723. return result;
  2724. }
  2725. LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri)
  2726. {
  2727. buddyListEntry *result = NULL;
  2728. buddyListMap::iterator iter = mBuddyListMap.find(&uri);
  2729. if(iter != mBuddyListMap.end())
  2730. {
  2731. result = iter->second;
  2732. }
  2733. return result;
  2734. }
  2735. LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id)
  2736. {
  2737. buddyListEntry *result = NULL;
  2738. buddyListMap::iterator iter;
  2739. for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
  2740. {
  2741. if(iter->second->mUUID == id)
  2742. {
  2743. result = iter->second;
  2744. break;
  2745. }
  2746. }
  2747. return result;
  2748. }
  2749. LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name)
  2750. {
  2751. buddyListEntry *result = NULL;
  2752. buddyListMap::iterator iter;
  2753. for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
  2754. {
  2755. if(iter->second->mDisplayName == name)
  2756. {
  2757. result = iter->second;
  2758. break;
  2759. }
  2760. }
  2761. return result;
  2762. }
  2763. void LLVoiceClient::deleteBuddy(const std::string &uri)
  2764. {
  2765. buddyListMap::iterator iter = mBuddyListMap.find(&uri);
  2766. if(iter != mBuddyListMap.end())
  2767. {
  2768. LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
  2769. buddyListEntry *buddy = iter->second;
  2770. mBuddyListMap.erase(iter);
  2771. delete buddy;
  2772. }
  2773. else
  2774. {
  2775. LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
  2776. }
  2777. }
  2778. void LLVoiceClient::deleteAllBuddies(void)
  2779. {
  2780. while(!mBuddyListMap.empty())
  2781. {
  2782. deleteBuddy(*(mBuddyListMap.begin()->first));
  2783. }
  2784. // Don't want to correlate with friends list when we've emptied the buddy list.
  2785. mBuddyListMapPopulated = false;
  2786. // Don't want to correlate with friends list when we've reset the block rules.
  2787. mBlockRulesListReceived = false;
  2788. mAutoAcceptRulesListReceived = false;
  2789. }
  2790. void LLVoiceClient::deleteAllBlockRules(void)
  2791. {
  2792. // Clear the block list entry flags from all local buddy list entries
  2793. buddyListMap::iterator buddy_it;
  2794. for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
  2795. {
  2796. buddy_it->second->mHasBlockListEntry = false;
  2797. }
  2798. }
  2799. void LLVoiceClient::deleteAllAutoAcceptRules(void)
  2800. {
  2801. // Clear the auto-accept list entry flags from all local buddy list entries
  2802. buddyListMap::iterator buddy_it;
  2803. for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
  2804. {
  2805. buddy_it->second->mHasAutoAcceptListEntry = false;
  2806. }
  2807. }
  2808. void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
  2809. {
  2810. buddyListEntry *buddy = NULL;
  2811. // blockMask is the SIP URI of a friends list entry
  2812. buddyListMap::iterator iter = mBuddyListMap.find(&blockMask);
  2813. if(iter != mBuddyListMap.end())
  2814. {
  2815. LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
  2816. buddy = iter->second;
  2817. }
  2818. if(buddy == NULL)
  2819. {
  2820. LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
  2821. buddy = addBuddy(blockMask);
  2822. }
  2823. if(buddy != NULL)
  2824. {
  2825. buddy->mHasBlockListEntry = true;
  2826. }
  2827. }
  2828. void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
  2829. {
  2830. buddyListEntry *buddy = NULL;
  2831. // blockMask is the SIP URI of a friends list entry
  2832. buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask);
  2833. if(iter != mBuddyListMap.end())
  2834. {
  2835. LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
  2836. buddy = iter->second;
  2837. }
  2838. if(buddy == NULL)
  2839. {
  2840. LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
  2841. buddy = addBuddy(autoAcceptMask);
  2842. }
  2843. if(buddy != NULL)
  2844. {
  2845. buddy->mHasAutoAcceptListEntry = true;
  2846. }
  2847. }
  2848. void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
  2849. {
  2850. // Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
  2851. mBlockRulesListReceived = true;
  2852. }
  2853. void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
  2854. {
  2855. // Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
  2856. mAutoAcceptRulesListReceived = true;
  2857. }
  2858. void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
  2859. {
  2860. mParticipantObservers.insert(observer);
  2861. }
  2862. void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
  2863. {
  2864. mParticipantObservers.erase(observer);
  2865. }
  2866. void LLVoiceClient::notifyParticipantObservers()
  2867. {
  2868. for (observer_set_t::iterator it = mParticipantObservers.begin();
  2869. it != mParticipantObservers.end();
  2870. )
  2871. {
  2872. LLVoiceClientParticipantObserver* observer = *it;
  2873. observer->onChange();
  2874. // In case onChange() deleted an entry.
  2875. it = mParticipantObservers.upper_bound(observer);
  2876. }
  2877. }
  2878. void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
  2879. {
  2880. mStatusObservers.insert(observer);
  2881. }
  2882. void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
  2883. {
  2884. mStatusObservers.erase(observer);
  2885. }
  2886. void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
  2887. {
  2888. if(mAudioSession)
  2889. {
  2890. if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
  2891. {
  2892. switch(mAudioSession->mErrorStatusCode)
  2893. {
  2894. case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;  break;
  2895. case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;  break;
  2896. case 20715:
  2897. //invalid channel, we may be using a set of poorly cached
  2898. //info
  2899. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  2900. break;
  2901. case 1009:
  2902. //invalid username and password
  2903. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  2904. break;
  2905. }
  2906. // Reset the error code to make sure it won't be reused later by accident.
  2907. mAudioSession->mErrorStatusCode = 0;
  2908. }
  2909. else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
  2910. {
  2911. switch(mAudioSession->mErrorStatusCode)
  2912. {
  2913. case 404: // NOT_FOUND
  2914. case 480: // TEMPORARILY_UNAVAILABLE
  2915. case 408: // REQUEST_TIMEOUT
  2916. // call failed because other user was not available
  2917. // treat this as an error case
  2918. status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
  2919. // Reset the error code to make sure it won't be reused later by accident.
  2920. mAudioSession->mErrorStatusCode = 0;
  2921. break;
  2922. }
  2923. }
  2924. }
  2925. LL_DEBUGS("Voice") 
  2926. << " " << LLVoiceClientStatusObserver::status2string(status)  
  2927. << ", session URI " << getAudioSessionURI() 
  2928. << (inSpatialChannel()?", proximal is true":", proximal is false")
  2929. << LL_ENDL;
  2930. for (status_observer_set_t::iterator it = mStatusObservers.begin();
  2931. it != mStatusObservers.end();
  2932. )
  2933. {
  2934. LLVoiceClientStatusObserver* observer = *it;
  2935. observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
  2936. // In case onError() deleted an entry.
  2937. it = mStatusObservers.upper_bound(observer);
  2938. }
  2939. }
  2940. void LLVoiceClient::addObserver(LLFriendObserver* observer)
  2941. {
  2942. mFriendObservers.insert(observer);
  2943. }
  2944. void LLVoiceClient::removeObserver(LLFriendObserver* observer)
  2945. {
  2946. mFriendObservers.erase(observer);
  2947. }
  2948. void LLVoiceClient::notifyFriendObservers()
  2949. {
  2950. for (friend_observer_set_t::iterator it = mFriendObservers.begin();
  2951. it != mFriendObservers.end();
  2952. )
  2953. {
  2954. LLFriendObserver* observer = *it;
  2955. it++;
  2956. // The only friend-related thing we notify on is online/offline transitions.
  2957. observer->changed(LLFriendObserver::ONLINE);
  2958. }
  2959. }
  2960. void LLVoiceClient::lookupName(const LLUUID &id)
  2961. {
  2962. BOOL is_group = FALSE;
  2963. gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup);
  2964. }
  2965. //static
  2966. void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
  2967. {
  2968. if(gVoiceClient)
  2969. {
  2970. std::string name = llformat("%s %s", first.c_str(), last.c_str());
  2971. gVoiceClient->avatarNameResolved(id, name);
  2972. }
  2973. }
  2974. void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
  2975. {
  2976. // If the avatar whose name just resolved is on our friends list, resync the friends list.
  2977. if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
  2978. {
  2979. mFriendsListDirty = true;
  2980. }
  2981. // Iterate over all sessions.
  2982. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
  2983. {
  2984. sessionState *session = *iter;
  2985. // Check for this user as a participant in this session
  2986. participantState *participant = session->findParticipantByID(id);
  2987. if(participant)
  2988. {
  2989. // Found -- fill in the name
  2990. participant->mAccountName = name;
  2991. // and post a "participants updated" message to listeners later.
  2992. session->mParticipantsChanged = true;
  2993. }
  2994. // Check whether this is a p2p session whose caller name just resolved
  2995. if(session->mCallerID == id)
  2996. {
  2997. // this session's "caller ID" just resolved.  Fill in the name.
  2998. session->mName = name;
  2999. if(session->mTextInvitePending)
  3000. {
  3001. session->mTextInvitePending = false;
  3002. // We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.
  3003. }
  3004. if(session->mVoiceInvitePending)
  3005. {
  3006. session->mVoiceInvitePending = false;
  3007. gIMMgr->inviteToSession(
  3008. session->mIMSessionID,
  3009. session->mName,
  3010. session->mCallerID, 
  3011. session->mName, 
  3012. IM_SESSION_P2P_INVITE, 
  3013. LLIMMgr::INVITATION_TYPE_VOICE,
  3014. session->mHandle,
  3015. session->mSIPURI);
  3016. }
  3017. }
  3018. }
  3019. }
  3020. class LLViewerParcelVoiceInfo : public LLHTTPNode
  3021. {
  3022. virtual void post(
  3023. LLHTTPNode::ResponsePtr response,
  3024. const LLSD& context,
  3025. const LLSD& input) const
  3026. {
  3027. //the parcel you are in has changed something about its
  3028. //voice information
  3029. //this is a misnomer, as it can also be when you are not in
  3030. //a parcel at all.  Should really be something like
  3031. //LLViewerVoiceInfoChanged.....
  3032. if ( input.has("body") )
  3033. {
  3034. LLSD body = input["body"];
  3035. //body has "region_name" (str), "parcel_local_id"(int),
  3036. //"voice_credentials" (map).
  3037. //body["voice_credentials"] has "channel_uri" (str),
  3038. //body["voice_credentials"] has "channel_credentials" (str)
  3039. //if we really wanted to be extra careful,
  3040. //we'd check the supplied
  3041. //local parcel id to make sure it's for the same parcel
  3042. //we believe we're in
  3043. if ( body.has("voice_credentials") )
  3044. {
  3045. LLSD voice_credentials = body["voice_credentials"];
  3046. std::string uri;
  3047. std::string credentials;
  3048. if ( voice_credentials.has("channel_uri") )
  3049. {
  3050. uri = voice_credentials["channel_uri"].asString();
  3051. }
  3052. if ( voice_credentials.has("channel_credentials") )
  3053. {
  3054. credentials =
  3055. voice_credentials["channel_credentials"].asString();
  3056. }
  3057. gVoiceClient->setSpatialChannel(uri, credentials);
  3058. }
  3059. }
  3060. }
  3061. };
  3062. class LLViewerRequiredVoiceVersion : public LLHTTPNode
  3063. {
  3064. static BOOL sAlertedUser;
  3065. virtual void post(
  3066. LLHTTPNode::ResponsePtr response,
  3067. const LLSD& context,
  3068. const LLSD& input) const
  3069. {
  3070. //You received this messsage (most likely on region cross or
  3071. //teleport)
  3072. if ( input.has("body") && input["body"].has("major_version") )
  3073. {
  3074. int major_voice_version =
  3075. input["body"]["major_version"].asInteger();
  3076. //  int minor_voice_version =
  3077. //  input["body"]["minor_version"].asInteger();
  3078. if (gVoiceClient &&
  3079. (major_voice_version > VOICE_MAJOR_VERSION) )
  3080. {
  3081. if (!sAlertedUser)
  3082. {
  3083. //sAlertedUser = TRUE;
  3084. LLNotificationsUtil::add("VoiceVersionMismatch");
  3085. gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
  3086. }
  3087. }
  3088. }
  3089. }
  3090. };
  3091. BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
  3092. LLHTTPRegistration<LLViewerParcelVoiceInfo>
  3093.     gHTTPRegistrationMessageParcelVoiceInfo(
  3094. "/message/ParcelVoiceInfo");
  3095. LLHTTPRegistration<LLViewerRequiredVoiceVersion>
  3096.     gHTTPRegistrationMessageRequiredVoiceVersion(
  3097. "/message/RequiredVoiceVersion");