aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llvoiceclient.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2009-02-12 02:06:41 -0600
committerJacek Antonelli2009-02-12 02:06:45 -0600
commit61f97b33f9850d21965d397b947a298c16ba576d (patch)
treea2edff0a7fbc83e2259eda952511b0fbdbea290b /linden/indra/newview/llvoiceclient.cpp
parentSecond Life viewer sources 1.22.7-RC (diff)
downloadmeta-impy-61f97b33f9850d21965d397b947a298c16ba576d.zip
meta-impy-61f97b33f9850d21965d397b947a298c16ba576d.tar.gz
meta-impy-61f97b33f9850d21965d397b947a298c16ba576d.tar.bz2
meta-impy-61f97b33f9850d21965d397b947a298c16ba576d.tar.xz
Second Life viewer sources 1.22.8-RC
Diffstat (limited to 'linden/indra/newview/llvoiceclient.cpp')
-rw-r--r--linden/indra/newview/llvoiceclient.cpp4540
1 files changed, 3583 insertions, 957 deletions
diff --git a/linden/indra/newview/llvoiceclient.cpp b/linden/indra/newview/llvoiceclient.cpp
index 3bec16f..145132a 100644
--- a/linden/indra/newview/llvoiceclient.cpp
+++ b/linden/indra/newview/llvoiceclient.cpp
@@ -62,15 +62,20 @@
62#include "llviewerwindow.h" 62#include "llviewerwindow.h"
63#include "llviewercamera.h" 63#include "llviewercamera.h"
64 64
65#include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel
66#include "llfloaterchat.h" // for LLFloaterChat::addChat()
67
65// for base64 decoding 68// for base64 decoding
66#include "apr_base64.h" 69#include "apr_base64.h"
67 70
68// for SHA1 hash 71// for SHA1 hash
69#include "apr_sha1.h" 72#include "apr_sha1.h"
70 73
71// If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name. 74// for MD5 hash
72// If we are connecting to agni and the user's last name is NOT "Linden", disable voice. 75#include "llmd5.h"
73#define AGNI_LINDENS_ONLY_CHANNEL "SL" 76
77#define USE_SESSION_GROUPS 0
78
74static bool sConnectingToAgni = false; 79static bool sConnectingToAgni = false;
75F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; 80F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
76 81
@@ -90,6 +95,44 @@ const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
90const F32 LOGIN_RETRY_SECONDS = 10.0f; 95const F32 LOGIN_RETRY_SECONDS = 10.0f;
91const int MAX_LOGIN_RETRIES = 12; 96const int MAX_LOGIN_RETRIES = 12;
92 97
98static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
99{
100 LLMD5 md5_uuid;
101 md5_uuid.update((const unsigned char*)str.data(), str.size());
102 md5_uuid.finalize();
103 md5_uuid.raw_digest(uuid.mData);
104}
105
106static int scale_mic_volume(float volume)
107{
108 // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
109 // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
110
111 volume -= 1.0f; // offset volume to the range [-1.0 ... 1.0], with 0 at the default.
112 int scaled_volume = 44; // offset scaled_volume by its default level
113 if(volume < 0.0f)
114 scaled_volume += ((int)(volume * 4.0f)); // (44 - 40)
115 else
116 scaled_volume += ((int)(volume * 31.0f)); // (75 - 44)
117
118 return scaled_volume;
119}
120
121static int scale_speaker_volume(float volume)
122{
123 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
124 // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
125
126 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
127 int scaled_volume = 62; // offset scaled_volume by its default level
128 if(volume < 0.0f)
129 scaled_volume += ((int)(volume * 124.0f)); // (62 - 0) * 2
130 else
131 scaled_volume += ((int)(volume * 26.0f)); // (75 - 62) * 2
132
133 return scaled_volume;
134}
135
93class LLViewerVoiceAccountProvisionResponder : 136class LLViewerVoiceAccountProvisionResponder :
94 public LLHTTPClient::Responder 137 public LLHTTPClient::Responder
95{ 138{
@@ -103,12 +146,13 @@ public:
103 { 146 {
104 if ( mRetries > 0 ) 147 if ( mRetries > 0 )
105 { 148 {
149 LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
106 if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision( 150 if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
107 mRetries - 1); 151 mRetries - 1);
108 } 152 }
109 else 153 else
110 { 154 {
111 //TODO: throw an error message? 155 LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
112 if ( gVoiceClient ) gVoiceClient->giveUp(); 156 if ( gVoiceClient ) gVoiceClient->giveUp();
113 } 157 }
114 } 158 }
@@ -117,9 +161,23 @@ public:
117 { 161 {
118 if ( gVoiceClient ) 162 if ( gVoiceClient )
119 { 163 {
164 std::string voice_sip_uri_hostname;
165 std::string voice_account_server_uri;
166
167 LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
168
169 if(content.has("voice_sip_uri_hostname"))
170 voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
171
172 // this key is actually misnamed -- it will be an entire URI, not just a hostname.
173 if(content.has("voice_account_server_name"))
174 voice_account_server_uri = content["voice_account_server_name"].asString();
175
120 gVoiceClient->login( 176 gVoiceClient->login(
121 content["username"].asString(), 177 content["username"].asString(),
122 content["password"].asString()); 178 content["password"].asString(),
179 voice_sip_uri_hostname,
180 voice_account_server_uri);
123 } 181 }
124 } 182 }
125 183
@@ -166,21 +224,27 @@ protected:
166 int ignoreDepth; 224 int ignoreDepth;
167 225
168 // Members for processing responses. The values are transient and only valid within a call to processResponse(). 226 // Members for processing responses. The values are transient and only valid within a call to processResponse().
227 bool squelchDebugOutput;
169 int returnCode; 228 int returnCode;
170 int statusCode; 229 int statusCode;
171 std::string statusString; 230 std::string statusString;
172 std::string uuidString; 231 std::string requestId;
173 std::string actionString; 232 std::string actionString;
174 std::string connectorHandle; 233 std::string connectorHandle;
234 std::string versionID;
175 std::string accountHandle; 235 std::string accountHandle;
176 std::string sessionHandle; 236 std::string sessionHandle;
177 std::string eventSessionHandle; 237 std::string sessionGroupHandle;
238 std::string alias;
239 std::string applicationString;
178 240
179 // Members for processing events. The values are transient and only valid within a call to processResponse(). 241 // Members for processing events. The values are transient and only valid within a call to processResponse().
180 std::string eventTypeString; 242 std::string eventTypeString;
181 int state; 243 int state;
182 std::string uriString; 244 std::string uriString;
183 bool isChannel; 245 bool isChannel;
246 bool incoming;
247 bool enabled;
184 std::string nameString; 248 std::string nameString;
185 std::string audioMediaString; 249 std::string audioMediaString;
186 std::string displayNameString; 250 std::string displayNameString;
@@ -190,6 +254,21 @@ protected:
190 bool isSpeaking; 254 bool isSpeaking;
191 int volume; 255 int volume;
192 F32 energy; 256 F32 energy;
257 std::string messageHeader;
258 std::string messageBody;
259 std::string notificationType;
260 bool hasText;
261 bool hasAudio;
262 bool hasVideo;
263 bool terminated;
264 std::string blockMask;
265 std::string presenceOnly;
266 std::string autoAcceptMask;
267 std::string autoAddAsBuddy;
268 int numberOfAliases;
269 std::string subscriptionHandle;
270 std::string subscriptionType;
271
193 272
194 // Members for processing text between tags 273 // Members for processing text between tags
195 std::string textBuffer; 274 std::string textBuffer;
@@ -222,8 +301,6 @@ void LLVivoxProtocolParser::reset()
222 responseDepth = 0; 301 responseDepth = 0;
223 ignoringTags = false; 302 ignoringTags = false;
224 accumulateText = false; 303 accumulateText = false;
225 textBuffer.clear();
226
227 energy = 0.f; 304 energy = 0.f;
228 ignoreDepth = 0; 305 ignoreDepth = 0;
229 isChannel = false; 306 isChannel = false;
@@ -232,10 +309,15 @@ void LLVivoxProtocolParser::reset()
232 isModeratorMuted = false; 309 isModeratorMuted = false;
233 isSpeaking = false; 310 isSpeaking = false;
234 participantType = 0; 311 participantType = 0;
235 returnCode = 0; 312 squelchDebugOutput = false;
313 returnCode = -1;
236 state = 0; 314 state = 0;
237 statusCode = 0; 315 statusCode = 0;
238 volume = 0; 316 volume = 0;
317 textBuffer.clear();
318 alias.clear();
319 numberOfAliases = 0;
320 applicationString.clear();
239} 321}
240 322
241//virtual 323//virtual
@@ -262,33 +344,11 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
262 mInput.append(buf, istr.gcount()); 344 mInput.append(buf, istr.gcount());
263 } 345 }
264 346
265 // MBW -- XXX -- This should no longer be necessary. Or even possible.
266 // We've read all the data out of the buffer. Make sure it doesn't accumulate.
267// buffer->clear();
268
269 // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. 347 // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser.
270 int start = 0; 348 int start = 0;
271 int delim; 349 int delim;
272 while((delim = mInput.find("\n\n\n", start)) != std::string::npos) 350 while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
273 { 351 {
274 // Turn this on to log incoming XML
275 if(0)
276 {
277 int foo = mInput.find("Set3DPosition", start);
278 int bar = mInput.find("ParticipantPropertiesEvent", start);
279 if(foo != std::string::npos && (foo < delim))
280 {
281 // This is a Set3DPosition response. Don't print it, since these are way too spammy.
282 }
283 else if(bar != std::string::npos && (bar < delim))
284 {
285 // This is a ParticipantPropertiesEvent response. Don't print it, since these are way too spammy.
286 }
287 else
288 {
289 LL_INFOS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
290 }
291 }
292 352
293 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) 353 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
294 reset(); 354 reset();
@@ -299,13 +359,19 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
299 XML_SetUserData(parser, this); 359 XML_SetUserData(parser, this);
300 XML_Parse(parser, mInput.data() + start, delim - start, false); 360 XML_Parse(parser, mInput.data() + start, delim - start, false);
301 361
362 // If this message isn't set to be squelched, output the raw XML received.
363 if(!squelchDebugOutput)
364 {
365 LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
366 }
367
302 start = delim + 3; 368 start = delim + 3;
303 } 369 }
304 370
305 if(start != 0) 371 if(start != 0)
306 mInput = mInput.substr(start); 372 mInput = mInput.substr(start);
307 373
308 LL_DEBUGS("Voice") << "at end, mInput is: " << mInput << LL_ENDL; 374 LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
309 375
310 if(!gVoiceClient->mConnected) 376 if(!gVoiceClient->mConnected)
311 { 377 {
@@ -360,9 +426,9 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
360 426
361 if (responseDepth == 0) 427 if (responseDepth == 0)
362 { 428 {
363 isEvent = strcmp("Event", tag) == 0; 429 isEvent = !stricmp("Event", tag);
364 430
365 if (strcmp("Response", tag) == 0 || isEvent) 431 if (!stricmp("Response", tag) || isEvent)
366 { 432 {
367 // Grab the attributes 433 // Grab the attributes
368 while (*attr) 434 while (*attr)
@@ -370,49 +436,62 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
370 const char *key = *attr++; 436 const char *key = *attr++;
371 const char *value = *attr++; 437 const char *value = *attr++;
372 438
373 if (strcmp("requestId", key) == 0) 439 if (!stricmp("requestId", key))
374 { 440 {
375 uuidString = value; 441 requestId = value;
376 } 442 }
377 else if (strcmp("action", key) == 0) 443 else if (!stricmp("action", key))
378 { 444 {
379 actionString = value; 445 actionString = value;
380 } 446 }
381 else if (strcmp("type", key) == 0) 447 else if (!stricmp("type", key))
382 { 448 {
383 eventTypeString = value; 449 eventTypeString = value;
384 } 450 }
385 } 451 }
386 } 452 }
387 LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")" << LL_ENDL; 453 LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL;
388 } 454 }
389 else 455 else
390 { 456 {
391 if (ignoringTags) 457 if (ignoringTags)
392 { 458 {
393 LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 459 LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
394 } 460 }
395 else 461 else
396 { 462 {
397 LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")" << LL_ENDL; 463 LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL;
398 464
399 // Ignore the InputXml stuff so we don't get confused 465 // Ignore the InputXml stuff so we don't get confused
400 if (strcmp("InputXml", tag) == 0) 466 if (!stricmp("InputXml", tag))
401 { 467 {
402 ignoringTags = true; 468 ignoringTags = true;
403 ignoreDepth = responseDepth; 469 ignoreDepth = responseDepth;
404 accumulateText = false; 470 accumulateText = false;
405 471
406 LL_DEBUGS("Voice") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; 472 LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
407 } 473 }
408 else if (strcmp("CaptureDevices", tag) == 0) 474 else if (!stricmp("CaptureDevices", tag))
409 { 475 {
410 gVoiceClient->clearCaptureDevices(); 476 gVoiceClient->clearCaptureDevices();
411 } 477 }
412 else if (strcmp("RenderDevices", tag) == 0) 478 else if (!stricmp("RenderDevices", tag))
413 { 479 {
414 gVoiceClient->clearRenderDevices(); 480 gVoiceClient->clearRenderDevices();
415 } 481 }
482 else if (!stricmp("Buddies", tag))
483 {
484 gVoiceClient->deleteAllBuddies();
485 }
486 else if (!stricmp("BlockRules", tag))
487 {
488 gVoiceClient->deleteAllBlockRules();
489 }
490 else if (!stricmp("AutoAcceptRules", tag))
491 {
492 gVoiceClient->deleteAllAutoAcceptRules();
493 }
494
416 } 495 }
417 } 496 }
418 responseDepth++; 497 responseDepth++;
@@ -431,90 +510,138 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
431 { 510 {
432 if (ignoreDepth == responseDepth) 511 if (ignoreDepth == responseDepth)
433 { 512 {
434 LL_DEBUGS("Voice") << "end of ignore" << LL_ENDL; 513 LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
435 ignoringTags = false; 514 ignoringTags = false;
436 } 515 }
437 else 516 else
438 { 517 {
439 LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 518 LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
440 } 519 }
441 } 520 }
442 521
443 if (!ignoringTags) 522 if (!ignoringTags)
444 { 523 {
445 LL_DEBUGS("Voice") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; 524 LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
446 525
447 // Closing a tag. Finalize the text we've accumulated and reset 526 // Closing a tag. Finalize the text we've accumulated and reset
448 if (strcmp("ReturnCode", tag) == 0) 527 if (!stricmp("ReturnCode", tag))
449 returnCode = strtol(string.c_str(), NULL, 10); 528 returnCode = strtol(string.c_str(), NULL, 10);
450 else if (strcmp("StatusCode", tag) == 0) 529 else if (!stricmp("SessionHandle", tag))
530 sessionHandle = string;
531 else if (!stricmp("SessionGroupHandle", tag))
532 sessionGroupHandle = string;
533 else if (!stricmp("StatusCode", tag))
451 statusCode = strtol(string.c_str(), NULL, 10); 534 statusCode = strtol(string.c_str(), NULL, 10);
452 else if (strcmp("ConnectorHandle", tag) == 0) 535 else if (!stricmp("StatusString", tag))
536 statusString = string;
537 else if (!stricmp("ParticipantURI", tag))
538 uriString = string;
539 else if (!stricmp("Volume", tag))
540 volume = strtol(string.c_str(), NULL, 10);
541 else if (!stricmp("Energy", tag))
542 energy = (F32)strtod(string.c_str(), NULL);
543 else if (!stricmp("IsModeratorMuted", tag))
544 isModeratorMuted = !stricmp(string.c_str(), "true");
545 else if (!stricmp("IsSpeaking", tag))
546 isSpeaking = !stricmp(string.c_str(), "true");
547 else if (!stricmp("Alias", tag))
548 alias = string;
549 else if (!stricmp("NumberOfAliases", tag))
550 numberOfAliases = strtol(string.c_str(), NULL, 10);
551 else if (!stricmp("Application", tag))
552 applicationString = string;
553 else if (!stricmp("ConnectorHandle", tag))
453 connectorHandle = string; 554 connectorHandle = string;
454 else if (strcmp("AccountHandle", tag) == 0) 555 else if (!stricmp("VersionID", tag))
556 versionID = string;
557 else if (!stricmp("AccountHandle", tag))
455 accountHandle = string; 558 accountHandle = string;
456 else if (strcmp("SessionHandle", tag) == 0) 559 else if (!stricmp("State", tag))
457 {
458 if (isEvent)
459 eventSessionHandle = string;
460 else
461 sessionHandle = string;
462 }
463 else if (strcmp("StatusString", tag) == 0)
464 statusString = string;
465 else if (strcmp("State", tag) == 0)
466 state = strtol(string.c_str(), NULL, 10); 560 state = strtol(string.c_str(), NULL, 10);
467 else if (strcmp("URI", tag) == 0) 561 else if (!stricmp("URI", tag))
468 uriString = string; 562 uriString = string;
469 else if (strcmp("IsChannel", tag) == 0) 563 else if (!stricmp("IsChannel", tag))
470 isChannel = string == "true" ? true : false; 564 isChannel = !stricmp(string.c_str(), "true");
471 else if (strcmp("Name", tag) == 0) 565 else if (!stricmp("Incoming", tag))
566 incoming = !stricmp(string.c_str(), "true");
567 else if (!stricmp("Enabled", tag))
568 enabled = !stricmp(string.c_str(), "true");
569 else if (!stricmp("Name", tag))
472 nameString = string; 570 nameString = string;
473 else if (strcmp("AudioMedia", tag) == 0) 571 else if (!stricmp("AudioMedia", tag))
474 audioMediaString = string; 572 audioMediaString = string;
475 else if (strcmp("ChannelName", tag) == 0) 573 else if (!stricmp("ChannelName", tag))
476 nameString = string; 574 nameString = string;
477 else if (strcmp("ParticipantURI", tag) == 0) 575 else if (!stricmp("DisplayName", tag))
478 uriString = string;
479 else if (strcmp("DisplayName", tag) == 0)
480 displayNameString = string; 576 displayNameString = string;
481 else if (strcmp("AccountName", tag) == 0) 577 else if (!stricmp("AccountName", tag))
482 nameString = string; 578 nameString = string;
483 else if (strcmp("ParticipantTyppe", tag) == 0) 579 else if (!stricmp("ParticipantType", tag))
484 participantType = strtol(string.c_str(), NULL, 10); 580 participantType = strtol(string.c_str(), NULL, 10);
485 else if (strcmp("IsLocallyMuted", tag) == 0) 581 else if (!stricmp("IsLocallyMuted", tag))
486 isLocallyMuted = string == "true" ? true : false; 582 isLocallyMuted = !stricmp(string.c_str(), "true");
487 else if (strcmp("IsModeratorMuted", tag) == 0) 583 else if (!stricmp("MicEnergy", tag))
488 isModeratorMuted = string == "true" ? true : false;
489 else if (strcmp("IsSpeaking", tag) == 0)
490 isSpeaking = string == "true" ? true : false;
491 else if (strcmp("Volume", tag) == 0)
492 volume = strtol(string.c_str(), NULL, 10);
493 else if (strcmp("Energy", tag) == 0)
494 energy = (F32)strtod(string.c_str(), NULL);
495 else if (strcmp("MicEnergy", tag) == 0)
496 energy = (F32)strtod(string.c_str(), NULL); 584 energy = (F32)strtod(string.c_str(), NULL);
497 else if (strcmp("ChannelName", tag) == 0) 585 else if (!stricmp("ChannelName", tag))
498 nameString = string; 586 nameString = string;
499 else if (strcmp("ChannelURI", tag) == 0) 587 else if (!stricmp("ChannelURI", tag))
500 uriString = string; 588 uriString = string;
501 else if (strcmp("ChannelListResult", tag) == 0) 589 else if (!stricmp("BuddyURI", tag))
502 { 590 uriString = string;
503 gVoiceClient->addChannelMapEntry(nameString, uriString); 591 else if (!stricmp("Presence", tag))
504 } 592 statusString = string;
505 else if (strcmp("Device", tag) == 0) 593 else if (!stricmp("Device", tag))
506 { 594 {
507 // This closing tag shouldn't clear the accumulated text. 595 // This closing tag shouldn't clear the accumulated text.
508 clearbuffer = false; 596 clearbuffer = false;
509 } 597 }
510 else if (strcmp("CaptureDevice", tag) == 0) 598 else if (!stricmp("CaptureDevice", tag))
511 { 599 {
512 gVoiceClient->addCaptureDevice(textBuffer); 600 gVoiceClient->addCaptureDevice(textBuffer);
513 } 601 }
514 else if (strcmp("RenderDevice", tag) == 0) 602 else if (!stricmp("RenderDevice", tag))
515 { 603 {
516 gVoiceClient->addRenderDevice(textBuffer); 604 gVoiceClient->addRenderDevice(textBuffer);
517 } 605 }
606 else if (!stricmp("Buddy", tag))
607 {
608 gVoiceClient->processBuddyListEntry(uriString, displayNameString);
609 }
610 else if (!stricmp("BlockRule", tag))
611 {
612 gVoiceClient->addBlockRule(blockMask, presenceOnly);
613 }
614 else if (!stricmp("BlockMask", tag))
615 blockMask = string;
616 else if (!stricmp("PresenceOnly", tag))
617 presenceOnly = string;
618 else if (!stricmp("AutoAcceptRule", tag))
619 {
620 gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
621 }
622 else if (!stricmp("AutoAcceptMask", tag))
623 autoAcceptMask = string;
624 else if (!stricmp("AutoAddAsBuddy", tag))
625 autoAddAsBuddy = string;
626 else if (!stricmp("MessageHeader", tag))
627 messageHeader = string;
628 else if (!stricmp("MessageBody", tag))
629 messageBody = string;
630 else if (!stricmp("NotificationType", tag))
631 notificationType = string;
632 else if (!stricmp("HasText", tag))
633 hasText = !stricmp(string.c_str(), "true");
634 else if (!stricmp("HasAudio", tag))
635 hasAudio = !stricmp(string.c_str(), "true");
636 else if (!stricmp("HasVideo", tag))
637 hasVideo = !stricmp(string.c_str(), "true");
638 else if (!stricmp("Terminated", tag))
639 terminated = !stricmp(string.c_str(), "true");
640 else if (!stricmp("SubscriptionHandle", tag))
641 subscriptionHandle = string;
642 else if (!stricmp("SubscriptionType", tag))
643 subscriptionType = string;
644
518 645
519 if(clearbuffer) 646 if(clearbuffer)
520 { 647 {
@@ -549,144 +676,296 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length)
549 676
550void LLVivoxProtocolParser::processResponse(std::string tag) 677void LLVivoxProtocolParser::processResponse(std::string tag)
551{ 678{
552 LL_DEBUGS("Voice") << tag << LL_ENDL; 679 LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
553 680
681 // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs.
682 // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
683 // so I believe this will give correct behavior.
684
685 if(returnCode == 0)
686 statusCode = 0;
687
554 if (isEvent) 688 if (isEvent)
555 { 689 {
556 if (eventTypeString == "LoginStateChangeEvent") 690 const char *eventTypeCstr = eventTypeString.c_str();
691 if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
557 { 692 {
558 gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state); 693 gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
559 } 694 }
560 else if (eventTypeString == "SessionNewEvent") 695 else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
561 { 696 {
562 gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString); 697 /*
698 <Event type="SessionAddedEvent">
699 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
700 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
701 <Uri>sip:confctl-1408789@bhr.vivox.com</Uri>
702 <IsChannel>true</IsChannel>
703 <Incoming>false</Incoming>
704 <ChannelName />
705 </Event>
706 */
707 gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
563 } 708 }
564 else if (eventTypeString == "SessionStateChangeEvent") 709 else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
565 { 710 {
566 gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString); 711 gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
567 } 712 }
568 else if (eventTypeString == "ParticipantStateChangeEvent") 713 else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
569 { 714 {
570 gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state, nameString, displayNameString, participantType); 715 gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle);
571 716 }
717 else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
718 {
719 /*
720 <Event type="MediaStreamUpdatedEvent">
721 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
722 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
723 <StatusCode>200</StatusCode>
724 <StatusString>OK</StatusString>
725 <State>2</State>
726 <Incoming>false</Incoming>
727 </Event>
728 */
729 gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
730 }
731 else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
732 {
733 /*
734 <Event type="TextStreamUpdatedEvent">
735 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
736 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
737 <Enabled>true</Enabled>
738 <State>1</State>
739 <Incoming>true</Incoming>
740 </Event>
741 */
742 gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
743 }
744 else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
745 {
746 /*
747 <Event type="ParticipantAddedEvent">
748 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
749 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
750 <ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
751 <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
752 <DisplayName />
753 <ParticipantType>0</ParticipantType>
754 </Event>
755 */
756 gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
572 } 757 }
573 else if (eventTypeString == "ParticipantPropertiesEvent") 758 else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
574 { 759 {
575 gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy); 760 /*
761 <Event type="ParticipantRemovedEvent">
762 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
763 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
764 <ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
765 <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
766 </Event>
767 */
768 gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
769 }
770 else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
771 {
772 /*
773 <Event type="ParticipantUpdatedEvent">
774 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
775 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
776 <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
777 <IsModeratorMuted>false</IsModeratorMuted>
778 <IsSpeaking>true</IsSpeaking>
779 <Volume>44</Volume>
780 <Energy>0.0879437</Energy>
781 </Event>
782 */
783
784 // These happen so often that logging them is pretty useless.
785 squelchDebugOutput = true;
786
787 gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
576 } 788 }
577 else if (eventTypeString == "AuxAudioPropertiesEvent") 789 else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
578 { 790 {
579 gVoiceClient->auxAudioPropertiesEvent(energy); 791 gVoiceClient->auxAudioPropertiesEvent(energy);
580 } 792 }
793 else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
794 {
795 gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString);
796 }
797 else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
798 {
799 // The buddy list was updated during parsing.
800 // Need to recheck against the friends list.
801 gVoiceClient->buddyListChanged();
802 }
803 else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
804 {
805 /*
806 <Event type="BuddyChangedEvent">
807 <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
808 <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
809 <DisplayName>Monroe Tester</DisplayName>
810 <BuddyData />
811 <GroupID>0</GroupID>
812 <ChangeType>Set</ChangeType>
813 </Event>
814 */
815 // TODO: Question: Do we need to process this at all?
816 }
817 else if (!stricmp(eventTypeCstr, "MessageEvent"))
818 {
819 gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
820 }
821 else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
822 {
823 gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType);
824 }
825 else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))
826 {
827 gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
828 }
829 else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
830 {
831 /*
832 <Event type="SessionUpdatedEvent">
833 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
834 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
835 <Uri>sip:confctl-9@bhd.vivox.com</Uri>
836 <IsMuted>0</IsMuted>
837 <Volume>50</Volume>
838 <TransmitEnabled>1</TransmitEnabled>
839 <IsFocused>0</IsFocused>
840 <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
841 <SessionFontID>0</SessionFontID>
842 </Event>
843 */
844 // We don't need to process this, but we also shouldn't warn on it, since that confuses people.
845 }
846 else
847 {
848 LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
849 }
581 } 850 }
582 else 851 else
583 { 852 {
584 if (actionString == "Connector.Create.1") 853 const char *actionCstr = actionString.c_str();
854 if (!stricmp(actionCstr, "Connector.Create.1"))
585 { 855 {
586 gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle); 856 gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
587 } 857 }
588 else if (actionString == "Account.Login.1") 858 else if (!stricmp(actionCstr, "Account.Login.1"))
589 { 859 {
590 gVoiceClient->loginResponse(statusCode, statusString, accountHandle); 860 gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
591 } 861 }
592 else if (actionString == "Session.Create.1") 862 else if (!stricmp(actionCstr, "Session.Create.1"))
593 { 863 {
594 gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle); 864 gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);
595 } 865 }
596 else if (actionString == "Session.Connect.1") 866 else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
597 { 867 {
598 gVoiceClient->sessionConnectResponse(statusCode, statusString); 868 gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);
599 } 869 }
600 else if (actionString == "Session.Terminate.1") 870 else if (!stricmp(actionCstr, "Session.Connect.1"))
601 { 871 {
602 gVoiceClient->sessionTerminateResponse(statusCode, statusString); 872 gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);
603 } 873 }
604 else if (actionString == "Account.Logout.1") 874 else if (!stricmp(actionCstr, "Account.Logout.1"))
605 { 875 {
606 gVoiceClient->logoutResponse(statusCode, statusString); 876 gVoiceClient->logoutResponse(statusCode, statusString);
607 } 877 }
608 else if (actionString == "Connector.InitiateShutdown.1") 878 else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
609 { 879 {
610 gVoiceClient->connectorShutdownResponse(statusCode, statusString); 880 gVoiceClient->connectorShutdownResponse(statusCode, statusString);
611 } 881 }
612 else if (actionString == "Account.ChannelGetList.1") 882 else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
613 { 883 {
614 gVoiceClient->channelGetListResponse(statusCode, statusString); 884 gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);
885 }
886 else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
887 {
888 gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);
889 }
890 else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
891 {
892 // We don't need to process these, but they're so spammy we don't want to log them.
893 squelchDebugOutput = true;
615 } 894 }
616/* 895/*
617 else if (actionString == "Connector.AccountCreate.1") 896 else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
618 { 897 {
619 898 gVoiceClient->channelGetListResponse(statusCode, statusString);
620 } 899 }
621 else if (actionString == "Connector.MuteLocalMic.1") 900 else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
622 { 901 {
623 902
624 } 903 }
625 else if (actionString == "Connector.MuteLocalSpeaker.1") 904 else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
626 { 905 {
627 906
628 } 907 }
629 else if (actionString == "Connector.SetLocalMicVolume.1") 908 else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
630 { 909 {
631 910
632 } 911 }
633 else if (actionString == "Connector.SetLocalSpeakerVolume.1") 912 else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
634 { 913 {
635 914
636 } 915 }
637 else if (actionString == "Session.ListenerSetPosition.1") 916 else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
638 { 917 {
639 918
640 } 919 }
641 else if (actionString == "Session.SpeakerSetPosition.1") 920 else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
642 { 921 {
643 922
644 } 923 }
645 else if (actionString == "Session.Set3DPosition.1") 924 else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
646 { 925 {
647 926
648 } 927 }
649 else if (actionString == "Session.AudioSourceSetPosition.1") 928 else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
650 { 929 {
651 930
652 } 931 }
653 else if (actionString == "Session.GetChannelParticipants.1") 932 else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
654 { 933 {
655 934
656 } 935 }
657 else if (actionString == "Account.ChannelCreate.1") 936 else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
658 { 937 {
659 938
660 } 939 }
661 else if (actionString == "Account.ChannelUpdate.1") 940 else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
662 { 941 {
663 942
664 } 943 }
665 else if (actionString == "Account.ChannelDelete.1") 944 else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
666 { 945 {
667 946
668 } 947 }
669 else if (actionString == "Account.ChannelCreateAndInvite.1") 948 else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
670 { 949 {
671 950
672 } 951 }
673 else if (actionString == "Account.ChannelFolderCreate.1") 952 else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
674 { 953 {
675 954
676 } 955 }
677 else if (actionString == "Account.ChannelFolderUpdate.1") 956 else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
678 { 957 {
679 958
680 } 959 }
681 else if (actionString == "Account.ChannelFolderDelete.1") 960 else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
682 { 961 {
683 962
684 } 963 }
685 else if (actionString == "Account.ChannelAddModerator.1") 964 else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
686 { 965 {
687 966
688 } 967 }
689 else if (actionString == "Account.ChannelDeleteModerator.1") 968 else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
690 { 969 {
691 970
692 } 971 }
@@ -700,9 +979,18 @@ class LLVoiceClientMuteListObserver : public LLMuteListObserver
700{ 979{
701 /* virtual */ void onChange() { gVoiceClient->muteListChanged();} 980 /* virtual */ void onChange() { gVoiceClient->muteListChanged();}
702}; 981};
982
983class LLVoiceClientFriendsObserver : public LLFriendObserver
984{
985public:
986 /* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);}
987};
988
703static LLVoiceClientMuteListObserver mutelist_listener; 989static LLVoiceClientMuteListObserver mutelist_listener;
704static bool sMuteListListener_listening = false; 990static bool sMuteListListener_listening = false;
705 991
992static LLVoiceClientFriendsObserver *friendslist_listener = NULL;
993
706/////////////////////////////////////////////////////////////////////////////////////////////// 994///////////////////////////////////////////////////////////////////////////////////////////////
707 995
708class LLVoiceClientCapResponder : public LLHTTPClient::Responder 996class LLVoiceClientCapResponder : public LLHTTPClient::Responder
@@ -726,11 +1014,8 @@ void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
726void LLVoiceClientCapResponder::result(const LLSD& content) 1014void LLVoiceClientCapResponder::result(const LLSD& content)
727{ 1015{
728 LLSD::map_const_iterator iter; 1016 LLSD::map_const_iterator iter;
729 for(iter = content.beginMap(); iter != content.endMap(); ++iter) 1017
730 { 1018 LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
731 LL_DEBUGS("Voice") << "LLVoiceClientCapResponder::result got "
732 << iter->first << LL_ENDL;
733 }
734 1019
735 if ( content.has("voice_credentials") ) 1020 if ( content.has("voice_credentials") )
736 { 1021 {
@@ -816,26 +1101,26 @@ LLVoiceClient::LLVoiceClient()
816 mUserPTTState = false; 1101 mUserPTTState = false;
817 mMuteMic = false; 1102 mMuteMic = false;
818 mSessionTerminateRequested = false; 1103 mSessionTerminateRequested = false;
1104 mRelogRequested = false;
819 mCommandCookie = 0; 1105 mCommandCookie = 0;
820 mNonSpatialChannel = false;
821 mNextSessionSpatial = true;
822 mNextSessionNoReconnect = false;
823 mSessionP2P = false;
824 mCurrentParcelLocalID = 0; 1106 mCurrentParcelLocalID = 0;
825 mLoginRetryCount = 0; 1107 mLoginRetryCount = 0;
826 mVivoxErrorStatusCode = 0;
827 1108
828 mNextSessionResetOnClose = false;
829 mSessionResetOnClose = false;
830 mSpeakerVolume = 0; 1109 mSpeakerVolume = 0;
831 mMicVolume = 0; 1110 mMicVolume = 0;
832 1111
1112 mAudioSession = NULL;
1113 mAudioSessionChanged = false;
1114
833 // Initial dirty state 1115 // Initial dirty state
834 mSpatialCoordsDirty = false; 1116 mSpatialCoordsDirty = false;
835 mPTTDirty = true; 1117 mPTTDirty = true;
836 mVolumeDirty = true; 1118 mFriendsListDirty = true;
837 mSpeakerVolumeDirty = true; 1119 mSpeakerVolumeDirty = true;
838 mMicVolumeDirty = true; 1120 mMicVolumeDirty = true;
1121 mBuddyListMapPopulated = false;
1122 mBlockRulesListReceived = false;
1123 mAutoAcceptRulesListReceived = false;
839 mCaptureDeviceDirty = false; 1124 mCaptureDeviceDirty = false;
840 mRenderDeviceDirty = false; 1125 mRenderDeviceDirty = false;
841 1126
@@ -856,14 +1141,12 @@ LLVoiceClient::LLVoiceClient()
856 // gMuteListp isn't set up at this point, so we defer this until later. 1141 // gMuteListp isn't set up at this point, so we defer this until later.
857// gMuteListp->addObserver(&mutelist_listener); 1142// gMuteListp->addObserver(&mutelist_listener);
858 1143
859 mParticipantMapChanged = false;
860
861 // stash the pump for later use 1144 // stash the pump for later use
862 // This now happens when init() is called instead. 1145 // This now happens when init() is called instead.
863 mPump = NULL; 1146 mPump = NULL;
864 1147
865#if LL_DARWIN || LL_LINUX 1148#if LL_DARWIN || LL_LINUX
866 // MBW -- XXX -- THIS DOES NOT BELONG HERE 1149 // HACK: THIS DOES NOT BELONG HERE
867 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. 1150 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
868 // This should cause us to ignore SIGPIPE and handle the error through proper channels. 1151 // This should cause us to ignore SIGPIPE and handle the error through proper channels.
869 // This should really be set up elsewhere. Where should it go? 1152 // This should really be set up elsewhere. Where should it go?
@@ -899,8 +1182,10 @@ void LLVoiceClient::terminate()
899{ 1182{
900 if(gVoiceClient) 1183 if(gVoiceClient)
901 { 1184 {
902 gVoiceClient->sessionTerminateSendMessage(); 1185// gVoiceClient->leaveAudioSession();
903 gVoiceClient->logout(); 1186 gVoiceClient->logout();
1187 // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to.
1188 // ms_sleep(2000);
904 gVoiceClient->connectorShutdown(); 1189 gVoiceClient->connectorShutdown();
905 gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. 1190 gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
906 1191
@@ -925,8 +1210,6 @@ void LLVoiceClient::updateSettings()
925 setPTTKey(keyString); 1210 setPTTKey(keyString);
926 setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); 1211 setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
927 setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); 1212 setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
928 std::string serverName = gSavedSettings.getString("VivoxDebugServerName");
929 setVivoxDebugServerName(serverName);
930 1213
931 std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); 1214 std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
932 setCaptureDevice(inputDevice); 1215 setCaptureDevice(inputDevice);
@@ -949,9 +1232,10 @@ bool LLVoiceClient::writeString(const std::string &str)
949 apr_size_t size = (apr_size_t)str.size(); 1232 apr_size_t size = (apr_size_t)str.size();
950 apr_size_t written = size; 1233 apr_size_t written = size;
951 1234
952 LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; 1235 //MARK: Turn this on to log outgoing XML
1236// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
953 1237
954 // MBW -- XXX -- check return code - sockets will fail (broken, etc.) 1238 // check return code - sockets will fail (broken, etc.)
955 err = apr_socket_send( 1239 err = apr_socket_send(
956 mSocket->getSocket(), 1240 mSocket->getSocket(),
957 (const char*)str.data(), 1241 (const char*)str.data(),
@@ -962,7 +1246,7 @@ bool LLVoiceClient::writeString(const std::string &str)
962 // Success. 1246 // Success.
963 result = true; 1247 result = true;
964 } 1248 }
965 // MBW -- XXX -- handle partial writes (written is number of bytes written) 1249 // TODO: handle partial writes (written is number of bytes written)
966 // Need to set socket to non-blocking before this will work. 1250 // Need to set socket to non-blocking before this will work.
967// else if(APR_STATUS_IS_EAGAIN(err)) 1251// else if(APR_STATUS_IS_EAGAIN(err))
968// { 1252// {
@@ -986,7 +1270,7 @@ bool LLVoiceClient::writeString(const std::string &str)
986void LLVoiceClient::connectorCreate() 1270void LLVoiceClient::connectorCreate()
987{ 1271{
988 std::ostringstream stream; 1272 std::ostringstream stream;
989 std::string logpath; 1273 std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
990 std::string loglevel = "0"; 1274 std::string loglevel = "0";
991 1275
992 // Transition to stateConnectorStarted when the connector handle comes back. 1276 // Transition to stateConnectorStarted when the connector handle comes back.
@@ -998,20 +1282,20 @@ void LLVoiceClient::connectorCreate()
998 { 1282 {
999 LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; 1283 LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
1000 loglevel = "10"; 1284 loglevel = "10";
1001 logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
1002 } 1285 }
1003 1286
1004 stream 1287 stream
1005 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">" 1288 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
1006 << "<ClientName>V2 SDK</ClientName>" 1289 << "<ClientName>V2 SDK</ClientName>"
1007 << "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>" 1290 << "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
1291 << "<Mode>Normal</Mode>"
1008 << "<Logging>" 1292 << "<Logging>"
1009 << "<Enabled>false</Enabled>"
1010 << "<Folder>" << logpath << "</Folder>" 1293 << "<Folder>" << logpath << "</Folder>"
1011 << "<FileNamePrefix>Connector</FileNamePrefix>" 1294 << "<FileNamePrefix>Connector</FileNamePrefix>"
1012 << "<FileNameSuffix>.log</FileNameSuffix>" 1295 << "<FileNameSuffix>.log</FileNameSuffix>"
1013 << "<LogLevel>" << loglevel << "</LogLevel>" 1296 << "<LogLevel>" << loglevel << "</LogLevel>"
1014 << "</Logging>" 1297 << "</Logging>"
1298 << "<Application>SecondLifeViewer.1</Application>"
1015 << "</Request>\n\n\n"; 1299 << "</Request>\n\n\n";
1016 1300
1017 writeString(stream.str()); 1301 writeString(stream.str());
@@ -1049,20 +1333,6 @@ void LLVoiceClient::userAuthorized(const std::string& firstName, const std::stri
1049 1333
1050 sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); 1334 sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid();
1051 1335
1052 // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.
1053 if(sConnectingToAgni)
1054 {
1055 // Use the release account server
1056 mAccountServerName = "bhr.vivox.com";
1057 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
1058 }
1059 else
1060 {
1061 // Use the development account server
1062 mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
1063 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
1064 }
1065
1066 mAccountName = nameFromID(agentID); 1336 mAccountName = nameFromID(agentID);
1067} 1337}
1068 1338
@@ -1084,24 +1354,66 @@ void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
1084} 1354}
1085 1355
1086void LLVoiceClient::login( 1356void LLVoiceClient::login(
1087 const std::string& accountName, 1357 const std::string& account_name,
1088 const std::string &password) 1358 const std::string& password,
1359 const std::string& voice_sip_uri_hostname,
1360 const std::string& voice_account_server_uri)
1089{ 1361{
1362 mVoiceSIPURIHostName = voice_sip_uri_hostname;
1363 mVoiceAccountServerURI = voice_account_server_uri;
1364
1090 if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut)) 1365 if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut))
1091 { 1366 {
1092 // Already logged in. This is an internal error. 1367 // Already logged in. This is an internal error.
1093 LL_ERRS("Voice") << "Can't login again. Called from wrong state." << LL_ENDL; 1368 LL_ERRS("Voice") << "Can't login again. Called from wrong state." << LL_ENDL;
1094 } 1369 }
1095 else if ( accountName != mAccountName ) 1370 else if ( account_name != mAccountName )
1096 { 1371 {
1097 //TODO: error? 1372 //TODO: error?
1098 LL_WARNS("Voice") << "Wrong account name! " << accountName 1373 LL_WARNS("Voice") << "Wrong account name! " << account_name
1099 << " instead of " << mAccountName << LL_ENDL; 1374 << " instead of " << mAccountName << LL_ENDL;
1100 } 1375 }
1101 else 1376 else
1102 { 1377 {
1103 mAccountPassword = password; 1378 mAccountPassword = password;
1104 } 1379 }
1380
1381 std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
1382
1383 if( !debugSIPURIHostName.empty() )
1384 {
1385 mVoiceSIPURIHostName = debugSIPURIHostName;
1386 }
1387
1388 if( mVoiceSIPURIHostName.empty() )
1389 {
1390 // we have an empty account server name
1391 // so we fall back to hardcoded defaults
1392
1393 if(sConnectingToAgni)
1394 {
1395 // Use the release account server
1396 mVoiceSIPURIHostName = "bhr.vivox.com";
1397 }
1398 else
1399 {
1400 // Use the development account server
1401 mVoiceSIPURIHostName = "bhd.vivox.com";
1402 }
1403 }
1404
1405 std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
1406
1407 if( !debugAccountServerURI.empty() )
1408 {
1409 mVoiceAccountServerURI = debugAccountServerURI;
1410 }
1411
1412 if( mVoiceAccountServerURI.empty() )
1413 {
1414 // If the account server URI isn't specified, construct it from the SIP URI hostname
1415 mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";
1416 }
1105} 1417}
1106 1418
1107void LLVoiceClient::idle(void* user_data) 1419void LLVoiceClient::idle(void* user_data)
@@ -1124,6 +1436,7 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
1124 CASE(stateDaemonLaunched); 1436 CASE(stateDaemonLaunched);
1125 CASE(stateConnecting); 1437 CASE(stateConnecting);
1126 CASE(stateIdle); 1438 CASE(stateIdle);
1439 CASE(stateNeedsProvision);
1127 CASE(stateConnectorStart); 1440 CASE(stateConnectorStart);
1128 CASE(stateConnectorStarting); 1441 CASE(stateConnectorStarting);
1129 CASE(stateConnectorStarted); 1442 CASE(stateConnectorStarted);
@@ -1132,12 +1445,11 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
1132 CASE(stateNeedsLogin); 1445 CASE(stateNeedsLogin);
1133 CASE(stateLoggingIn); 1446 CASE(stateLoggingIn);
1134 CASE(stateLoggedIn); 1447 CASE(stateLoggedIn);
1448 CASE(stateCreatingSessionGroup);
1135 CASE(stateNoChannel); 1449 CASE(stateNoChannel);
1136 CASE(stateMicTuningStart); 1450 CASE(stateMicTuningStart);
1137 CASE(stateMicTuningRunning); 1451 CASE(stateMicTuningRunning);
1138 CASE(stateMicTuningStop); 1452 CASE(stateMicTuningStop);
1139 CASE(stateSessionCreate);
1140 CASE(stateSessionConnect);
1141 CASE(stateJoiningSession); 1453 CASE(stateJoiningSession);
1142 CASE(stateSessionJoined); 1454 CASE(stateSessionJoined);
1143 CASE(stateRunning); 1455 CASE(stateRunning);
@@ -1176,6 +1488,7 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv
1176 CASE(STATUS_JOINING); 1488 CASE(STATUS_JOINING);
1177 CASE(STATUS_JOINED); 1489 CASE(STATUS_JOINED);
1178 CASE(STATUS_LEFT_CHANNEL); 1490 CASE(STATUS_LEFT_CHANNEL);
1491 CASE(STATUS_VOICE_DISABLED);
1179 CASE(BEGIN_ERROR_STATUS); 1492 CASE(BEGIN_ERROR_STATUS);
1180 CASE(ERROR_CHANNEL_FULL); 1493 CASE(ERROR_CHANNEL_FULL);
1181 CASE(ERROR_CHANNEL_LOCKED); 1494 CASE(ERROR_CHANNEL_LOCKED);
@@ -1221,12 +1534,15 @@ void LLVoiceClient::stateMachine()
1221 killGateway(); 1534 killGateway();
1222 } 1535 }
1223 1536
1224 sessionTerminateSendMessage(); 1537// leaveAudioSession();
1225 logout(); 1538 logout();
1539 // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to.
1540 // ms_sleep(2000);
1226 connectorShutdown(); 1541 connectorShutdown();
1227 closeSocket(); 1542 closeSocket();
1228 removeAllParticipants(); 1543 deleteAllSessions();
1229 1544 deleteAllBuddies();
1545
1230 setState(stateDisabled); 1546 setState(stateDisabled);
1231 } 1547 }
1232 } 1548 }
@@ -1242,7 +1558,7 @@ void LLVoiceClient::stateMachine()
1242 std::string regionName = region->getName(); 1558 std::string regionName = region->getName();
1243 std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); 1559 std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
1244 1560
1245 LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL; 1561// LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL;
1246 1562
1247 // The region name starts out empty and gets filled in later. 1563 // The region name starts out empty and gets filled in later.
1248 // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. 1564 // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
@@ -1263,6 +1579,7 @@ void LLVoiceClient::stateMachine()
1263 1579
1264 switch(getState()) 1580 switch(getState())
1265 { 1581 {
1582 //MARK: stateDisabled
1266 case stateDisabled: 1583 case stateDisabled:
1267 if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode)) 1584 if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode))
1268 { 1585 {
@@ -1270,6 +1587,7 @@ void LLVoiceClient::stateMachine()
1270 } 1587 }
1271 break; 1588 break;
1272 1589
1590 //MARK: stateStart
1273 case stateStart: 1591 case stateStart:
1274 if(gSavedSettings.getBOOL("CmdLineDisableVoice")) 1592 if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
1275 { 1593 {
@@ -1300,7 +1618,9 @@ void LLVoiceClient::stateMachine()
1300 if(!LLFile::stat(exe_path, &s)) 1618 if(!LLFile::stat(exe_path, &s))
1301 { 1619 {
1302 // vivox executable exists. Build the command line and launch the daemon. 1620 // vivox executable exists. Build the command line and launch the daemon.
1303 std::string args = " -p tcp -h -c"; 1621 // SLIM SDK: these arguments are no longer necessary.
1622// std::string args = " -p tcp -h -c";
1623 std::string args;
1304 std::string cmd; 1624 std::string cmd;
1305 std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); 1625 std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
1306 1626
@@ -1385,14 +1705,15 @@ void LLVoiceClient::stateMachine()
1385 } 1705 }
1386 else 1706 else
1387 { 1707 {
1388 LL_INFOS("Voice") << exe_path << "not found." << LL_ENDL; 1708 LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
1389 } 1709 }
1390 } 1710 }
1391 else 1711 else
1392 { 1712 {
1713 // SLIM SDK: port changed from 44124 to 44125.
1393 // We can connect to a client gateway running on another host. This is useful for testing. 1714 // We can connect to a client gateway running on another host. This is useful for testing.
1394 // To do this, launch the gateway on a nearby host like this: 1715 // To do this, launch the gateway on a nearby host like this:
1395 // vivox-gw.exe -p tcp -i 0.0.0.0:44124 1716 // vivox-gw.exe -p tcp -i 0.0.0.0:44125
1396 // and put that host's IP address here. 1717 // and put that host's IP address here.
1397 mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); 1718 mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort"));
1398 } 1719 }
@@ -1404,17 +1725,23 @@ void LLVoiceClient::stateMachine()
1404 1725
1405 // Dirty the states we'll need to sync with the daemon when it comes up. 1726 // Dirty the states we'll need to sync with the daemon when it comes up.
1406 mPTTDirty = true; 1727 mPTTDirty = true;
1728 mMicVolumeDirty = true;
1407 mSpeakerVolumeDirty = true; 1729 mSpeakerVolumeDirty = true;
1730 mSpeakerMuteDirty = true;
1408 // These only need to be set if they're not default (i.e. empty string). 1731 // These only need to be set if they're not default (i.e. empty string).
1409 mCaptureDeviceDirty = !mCaptureDevice.empty(); 1732 mCaptureDeviceDirty = !mCaptureDevice.empty();
1410 mRenderDeviceDirty = !mRenderDevice.empty(); 1733 mRenderDeviceDirty = !mRenderDevice.empty();
1734
1735 mMainSessionGroupHandle.clear();
1411 } 1736 }
1412 break; 1737 break;
1413 1738
1739 //MARK: stateDaemonLaunched
1414 case stateDaemonLaunched: 1740 case stateDaemonLaunched:
1415 LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
1416 if(mUpdateTimer.hasExpired()) 1741 if(mUpdateTimer.hasExpired())
1417 { 1742 {
1743 LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
1744
1418 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); 1745 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
1419 1746
1420 if(!mSocket) 1747 if(!mSocket)
@@ -1435,6 +1762,7 @@ void LLVoiceClient::stateMachine()
1435 } 1762 }
1436 break; 1763 break;
1437 1764
1765 //MARK: stateConnecting
1438 case stateConnecting: 1766 case stateConnecting:
1439 // Can't do this until we have the pump available. 1767 // Can't do this until we have the pump available.
1440 if(mPump) 1768 if(mPump)
@@ -1457,6 +1785,7 @@ void LLVoiceClient::stateMachine()
1457 1785
1458 break; 1786 break;
1459 1787
1788 //MARK: stateIdle
1460 case stateIdle: 1789 case stateIdle:
1461 // Initial devices query 1790 // Initial devices query
1462 getCaptureDevicesSendMessage(); 1791 getCaptureDevicesSendMessage();
@@ -1464,17 +1793,48 @@ void LLVoiceClient::stateMachine()
1464 1793
1465 mLoginRetryCount = 0; 1794 mLoginRetryCount = 0;
1466 1795
1467 setState(stateConnectorStart); 1796 setState(stateNeedsProvision);
1468 1797
1469 break; 1798 break;
1470 1799
1800 //MARK: stateNeedsProvision
1801 case stateNeedsProvision:
1802 if(!mVoiceEnabled)
1803 {
1804 // We were never logged in. This will shut down the connector.
1805 setState(stateLoggedOut);
1806 }
1807 else if(!mAccountName.empty())
1808 {
1809 LLViewerRegion *region = gAgent.getRegion();
1810
1811 if(region)
1812 {
1813 if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
1814 {
1815 if ( mAccountPassword.empty() )
1816 {
1817 requestVoiceAccountProvision();
1818 }
1819 setState(stateConnectorStart);
1820 }
1821 }
1822 }
1823 else if(mTuningMode)
1824 {
1825 mTuningExitState = stateNeedsProvision;
1826 setState(stateMicTuningStart);
1827 }
1828 break;
1829
1830 //MARK: stateConnectorStart
1471 case stateConnectorStart: 1831 case stateConnectorStart:
1472 if(!mVoiceEnabled) 1832 if(!mVoiceEnabled)
1473 { 1833 {
1474 // We were never logged in. This will shut down the connector. 1834 // We were never logged in. This will shut down the connector.
1475 setState(stateLoggedOut); 1835 setState(stateLoggedOut);
1476 } 1836 }
1477 else if(!mAccountServerURI.empty()) 1837 else if(!mVoiceAccountServerURI.empty())
1478 { 1838 {
1479 connectorCreate(); 1839 connectorCreate();
1480 } 1840 }
@@ -1485,34 +1845,26 @@ void LLVoiceClient::stateMachine()
1485 } 1845 }
1486 break; 1846 break;
1487 1847
1848 //MARK: stateConnectorStarting
1488 case stateConnectorStarting: // waiting for connector handle 1849 case stateConnectorStarting: // waiting for connector handle
1489 // connectorCreateResponse() will transition from here to stateConnectorStarted. 1850 // connectorCreateResponse() will transition from here to stateConnectorStarted.
1490 break; 1851 break;
1491 1852
1853 //MARK: stateConnectorStarted
1492 case stateConnectorStarted: // connector handle received 1854 case stateConnectorStarted: // connector handle received
1493 if(!mVoiceEnabled) 1855 if(!mVoiceEnabled)
1494 { 1856 {
1495 // We were never logged in. This will shut down the connector. 1857 // We were never logged in. This will shut down the connector.
1496 setState(stateLoggedOut); 1858 setState(stateLoggedOut);
1497 } 1859 }
1498 else if(!mAccountName.empty()) 1860 else
1499 { 1861 {
1500 LLViewerRegion *region = gAgent.getRegion(); 1862 // The connector is started. Send a login message.
1501 1863 setState(stateNeedsLogin);
1502 if(region)
1503 {
1504 if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
1505 {
1506 if ( mAccountPassword.empty() )
1507 {
1508 requestVoiceAccountProvision();
1509 }
1510 setState(stateNeedsLogin);
1511 }
1512 }
1513 } 1864 }
1514 break; 1865 break;
1515 1866
1867 //MARK: stateMicTuningStart
1516 case stateMicTuningStart: 1868 case stateMicTuningStart:
1517 if(mUpdateTimer.hasExpired()) 1869 if(mUpdateTimer.hasExpired())
1518 { 1870 {
@@ -1520,19 +1872,9 @@ void LLVoiceClient::stateMachine()
1520 { 1872 {
1521 // These can't be changed while in tuning mode. Set them before starting. 1873 // These can't be changed while in tuning mode. Set them before starting.
1522 std::ostringstream stream; 1874 std::ostringstream stream;
1523 1875
1524 if(mCaptureDeviceDirty) 1876 buildSetCaptureDevice(stream);
1525 { 1877 buildSetRenderDevice(stream);
1526 buildSetCaptureDevice(stream);
1527 }
1528
1529 if(mRenderDeviceDirty)
1530 {
1531 buildSetRenderDevice(stream);
1532 }
1533
1534 mCaptureDeviceDirty = false;
1535 mRenderDeviceDirty = false;
1536 1878
1537 if(!stream.str().empty()) 1879 if(!stream.str().empty())
1538 { 1880 {
@@ -1554,6 +1896,7 @@ void LLVoiceClient::stateMachine()
1554 1896
1555 break; 1897 break;
1556 1898
1899 //MARK: stateMicTuningRunning
1557 case stateMicTuningRunning: 1900 case stateMicTuningRunning:
1558 if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty) 1901 if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty)
1559 { 1902 {
@@ -1595,6 +1938,7 @@ void LLVoiceClient::stateMachine()
1595 } 1938 }
1596 break; 1939 break;
1597 1940
1941 //MARK: stateMicTuningStop
1598 case stateMicTuningStop: 1942 case stateMicTuningStop:
1599 { 1943 {
1600 // transition out of mic tuning 1944 // transition out of mic tuning
@@ -1609,6 +1953,7 @@ void LLVoiceClient::stateMachine()
1609 } 1953 }
1610 break; 1954 break;
1611 1955
1956 //MARK: stateLoginRetry
1612 case stateLoginRetry: 1957 case stateLoginRetry:
1613 if(mLoginRetryCount == 0) 1958 if(mLoginRetryCount == 0)
1614 { 1959 {
@@ -1632,6 +1977,7 @@ void LLVoiceClient::stateMachine()
1632 } 1977 }
1633 break; 1978 break;
1634 1979
1980 //MARK: stateLoginRetryWait
1635 case stateLoginRetryWait: 1981 case stateLoginRetryWait:
1636 if(mUpdateTimer.hasExpired()) 1982 if(mUpdateTimer.hasExpired())
1637 { 1983 {
@@ -1639,6 +1985,7 @@ void LLVoiceClient::stateMachine()
1639 } 1985 }
1640 break; 1986 break;
1641 1987
1988 //MARK: stateNeedsLogin
1642 case stateNeedsLogin: 1989 case stateNeedsLogin:
1643 if(!mAccountPassword.empty()) 1990 if(!mAccountPassword.empty())
1644 { 1991 {
@@ -1647,16 +1994,22 @@ void LLVoiceClient::stateMachine()
1647 } 1994 }
1648 break; 1995 break;
1649 1996
1997 //MARK: stateLoggingIn
1650 case stateLoggingIn: // waiting for account handle 1998 case stateLoggingIn: // waiting for account handle
1651 // loginResponse() will transition from here to stateLoggedIn. 1999 // loginResponse() will transition from here to stateLoggedIn.
1652 break; 2000 break;
1653 2001
2002 //MARK: stateLoggedIn
1654 case stateLoggedIn: // account handle received 2003 case stateLoggedIn: // account handle received
1655 // Initial kick-off of channel lookup logic
1656 parcelChanged();
1657 2004
1658 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); 2005 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
1659 2006
2007 // request the current set of block rules (we'll need them when updating the friends list)
2008 accountListBlockRulesSendMessage();
2009
2010 // request the current set of auto-accept rules
2011 accountListAutoAcceptRulesSendMessage();
2012
1660 // Set up the mute list observer if it hasn't been set up already. 2013 // Set up the mute list observer if it hasn't been set up already.
1661 if((!sMuteListListener_listening)) 2014 if((!sMuteListListener_listening))
1662 { 2015 {
@@ -1664,13 +2017,67 @@ void LLVoiceClient::stateMachine()
1664 sMuteListListener_listening = true; 2017 sMuteListListener_listening = true;
1665 } 2018 }
1666 2019
2020 // Set up the friends list observer if it hasn't been set up already.
2021 if(friendslist_listener == NULL)
2022 {
2023 friendslist_listener = new LLVoiceClientFriendsObserver;
2024 LLAvatarTracker::instance().addObserver(friendslist_listener);
2025 }
2026
2027 // Set the initial state of mic mute, local speaker volume, etc.
2028 {
2029 std::ostringstream stream;
2030
2031 buildLocalAudioUpdates(stream);
2032
2033 if(!stream.str().empty())
2034 {
2035 writeString(stream.str());
2036 }
2037 }
2038
2039#if USE_SESSION_GROUPS
2040 // create the main session group
2041 sessionGroupCreateSendMessage();
2042
2043 setState(stateCreatingSessionGroup);
2044#else
2045 // Not using session groups -- skip the stateCreatingSessionGroup state.
1667 setState(stateNoChannel); 2046 setState(stateNoChannel);
2047
2048 // Initial kick-off of channel lookup logic
2049 parcelChanged();
2050#endif
2051 break;
2052
2053 //MARK: stateCreatingSessionGroup
2054 case stateCreatingSessionGroup:
2055 if(mSessionTerminateRequested || !mVoiceEnabled)
2056 {
2057 // TODO: Question: is this the right way out of this state
2058 setState(stateSessionTerminated);
2059 }
2060 else if(!mMainSessionGroupHandle.empty())
2061 {
2062 setState(stateNoChannel);
2063
2064 // Start looped recording (needed for "panic button" anti-griefing tool)
2065 recordingLoopStart();
2066
2067 // Initial kick-off of channel lookup logic
2068 parcelChanged();
2069 }
1668 break; 2070 break;
1669 2071
2072 //MARK: stateNoChannel
1670 case stateNoChannel: 2073 case stateNoChannel:
2074 // Do this here as well as inside sendPositionalUpdate().
2075 // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
2076 sendFriendsListUpdates();
2077
1671 if(mSessionTerminateRequested || !mVoiceEnabled) 2078 if(mSessionTerminateRequested || !mVoiceEnabled)
1672 { 2079 {
1673 // MBW -- XXX -- Is this the right way out of this state? 2080 // TODO: Question: Is this the right way out of this state?
1674 setState(stateSessionTerminated); 2081 setState(stateSessionTerminated);
1675 } 2082 }
1676 else if(mTuningMode) 2083 else if(mTuningMode)
@@ -1678,30 +2085,49 @@ void LLVoiceClient::stateMachine()
1678 mTuningExitState = stateNoChannel; 2085 mTuningExitState = stateNoChannel;
1679 setState(stateMicTuningStart); 2086 setState(stateMicTuningStart);
1680 } 2087 }
1681 else if(!mNextSessionHandle.empty()) 2088 else if(sessionNeedsRelog(mNextAudioSession))
1682 { 2089 {
1683 setState(stateSessionConnect); 2090 requestRelog();
2091 setState(stateSessionTerminated);
2092 }
2093 else if(mNextAudioSession)
2094 {
2095 sessionState *oldSession = mAudioSession;
2096
2097 mAudioSession = mNextAudioSession;
2098 if(!mAudioSession->mReconnect)
2099 {
2100 mNextAudioSession = NULL;
2101 }
2102
2103 // The old session may now need to be deleted.
2104 reapSession(oldSession);
2105
2106 if(!mAudioSession->mHandle.empty())
2107 {
2108 // Connect to a session by session handle
2109
2110 sessionMediaConnectSendMessage(mAudioSession);
2111 }
2112 else
2113 {
2114 // Connect to a session by URI
2115 sessionCreateSendMessage(mAudioSession, true, false);
2116 }
2117
2118 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
2119 setState(stateJoiningSession);
1684 } 2120 }
1685 else if(!mNextSessionURI.empty()) 2121 else if(!mSpatialSessionURI.empty())
1686 { 2122 {
1687 setState(stateSessionCreate); 2123 // If we're not headed elsewhere and have a spatial URI, return to spatial.
2124 switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
1688 } 2125 }
1689 break; 2126 break;
1690 2127
1691 case stateSessionCreate: 2128 //MARK: stateJoiningSession
1692 sessionCreateSendMessage();
1693 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
1694 setState(stateJoiningSession);
1695 break;
1696
1697 case stateSessionConnect:
1698 sessionConnectSendMessage();
1699 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
1700 setState(stateJoiningSession);
1701 break;
1702
1703 case stateJoiningSession: // waiting for session handle 2129 case stateJoiningSession: // waiting for session handle
1704 // sessionCreateResponse() will transition from here to stateSessionJoined. 2130 // joinedAudioSession() will transition from here to stateSessionJoined.
1705 if(!mVoiceEnabled) 2131 if(!mVoiceEnabled)
1706 { 2132 {
1707 // User bailed out during connect -- jump straight to teardown. 2133 // User bailed out during connect -- jump straight to teardown.
@@ -1709,30 +2135,27 @@ void LLVoiceClient::stateMachine()
1709 } 2135 }
1710 else if(mSessionTerminateRequested) 2136 else if(mSessionTerminateRequested)
1711 { 2137 {
1712 if(!mSessionHandle.empty()) 2138 if(mAudioSession && !mAudioSession->mHandle.empty())
1713 { 2139 {
1714 // Only allow direct exits from this state in p2p calls (for cancelling an invite). 2140 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
1715 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. 2141 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
1716 if(mSessionP2P) 2142 if(mAudioSession->mIsP2P)
1717 { 2143 {
1718 sessionTerminateSendMessage(); 2144 sessionMediaDisconnectSendMessage(mAudioSession);
1719 setState(stateSessionTerminated); 2145 setState(stateSessionTerminated);
1720 } 2146 }
1721 } 2147 }
1722 } 2148 }
1723 break; 2149 break;
1724 2150
2151 //MARK: stateSessionJoined
1725 case stateSessionJoined: // session handle received 2152 case stateSessionJoined: // session handle received
1726 // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4 2153 // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
1727 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. 2154 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
1728 // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. 2155 // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
1729 // This is a cheap way to make sure both have happened before proceeding. 2156 // This is a cheap way to make sure both have happened before proceeding.
1730 if(!mSessionHandle.empty()) 2157 if(mAudioSession && mAudioSession->mVoiceEnabled)
1731 { 2158 {
1732 // Events that need to happen when a session is joined could go here.
1733 // Maybe send initial spatial data?
1734 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
1735
1736 // Dirty state that may need to be sync'ed with the daemon. 2159 // Dirty state that may need to be sync'ed with the daemon.
1737 mPTTDirty = true; 2160 mPTTDirty = true;
1738 mSpeakerVolumeDirty = true; 2161 mSpeakerVolumeDirty = true;
@@ -1743,6 +2166,11 @@ void LLVoiceClient::stateMachine()
1743 // Start the throttle timer 2166 // Start the throttle timer
1744 mUpdateTimer.start(); 2167 mUpdateTimer.start();
1745 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); 2168 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
2169
2170 // Events that need to happen when a session is joined could go here.
2171 // Maybe send initial spatial data?
2172 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
2173
1746 } 2174 }
1747 else if(!mVoiceEnabled) 2175 else if(!mVoiceEnabled)
1748 { 2176 {
@@ -1753,21 +2181,20 @@ void LLVoiceClient::stateMachine()
1753 { 2181 {
1754 // Only allow direct exits from this state in p2p calls (for cancelling an invite). 2182 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
1755 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. 2183 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
1756 if(mSessionP2P) 2184 if(mAudioSession && mAudioSession->mIsP2P)
1757 { 2185 {
1758 sessionTerminateSendMessage(); 2186 sessionMediaDisconnectSendMessage(mAudioSession);
1759 setState(stateSessionTerminated); 2187 setState(stateSessionTerminated);
1760 } 2188 }
1761 } 2189 }
1762 break; 2190 break;
1763 2191
2192 //MARK: stateRunning
1764 case stateRunning: // steady state 2193 case stateRunning: // steady state
1765 // sessionTerminateSendMessage() will transition from here to stateLeavingSession
1766
1767 // Disabling voice or disconnect requested. 2194 // Disabling voice or disconnect requested.
1768 if(!mVoiceEnabled || mSessionTerminateRequested) 2195 if(!mVoiceEnabled || mSessionTerminateRequested)
1769 { 2196 {
1770 sessionTerminateSendMessage(); 2197 leaveAudioSession();
1771 } 2198 }
1772 else 2199 else
1773 { 2200 {
@@ -1800,7 +2227,7 @@ void LLVoiceClient::stateMachine()
1800 } 2227 }
1801 } 2228 }
1802 2229
1803 if(mNonSpatialChannel) 2230 if(!inSpatialChannel())
1804 { 2231 {
1805 // When in a non-spatial channel, never send positional updates. 2232 // When in a non-spatial channel, never send positional updates.
1806 mSpatialCoordsDirty = false; 2233 mSpatialCoordsDirty = false;
@@ -1813,7 +2240,7 @@ void LLVoiceClient::stateMachine()
1813 2240
1814 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast) 2241 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
1815 // or every 10hz, whichever is sooner. 2242 // or every 10hz, whichever is sooner.
1816 if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired()) 2243 if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
1817 { 2244 {
1818 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); 2245 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
1819 sendPositionalUpdate(); 2246 sendPositionalUpdate();
@@ -1821,25 +2248,38 @@ void LLVoiceClient::stateMachine()
1821 } 2248 }
1822 break; 2249 break;
1823 2250
2251 //MARK: stateLeavingSession
1824 case stateLeavingSession: // waiting for terminate session response 2252 case stateLeavingSession: // waiting for terminate session response
1825 // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. 2253 // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
1826 break; 2254 break;
1827 2255
2256 //MARK: stateSessionTerminated
1828 case stateSessionTerminated: 2257 case stateSessionTerminated:
1829 // Always reset the terminate request flag when we get here.
1830 mSessionTerminateRequested = false;
1831 2258
2259 // Must do this first, since it uses mAudioSession.
1832 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); 2260 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
2261
2262 if(mAudioSession)
2263 {
2264 sessionState *oldSession = mAudioSession;
2265
2266 mAudioSession = NULL;
2267 // We just notified status observers about this change. Don't do it again.
2268 mAudioSessionChanged = false;
1833 2269
1834 if(mVoiceEnabled) 2270 // The old session may now need to be deleted.
2271 reapSession(oldSession);
2272 }
2273 else
1835 { 2274 {
1836 // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now. 2275 LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
1837 // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter. 2276 }
1838 if(mNextSessionSpatial && mNextSessionURI.empty()) 2277
1839 { 2278 // Always reset the terminate request flag when we get here.
1840 mNonSpatialChannel = !mNextSessionSpatial; 2279 mSessionTerminateRequested = false;
1841 } 2280
1842 2281 if(mVoiceEnabled && !mRelogRequested)
2282 {
1843 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). 2283 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
1844 setState(stateNoChannel); 2284 setState(stateNoChannel);
1845 } 2285 }
@@ -1847,49 +2287,71 @@ void LLVoiceClient::stateMachine()
1847 { 2287 {
1848 // Shutting down voice, continue with disconnecting. 2288 // Shutting down voice, continue with disconnecting.
1849 logout(); 2289 logout();
2290
2291 // The state machine will take it from here
2292 mRelogRequested = false;
1850 } 2293 }
1851 2294
1852 break; 2295 break;
1853 2296
2297 //MARK: stateLoggingOut
1854 case stateLoggingOut: // waiting for logout response 2298 case stateLoggingOut: // waiting for logout response
1855 // The handler for the Account.Logout response will transition from here to stateLoggedOut. 2299 // The handler for the Account.Logout response will transition from here to stateLoggedOut.
1856 break; 2300 break;
2301 //MARK: stateLoggedOut
1857 case stateLoggedOut: // logout response received 2302 case stateLoggedOut: // logout response received
1858 // shut down the connector 2303 // shut down the connector
1859 connectorShutdown(); 2304 connectorShutdown();
1860 break; 2305 break;
1861 2306
2307 //MARK: stateConnectorStopping
1862 case stateConnectorStopping: // waiting for connector stop 2308 case stateConnectorStopping: // waiting for connector stop
1863 // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. 2309 // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
1864 break; 2310 break;
1865 2311
2312 //MARK: stateConnectorStopped
1866 case stateConnectorStopped: // connector stop received 2313 case stateConnectorStopped: // connector stop received
1867 // Clean up and reset everything. 2314 // Clean up and reset everything.
1868 closeSocket(); 2315 closeSocket();
1869 removeAllParticipants(); 2316 deleteAllSessions();
2317 deleteAllBuddies();
1870 setState(stateDisabled); 2318 setState(stateDisabled);
1871 break; 2319 break;
1872 2320
2321 //MARK: stateConnectorFailed
1873 case stateConnectorFailed: 2322 case stateConnectorFailed:
1874 setState(stateConnectorFailedWaiting); 2323 setState(stateConnectorFailedWaiting);
1875 break; 2324 break;
2325 //MARK: stateConnectorFailedWaiting
1876 case stateConnectorFailedWaiting: 2326 case stateConnectorFailedWaiting:
1877 break; 2327 break;
1878 2328
2329 //MARK: stateLoginFailed
1879 case stateLoginFailed: 2330 case stateLoginFailed:
1880 setState(stateLoginFailedWaiting); 2331 setState(stateLoginFailedWaiting);
1881 break; 2332 break;
2333 //MARK: stateLoginFailedWaiting
1882 case stateLoginFailedWaiting: 2334 case stateLoginFailedWaiting:
1883 // No way to recover from these. Yet. 2335 // No way to recover from these. Yet.
1884 break; 2336 break;
1885 2337
2338 //MARK: stateJoinSessionFailed
1886 case stateJoinSessionFailed: 2339 case stateJoinSessionFailed:
1887 // Transition to error state. Send out any notifications here. 2340 // Transition to error state. Send out any notifications here.
1888 LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << LL_ENDL; 2341 if(mAudioSession)
2342 {
2343 LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
2344 }
2345 else
2346 {
2347 LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
2348 }
2349
1889 notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); 2350 notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
1890 setState(stateJoinSessionFailedWaiting); 2351 setState(stateJoinSessionFailedWaiting);
1891 break; 2352 break;
1892 2353
2354 //MARK: stateJoinSessionFailedWaiting
1893 case stateJoinSessionFailedWaiting: 2355 case stateJoinSessionFailedWaiting:
1894 // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. 2356 // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
1895 // Region crossings may leave this state and try the join again. 2357 // Region crossings may leave this state and try the join again.
@@ -1899,22 +2361,29 @@ void LLVoiceClient::stateMachine()
1899 } 2361 }
1900 break; 2362 break;
1901 2363
2364 //MARK: stateJail
1902 case stateJail: 2365 case stateJail:
1903 // We have given up. Do nothing. 2366 // We have given up. Do nothing.
1904 break; 2367 break;
1905 2368
1906 case stateMicTuningNoLogin: 2369 //MARK: stateMicTuningNoLogin
1907 // *TODO: Implement me. 2370 case stateMicTuningNoLogin:
1908 LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL; 2371 // *TODO: Implement me.
2372 LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL;
1909 break; 2373 break;
1910 } 2374 }
1911 2375
1912 if(mParticipantMapChanged) 2376 if(mAudioSession && mAudioSession->mParticipantsChanged)
1913 { 2377 {
1914 mParticipantMapChanged = false; 2378 mAudioSession->mParticipantsChanged = false;
1915 notifyObservers(); 2379 mAudioSessionChanged = true;
2380 }
2381
2382 if(mAudioSessionChanged)
2383 {
2384 mAudioSessionChanged = false;
2385 notifyParticipantObservers();
1916 } 2386 }
1917
1918} 2387}
1919 2388
1920void LLVoiceClient::closeSocket(void) 2389void LLVoiceClient::closeSocket(void)
@@ -1932,6 +2401,9 @@ void LLVoiceClient::loginSendMessage()
1932 << "<AccountName>" << mAccountName << "</AccountName>" 2401 << "<AccountName>" << mAccountName << "</AccountName>"
1933 << "<AccountPassword>" << mAccountPassword << "</AccountPassword>" 2402 << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
1934 << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>" 2403 << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
2404 << "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
2405 << "<BuddyManagementMode>Application</BuddyManagementMode>"
2406 << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
1935 << "</Request>\n\n\n"; 2407 << "</Request>\n\n\n";
1936 2408
1937 writeString(stream.str()); 2409 writeString(stream.str());
@@ -1939,7 +2411,10 @@ void LLVoiceClient::loginSendMessage()
1939 2411
1940void LLVoiceClient::logout() 2412void LLVoiceClient::logout()
1941{ 2413{
1942 mAccountPassword = ""; 2414 // Ensure that we'll re-request provisioning before logging in again
2415 mAccountPassword.clear();
2416 mVoiceAccountServerURI.clear();
2417
1943 setState(stateLoggingOut); 2418 setState(stateLoggingOut);
1944 logoutSendMessage(); 2419 logoutSendMessage();
1945} 2420}
@@ -1961,78 +2436,164 @@ void LLVoiceClient::logoutSendMessage()
1961 } 2436 }
1962} 2437}
1963 2438
1964void LLVoiceClient::channelGetListSendMessage() 2439void LLVoiceClient::accountListBlockRulesSendMessage()
1965{ 2440{
1966 std::ostringstream stream; 2441 if(!mAccountHandle.empty())
1967 stream 2442 {
1968 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">" 2443 std::ostringstream stream;
1969 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
1970 << "</Request>\n\n\n";
1971 2444
1972 writeString(stream.str()); 2445 LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
2446
2447 stream
2448 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
2449 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
2450 << "</Request>"
2451 << "\n\n\n";
2452
2453 writeString(stream.str());
2454 }
2455}
2456
2457void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
2458{
2459 if(!mAccountHandle.empty())
2460 {
2461 std::ostringstream stream;
2462
2463 LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
2464
2465 stream
2466 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
2467 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
2468 << "</Request>"
2469 << "\n\n\n";
2470
2471 writeString(stream.str());
2472 }
1973} 2473}
1974 2474
1975void LLVoiceClient::sessionCreateSendMessage() 2475void LLVoiceClient::sessionGroupCreateSendMessage()
1976{ 2476{
1977 LL_DEBUGS("Voice") << "requesting join: " << mNextSessionURI << LL_ENDL; 2477 if(!mAccountHandle.empty())
2478 {
2479 std::ostringstream stream;
2480
2481 LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
1978 2482
1979 mSessionURI = mNextSessionURI; 2483 stream
1980 mNonSpatialChannel = !mNextSessionSpatial; 2484 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
1981 mSessionResetOnClose = mNextSessionResetOnClose; 2485 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
1982 mNextSessionResetOnClose = false; 2486 << "<Type>Normal</Type>"
1983 if(mNextSessionNoReconnect) 2487 << "</Request>"
2488 << "\n\n\n";
2489
2490 writeString(stream.str());
2491 }
2492}
2493
2494void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
2495{
2496 LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
2497
2498 session->mCreateInProgress = true;
2499 if(startAudio)
1984 { 2500 {
1985 // Clear the stashed URI so it can't reconnect 2501 session->mMediaConnectInProgress = true;
1986 mNextSessionURI.clear();
1987 } 2502 }
1988 // Only p2p sessions are created with "no reconnect".
1989 mSessionP2P = mNextSessionNoReconnect;
1990 2503
1991 std::ostringstream stream; 2504 std::ostringstream stream;
1992 stream 2505 stream
1993 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">" 2506 << "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
1994 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" 2507 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
1995 << "<URI>" << mSessionURI << "</URI>"; 2508 << "<URI>" << session->mSIPURI << "</URI>";
1996 2509
1997 static const std::string allowed_chars = 2510 static const std::string allowed_chars =
1998 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 2511 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
1999 "0123456789" 2512 "0123456789"
2000 "-._~"; 2513 "-._~";
2001 2514
2002 if(!mNextSessionHash.empty()) 2515 if(!session->mHash.empty())
2003 { 2516 {
2004 stream 2517 stream
2005 << "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>" 2518 << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
2006 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; 2519 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
2007 } 2520 }
2008 2521
2009 stream 2522 stream
2523 << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
2524 << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
2010 << "<Name>" << mChannelName << "</Name>" 2525 << "<Name>" << mChannelName << "</Name>"
2011 << "</Request>\n\n\n"; 2526 << "</Request>\n\n\n";
2012 writeString(stream.str()); 2527 writeString(stream.str());
2013} 2528}
2014 2529
2015void LLVoiceClient::sessionConnectSendMessage() 2530void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
2016{ 2531{
2017 LL_DEBUGS("Voice") << "connecting to session handle: " << mNextSessionHandle << LL_ENDL; 2532 LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
2018 2533
2019 mSessionHandle = mNextSessionHandle; 2534 session->mCreateInProgress = true;
2020 mSessionURI = mNextP2PSessionURI; 2535 if(startAudio)
2021 mNextSessionHandle.clear(); // never want to re-use these. 2536 {
2022 mNextP2PSessionURI.clear(); 2537 session->mMediaConnectInProgress = true;
2023 mNonSpatialChannel = !mNextSessionSpatial; 2538 }
2024 mSessionResetOnClose = mNextSessionResetOnClose; 2539
2025 mNextSessionResetOnClose = false; 2540 std::string password;
2026 // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session. 2541 if(!session->mHash.empty())
2027 mSessionP2P = true; 2542 {
2543 static const std::string allowed_chars =
2544 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2545 "0123456789"
2546 "-._~"
2547 ;
2548 password = LLURI::escape(session->mHash, allowed_chars);
2549 }
2550
2551 std::ostringstream stream;
2552 stream
2553 << "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
2554 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2555 << "<URI>" << session->mSIPURI << "</URI>"
2556 << "<Name>" << mChannelName << "</Name>"
2557 << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
2558 << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
2559 << "<Password>" << password << "</Password>"
2560 << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
2561 << "</Request>\n\n\n"
2562 ;
2563
2564 writeString(stream.str());
2565}
2566
2567void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
2568{
2569 LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
2570
2571 session->mMediaConnectInProgress = true;
2028 2572
2029 std::ostringstream stream; 2573 std::ostringstream stream;
2574
2575 stream
2576 << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
2577 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2578 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2579 << "<Media>Audio</Media>"
2580 << "</Request>\n\n\n";
2581
2582 writeString(stream.str());
2583}
2584
2585void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
2586{
2587 LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
2030 2588
2589 std::ostringstream stream;
2590
2031 stream 2591 stream
2032 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">" 2592 << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
2033 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>" 2593 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2034 << "<AudioMedia>default</AudioMedia>" 2594 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2035 << "</Request>\n\n\n"; 2595 << "</Request>\n\n\n";
2596
2036 writeString(stream.str()); 2597 writeString(stream.str());
2037} 2598}
2038 2599
@@ -2041,52 +2602,112 @@ void LLVoiceClient::sessionTerminate()
2041 mSessionTerminateRequested = true; 2602 mSessionTerminateRequested = true;
2042} 2603}
2043 2604
2044void LLVoiceClient::sessionTerminateSendMessage() 2605void LLVoiceClient::requestRelog()
2045{ 2606{
2046 LL_DEBUGS("Voice") << "leaving session: " << mSessionURI << LL_ENDL; 2607 mSessionTerminateRequested = true;
2608 mRelogRequested = true;
2609}
2047 2610
2048 switch(getState()) 2611
2612void LLVoiceClient::leaveAudioSession()
2613{
2614 if(mAudioSession)
2049 { 2615 {
2050 case stateNoChannel: 2616 LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
2051 // In this case, we want to pretend the join failed so our state machine doesn't get stuck. 2617
2052 // Skip the join failed transition state so we don't send out error notifications. 2618 switch(getState())
2053 setState(stateJoinSessionFailedWaiting); 2619 {
2054 break; 2620 case stateNoChannel:
2055 case stateJoiningSession: 2621 // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
2056 case stateSessionJoined: 2622 // Skip the join failed transition state so we don't send out error notifications.
2057 case stateRunning: 2623 setState(stateJoinSessionFailedWaiting);
2058 if(!mSessionHandle.empty()) 2624 break;
2059 { 2625 case stateJoiningSession:
2060 sessionTerminateByHandle(mSessionHandle); 2626 case stateSessionJoined:
2061 setState(stateLeavingSession); 2627 case stateRunning:
2062 } 2628 if(!mAudioSession->mHandle.empty())
2063 else 2629 {
2064 { 2630
2065 LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; 2631#if RECORD_EVERYTHING
2632 // HACK: for testing only
2633 // Save looped recording
2634 std::string savepath("/tmp/vivoxrecording");
2635 {
2636 time_t now = time(NULL);
2637 const size_t BUF_SIZE = 64;
2638 char time_str[BUF_SIZE]; /* Flawfinder: ignore */
2639
2640 strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
2641 savepath += time_str;
2642 }
2643 recordingLoopSave(savepath);
2644#endif
2645
2646 sessionMediaDisconnectSendMessage(mAudioSession);
2647 setState(stateLeavingSession);
2648 }
2649 else
2650 {
2651 LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
2652 setState(stateSessionTerminated);
2653 }
2654 break;
2655 case stateJoinSessionFailed:
2656 case stateJoinSessionFailedWaiting:
2066 setState(stateSessionTerminated); 2657 setState(stateSessionTerminated);
2067 } 2658 break;
2068 break; 2659
2069 case stateJoinSessionFailed: 2660 default:
2070 case stateJoinSessionFailedWaiting: 2661 LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
2071 setState(stateSessionTerminated); 2662 break;
2072 break; 2663 }
2073 2664 }
2074 default: 2665 else
2075 LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; 2666 {
2076 break; 2667 LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
2668 setState(stateSessionTerminated);
2077 } 2669 }
2078} 2670}
2079 2671
2080void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle) 2672void LLVoiceClient::sessionTerminateSendMessage(sessionState *session)
2081{ 2673{
2082 LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << sessionHandle << LL_ENDL;
2083
2084 std::ostringstream stream; 2674 std::ostringstream stream;
2675
2676 LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
2085 stream 2677 stream
2086 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">" 2678 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
2087 << "<SessionHandle>" << sessionHandle << "</SessionHandle>" 2679 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2088 << "</Request>" 2680 << "</Request>\n\n\n";
2089 << "\n\n\n"; 2681
2682 writeString(stream.str());
2683}
2684
2685void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
2686{
2687 std::ostringstream stream;
2688
2689 LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
2690 stream
2691 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
2692 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2693 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2694 << "<Media>Audio</Media>"
2695 << "</Request>\n\n\n";
2696
2697 writeString(stream.str());
2698
2699}
2700
2701void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
2702{
2703 std::ostringstream stream;
2704
2705 LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
2706 stream
2707 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
2708 << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
2709 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
2710 << "</Request>\n\n\n";
2090 2711
2091 writeString(stream.str()); 2712 writeString(stream.str());
2092} 2713}
@@ -2113,14 +2734,12 @@ void LLVoiceClient::getRenderDevicesSendMessage()
2113 2734
2114void LLVoiceClient::clearCaptureDevices() 2735void LLVoiceClient::clearCaptureDevices()
2115{ 2736{
2116 // MBW -- XXX -- do something here
2117 LL_DEBUGS("Voice") << "called" << LL_ENDL; 2737 LL_DEBUGS("Voice") << "called" << LL_ENDL;
2118 mCaptureDevices.clear(); 2738 mCaptureDevices.clear();
2119} 2739}
2120 2740
2121void LLVoiceClient::addCaptureDevice(const std::string& name) 2741void LLVoiceClient::addCaptureDevice(const std::string& name)
2122{ 2742{
2123 // MBW -- XXX -- do something here
2124 LL_DEBUGS("Voice") << name << LL_ENDL; 2743 LL_DEBUGS("Voice") << name << LL_ENDL;
2125 2744
2126 mCaptureDevices.push_back(name); 2745 mCaptureDevices.push_back(name);
@@ -2152,15 +2771,13 @@ void LLVoiceClient::setCaptureDevice(const std::string& name)
2152} 2771}
2153 2772
2154void LLVoiceClient::clearRenderDevices() 2773void LLVoiceClient::clearRenderDevices()
2155{ 2774{
2156 // MBW -- XXX -- do something here
2157 LL_DEBUGS("Voice") << "called" << LL_ENDL; 2775 LL_DEBUGS("Voice") << "called" << LL_ENDL;
2158 mRenderDevices.clear(); 2776 mRenderDevices.clear();
2159} 2777}
2160 2778
2161void LLVoiceClient::addRenderDevice(const std::string& name) 2779void LLVoiceClient::addRenderDevice(const std::string& name)
2162{ 2780{
2163 // MBW -- XXX -- do something here
2164 LL_DEBUGS("Voice") << name << LL_ENDL; 2781 LL_DEBUGS("Voice") << name << LL_ENDL;
2165 mRenderDevices.push_back(name); 2782 mRenderDevices.push_back(name);
2166} 2783}
@@ -2272,29 +2889,22 @@ void LLVoiceClient::tuningCaptureStopSendMessage()
2272 2889
2273void LLVoiceClient::tuningSetMicVolume(float volume) 2890void LLVoiceClient::tuningSetMicVolume(float volume)
2274{ 2891{
2275 int scaledVolume = ((int)(volume * 100.0f)) - 100; 2892 int scaled_volume = scale_mic_volume(volume);
2276 if(scaledVolume != mTuningMicVolume) 2893
2894 if(scaled_volume != mTuningMicVolume)
2277 { 2895 {
2278 mTuningMicVolume = scaledVolume; 2896 mTuningMicVolume = scaled_volume;
2279 mTuningMicVolumeDirty = true; 2897 mTuningMicVolumeDirty = true;
2280 } 2898 }
2281} 2899}
2282 2900
2283void LLVoiceClient::tuningSetSpeakerVolume(float volume) 2901void LLVoiceClient::tuningSetSpeakerVolume(float volume)
2284{ 2902{
2285 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. 2903 int scaled_volume = scale_speaker_volume(volume);
2286 // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
2287
2288 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
2289 int scaledVolume = 24; // offset scaledVolume by its default level
2290 if(volume < 0.0f)
2291 scaledVolume += ((int)(volume * 248.0f)); // (24 - (-100)) * 2
2292 else
2293 scaledVolume += ((int)(volume * 52.0f)); // (50 - 24) * 2
2294 2904
2295 if(scaledVolume != mTuningSpeakerVolume) 2905 if(scaled_volume != mTuningSpeakerVolume)
2296 { 2906 {
2297 mTuningSpeakerVolume = scaledVolume; 2907 mTuningSpeakerVolume = scaled_volume;
2298 mTuningSpeakerVolumeDirty = true; 2908 mTuningSpeakerVolumeDirty = true;
2299 } 2909 }
2300} 2910}
@@ -2334,7 +2944,8 @@ void LLVoiceClient::daemonDied()
2334 LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; 2944 LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL;
2335 2945
2336 closeSocket(); 2946 closeSocket();
2337 removeAllParticipants(); 2947 deleteAllSessions();
2948 deleteAllBuddies();
2338 2949
2339 // Try to relaunch the daemon 2950 // Try to relaunch the daemon
2340 setState(stateDisabled); 2951 setState(stateDisabled);
@@ -2344,56 +2955,185 @@ void LLVoiceClient::giveUp()
2344{ 2955{
2345 // All has failed. Clean up and stop trying. 2956 // All has failed. Clean up and stop trying.
2346 closeSocket(); 2957 closeSocket();
2347 removeAllParticipants(); 2958 deleteAllSessions();
2959 deleteAllBuddies();
2348 2960
2349 setState(stateJail); 2961 setState(stateJail);
2350} 2962}
2351 2963
2964static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
2965{
2966 F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the new position and velocity
2967 F64 npos[3];
2968
2969 // The original XML command was sent like this:
2970 /*
2971 << "<Position>"
2972 << "<X>" << pos[VX] << "</X>"
2973 << "<Y>" << pos[VZ] << "</Y>"
2974 << "<Z>" << pos[VY] << "</Z>"
2975 << "</Position>"
2976 << "<Velocity>"
2977 << "<X>" << mAvatarVelocity[VX] << "</X>"
2978 << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
2979 << "<Z>" << mAvatarVelocity[VY] << "</Z>"
2980 << "</Velocity>"
2981 << "<AtOrientation>"
2982 << "<X>" << l.mV[VX] << "</X>"
2983 << "<Y>" << u.mV[VX] << "</Y>"
2984 << "<Z>" << a.mV[VX] << "</Z>"
2985 << "</AtOrientation>"
2986 << "<UpOrientation>"
2987 << "<X>" << l.mV[VZ] << "</X>"
2988 << "<Y>" << u.mV[VY] << "</Y>"
2989 << "<Z>" << a.mV[VZ] << "</Z>"
2990 << "</UpOrientation>"
2991 << "<LeftOrientation>"
2992 << "<X>" << l.mV [VY] << "</X>"
2993 << "<Y>" << u.mV [VZ] << "</Y>"
2994 << "<Z>" << a.mV [VY] << "</Z>"
2995 << "</LeftOrientation>";
2996 */
2997
2998#if 1
2999 // This was the original transform done when building the XML command
3000 nat[0] = left.mV[VX];
3001 nat[1] = up.mV[VX];
3002 nat[2] = at.mV[VX];
3003
3004 nup[0] = left.mV[VZ];
3005 nup[1] = up.mV[VY];
3006 nup[2] = at.mV[VZ];
3007
3008 nl[0] = left.mV[VY];
3009 nl[1] = up.mV[VZ];
3010 nl[2] = at.mV[VY];
3011
3012 npos[0] = pos.mdV[VX];
3013 npos[1] = pos.mdV[VZ];
3014 npos[2] = pos.mdV[VY];
3015
3016 nvel[0] = vel.mV[VX];
3017 nvel[1] = vel.mV[VZ];
3018 nvel[2] = vel.mV[VY];
3019
3020 for(int i=0;i<3;++i) {
3021 at.mV[i] = nat[i];
3022 up.mV[i] = nup[i];
3023 left.mV[i] = nl[i];
3024 pos.mdV[i] = npos[i];
3025 }
3026
3027 // This was the original transform done in the SDK
3028 nat[0] = at.mV[2];
3029 nat[1] = 0; // y component of at vector is always 0, this was up[2]
3030 nat[2] = -1 * left.mV[2];
3031
3032 // We override whatever the application gives us
3033 nup[0] = 0; // x component of up vector is always 0
3034 nup[1] = 1; // y component of up vector is always 1
3035 nup[2] = 0; // z component of up vector is always 0
3036
3037 nl[0] = at.mV[0];
3038 nl[1] = 0; // y component of left vector is always zero, this was up[0]
3039 nl[2] = -1 * left.mV[0];
3040
3041 npos[2] = pos.mdV[2] * -1.0;
3042 npos[1] = pos.mdV[1];
3043 npos[0] = pos.mdV[0];
3044
3045 for(int i=0;i<3;++i) {
3046 at.mV[i] = nat[i];
3047 up.mV[i] = nup[i];
3048 left.mV[i] = nl[i];
3049 pos.mdV[i] = npos[i];
3050 }
3051#else
3052 // This is the compose of the two transforms (at least, that's what I'm trying for)
3053 nat[0] = at.mV[VX];
3054 nat[1] = 0; // y component of at vector is always 0, this was up[2]
3055 nat[2] = -1 * up.mV[VZ];
3056
3057 // We override whatever the application gives us
3058 nup[0] = 0; // x component of up vector is always 0
3059 nup[1] = 1; // y component of up vector is always 1
3060 nup[2] = 0; // z component of up vector is always 0
3061
3062 nl[0] = left.mV[VX];
3063 nl[1] = 0; // y component of left vector is always zero, this was up[0]
3064 nl[2] = -1 * left.mV[VY];
3065
3066 npos[0] = pos.mdV[VX];
3067 npos[1] = pos.mdV[VZ];
3068 npos[2] = pos.mdV[VY] * -1.0;
3069
3070 nvel[0] = vel.mV[VX];
3071 nvel[1] = vel.mV[VZ];
3072 nvel[2] = vel.mV[VY];
3073
3074 for(int i=0;i<3;++i) {
3075 at.mV[i] = nat[i];
3076 up.mV[i] = nup[i];
3077 left.mV[i] = nl[i];
3078 pos.mdV[i] = npos[i];
3079 }
3080
3081#endif
3082}
3083
2352void LLVoiceClient::sendPositionalUpdate(void) 3084void LLVoiceClient::sendPositionalUpdate(void)
2353{ 3085{
2354 std::ostringstream stream; 3086 std::ostringstream stream;
2355 3087
2356 if(mSpatialCoordsDirty) 3088 if(mSpatialCoordsDirty)
2357 { 3089 {
2358 LLVector3 l, u, a; 3090 LLVector3 l, u, a, vel;
3091 LLVector3d pos;
3092
3093 mSpatialCoordsDirty = false;
2359 3094
2360 // Always send both speaker and listener positions together. 3095 // Always send both speaker and listener positions together.
2361 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">" 3096 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
2362 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"; 3097 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
2363 3098
2364 stream << "<SpeakerPosition>"; 3099 stream << "<SpeakerPosition>";
2365 3100
3101// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
2366 l = mAvatarRot.getLeftRow(); 3102 l = mAvatarRot.getLeftRow();
2367 u = mAvatarRot.getUpRow(); 3103 u = mAvatarRot.getUpRow();
2368 a = mAvatarRot.getFwdRow(); 3104 a = mAvatarRot.getFwdRow();
2369 3105 pos = mAvatarPosition;
2370 LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; 3106 vel = mAvatarVelocity;
2371 3107
3108 // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
3109 // The old transform is replicated by this function.
3110 oldSDKTransform(l, u, a, pos, vel);
3111
2372 stream 3112 stream
2373 << "<Position>" 3113 << "<Position>"
2374 << "<X>" << mAvatarPosition[VX] << "</X>" 3114 << "<X>" << pos.mdV[VX] << "</X>"
2375 << "<Y>" << mAvatarPosition[VZ] << "</Y>" 3115 << "<Y>" << pos.mdV[VY] << "</Y>"
2376 << "<Z>" << mAvatarPosition[VY] << "</Z>" 3116 << "<Z>" << pos.mdV[VZ] << "</Z>"
2377 << "</Position>" 3117 << "</Position>"
2378 << "<Velocity>" 3118 << "<Velocity>"
2379 << "<X>" << mAvatarVelocity[VX] << "</X>" 3119 << "<X>" << vel.mV[VX] << "</X>"
2380 << "<Y>" << mAvatarVelocity[VZ] << "</Y>" 3120 << "<Y>" << vel.mV[VY] << "</Y>"
2381 << "<Z>" << mAvatarVelocity[VY] << "</Z>" 3121 << "<Z>" << vel.mV[VZ] << "</Z>"
2382 << "</Velocity>" 3122 << "</Velocity>"
2383 << "<AtOrientation>" 3123 << "<AtOrientation>"
2384 << "<X>" << l.mV[VX] << "</X>" 3124 << "<X>" << a.mV[VX] << "</X>"
2385 << "<Y>" << u.mV[VX] << "</Y>" 3125 << "<Y>" << a.mV[VY] << "</Y>"
2386 << "<Z>" << a.mV[VX] << "</Z>" 3126 << "<Z>" << a.mV[VZ] << "</Z>"
2387 << "</AtOrientation>" 3127 << "</AtOrientation>"
2388 << "<UpOrientation>" 3128 << "<UpOrientation>"
2389 << "<X>" << l.mV[VZ] << "</X>" 3129 << "<X>" << u.mV[VX] << "</X>"
2390 << "<Y>" << u.mV[VY] << "</Y>" 3130 << "<Y>" << u.mV[VY] << "</Y>"
2391 << "<Z>" << a.mV[VZ] << "</Z>" 3131 << "<Z>" << u.mV[VZ] << "</Z>"
2392 << "</UpOrientation>" 3132 << "</UpOrientation>"
2393 << "<LeftOrientation>" 3133 << "<LeftOrientation>"
2394 << "<X>" << l.mV [VY] << "</X>" 3134 << "<X>" << l.mV [VX] << "</X>"
2395 << "<Y>" << u.mV [VZ] << "</Y>" 3135 << "<Y>" << l.mV [VY] << "</Y>"
2396 << "<Z>" << a.mV [VY] << "</Z>" 3136 << "<Z>" << l.mV [VZ] << "</Z>"
2397 << "</LeftOrientation>"; 3137 << "</LeftOrientation>";
2398 3138
2399 stream << "</SpeakerPosition>"; 3139 stream << "</SpeakerPosition>";
@@ -2429,43 +3169,158 @@ void LLVoiceClient::sendPositionalUpdate(void)
2429 l = earRot.getLeftRow(); 3169 l = earRot.getLeftRow();
2430 u = earRot.getUpRow(); 3170 u = earRot.getUpRow();
2431 a = earRot.getFwdRow(); 3171 a = earRot.getFwdRow();
3172 pos = earPosition;
3173 vel = earVelocity;
2432 3174
2433 LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; 3175// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
2434 3176
3177 oldSDKTransform(l, u, a, pos, vel);
3178
2435 stream 3179 stream
2436 << "<Position>" 3180 << "<Position>"
2437 << "<X>" << earPosition[VX] << "</X>" 3181 << "<X>" << pos.mdV[VX] << "</X>"
2438 << "<Y>" << earPosition[VZ] << "</Y>" 3182 << "<Y>" << pos.mdV[VY] << "</Y>"
2439 << "<Z>" << earPosition[VY] << "</Z>" 3183 << "<Z>" << pos.mdV[VZ] << "</Z>"
2440 << "</Position>" 3184 << "</Position>"
2441 << "<Velocity>" 3185 << "<Velocity>"
2442 << "<X>" << earVelocity[VX] << "</X>" 3186 << "<X>" << vel.mV[VX] << "</X>"
2443 << "<Y>" << earVelocity[VZ] << "</Y>" 3187 << "<Y>" << vel.mV[VY] << "</Y>"
2444 << "<Z>" << earVelocity[VY] << "</Z>" 3188 << "<Z>" << vel.mV[VZ] << "</Z>"
2445 << "</Velocity>" 3189 << "</Velocity>"
2446 << "<AtOrientation>" 3190 << "<AtOrientation>"
2447 << "<X>" << l.mV[VX] << "</X>" 3191 << "<X>" << a.mV[VX] << "</X>"
2448 << "<Y>" << u.mV[VX] << "</Y>" 3192 << "<Y>" << a.mV[VY] << "</Y>"
2449 << "<Z>" << a.mV[VX] << "</Z>" 3193 << "<Z>" << a.mV[VZ] << "</Z>"
2450 << "</AtOrientation>" 3194 << "</AtOrientation>"
2451 << "<UpOrientation>" 3195 << "<UpOrientation>"
2452 << "<X>" << l.mV[VZ] << "</X>" 3196 << "<X>" << u.mV[VX] << "</X>"
2453 << "<Y>" << u.mV[VY] << "</Y>" 3197 << "<Y>" << u.mV[VY] << "</Y>"
2454 << "<Z>" << a.mV[VZ] << "</Z>" 3198 << "<Z>" << u.mV[VZ] << "</Z>"
2455 << "</UpOrientation>" 3199 << "</UpOrientation>"
2456 << "<LeftOrientation>" 3200 << "<LeftOrientation>"
2457 << "<X>" << l.mV [VY] << "</X>" 3201 << "<X>" << l.mV [VX] << "</X>"
2458 << "<Y>" << u.mV [VZ] << "</Y>" 3202 << "<Y>" << l.mV [VY] << "</Y>"
2459 << "<Z>" << a.mV [VY] << "</Z>" 3203 << "<Z>" << l.mV [VZ] << "</Z>"
2460 << "</LeftOrientation>"; 3204 << "</LeftOrientation>";
2461 3205
3206
2462 stream << "</ListenerPosition>"; 3207 stream << "</ListenerPosition>";
2463 3208
2464 stream << "</Request>\n\n\n"; 3209 stream << "</Request>\n\n\n";
2465 } 3210 }
3211
3212 if(mAudioSession && mAudioSession->mVolumeDirty)
3213 {
3214 participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
3215
3216 mAudioSession->mVolumeDirty = false;
3217
3218 for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
3219 {
3220 participantState *p = iter->second;
3221
3222 if(p->mVolumeDirty)
3223 {
3224 // Can't set volume/mute for yourself
3225 if(!p->mIsSelf)
3226 {
3227 int volume = p->mUserVolume;
3228 bool mute = p->mOnMuteList;
3229
3230 // SLIM SDK: scale volume from 0-400 (with 100 as "normal") to 0-100 (with 56 as "normal")
3231 if(volume < 100)
3232 volume = (volume * 56) / 100;
3233 else
3234 volume = (((volume - 100) * (100 - 56)) / 300) + 56;
3235
3236 if(mute)
3237 {
3238 // SetParticipantMuteForMe doesn't work in p2p sessions.
3239 // If we want the user to be muted, set their volume to 0 as well.
3240 // This isn't perfect, but it will at least reduce their volume to a minimum.
3241 volume = 0;
3242 }
3243
3244 if(volume == 0)
3245 mute = true;
3246
3247 LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
3248
3249 // SLIM SDK: Send both volume and mute commands.
3250
3251 // Send a "volume for me" command for the user.
3252 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
3253 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
3254 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
3255 << "<Volume>" << volume << "</Volume>"
3256 << "</Request>\n\n\n";
3257
3258 // Send a "mute for me" command for the user
3259 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
3260 << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
3261 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
3262 << "<Mute>" << (mute?"1":"0") << "</Mute>"
3263 << "</Request>\n\n\n";
3264 }
3265
3266 p->mVolumeDirty = false;
3267 }
3268 }
3269 }
3270
3271 buildLocalAudioUpdates(stream);
3272
3273 if(!stream.str().empty())
3274 {
3275 writeString(stream.str());
3276 }
3277
3278 // Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
3279 // Batching them all together can choke SLVoice, so send them in separate writes.
3280 sendFriendsListUpdates();
3281}
3282
3283void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
3284{
3285 if(mCaptureDeviceDirty)
3286 {
3287 LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
3288
3289 stream
3290 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
3291 << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
3292 << "</Request>"
3293 << "\n\n\n";
3294
3295 mCaptureDeviceDirty = false;
3296 }
3297}
3298
3299void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
3300{
3301 if(mRenderDeviceDirty)
3302 {
3303 LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
3304
3305 stream
3306 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
3307 << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
3308 << "</Request>"
3309 << "\n\n\n";
3310 mRenderDeviceDirty = false;
3311 }
3312}
3313
3314void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
3315{
3316 buildSetCaptureDevice(stream);
3317
3318 buildSetRenderDevice(stream);
2466 3319
2467 if(mPTTDirty) 3320 if(mPTTDirty)
2468 { 3321 {
3322 mPTTDirty = false;
3323
2469 // Send a local mute command. 3324 // Send a local mute command.
2470 // NOTE that the state of "PTT" is the inverse of "local mute". 3325 // NOTE that the state of "PTT" is the inverse of "local mute".
2471 // (i.e. when PTT is true, we send a mute command with "false", and vice versa) 3326 // (i.e. when PTT is true, we send a mute command with "false", and vice versa)
@@ -2476,119 +3331,337 @@ void LLVoiceClient::sendPositionalUpdate(void)
2476 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3331 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2477 << "<Value>" << (mPTT?"false":"true") << "</Value>" 3332 << "<Value>" << (mPTT?"false":"true") << "</Value>"
2478 << "</Request>\n\n\n"; 3333 << "</Request>\n\n\n";
2479
2480 }
2481
2482 if(mVolumeDirty)
2483 {
2484 participantMap::iterator iter = mParticipantMap.begin();
2485 3334
2486 for(; iter != mParticipantMap.end(); iter++)
2487 {
2488 participantState *p = iter->second;
2489
2490 if(p->mVolumeDirty)
2491 {
2492 int volume = p->mOnMuteList?0:p->mUserVolume;
2493
2494 LL_INFOS("Voice") << "Setting volume for avatar " << p->mAvatarID << " to " << volume << LL_ENDL;
2495
2496 // Send a mute/unumte command for the user (actually "volume for me").
2497 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
2498 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
2499 << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
2500 << "<Volume>" << volume << "</Volume>"
2501 << "</Request>\n\n\n";
2502
2503 p->mVolumeDirty = false;
2504 }
2505 }
2506 } 3335 }
2507 3336
2508 if(mSpeakerMuteDirty) 3337 if(mSpeakerMuteDirty)
2509 { 3338 {
2510 const char *muteval = ((mSpeakerVolume == -100)?"true":"false"); 3339 const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
3340
3341 mSpeakerMuteDirty = false;
3342
2511 LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; 3343 LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
2512 3344
2513 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">" 3345 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
2514 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3346 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2515 << "<Value>" << muteval << "</Value>" 3347 << "<Value>" << muteval << "</Value>"
2516 << "</Request>\n\n\n"; 3348 << "</Request>\n\n\n";
3349
2517 } 3350 }
2518 3351
2519 if(mSpeakerVolumeDirty) 3352 if(mSpeakerVolumeDirty)
2520 { 3353 {
3354 mSpeakerVolumeDirty = false;
3355
2521 LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; 3356 LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
2522 3357
2523 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">" 3358 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
2524 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3359 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2525 << "<Value>" << mSpeakerVolume << "</Value>" 3360 << "<Value>" << mSpeakerVolume << "</Value>"
2526 << "</Request>\n\n\n"; 3361 << "</Request>\n\n\n";
3362
2527 } 3363 }
2528 3364
2529 if(mMicVolumeDirty) 3365 if(mMicVolumeDirty)
2530 { 3366 {
3367 mMicVolumeDirty = false;
3368
2531 LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; 3369 LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
2532 3370
2533 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">" 3371 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
2534 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" 3372 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
2535 << "<Value>" << mMicVolume << "</Value>" 3373 << "<Value>" << mMicVolume << "</Value>"
2536 << "</Request>\n\n\n"; 3374 << "</Request>\n\n\n";
2537 } 3375 }
2538 3376
2539 3377
2540 // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here? 3378}
2541 if(mCaptureDeviceDirty)
2542 {
2543 buildSetCaptureDevice(stream);
2544 }
2545 3379
2546 if(mRenderDeviceDirty) 3380void LLVoiceClient::checkFriend(const LLUUID& id)
3381{
3382 std::string name;
3383 buddyListEntry *buddy = findBuddy(id);
3384
3385 // Make sure we don't add a name before it's been looked up.
3386 if(gCacheName->getFullName(id, name))
2547 { 3387 {
2548 buildSetRenderDevice(stream); 3388
3389 const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
3390 bool canSeeMeOnline = false;
3391 if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
3392 canSeeMeOnline = true;
3393
3394 // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary.
3395
3396 if(buddy)
3397 {
3398 // This buddy is already in both lists.
3399
3400 if(name != buddy->mDisplayName)
3401 {
3402 // The buddy is in the list with the wrong name. Update it with the correct name.
3403 LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL;
3404 buddy->mDisplayName = name;
3405 buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent.
3406 }
3407 }
3408 else
3409 {
3410 // This buddy was not in the vivox list, needs to be added.
3411 buddy = addBuddy(sipURIFromID(id), name);
3412 buddy->mUUID = id;
3413 }
3414
3415 // In all the above cases, the buddy is in the SL friends list (which is how we got here).
3416 buddy->mInSLFriends = true;
3417 buddy->mCanSeeMeOnline = canSeeMeOnline;
3418 buddy->mNameResolved = true;
3419
2549 } 3420 }
2550 3421 else
2551 mSpatialCoordsDirty = false;
2552 mPTTDirty = false;
2553 mVolumeDirty = false;
2554 mSpeakerVolumeDirty = false;
2555 mMicVolumeDirty = false;
2556 mSpeakerMuteDirty = false;
2557 mCaptureDeviceDirty = false;
2558 mRenderDeviceDirty = false;
2559
2560 if(!stream.str().empty())
2561 { 3422 {
2562 writeString(stream.str()); 3423 // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has.
3424 if(buddy)
3425 {
3426 buddy->mNameResolved = false;
3427 }
3428
3429 // Initiate a lookup.
3430 // The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
3431 lookupName(id);
2563 } 3432 }
2564} 3433}
2565 3434
2566void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) 3435void LLVoiceClient::clearAllLists()
2567{ 3436{
2568 LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; 3437 // FOR TESTING ONLY
2569 3438
2570 stream 3439 // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
2571 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">" 3440 buddyListMap::iterator buddy_it;
2572 << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>" 3441 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
2573 << "</Request>" 3442 {
2574 << "\n\n\n"; 3443 buddyListEntry *buddy = buddy_it->second;
3444 buddy_it++;
3445
3446 std::ostringstream stream;
3447
3448 if(buddy->mInVivoxBuddies)
3449 {
3450 // delete this entry from the vivox buddy list
3451 buddy->mInVivoxBuddies = false;
3452 LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3453 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
3454 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3455 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3456 << "</Request>\n\n\n";
3457 }
3458
3459 if(buddy->mHasBlockListEntry)
3460 {
3461 // Delete the associated block list entry (so the block list doesn't fill up with junk)
3462 buddy->mHasBlockListEntry = false;
3463 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3464 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3465 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3466 << "</Request>\n\n\n";
3467 }
3468 if(buddy->mHasAutoAcceptListEntry)
3469 {
3470 // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
3471 buddy->mHasAutoAcceptListEntry = false;
3472 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3473 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3474 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3475 << "</Request>\n\n\n";
3476 }
3477
3478 writeString(stream.str());
3479
3480 }
2575} 3481}
2576 3482
2577void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream) 3483void LLVoiceClient::sendFriendsListUpdates()
2578{ 3484{
2579 LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; 3485 if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
3486 {
3487 mFriendsListDirty = false;
3488
3489 if(0)
3490 {
3491 // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
3492 clearAllLists();
3493 return;
3494 }
3495
3496 LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
3497
3498 buddyListMap::iterator buddy_it;
3499 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
3500 {
3501 // reset the temp flags in the local buddy list
3502 buddy_it->second->mInSLFriends = false;
3503 }
3504
3505 // correlate with the friends list
3506 {
3507 LLCollectAllBuddies collect;
3508 LLAvatarTracker::instance().applyFunctor(collect);
3509 LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
3510 LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
3511
3512 for ( ; it != end; ++it)
3513 {
3514 checkFriend(it->second);
3515 }
3516 it = collect.mOffline.begin();
3517 end = collect.mOffline.end();
3518 for ( ; it != end; ++it)
3519 {
3520 checkFriend(it->second);
3521 }
3522 }
3523
3524 LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
2580 3525
2581 stream 3526 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
2582 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">" 3527 {
2583 << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>" 3528 buddyListEntry *buddy = buddy_it->second;
2584 << "</Request>" 3529 buddy_it++;
2585 << "\n\n\n"; 3530
3531 // Ignore entries that aren't resolved yet.
3532 if(buddy->mNameResolved)
3533 {
3534 std::ostringstream stream;
3535
3536 if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
3537 {
3538 if(mNumberOfAliases > 0)
3539 {
3540 // Add (or update) this entry in the vivox buddy list
3541 buddy->mInVivoxBuddies = true;
3542 buddy->mNeedsNameUpdate = false;
3543 LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3544 stream
3545 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">"
3546 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3547 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3548 << "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
3549 << "<BuddyData></BuddyData>" // Without this, SLVoice doesn't seem to parse the command.
3550 << "<GroupID>0</GroupID>"
3551 << "</Request>\n\n\n";
3552 }
3553 }
3554 else if(!buddy->mInSLFriends)
3555 {
3556 // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list.
3557 if(buddy->mInVivoxBuddies)
3558 {
3559 // delete this entry from the vivox buddy list
3560 buddy->mInVivoxBuddies = false;
3561 LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
3562 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
3563 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3564 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
3565 << "</Request>\n\n\n";
3566 }
3567
3568 if(buddy->mHasBlockListEntry)
3569 {
3570 // Delete the associated block list entry, if any
3571 buddy->mHasBlockListEntry = false;
3572 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3573 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3574 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3575 << "</Request>\n\n\n";
3576 }
3577 if(buddy->mHasAutoAcceptListEntry)
3578 {
3579 // Delete the associated auto-accept list entry, if any
3580 buddy->mHasAutoAcceptListEntry = false;
3581 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3582 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3583 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3584 << "</Request>\n\n\n";
3585 }
3586 }
3587
3588 if(buddy->mInSLFriends)
3589 {
3590
3591 if(buddy->mCanSeeMeOnline)
3592 {
3593 // Buddy should not be blocked.
3594
3595 // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
3596
3597 // If the buddy has a block list entry, delete it.
3598 if(buddy->mHasBlockListEntry)
3599 {
3600 buddy->mHasBlockListEntry = false;
3601 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
3602 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3603 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3604 << "</Request>\n\n\n";
3605
3606
3607 // If we just deleted a block list entry, add an auto-accept entry.
3608 if(!buddy->mHasAutoAcceptListEntry)
3609 {
3610 buddy->mHasAutoAcceptListEntry = true;
3611 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">"
3612 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3613 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3614 << "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
3615 << "</Request>\n\n\n";
3616 }
3617 }
3618 }
3619 else
3620 {
3621 // Buddy should be blocked.
3622
3623 // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
3624
3625 // If this buddy has an autoaccept entry, delete it
3626 if(buddy->mHasAutoAcceptListEntry)
3627 {
3628 buddy->mHasAutoAcceptListEntry = false;
3629 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
3630 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3631 << "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
3632 << "</Request>\n\n\n";
3633
3634 // If we just deleted an auto-accept entry, add a block list entry.
3635 if(!buddy->mHasBlockListEntry)
3636 {
3637 buddy->mHasBlockListEntry = true;
3638 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">"
3639 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
3640 << "<BlockMask>" << buddy->mURI << "</BlockMask>"
3641 << "<PresenceOnly>1</PresenceOnly>"
3642 << "</Request>\n\n\n";
3643 }
3644 }
3645 }
3646
3647 if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
3648 {
3649 // Delete this entry from the local buddy list. This should NOT invalidate the iterator,
3650 // since it has already been incremented to the next entry.
3651 deleteBuddy(buddy->mURI);
3652 }
3653
3654 }
3655 writeString(stream.str());
3656 }
3657 }
3658 }
2586} 3659}
2587 3660
2588///////////////////////////// 3661/////////////////////////////
2589// Response/Event handlers 3662// Response/Event handlers
2590 3663
2591void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle) 3664void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
2592{ 3665{
2593 if(statusCode != 0) 3666 if(statusCode != 0)
2594 { 3667 {
@@ -2598,6 +3671,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
2598 else 3671 else
2599 { 3672 {
2600 // Connector created, move forward. 3673 // Connector created, move forward.
3674 LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
2601 mConnectorHandle = connectorHandle; 3675 mConnectorHandle = connectorHandle;
2602 if(getState() == stateConnectorStarting) 3676 if(getState() == stateConnectorStarting)
2603 { 3677 {
@@ -2606,7 +3680,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
2606 } 3680 }
2607} 3681}
2608 3682
2609void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle) 3683void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
2610{ 3684{
2611 LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; 3685 LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
2612 3686
@@ -2627,7 +3701,8 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
2627 { 3701 {
2628 // Login succeeded, move forward. 3702 // Login succeeded, move forward.
2629 mAccountHandle = accountHandle; 3703 mAccountHandle = accountHandle;
2630 // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received. 3704 mNumberOfAliases = numberOfAliases;
3705 // This needs to wait until the AccountLoginStateChangeEvent is received.
2631// if(getState() == stateLoggingIn) 3706// if(getState() == stateLoggingIn)
2632// { 3707// {
2633// setState(stateLoggedIn); 3708// setState(stateLoggedIn);
@@ -2635,106 +3710,91 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
2635 } 3710 }
2636} 3711}
2637 3712
2638void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString) 3713void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
2639{ 3714{
3715 sessionState *session = findSessionBeingCreatedByURI(requestId);
3716
3717 if(session)
3718 {
3719 session->mCreateInProgress = false;
3720 }
3721
2640 if(statusCode != 0) 3722 if(statusCode != 0)
2641 { 3723 {
2642 LL_WARNS("Voice") << "Account.ChannelGetList response failure: " << statusString << LL_ENDL; 3724 LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
2643 switchChannel(); 3725 if(session)
3726 {
3727 session->mErrorStatusCode = statusCode;
3728 session->mErrorStatusString = statusString;
3729 if(session == mAudioSession)
3730 {
3731 setState(stateJoinSessionFailed);
3732 }
3733 else
3734 {
3735 reapSession(session);
3736 }
3737 }
2644 } 3738 }
2645 else 3739 else
2646 { 3740 {
2647 // Got the channel list, try to do a lookup. 3741 LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
2648 std::string uri = findChannelURI(mChannelName); 3742 if(session)
2649 if(uri.empty())
2650 {
2651 // Lookup failed, can't join a channel for this area.
2652 LL_INFOS("Voice") << "failed to map channel name: " << mChannelName << LL_ENDL;
2653 }
2654 else
2655 { 3743 {
2656 // We have a sip URL for this area. 3744 setSessionHandle(session, sessionHandle);
2657 LL_INFOS("Voice") << "mapped channel " << mChannelName << " to URI "<< uri << LL_ENDL;
2658 } 3745 }
2659
2660 // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
2661 switchChannel(uri);
2662 } 3746 }
2663} 3747}
2664 3748
2665void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle) 3749void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
2666{ 3750{
3751 sessionState *session = findSessionBeingCreatedByURI(requestId);
3752
3753 if(session)
3754 {
3755 session->mCreateInProgress = false;
3756 }
3757
2667 if(statusCode != 0) 3758 if(statusCode != 0)
2668 { 3759 {
2669 LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; 3760 LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
2670// if(statusCode == 1015) 3761 if(session)
2671// {
2672// if(getState() == stateJoiningSession)
2673// {
2674// // this happened during a real join. Going to sessionTerminated should cause a retry in appropriate cases.
2675// LL_WARNS("Voice") << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< LL_ENDL;
2676// if(!sessionHandle.empty())
2677// {
2678// // This session is bad. Terminate it.
2679// mSessionHandle = sessionHandle;
2680// sessionTerminateByHandle(sessionHandle);
2681// setState(stateLeavingSession);
2682// }
2683// else if(!mSessionStateEventHandle.empty())
2684// {
2685// mSessionHandle = mSessionStateEventHandle;
2686// sessionTerminateByHandle(mSessionStateEventHandle);
2687// setState(stateLeavingSession);
2688// }
2689// else
2690// {
2691// setState(stateSessionTerminated);
2692// }
2693// }
2694// else
2695// {
2696// // We didn't think we were in the middle of a join. Don't change state.
2697// LL_WARNS("Voice") << "Not in stateJoiningSession, ignoring" << LL_ENDL;
2698// }
2699// }
2700// else
2701 { 3762 {
2702 mVivoxErrorStatusCode = statusCode; 3763 session->mErrorStatusCode = statusCode;
2703 mVivoxErrorStatusString = statusString; 3764 session->mErrorStatusString = statusString;
2704 setState(stateJoinSessionFailed); 3765 if(session == mAudioSession)
3766 {
3767 setState(stateJoinSessionFailed);
3768 }
3769 else
3770 {
3771 reapSession(session);
3772 }
2705 } 3773 }
2706 } 3774 }
2707 else 3775 else
2708 { 3776 {
2709 LL_DEBUGS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; 3777 LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
2710 if(getState() == stateJoiningSession) 3778 if(session)
2711 { 3779 {
2712 // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early... 3780 setSessionHandle(session, sessionHandle);
2713 mSessionHandle = sessionHandle;
2714 }
2715 else
2716 {
2717 // We should never get a session.create response in any state except stateJoiningSession. Things are out of sync. Kill this session.
2718 sessionTerminateByHandle(sessionHandle);
2719 } 3781 }
2720 } 3782 }
2721} 3783}
2722 3784
2723void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString) 3785void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
2724{ 3786{
3787 sessionState *session = findSession(requestId);
2725 if(statusCode != 0) 3788 if(statusCode != 0)
2726 { 3789 {
2727 LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; 3790 LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
2728// if(statusCode == 1015) 3791 if(session)
2729// {
2730// LL_WARNS("Voice") << "terminating existing session" << LL_ENDL;
2731// sessionTerminate();
2732// }
2733// else
2734 { 3792 {
2735 mVivoxErrorStatusCode = statusCode; 3793 session->mMediaConnectInProgress = false;
2736 mVivoxErrorStatusString = statusString; 3794 session->mErrorStatusCode = statusCode;
2737 setState(stateJoinSessionFailed); 3795 session->mErrorStatusString = statusString;
3796 if(session == mAudioSession)
3797 setState(stateJoinSessionFailed);
2738 } 3798 }
2739 } 3799 }
2740 else 3800 else
@@ -2743,27 +3803,12 @@ void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusSt
2743 } 3803 }
2744} 3804}
2745 3805
2746void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
2747{
2748 if(statusCode != 0)
2749 {
2750 LL_WARNS("Voice") << "Session.Terminate response failure: (" << statusCode << "): " << statusString << LL_ENDL;
2751 if(getState() == stateLeavingSession)
2752 {
2753 // This is probably "(404): Server reporting Failure. Not a member of this conference."
2754 // Do this so we don't get stuck.
2755 setState(stateSessionTerminated);
2756 }
2757 }
2758
2759}
2760
2761void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString) 3806void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
2762{ 3807{
2763 if(statusCode != 0) 3808 if(statusCode != 0)
2764 { 3809 {
2765 LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; 3810 LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
2766 // MBW -- XXX -- Should this ever fail? do we care if it does? 3811 // Should this ever fail? do we care if it does?
2767 } 3812 }
2768 3813
2769 if(getState() == stateLoggingOut) 3814 if(getState() == stateLoggingOut)
@@ -2777,7 +3822,7 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
2777 if(statusCode != 0) 3822 if(statusCode != 0)
2778 { 3823 {
2779 LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; 3824 LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
2780 // MBW -- XXX -- Should this ever fail? do we care if it does? 3825 // Should this ever fail? do we care if it does?
2781 } 3826 }
2782 3827
2783 mConnected = false; 3828 mConnected = false;
@@ -2788,109 +3833,277 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
2788 } 3833 }
2789} 3834}
2790 3835
2791void LLVoiceClient::sessionStateChangeEvent( 3836void LLVoiceClient::sessionAddedEvent(
2792 std::string &uriString, 3837 std::string &uriString,
2793 int statusCode, 3838 std::string &alias,
2794 std::string &statusString, 3839 std::string &sessionHandle,
2795 std::string &sessionHandle, 3840 std::string &sessionGroupHandle,
2796 int state,
2797 bool isChannel, 3841 bool isChannel,
2798 std::string &nameString) 3842 bool incoming,
3843 std::string &nameString,
3844 std::string &applicationString)
2799{ 3845{
2800 switch(state) 3846 sessionState *session = NULL;
2801 {
2802 case 4: // I see this when joining the session
2803 LL_INFOS("Voice") << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
2804 3847
2805 // Session create succeeded, move forward. 3848 LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
2806 mSessionStateEventHandle = sessionHandle; 3849
2807 mSessionStateEventURI = uriString; 3850 session = addSession(uriString, sessionHandle);
2808 if(sessionHandle == mSessionHandle) 3851 if(session)
3852 {
3853 session->mGroupHandle = sessionGroupHandle;
3854 session->mIsChannel = isChannel;
3855 session->mIncoming = incoming;
3856 session->mAlias = alias;
3857
3858 // Generate a caller UUID -- don't need to do this for channels
3859 if(!session->mIsChannel)
3860 {
3861 if(IDFromName(session->mSIPURI, session->mCallerID))
2809 { 3862 {
2810 // This is the session we're joining. 3863 // Normal URI(base64-encoded UUID)
2811 if(getState() == stateJoiningSession)
2812 {
2813 setState(stateSessionJoined);
2814 //RN: the uriString being returned by vivox here is actually your account uri, not the channel
2815 // you are attempting to join, so ignore it
2816 //LL_DEBUGS("Voice") << "received URI " << uriString << "(previously " << mSessionURI << ")" << LL_ENDL;
2817 //mSessionURI = uriString;
2818 }
2819 } 3864 }
2820 else if(sessionHandle == mNextSessionHandle) 3865 else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
2821 { 3866 {
2822// LL_DEBUGS("Voice") << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << LL_ENDL; 3867 // Wrong URI, but an alias is available. Stash the incoming URI as an alternate
3868 session->mAlternateSIPURI = session->mSIPURI;
3869
3870 // and generate a proper URI from the ID.
3871 setSessionURI(session, sipURIFromID(session->mCallerID));
2823 } 3872 }
2824 else 3873 else
2825 { 3874 {
2826 LL_WARNS("Voice") << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL; 3875 LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
2827 // MBW -- XXX -- Should we send a Session.Terminate here? 3876 setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
3877 session->mSynthesizedCallerID = true;
3878
3879 // Can't look up the name in this case -- we have to extract it from the URI.
3880 std::string namePortion = nameFromsipURI(session->mSIPURI);
3881 if(namePortion.empty())
3882 {
3883 // Didn't seem to be a SIP URI, just use the whole provided name.
3884 namePortion = nameString;
3885 }
3886
3887 // Some incoming names may be separated with an underscore instead of a space. Fix this.
3888 LLStringUtil::replaceChar(namePortion, '_', ' ');
3889
3890 // Act like we just finished resolving the name (this stores it in all the right places)
3891 avatarNameResolved(session->mCallerID, namePortion);
2828 } 3892 }
2829 3893
2830 break; 3894 LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
2831 case 5: // I see this when leaving the session 3895
2832 LL_INFOS("Voice") << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL; 3896 if(!session->mSynthesizedCallerID)
3897 {
3898 // If we got here, we don't have a proper name. Initiate a lookup.
3899 lookupName(session->mCallerID);
3900 }
3901 }
3902 }
3903}
3904
3905void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
3906{
3907 LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
3908
3909#if USE_SESSION_GROUPS
3910 if(mMainSessionGroupHandle.empty())
3911 {
3912 // This is the first (i.e. "main") session group. Save its handle.
3913 mMainSessionGroupHandle = sessionGroupHandle;
3914 }
3915 else
3916 {
3917 LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
3918 }
3919#endif
3920}
3921
3922void LLVoiceClient::joinedAudioSession(sessionState *session)
3923{
3924 if(mAudioSession != session)
3925 {
3926 sessionState *oldSession = mAudioSession;
3927
3928 mAudioSession = session;
3929 mAudioSessionChanged = true;
3930
3931 // The old session may now need to be deleted.
3932 reapSession(oldSession);
3933 }
3934
3935 // This is the session we're joining.
3936 if(getState() == stateJoiningSession)
3937 {
3938 setState(stateSessionJoined);
3939
3940 // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
3941 // Add the current user as a participant here.
3942 participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
3943 if(participant)
3944 {
3945 participant->mIsSelf = true;
3946 lookupName(participant->mAvatarID);
2833 3947
2834 // Set the session handle to the empty string. If we get back to stateJoiningSession, we'll want to wait for the new session handle. 3948 LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName
2835 if(sessionHandle == mSessionHandle) 3949 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
3950 }
3951
3952 if(!session->mIsChannel)
3953 {
3954 // this is a p2p session. Make sure the other end is added as a participant.
3955 participantState *participant = session->addParticipant(session->mSIPURI);
3956 if(participant)
2836 { 3957 {
2837 // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle 3958 if(participant->mAvatarIDValid)
2838 // mSessionURI.clear();
2839 // clear the session handle here just for sanity.
2840 mSessionHandle.clear();
2841 if(mSessionResetOnClose)
2842 { 3959 {
2843 mSessionResetOnClose = false; 3960 lookupName(participant->mAvatarID);
2844 mNonSpatialChannel = false; 3961 }
2845 mNextSessionSpatial = true; 3962 else if(!session->mName.empty())
2846 parcelChanged(); 3963 {
3964 participant->mDisplayName = session->mName;
3965 avatarNameResolved(participant->mAvatarID, session->mName);
2847 } 3966 }
2848
2849 removeAllParticipants();
2850 3967
2851 switch(getState()) 3968 // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
3969 LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName
3970 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
3971 }
3972 }
3973 }
3974}
3975
3976void LLVoiceClient::sessionRemovedEvent(
3977 std::string &sessionHandle,
3978 std::string &sessionGroupHandle)
3979{
3980 LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
3981
3982 sessionState *session = findSession(sessionHandle);
3983 if(session)
3984 {
3985 leftAudioSession(session);
3986
3987 // This message invalidates the session's handle. Set it to empty.
3988 setSessionHandle(session);
3989
3990 // Reset the media state (we now have no info)
3991 session->mMediaStreamState = streamStateUnknown;
3992 session->mTextStreamState = streamStateUnknown;
3993
3994 // Conditionally delete the session
3995 reapSession(session);
3996 }
3997 else
3998 {
3999 LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
4000 }
4001}
4002
4003void LLVoiceClient::reapSession(sessionState *session)
4004{
4005 if(session)
4006 {
4007 if(!session->mHandle.empty())
4008 {
4009 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
4010 }
4011 else if(session->mCreateInProgress)
4012 {
4013 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
4014 }
4015 else if(session->mMediaConnectInProgress)
4016 {
4017 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
4018 }
4019 else if(session == mAudioSession)
4020 {
4021 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
4022 }
4023 else if(session == mNextAudioSession)
4024 {
4025 LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
4026 }
4027 else
4028 {
4029 // TODO: Question: Should we check for queued text messages here?
4030 // We don't have a reason to keep tracking this session, so just delete it.
4031 LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
4032 deleteSession(session);
4033 session = NULL;
4034 }
4035 }
4036 else
4037 {
4038// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
4039 }
4040}
4041
4042// Returns true if the session seems to indicate we've moved to a region on a different voice server
4043bool LLVoiceClient::sessionNeedsRelog(sessionState *session)
4044{
4045 bool result = false;
4046
4047 if(session != NULL)
4048 {
4049 // Only make this check for spatial channels (so it won't happen for group or p2p calls)
4050 if(session->mIsSpatial)
4051 {
4052 std::string::size_type atsign;
4053
4054 atsign = session->mSIPURI.find("@");
4055
4056 if(atsign != std::string::npos)
4057 {
4058 std::string urihost = session->mSIPURI.substr(atsign + 1);
4059 if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
2852 { 4060 {
2853 case stateJoiningSession: 4061 // The hostname in this URI is different from what we expect. This probably means we need to relog.
2854 case stateSessionJoined:
2855 case stateRunning:
2856 case stateLeavingSession:
2857 case stateJoinSessionFailed:
2858 case stateJoinSessionFailedWaiting:
2859 // normal transition
2860 LL_INFOS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
2861 setState(stateSessionTerminated);
2862 break;
2863 4062
2864 case stateSessionTerminated: 4063 // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
2865 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. 4064 // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
2866 LL_WARNS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
2867 break;
2868 4065
2869 default: 4066 result = true;
2870 LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
2871 setState(stateSessionTerminated);
2872 break;
2873 } 4067 }
2874
2875 // store status values for later notification of observers
2876 mVivoxErrorStatusCode = statusCode;
2877 mVivoxErrorStatusString = statusString;
2878 }
2879 else
2880 {
2881 LL_INFOS("Voice") << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
2882 } 4068 }
4069 }
4070 }
4071
4072 return result;
4073}
2883 4074
2884 mSessionStateEventHandle.clear(); 4075void LLVoiceClient::leftAudioSession(
2885 mSessionStateEventURI.clear(); 4076 sessionState *session)
2886 break; 4077{
2887 default: 4078 if(mAudioSession == session)
2888 LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL; 4079 {
2889 break; 4080 switch(getState())
4081 {
4082 case stateJoiningSession:
4083 case stateSessionJoined:
4084 case stateRunning:
4085 case stateLeavingSession:
4086 case stateJoinSessionFailed:
4087 case stateJoinSessionFailedWaiting:
4088 // normal transition
4089 LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
4090 setState(stateSessionTerminated);
4091 break;
4092
4093 case stateSessionTerminated:
4094 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
4095 LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
4096 break;
4097
4098 default:
4099 LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
4100 setState(stateSessionTerminated);
4101 break;
4102 }
2890 } 4103 }
2891} 4104}
2892 4105
2893void LLVoiceClient::loginStateChangeEvent( 4106void LLVoiceClient::accountLoginStateChangeEvent(
2894 std::string &accountHandle, 4107 std::string &accountHandle,
2895 int statusCode, 4108 int statusCode,
2896 std::string &statusString, 4109 std::string &statusString,
@@ -2923,110 +4136,571 @@ void LLVoiceClient::loginStateChangeEvent(
2923 } 4136 }
2924} 4137}
2925 4138
2926void LLVoiceClient::sessionNewEvent( 4139void LLVoiceClient::mediaStreamUpdatedEvent(
2927 std::string &accountHandle, 4140 std::string &sessionHandle,
2928 std::string &eventSessionHandle, 4141 std::string &sessionGroupHandle,
2929 int state, 4142 int statusCode,
2930 std::string &nameString, 4143 std::string &statusString,
2931 std::string &uriString) 4144 int state,
4145 bool incoming)
2932{ 4146{
2933 LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; 4147 sessionState *session = findSession(sessionHandle);
2934 4148
2935 switch(state) 4149 LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
4150
4151 if(session)
2936 { 4152 {
2937 case 0: 4153 // We know about this session
2938 { 4154
2939 LL_DEBUGS("Voice") << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << LL_ENDL; 4155 // Save the state for later use
4156 session->mMediaStreamState = state;
4157
4158 switch(statusCode)
4159 {
4160 case 0:
4161 case 200:
4162 // generic success
4163 // Don't change the saved error code (it may have been set elsewhere)
4164 break;
4165 default:
4166 // save the status code for later
4167 session->mErrorStatusCode = statusCode;
4168 break;
4169 }
4170
4171 switch(state)
4172 {
4173 case streamStateIdle:
4174 // Standard "left audio session"
4175 session->mVoiceEnabled = false;
4176 session->mMediaConnectInProgress = false;
4177 leftAudioSession(session);
4178 break;
4179
4180 case streamStateConnected:
4181 session->mVoiceEnabled = true;
4182 session->mMediaConnectInProgress = false;
4183 joinedAudioSession(session);
4184 break;
4185
4186 case streamStateRinging:
4187 if(incoming)
4188 {
4189 // Send the voice chat invite to the GUI layer
4190 // TODO: Question: Should we correlate with the mute list here?
4191 session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
4192 session->mVoiceInvitePending = true;
4193 if(session->mName.empty())
4194 {
4195 lookupName(session->mCallerID);
4196 }
4197 else
4198 {
4199 // Act like we just finished resolving the name
4200 avatarNameResolved(session->mCallerID, session->mName);
4201 }
4202 }
4203 break;
4204
4205 default:
4206 LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
4207 break;
4208
4209 }
4210
4211 }
4212 else
4213 {
4214 LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
4215 }
4216}
2940 4217
2941 LLUUID caller_id; 4218void LLVoiceClient::textStreamUpdatedEvent(
2942 if(IDFromName(nameString, caller_id)) 4219 std::string &sessionHandle,
4220 std::string &sessionGroupHandle,
4221 bool enabled,
4222 int state,
4223 bool incoming)
4224{
4225 sessionState *session = findSession(sessionHandle);
4226
4227 if(session)
4228 {
4229 // Save the state for later use
4230 session->mTextStreamState = state;
4231
4232 // We know about this session
4233 switch(state)
4234 {
4235 case 0: // We see this when the text stream closes
4236 LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
4237 break;
4238
4239 case 1: // We see this on an incoming call from the Connector
4240 // Try to send any text messages queued for this session.
4241 sendQueuedTextMessages(session);
4242
4243 // Send the text chat invite to the GUI layer
4244 // TODO: Question: Should we correlate with the mute list here?
4245 session->mTextInvitePending = true;
4246 if(session->mName.empty())
2943 { 4247 {
2944 gIMMgr->inviteToSession( 4248 lookupName(session->mCallerID);
2945 LLIMMgr::computeSessionID(
2946 IM_SESSION_P2P_INVITE,
2947 caller_id),
2948 LLStringUtil::null,
2949 caller_id,
2950 LLStringUtil::null,
2951 IM_SESSION_P2P_INVITE,
2952 LLIMMgr::INVITATION_TYPE_VOICE,
2953 eventSessionHandle);
2954 } 4249 }
2955 else 4250 else
2956 { 4251 {
2957 LL_WARNS("Voice") << "Could not generate caller id from uri " << uriString << LL_ENDL; 4252 // Act like we just finished resolving the name
4253 avatarNameResolved(session->mCallerID, session->mName);
2958 } 4254 }
2959 } 4255 break;
2960 break; 4256
2961 4257 default:
2962 default: 4258 LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
2963 LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL; 4259 break;
2964 break; 4260
4261 }
2965 } 4262 }
2966} 4263}
2967 4264
2968void LLVoiceClient::participantStateChangeEvent( 4265void LLVoiceClient::participantAddedEvent(
4266 std::string &sessionHandle,
4267 std::string &sessionGroupHandle,
2969 std::string &uriString, 4268 std::string &uriString,
2970 int statusCode, 4269 std::string &alias,
2971 std::string &statusString,
2972 int state,
2973 std::string &nameString, 4270 std::string &nameString,
2974 std::string &displayNameString, 4271 std::string &displayNameString,
2975 int participantType) 4272 int participantType)
2976{ 4273{
2977 participantState *participant = NULL; 4274 sessionState *session = findSession(sessionHandle);
2978 LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; 4275 if(session)
2979
2980 switch(state)
2981 { 4276 {
2982 case 7: // I see this when a participant joins 4277 participantState *participant = session->addParticipant(uriString);
2983 participant = addParticipant(uriString); 4278 if(participant)
2984 if(participant) 4279 {
4280 participant->mAccountName = nameString;
4281
4282 LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName
4283 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
4284
4285 if(participant->mAvatarIDValid)
2985 { 4286 {
2986 participant->mName = nameString; 4287 // Initiate a lookup
2987 LL_DEBUGS("Voice") << "added participant \"" << participant->mName 4288 lookupName(participant->mAvatarID);
2988 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
2989 } 4289 }
2990 break; 4290 else
2991 case 9: // I see this when a participant leaves
2992 participant = findParticipant(uriString);
2993 if(participant)
2994 { 4291 {
2995 removeParticipant(participant); 4292 // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
4293 std::string namePortion = nameFromsipURI(uriString);
4294 if(namePortion.empty())
4295 {
4296 // Problem with the SIP URI, fall back to the display name
4297 namePortion = displayNameString;
4298 }
4299 if(namePortion.empty())
4300 {
4301 // Problems with both of the above, fall back to the account name
4302 namePortion = nameString;
4303 }
4304
4305 // Set the display name (which is a hint to the active speakers window not to do its own lookup)
4306 participant->mDisplayName = namePortion;
4307 avatarNameResolved(participant->mAvatarID, namePortion);
2996 } 4308 }
2997 break; 4309 }
2998 default:
2999 LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
3000 break;
3001 } 4310 }
3002} 4311}
3003 4312
3004void LLVoiceClient::participantPropertiesEvent( 4313void LLVoiceClient::participantRemovedEvent(
4314 std::string &sessionHandle,
4315 std::string &sessionGroupHandle,
3005 std::string &uriString, 4316 std::string &uriString,
3006 int statusCode, 4317 std::string &alias,
3007 std::string &statusString, 4318 std::string &nameString)
3008 bool isLocallyMuted, 4319{
4320 sessionState *session = findSession(sessionHandle);
4321 if(session)
4322 {
4323 participantState *participant = session->findParticipant(uriString);
4324 if(participant)
4325 {
4326 session->removeParticipant(participant);
4327 }
4328 else
4329 {
4330 LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
4331 }
4332 }
4333 else
4334 {
4335 LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
4336 }
4337}
4338
4339
4340void LLVoiceClient::participantUpdatedEvent(
4341 std::string &sessionHandle,
4342 std::string &sessionGroupHandle,
4343 std::string &uriString,
4344 std::string &alias,
3009 bool isModeratorMuted, 4345 bool isModeratorMuted,
3010 bool isSpeaking, 4346 bool isSpeaking,
3011 int volume, 4347 int volume,
3012 F32 energy) 4348 F32 energy)
3013{ 4349{
3014 participantState *participant = findParticipant(uriString); 4350 sessionState *session = findSession(sessionHandle);
3015 if(participant) 4351 if(session)
3016 { 4352 {
3017 participant->mPTT = !isLocallyMuted; 4353 participantState *participant = session->findParticipant(uriString);
3018 participant->mIsSpeaking = isSpeaking; 4354
3019 participant->mIsModeratorMuted = isModeratorMuted; 4355 if(participant)
3020 if (isSpeaking) 4356 {
4357 participant->mIsSpeaking = isSpeaking;
4358 participant->mIsModeratorMuted = isModeratorMuted;
4359
4360 // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
4361 if (isSpeaking)
4362 {
4363 participant->mSpeakingTimeout.reset();
4364 participant->mPower = energy;
4365 }
4366 else
4367 {
4368 participant->mPower = 0.0f;
4369 }
4370 participant->mVolume = volume;
4371 }
4372 else
3021 { 4373 {
3022 participant->mSpeakingTimeout.reset(); 4374 LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
3023 } 4375 }
3024 participant->mPower = energy;
3025 participant->mVolume = volume;
3026 } 4376 }
3027 else 4377 else
3028 { 4378 {
3029 LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; 4379 LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
4380 }
4381}
4382
4383void LLVoiceClient::buddyPresenceEvent(
4384 std::string &uriString,
4385 std::string &alias,
4386 std::string &statusString,
4387 std::string &applicationString)
4388{
4389 buddyListEntry *buddy = findBuddy(uriString);
4390
4391 if(buddy)
4392 {
4393 LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL;
4394 LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
4395
4396 if(applicationString.empty())
4397 {
4398 // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way.
4399 // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
4400
4401 if ( stricmp("Unknown", statusString.c_str())== 0)
4402 {
4403 // User went offline with a non-SLim-enabled viewer.
4404 buddy->mOnlineSL = false;
4405 }
4406 else if ( stricmp("Online", statusString.c_str())== 0)
4407 {
4408 // User came online with a non-SLim-enabled viewer.
4409 buddy->mOnlineSL = true;
4410 }
4411 else
4412 {
4413 // If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
4414 // 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.
4415 buddy->mOnlineSLim = true;
4416 }
4417 }
4418 else if(applicationString.find("SecondLifeViewer") != std::string::npos)
4419 {
4420 // This presence event is from a viewer that sets the application string
4421 if ( stricmp("Unknown", statusString.c_str())== 0)
4422 {
4423 // Viewer says they're offline
4424 buddy->mOnlineSL = false;
4425 }
4426 else
4427 {
4428 // Viewer says they're online
4429 buddy->mOnlineSL = true;
4430 }
4431 }
4432 else
4433 {
4434 // This presence event is from something which is NOT the SL viewer (assume it's SLim).
4435 if ( stricmp("Unknown", statusString.c_str())== 0)
4436 {
4437 // SLim says they're offline
4438 buddy->mOnlineSLim = false;
4439 }
4440 else
4441 {
4442 // SLim says they're online
4443 buddy->mOnlineSLim = true;
4444 }
4445 }
4446
4447 LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
4448
4449 // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
4450 LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
4451
4452 notifyFriendObservers();
4453 }
4454 else
4455 {
4456 LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
4457 }
4458}
4459
4460void LLVoiceClient::messageEvent(
4461 std::string &sessionHandle,
4462 std::string &uriString,
4463 std::string &alias,
4464 std::string &messageHeader,
4465 std::string &messageBody,
4466 std::string &applicationString)
4467{
4468 LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
4469// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
4470
4471 if(messageHeader.find("text/html") != std::string::npos)
4472 {
4473 std::string rawMessage;
4474
4475 {
4476 const std::string startMarker = "<body";
4477 const std::string startMarker2 = ">";
4478 const std::string endMarker = "</body>";
4479 const std::string startSpan = "<span";
4480 const std::string endSpan = "</span>";
4481 std::string::size_type start;
4482 std::string::size_type end;
4483
4484 // Default to displaying the raw string, so the message gets through.
4485 rawMessage = messageBody;
4486
4487 // Find the actual message text within the XML fragment
4488 start = messageBody.find(startMarker);
4489 start = messageBody.find(startMarker2, start);
4490 end = messageBody.find(endMarker);
4491
4492 if(start != std::string::npos)
4493 {
4494 start += startMarker2.size();
4495
4496 if(end != std::string::npos)
4497 end -= start;
4498
4499 rawMessage.assign(messageBody, start, end);
4500 }
4501 else
4502 {
4503 // Didn't find a <body>, try looking for a <span> instead.
4504 start = messageBody.find(startSpan);
4505 start = messageBody.find(startMarker2, start);
4506 end = messageBody.find(endSpan);
4507
4508 if(start != std::string::npos)
4509 {
4510 start += startMarker2.size();
4511
4512 if(end != std::string::npos)
4513 end -= start;
4514
4515 rawMessage.assign(messageBody, start, end);
4516 }
4517 }
4518 }
4519
4520// LL_DEBUGS("Voice") << " raw message = \n" << rawMessage << LL_ENDL;
4521
4522 // strip formatting tags
4523 {
4524 std::string::size_type start;
4525 std::string::size_type end;
4526
4527 while((start = rawMessage.find('<')) != std::string::npos)
4528 {
4529 if((end = rawMessage.find('>', start + 1)) != std::string::npos)
4530 {
4531 // Strip out the tag
4532 rawMessage.erase(start, (end + 1) - start);
4533 }
4534 else
4535 {
4536 // Avoid an infinite loop
4537 break;
4538 }
4539 }
4540 }
4541
4542 // Decode ampersand-escaped chars
4543 {
4544 std::string::size_type mark = 0;
4545
4546 // The text may contain text encoded with &lt;, &gt;, and &amp;
4547 mark = 0;
4548 while((mark = rawMessage.find("&lt;", mark)) != std::string::npos)
4549 {
4550 rawMessage.replace(mark, 4, "<");
4551 mark += 1;
4552 }
4553
4554 mark = 0;
4555 while((mark = rawMessage.find("&gt;", mark)) != std::string::npos)
4556 {
4557 rawMessage.replace(mark, 4, ">");
4558 mark += 1;
4559 }
4560
4561 mark = 0;
4562 while((mark = rawMessage.find("&amp;", mark)) != std::string::npos)
4563 {
4564 rawMessage.replace(mark, 5, "&");
4565 mark += 1;
4566 }
4567 }
4568
4569 // strip leading/trailing whitespace (since we always seem to get a couple newlines)
4570 LLStringUtil::trim(rawMessage);
4571
4572// LL_DEBUGS("Voice") << " stripped message = \n" << rawMessage << LL_ENDL;
4573
4574 sessionState *session = findSession(sessionHandle);
4575 if(session)
4576 {
4577 bool is_busy = gAgent.getBusy();
4578 bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
4579 bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
4580 bool quiet_chat = false;
4581 LLChat chat;
4582
4583 chat.mMuted = is_muted && !is_linden;
4584
4585 if(!chat.mMuted)
4586 {
4587 chat.mFromID = session->mCallerID;
4588 chat.mFromName = session->mName;
4589 chat.mSourceType = CHAT_SOURCE_AGENT;
4590
4591 if(is_busy && !is_linden)
4592 {
4593 quiet_chat = true;
4594 // TODO: Question: Return busy mode response here? Or maybe when session is started instead?
4595 }
4596
4597 std::string fullMessage = std::string(": ") + rawMessage;
4598
4599 LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
4600 gIMMgr->addMessage(session->mIMSessionID,
4601 session->mCallerID,
4602 session->mName.c_str(),
4603 fullMessage.c_str(),
4604 LLStringUtil::null, // default arg
4605 IM_NOTHING_SPECIAL, // default arg
4606 0, // default arg
4607 LLUUID::null, // default arg
4608 LLVector3::zero, // default arg
4609 true); // prepend name and make it a link to the user's profile
4610
4611 chat.mText = std::string("IM: ") + session->mName + std::string(": ") + rawMessage;
4612 // If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
4613 LLFloaterChat::addChat( chat, TRUE, quiet_chat );
4614 }
4615 }
4616 }
4617}
4618
4619void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
4620{
4621 sessionState *session = findSession(sessionHandle);
4622
4623 if(session)
4624 {
4625 participantState *participant = session->findParticipant(uriString);
4626 if(participant)
4627 {
4628 if (!stricmp(notificationType.c_str(), "Typing"))
4629 {
4630 // Other end started typing
4631 // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
4632 // It requires an LLIMInfo for the message, which we don't have here.
4633 }
4634 else if (!stricmp(notificationType.c_str(), "NotTyping"))
4635 {
4636 // Other end stopped typing
4637 // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
4638 // It requires an LLIMInfo for the message, which we don't have here.
4639 }
4640 else
4641 {
4642 LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
4643 }
4644 }
4645 else
4646 {
4647 LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
4648 }
4649 }
4650 else
4651 {
4652 LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
4653 }
4654}
4655
4656void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
4657{
4658 buddyListEntry *buddy = findBuddy(buddyURI);
4659
4660 if(!buddy)
4661 {
4662 // Couldn't find buddy by URI, try converting the alias...
4663 if(!alias.empty())
4664 {
4665 LLUUID id;
4666 if(IDFromName(alias, id))
4667 {
4668 buddy = findBuddy(id);
4669 }
4670 }
4671 }
4672
4673 if(buddy)
4674 {
4675 std::ostringstream stream;
4676
4677 if(buddy->mCanSeeMeOnline)
4678 {
4679 // Sending the response will create an auto-accept rule
4680 buddy->mHasAutoAcceptListEntry = true;
4681 }
4682 else
4683 {
4684 // Sending the response will create a block rule
4685 buddy->mHasBlockListEntry = true;
4686 }
4687
4688 if(buddy->mInSLFriends)
4689 {
4690 buddy->mInVivoxBuddies = true;
4691 }
4692
4693 stream
4694 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">"
4695 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
4696 << "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
4697 << "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
4698 << "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
4699 << "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
4700 << "</Request>"
4701 << "\n\n\n";
4702
4703 writeString(stream.str());
3030 } 4704 }
3031} 4705}
3032 4706
@@ -3036,155 +4710,226 @@ void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
3036 mTuningEnergy = energy; 4710 mTuningEnergy = energy;
3037} 4711}
3038 4712
4713void LLVoiceClient::buddyListChanged()
4714{
4715 // This is called after we receive a BuddyAndGroupListChangedEvent.
4716 mBuddyListMapPopulated = true;
4717 mFriendsListDirty = true;
4718}
4719
3039void LLVoiceClient::muteListChanged() 4720void LLVoiceClient::muteListChanged()
3040{ 4721{
3041 // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. 4722 // The user's mute list has been updated. Go through the current participant list and sync it with the mute list.
3042 4723 if(mAudioSession)
3043 participantMap::iterator iter = mParticipantMap.begin();
3044
3045 for(; iter != mParticipantMap.end(); iter++)
3046 { 4724 {
3047 participantState *p = iter->second; 4725 participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
3048 4726
3049 // Check to see if this participant is on the mute list already 4727 for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
3050 updateMuteState(p); 4728 {
4729 participantState *p = iter->second;
4730
4731 // Check to see if this participant is on the mute list already
4732 if(p->updateMuteState())
4733 mAudioSession->mVolumeDirty = true;
4734 }
4735 }
4736}
4737
4738void LLVoiceClient::updateFriends(U32 mask)
4739{
4740 if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
4741 {
4742 // Just resend the whole friend list to the daemon
4743 mFriendsListDirty = true;
3051 } 4744 }
3052} 4745}
3053 4746
3054///////////////////////////// 4747/////////////////////////////
3055// Managing list of participants 4748// Managing list of participants
3056LLVoiceClient::participantState::participantState(const std::string &uri) : 4749LLVoiceClient::participantState::participantState(const std::string &uri) :
3057 mURI(uri), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), mLastSpokeTimestamp(0.f), mPower(0.f), mVolume(0), mServiceType(serviceTypeUnknown), 4750 mURI(uri),
3058 mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false) 4751 mPTT(false),
4752 mIsSpeaking(false),
4753 mIsModeratorMuted(false),
4754 mLastSpokeTimestamp(0.f),
4755 mPower(0.f),
4756 mVolume(0),
4757 mOnMuteList(false),
4758 mUserVolume(100),
4759 mVolumeDirty(false),
4760 mAvatarIDValid(false),
4761 mIsSelf(false)
3059{ 4762{
3060} 4763}
3061 4764
3062LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri) 4765LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri)
3063{ 4766{
3064 participantState *result = NULL; 4767 participantState *result = NULL;
3065 4768 bool useAlternateURI = false;
3066 participantMap::iterator iter = mParticipantMap.find(uri);
3067 4769
3068 if(iter != mParticipantMap.end()) 4770 // Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it
4771 // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
3069 { 4772 {
3070 // Found a matching participant already in the map. 4773 participantMap::iterator iter = mParticipantsByURI.find(&uri);
3071 result = iter->second;
3072 }
3073 4774
4775 if(iter == mParticipantsByURI.end())
4776 {
4777 if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
4778 {
4779 // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
4780 // Use mSIPURI instead, since it will be properly encoded.
4781 iter = mParticipantsByURI.find(&(mSIPURI));
4782 useAlternateURI = true;
4783 }
4784 }
4785
4786 if(iter != mParticipantsByURI.end())
4787 {
4788 result = iter->second;
4789 }
4790 }
4791
3074 if(!result) 4792 if(!result)
3075 { 4793 {
3076 // participant isn't already in one list or the other. 4794 // participant isn't already in one list or the other.
3077 result = new participantState(uri); 4795 result = new participantState(useAlternateURI?mSIPURI:uri);
3078 mParticipantMap.insert(participantMap::value_type(uri, result)); 4796 mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result));
3079 mParticipantMapChanged = true; 4797 mParticipantsChanged = true;
3080 4798
3081 // Try to do a reverse transform on the URI to get the GUID back. 4799 // Try to do a reverse transform on the URI to get the GUID back.
3082 { 4800 {
3083 LLUUID id; 4801 LLUUID id;
3084 if(IDFromName(uri, id)) 4802 if(IDFromName(result->mURI, id))
3085 { 4803 {
3086 result->mAvatarIDValid = true; 4804 result->mAvatarIDValid = true;
3087 result->mAvatarID = id; 4805 result->mAvatarID = id;
3088 4806
3089 updateMuteState(result); 4807 if(result->updateMuteState())
4808 mVolumeDirty = true;
4809 }
4810 else
4811 {
4812 // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
4813 // This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
4814 setUUIDFromStringHash(result->mAvatarID, uri);
3090 } 4815 }
3091 } 4816 }
3092 4817
4818 mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
4819
3093 LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; 4820 LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
3094 } 4821 }
3095 4822
3096 return result; 4823 return result;
3097} 4824}
3098 4825
3099void LLVoiceClient::updateMuteState(participantState *p) 4826bool LLVoiceClient::participantState::updateMuteState()
3100{ 4827{
3101 if(p->mAvatarIDValid) 4828 bool result = false;
4829
4830 if(mAvatarIDValid)
3102 { 4831 {
3103 bool isMuted = LLMuteList::getInstance()->isMuted(p->mAvatarID, LLMute::flagVoiceChat); 4832 bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
3104 if(p->mOnMuteList != isMuted) 4833 if(mOnMuteList != isMuted)
3105 { 4834 {
3106 p->mOnMuteList = isMuted; 4835 mOnMuteList = isMuted;
3107 p->mVolumeDirty = true;
3108 mVolumeDirty = true; 4836 mVolumeDirty = true;
4837 result = true;
3109 } 4838 }
3110 } 4839 }
4840 return result;
3111} 4841}
3112 4842
3113void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant) 4843void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant)
3114{ 4844{
3115 if(participant) 4845 if(participant)
3116 { 4846 {
3117 participantMap::iterator iter = mParticipantMap.find(participant->mURI); 4847 participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI));
3118 4848 participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID));
4849
3119 LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; 4850 LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
3120 4851
3121 mParticipantMap.erase(iter); 4852 if(iter == mParticipantsByURI.end())
3122 delete participant; 4853 {
3123 mParticipantMapChanged = true; 4854 LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
4855 }
4856 else if(iter2 == mParticipantsByUUID.end())
4857 {
4858 LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
4859 }
4860 else if(iter->second != iter2->second)
4861 {
4862 LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
4863 }
4864 else
4865 {
4866 mParticipantsByURI.erase(iter);
4867 mParticipantsByUUID.erase(iter2);
4868
4869 delete participant;
4870 mParticipantsChanged = true;
4871 }
3124 } 4872 }
3125} 4873}
3126 4874
3127void LLVoiceClient::removeAllParticipants() 4875void LLVoiceClient::sessionState::removeAllParticipants()
3128{ 4876{
3129 LL_DEBUGS("Voice") << "called" << LL_ENDL; 4877 LL_DEBUGS("Voice") << "called" << LL_ENDL;
3130 4878
3131 while(!mParticipantMap.empty()) 4879 while(!mParticipantsByURI.empty())
4880 {
4881 removeParticipant(mParticipantsByURI.begin()->second);
4882 }
4883
4884 if(!mParticipantsByUUID.empty())
3132 { 4885 {
3133 removeParticipant(mParticipantMap.begin()->second); 4886 LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL
3134 } 4887 }
3135} 4888}
3136 4889
3137LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void) 4890LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
3138{ 4891{
3139 return &mParticipantMap; 4892 participantMap *result = NULL;
4893 if(mAudioSession)
4894 {
4895 result = &(mAudioSession->mParticipantsByURI);
4896 }
4897 return result;
3140} 4898}
3141 4899
3142 4900
3143LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri) 4901LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
3144{ 4902{
3145 participantState *result = NULL; 4903 participantState *result = NULL;
3146 4904
3147 // Don't find any participants if we're not connected. This is so that we don't continue to get stale data 4905 participantMap::iterator iter = mParticipantsByURI.find(&uri);
3148 // after the daemon dies. 4906
3149 if(mConnected) 4907 if(iter == mParticipantsByURI.end())
3150 { 4908 {
3151 participantMap::iterator iter = mParticipantMap.find(uri); 4909 if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
3152
3153 if(iter != mParticipantMap.end())
3154 { 4910 {
3155 result = iter->second; 4911 // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
4912 // Look up the other URI
4913 iter = mParticipantsByURI.find(&(mSIPURI));
3156 } 4914 }
3157 } 4915 }
3158 4916
4917 if(iter != mParticipantsByURI.end())
4918 {
4919 result = iter->second;
4920 }
4921
3159 return result; 4922 return result;
3160} 4923}
3161 4924
3162 4925LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
3163LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
3164{ 4926{
3165 participantState * result = NULL; 4927 participantState * result = NULL;
4928 participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id);
3166 4929
3167 // You'd think this would work, but it doesn't... 4930 if(iter != mParticipantsByUUID.end())
3168// std::string uri = sipURIFromAvatar(avatar);
3169
3170 // Currently, the URI is just the account name.
3171 std::string loginName = nameFromAvatar(avatar);
3172 result = findParticipant(loginName);
3173
3174 if(result != NULL)
3175 { 4931 {
3176 if(!result->mAvatarIDValid) 4932 result = iter->second;
3177 {
3178 result->mAvatarID = avatar->getID();
3179 result->mAvatarIDValid = true;
3180
3181 // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
3182 mParticipantMapChanged = true;
3183
3184 updateMuteState(result);
3185 }
3186
3187
3188 } 4933 }
3189 4934
3190 return result; 4935 return result;
@@ -3193,43 +4938,19 @@ LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvat
3193LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id) 4938LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
3194{ 4939{
3195 participantState * result = NULL; 4940 participantState * result = NULL;
3196
3197 // Currently, the URI is just the account name.
3198 std::string loginName = nameFromID(id);
3199 result = findParticipant(loginName);
3200
3201 return result;
3202}
3203
3204
3205void LLVoiceClient::clearChannelMap(void)
3206{
3207 mChannelMap.clear();
3208}
3209
3210void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
3211{
3212 LL_DEBUGS("Voice") << "Adding channel name mapping: " << name << " -> " << uri << LL_ENDL;
3213 mChannelMap.insert(channelMap::value_type(name, uri));
3214}
3215
3216std::string LLVoiceClient::findChannelURI(std::string &name)
3217{
3218 std::string result;
3219 4941
3220 channelMap::iterator iter = mChannelMap.find(name); 4942 if(mAudioSession)
3221
3222 if(iter != mChannelMap.end())
3223 { 4943 {
3224 result = iter->second; 4944 result = mAudioSession->findParticipantByID(id);
3225 } 4945 }
3226 4946
3227 return result; 4947 return result;
3228} 4948}
3229 4949
4950
3230void LLVoiceClient::parcelChanged() 4951void LLVoiceClient::parcelChanged()
3231{ 4952{
3232 if(getState() >= stateLoggedIn) 4953 if(getState() >= stateNoChannel)
3233 { 4954 {
3234 // If the user is logged in, start a channel lookup. 4955 // If the user is logged in, start a channel lookup.
3235 LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; 4956 LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
@@ -3243,7 +4964,7 @@ void LLVoiceClient::parcelChanged()
3243 } 4964 }
3244 else 4965 else
3245 { 4966 {
3246 // The transition to stateLoggedIn needs to kick this off again. 4967 // The transition to stateNoChannel needs to kick this off again.
3247 LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; 4968 LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
3248 } 4969 }
3249} 4970}
@@ -3251,12 +4972,17 @@ void LLVoiceClient::parcelChanged()
3251void LLVoiceClient::switchChannel( 4972void LLVoiceClient::switchChannel(
3252 std::string uri, 4973 std::string uri,
3253 bool spatial, 4974 bool spatial,
3254 bool noReconnect, 4975 bool no_reconnect,
4976 bool is_p2p,
3255 std::string hash) 4977 std::string hash)
3256{ 4978{
3257 bool needsSwitch = false; 4979 bool needsSwitch = false;
3258 4980
3259 LL_DEBUGS("Voice") << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << LL_ENDL; 4981 LL_DEBUGS("Voice")
4982 << "called in state " << state2string(getState())
4983 << " with uri \"" << uri << "\""
4984 << (spatial?", spatial is true":", spatial is false")
4985 << LL_ENDL;
3260 4986
3261 switch(getState()) 4987 switch(getState())
3262 { 4988 {
@@ -3266,18 +4992,18 @@ void LLVoiceClient::switchChannel(
3266 // Always switch to the new URI from these states. 4992 // Always switch to the new URI from these states.
3267 needsSwitch = true; 4993 needsSwitch = true;
3268 break; 4994 break;
3269 4995
3270 default: 4996 default:
3271 if(mSessionTerminateRequested) 4997 if(mSessionTerminateRequested)
3272 { 4998 {
3273 // If a terminate has been requested, we need to compare against where the URI we're already headed to. 4999 // If a terminate has been requested, we need to compare against where the URI we're already headed to.
3274 if(mNextSessionURI != uri) 5000 if(mNextAudioSession && (mNextAudioSession->mSIPURI != uri))
3275 needsSwitch = true; 5001 needsSwitch = true;
3276 } 5002 }
3277 else 5003 else
3278 { 5004 {
3279 // Otherwise, compare against the URI we're in now. 5005 // Otherwise, compare against the URI we're in now.
3280 if(mSessionURI != uri) 5006 if(mAudioSession && (mAudioSession->mSIPURI != uri))
3281 needsSwitch = true; 5007 needsSwitch = true;
3282 } 5008 }
3283 break; 5009 break;
@@ -3285,22 +5011,28 @@ void LLVoiceClient::switchChannel(
3285 5011
3286 if(needsSwitch) 5012 if(needsSwitch)
3287 { 5013 {
3288 mNextSessionURI = uri;
3289 mNextSessionHash = hash;
3290 mNextSessionHandle.clear();
3291 mNextP2PSessionURI.clear();
3292 mNextSessionSpatial = spatial;
3293 mNextSessionNoReconnect = noReconnect;
3294
3295 if(uri.empty()) 5014 if(uri.empty())
3296 { 5015 {
3297 // Leave any channel we may be in 5016 // Leave any channel we may be in
3298 LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; 5017 LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
5018
5019 sessionState *oldSession = mNextAudioSession;
5020 mNextAudioSession = NULL;
5021
5022 // The old session may now need to be deleted.
5023 reapSession(oldSession);
5024
3299 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); 5025 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
3300 } 5026 }
3301 else 5027 else
3302 { 5028 {
3303 LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; 5029 LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
5030
5031 mNextAudioSession = addSession(uri);
5032 mNextAudioSession->mHash = hash;
5033 mNextAudioSession->mIsSpatial = spatial;
5034 mNextAudioSession->mReconnect = !no_reconnect;
5035 mNextAudioSession->mIsP2P = is_p2p;
3304 } 5036 }
3305 5037
3306 if(getState() <= stateNoChannel) 5038 if(getState() <= stateNoChannel)
@@ -3315,15 +5047,10 @@ void LLVoiceClient::switchChannel(
3315 } 5047 }
3316} 5048}
3317 5049
3318void LLVoiceClient::joinSession(std::string handle, std::string uri) 5050void LLVoiceClient::joinSession(sessionState *session)
3319{ 5051{
3320 mNextSessionURI.clear(); 5052 mNextAudioSession = session;
3321 mNextSessionHash.clear(); 5053
3322 mNextP2PSessionURI = uri;
3323 mNextSessionHandle = handle;
3324 mNextSessionSpatial = false;
3325 mNextSessionNoReconnect = false;
3326
3327 if(getState() <= stateNoChannel) 5054 if(getState() <= stateNoChannel)
3328 { 5055 {
3329 // We're already set up to join a channel, just needed to fill in the session handle 5056 // We're already set up to join a channel, just needed to fill in the session handle
@@ -3339,7 +5066,7 @@ void LLVoiceClient::setNonSpatialChannel(
3339 const std::string &uri, 5066 const std::string &uri,
3340 const std::string &credentials) 5067 const std::string &credentials)
3341{ 5068{
3342 switchChannel(uri, false, false, credentials); 5069 switchChannel(uri, false, false, false, credentials);
3343} 5070}
3344 5071
3345void LLVoiceClient::setSpatialChannel( 5072void LLVoiceClient::setSpatialChannel(
@@ -3347,51 +5074,216 @@ void LLVoiceClient::setSpatialChannel(
3347 const std::string &credentials) 5074 const std::string &credentials)
3348{ 5075{
3349 mSpatialSessionURI = uri; 5076 mSpatialSessionURI = uri;
5077 mSpatialSessionCredentials = credentials;
3350 mAreaVoiceDisabled = mSpatialSessionURI.empty(); 5078 mAreaVoiceDisabled = mSpatialSessionURI.empty();
3351 5079
3352 LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; 5080 LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
3353 5081
3354 if(mNonSpatialChannel || !mNextSessionSpatial) 5082 if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
3355 { 5083 {
3356 // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. 5084 // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
3357 LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; 5085 LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
3358 } 5086 }
3359 else 5087 else
3360 { 5088 {
3361 switchChannel(mSpatialSessionURI, true, false, credentials); 5089 switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
3362 } 5090 }
3363} 5091}
3364 5092
3365void LLVoiceClient::callUser(LLUUID &uuid) 5093void LLVoiceClient::callUser(const LLUUID &uuid)
3366{ 5094{
3367 std::string userURI = sipURIFromID(uuid); 5095 std::string userURI = sipURIFromID(uuid);
3368 5096
3369 switchChannel(userURI, false, true); 5097 switchChannel(userURI, false, true, true);
5098}
5099
5100LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid)
5101{
5102 // Figure out if a session with the user already exists
5103 sessionState *session = findSession(uuid);
5104 if(!session)
5105 {
5106 // No session with user, need to start one.
5107 std::string uri = sipURIFromID(uuid);
5108 session = addSession(uri);
5109 session->mIsSpatial = false;
5110 session->mReconnect = false;
5111 session->mIsP2P = true;
5112 session->mCallerID = uuid;
5113 }
5114
5115 if(session)
5116 {
5117 if(session->mHandle.empty())
5118 {
5119 // Session isn't active -- start it up.
5120 sessionCreateSendMessage(session, false, true);
5121 }
5122 else
5123 {
5124 // Session is already active -- start up text.
5125 sessionTextConnectSendMessage(session);
5126 }
5127 }
5128
5129 return session;
5130}
5131
5132bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
5133{
5134 bool result = false;
5135
5136 // Attempt to locate the indicated session
5137 sessionState *session = startUserIMSession(participant_id);
5138 if(session)
5139 {
5140 // found the session, attempt to send the message
5141 session->mTextMsgQueue.push(message);
5142
5143 // Try to send queued messages (will do nothing if the session is not open yet)
5144 sendQueuedTextMessages(session);
5145
5146 // The message is queued, so we succeed.
5147 result = true;
5148 }
5149 else
5150 {
5151 LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
5152 }
5153
5154 return result;
3370} 5155}
3371 5156
3372void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id) 5157void LLVoiceClient::sendQueuedTextMessages(sessionState *session)
3373{ 5158{
3374 joinSession(sessionHandle, sipURIFromID(other_user_id)); 5159 if(session->mTextStreamState == 1)
5160 {
5161 if(!session->mTextMsgQueue.empty())
5162 {
5163 std::ostringstream stream;
5164
5165 while(!session->mTextMsgQueue.empty())
5166 {
5167 std::string message = session->mTextMsgQueue.front();
5168 session->mTextMsgQueue.pop();
5169 stream
5170 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
5171 << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
5172 << "<MessageHeader>text/HTML</MessageHeader>"
5173 << "<MessageBody>" << message << "</MessageBody>"
5174 << "</Request>"
5175 << "\n\n\n";
5176 }
5177 writeString(stream.str());
5178 }
5179 }
5180 else
5181 {
5182 // Session isn't connected yet, defer until later.
5183 }
5184}
5185
5186void LLVoiceClient::endUserIMSession(const LLUUID &uuid)
5187{
5188 // Figure out if a session with the user exists
5189 sessionState *session = findSession(uuid);
5190 if(session)
5191 {
5192 // found the session
5193 if(!session->mHandle.empty())
5194 {
5195 sessionTextDisconnectSendMessage(session);
5196 }
5197 }
5198 else
5199 {
5200 LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
5201 }
5202}
5203
5204bool LLVoiceClient::answerInvite(std::string &sessionHandle)
5205{
5206 // this is only ever used to answer incoming p2p call invites.
5207
5208 sessionState *session = findSession(sessionHandle);
5209 if(session)
5210 {
5211 session->mIsSpatial = false;
5212 session->mReconnect = false;
5213 session->mIsP2P = true;
5214
5215 joinSession(session);
5216 return true;
5217 }
5218
5219 return false;
5220}
5221
5222bool LLVoiceClient::isOnlineSIP(const LLUUID &id)
5223{
5224 bool result = false;
5225 buddyListEntry *buddy = findBuddy(id);
5226 if(buddy)
5227 {
5228 result = buddy->mOnlineSLim;
5229 LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
5230 }
5231
5232 if(!result)
5233 {
5234 // 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.
5235 sessionState *session = findSession(id);
5236 if(session && !session->mHandle.empty())
5237 {
5238 if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
5239 {
5240 LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
5241 // we have a p2p text session open with this user, so by definition they're online.
5242 result = true;
5243 }
5244 }
5245 }
5246
5247 return result;
3375} 5248}
3376 5249
3377void LLVoiceClient::declineInvite(std::string &sessionHandle) 5250void LLVoiceClient::declineInvite(std::string &sessionHandle)
3378{ 5251{
3379 sessionTerminateByHandle(sessionHandle); 5252 sessionState *session = findSession(sessionHandle);
5253 if(session)
5254 {
5255 sessionMediaDisconnectSendMessage(session);
5256 }
3380} 5257}
3381 5258
3382void LLVoiceClient::leaveNonSpatialChannel() 5259void LLVoiceClient::leaveNonSpatialChannel()
3383{ 5260{
3384 switchChannel(mSpatialSessionURI); 5261 LL_DEBUGS("Voice")
5262 << "called in state " << state2string(getState())
5263 << LL_ENDL;
5264
5265 // Make sure we don't rejoin the current session.
5266 sessionState *oldNextSession = mNextAudioSession;
5267 mNextAudioSession = NULL;
5268
5269 // Most likely this will still be the current session at this point, but check it anyway.
5270 reapSession(oldNextSession);
5271
5272 verifySessionState();
5273
5274 sessionTerminate();
3385} 5275}
3386 5276
3387std::string LLVoiceClient::getCurrentChannel() 5277std::string LLVoiceClient::getCurrentChannel()
3388{ 5278{
5279 std::string result;
5280
3389 if((getState() == stateRunning) && !mSessionTerminateRequested) 5281 if((getState() == stateRunning) && !mSessionTerminateRequested)
3390 { 5282 {
3391 return mSessionURI; 5283 result = getAudioSessionURI();
3392 } 5284 }
3393 5285
3394 return ""; 5286 return result;
3395} 5287}
3396 5288
3397bool LLVoiceClient::inProximalChannel() 5289bool LLVoiceClient::inProximalChannel()
@@ -3400,7 +5292,7 @@ bool LLVoiceClient::inProximalChannel()
3400 5292
3401 if((getState() == stateRunning) && !mSessionTerminateRequested) 5293 if((getState() == stateRunning) && !mSessionTerminateRequested)
3402 { 5294 {
3403 result = !mNonSpatialChannel; 5295 result = inSpatialChannel();
3404 } 5296 }
3405 5297
3406 return result; 5298 return result;
@@ -3412,7 +5304,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
3412 result = "sip:"; 5304 result = "sip:";
3413 result += nameFromID(id); 5305 result += nameFromID(id);
3414 result += "@"; 5306 result += "@";
3415 result += mAccountServerName; 5307 result += mVoiceSIPURIHostName;
3416 5308
3417 return result; 5309 return result;
3418} 5310}
@@ -3425,7 +5317,7 @@ std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
3425 result = "sip:"; 5317 result = "sip:";
3426 result += nameFromID(avatar->getID()); 5318 result += nameFromID(avatar->getID());
3427 result += "@"; 5319 result += "@";
3428 result += mAccountServerName; 5320 result += mVoiceSIPURIHostName;
3429 } 5321 }
3430 5322
3431 return result; 5323 return result;
@@ -3444,6 +5336,13 @@ std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
3444std::string LLVoiceClient::nameFromID(const LLUUID &uuid) 5336std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
3445{ 5337{
3446 std::string result; 5338 std::string result;
5339
5340 if (uuid.isNull()) {
5341 //VIVOX, the uuid emtpy look for the mURIString and return that instead.
5342 //result.assign(uuid.mURIStringName);
5343 LLStringUtil::replaceChar(result, '_', ' ');
5344 return result;
5345 }
3447 // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. 5346 // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
3448 result = "x"; 5347 result = "x";
3449 5348
@@ -3457,13 +5356,24 @@ std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
3457 // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: 5356 // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
3458 // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') 5357 // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
3459 5358
5359 // The reverse transform can be done with:
5360 // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
5361
3460 return result; 5362 return result;
3461} 5363}
3462 5364
3463bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid) 5365bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
3464{ 5366{
3465 bool result = false; 5367 bool result = false;
3466 5368
5369 // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
5370 // If it is, convert to a bare name before doing the transform.
5371 std::string name = nameFromsipURI(inName);
5372
5373 // Doesn't look like a SIP URI, assume it's an actual name.
5374 if(name.empty())
5375 name = inName;
5376
3467 // This will only work if the name is of the proper form. 5377 // This will only work if the name is of the proper form.
3468 // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: 5378 // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
3469 // "xFnPP04IpREWNkuw1cOXlhw==" 5379 // "xFnPP04IpREWNkuw1cOXlhw=="
@@ -3485,6 +5395,13 @@ bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
3485 memcpy(uuid.mData, rawuuid, UUID_BYTES); 5395 memcpy(uuid.mData, rawuuid, UUID_BYTES);
3486 result = true; 5396 result = true;
3487 } 5397 }
5398 }
5399
5400 if(!result)
5401 {
5402 // VIVOX: not a standard account name, just copy the URI name mURIString field
5403 // and hope for the best. bpj
5404 uuid.setNull(); // VIVOX, set the uuid field to nulls
3488 } 5405 }
3489 5406
3490 return result; 5407 return result;
@@ -3501,13 +5418,59 @@ std::string LLVoiceClient::sipURIFromName(std::string &name)
3501 result = "sip:"; 5418 result = "sip:";
3502 result += name; 5419 result += name;
3503 result += "@"; 5420 result += "@";
3504 result += mAccountServerName; 5421 result += mVoiceSIPURIHostName;
3505 5422
3506// LLStringUtil::toLower(result); 5423// LLStringUtil::toLower(result);
3507 5424
3508 return result; 5425 return result;
3509} 5426}
3510 5427
5428std::string LLVoiceClient::nameFromsipURI(const std::string &uri)
5429{
5430 std::string result;
5431
5432 std::string::size_type sipOffset, atOffset;
5433 sipOffset = uri.find("sip:");
5434 atOffset = uri.find("@");
5435 if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
5436 {
5437 result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
5438 }
5439
5440 return result;
5441}
5442
5443bool LLVoiceClient::inSpatialChannel(void)
5444{
5445 bool result = false;
5446
5447 if(mAudioSession)
5448 result = mAudioSession->mIsSpatial;
5449
5450 return result;
5451}
5452
5453std::string LLVoiceClient::getAudioSessionURI()
5454{
5455 std::string result;
5456
5457 if(mAudioSession)
5458 result = mAudioSession->mSIPURI;
5459
5460 return result;
5461}
5462
5463std::string LLVoiceClient::getAudioSessionHandle()
5464{
5465 std::string result;
5466
5467 if(mAudioSession)
5468 result = mAudioSession->mHandle;
5469
5470 return result;
5471}
5472
5473
3511///////////////////////////// 5474/////////////////////////////
3512// Sending updates of current state 5475// Sending updates of current state
3513 5476
@@ -3546,7 +5509,8 @@ void LLVoiceClient::updatePosition(void)
3546 LLMatrix3 rot; 5509 LLMatrix3 rot;
3547 LLVector3d pos; 5510 LLVector3d pos;
3548 5511
3549 // MBW -- XXX -- Setting both camera and avatar velocity to 0 for now. May figure it out later... 5512 // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
5513 // They're currently always set to zero.
3550 5514
3551 // Send the current camera position to the voice code 5515 // Send the current camera position to the voice code
3552 rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); 5516 rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis());
@@ -3561,7 +5525,7 @@ void LLVoiceClient::updatePosition(void)
3561 rot = agent->getRootJoint()->getWorldRotation().getMatrix3(); 5525 rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
3562 5526
3563 pos = agent->getPositionGlobal(); 5527 pos = agent->getPositionGlobal();
3564 // MBW -- XXX -- Can we get the head offset from outside the LLVOAvatar? 5528 // TODO: Can we get the head offset from outside the LLVOAvatar?
3565// pos += LLVector3d(mHeadOffset); 5529// pos += LLVector3d(mHeadOffset);
3566 pos += LLVector3d(0.f, 0.f, 1.f); 5530 pos += LLVector3d(0.f, 0.f, 1.f);
3567 5531
@@ -3748,56 +5712,34 @@ void LLVoiceClient::setEarLocation(S32 loc)
3748 5712
3749void LLVoiceClient::setVoiceVolume(F32 volume) 5713void LLVoiceClient::setVoiceVolume(F32 volume)
3750{ 5714{
3751 LL_DEBUGS("Voice") << "volume is " << volume << LL_ENDL; 5715 int scaled_volume = scale_speaker_volume(volume);
3752 5716
3753 // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. 5717 if(scaled_volume != mSpeakerVolume)
3754 // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
3755
3756 volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
3757 int scaledVolume = 24; // offset scaledVolume by its default level
3758 if(volume < 0.0f)
3759 scaledVolume += ((int)(volume * 248.0f)); // (24 - (-100)) * 2
3760 else
3761 scaledVolume += ((int)(volume * 52.0f)); // (50 - 24) * 2
3762
3763 if(scaledVolume != mSpeakerVolume)
3764 { 5718 {
3765 if((scaledVolume == -100) || (mSpeakerVolume == -100)) 5719 if((scaled_volume == 0) || (mSpeakerVolume == 0))
3766 { 5720 {
3767 mSpeakerMuteDirty = true; 5721 mSpeakerMuteDirty = true;
3768 } 5722 }
3769 5723
3770 mSpeakerVolume = scaledVolume; 5724 mSpeakerVolume = scaled_volume;
3771 mSpeakerVolumeDirty = true; 5725 mSpeakerVolumeDirty = true;
3772 } 5726 }
3773} 5727}
3774 5728
3775void LLVoiceClient::setMicGain(F32 volume) 5729void LLVoiceClient::setMicGain(F32 volume)
3776{ 5730{
3777 int scaledVolume = ((int)(volume * 100.0f)) - 100; 5731 int scaled_volume = scale_mic_volume(volume);
3778 if(scaledVolume != mMicVolume) 5732
5733 if(scaled_volume != mMicVolume)
3779 { 5734 {
3780 mMicVolume = scaledVolume; 5735 mMicVolume = scaled_volume;
3781 mMicVolumeDirty = true; 5736 mMicVolumeDirty = true;
3782 } 5737 }
3783} 5738}
3784 5739
3785void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
3786{
3787 if(!mAccountServerName.empty())
3788 {
3789 // The name has been filled in already, which means we know whether we're connecting to agni or not.
3790 if(!sConnectingToAgni)
3791 {
3792 // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
3793 mAccountServerName = serverName;
3794 }
3795 }
3796}
3797
3798void LLVoiceClient::keyDown(KEY key, MASK mask) 5740void LLVoiceClient::keyDown(KEY key, MASK mask)
3799{ 5741{
3800 LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL; 5742// LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
3801 5743
3802 if (gKeyboard->getKeyRepeated(key)) 5744 if (gKeyboard->getKeyRepeated(key))
3803 { 5745 {
@@ -3935,19 +5877,6 @@ BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
3935 return result; 5877 return result;
3936} 5878}
3937 5879
3938BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
3939{
3940 BOOL result = FALSE;
3941
3942 participantState *participant = findParticipantByID(id);
3943 if(participant)
3944 {
3945 result = participant->mPTT;
3946 }
3947
3948 return result;
3949}
3950
3951BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) 5880BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
3952{ 5881{
3953 BOOL result = FALSE; 5882 BOOL result = FALSE;
@@ -3979,144 +5908,841 @@ F32 LLVoiceClient::getUserVolume(const LLUUID& id)
3979 5908
3980void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) 5909void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
3981{ 5910{
5911 if(mAudioSession)
5912 {
5913 participantState *participant = findParticipantByID(id);
5914 if (participant)
5915 {
5916 // volume can amplify by as much as 4x!
5917 S32 ivol = (S32)(400.f * volume * volume);
5918 participant->mUserVolume = llclamp(ivol, 0, 400);
5919 participant->mVolumeDirty = TRUE;
5920 mAudioSession->mVolumeDirty = TRUE;
5921 }
5922 }
5923}
5924
5925std::string LLVoiceClient::getGroupID(const LLUUID& id)
5926{
5927 std::string result;
5928
3982 participantState *participant = findParticipantByID(id); 5929 participantState *participant = findParticipantByID(id);
3983 if (participant) 5930 if(participant)
3984 { 5931 {
3985 // volume can amplify by as much as 4x! 5932 result = participant->mGroupID;
3986 S32 ivol = (S32)(400.f * volume * volume);
3987 participant->mUserVolume = llclamp(ivol, 0, 400);
3988 participant->mVolumeDirty = TRUE;
3989 mVolumeDirty = TRUE;
3990 } 5933 }
5934
5935 return result;
5936}
5937
5938BOOL LLVoiceClient::getAreaVoiceDisabled()
5939{
5940 return mAreaVoiceDisabled;
3991} 5941}
3992 5942
5943void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
5944{
5945// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
5946
5947 if(!mMainSessionGroupHandle.empty())
5948 {
5949 std::ostringstream stream;
5950 stream
5951 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5952 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5953 << "<RecordingControlType>Start</RecordingControlType>"
5954 << "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
5955 << "<Filename>" << "" << "</Filename>"
5956 << "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
5957 << "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
5958 << "</Request>\n\n\n";
5959
3993 5960
5961 writeString(stream.str());
5962 }
5963}
3994 5964
3995LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id) 5965void LLVoiceClient::recordingLoopSave(const std::string& filename)
3996{ 5966{
3997 serviceType result = serviceTypeUnknown; 5967// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
3998 5968
3999 participantState *participant = findParticipantByID(id); 5969 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
4000 if(participant) 5970 {
5971 std::ostringstream stream;
5972 stream
5973 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5974 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5975 << "<RecordingControlType>Flush</RecordingControlType>"
5976 << "<Filename>" << filename << "</Filename>"
5977 << "</Request>\n\n\n";
5978
5979 writeString(stream.str());
5980 }
5981}
5982
5983void LLVoiceClient::recordingStop()
5984{
5985// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
5986
5987 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
5988 {
5989 std::ostringstream stream;
5990 stream
5991 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
5992 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
5993 << "<RecordingControlType>Stop</RecordingControlType>"
5994 << "</Request>\n\n\n";
5995
5996 writeString(stream.str());
5997 }
5998}
5999
6000void LLVoiceClient::filePlaybackStart(const std::string& filename)
6001{
6002// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
6003
6004 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
6005 {
6006 std::ostringstream stream;
6007 stream
6008 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
6009 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
6010 << "<RecordingControlType>Start</RecordingControlType>"
6011 << "<Filename>" << filename << "</Filename>"
6012 << "</Request>\n\n\n";
6013
6014 writeString(stream.str());
6015 }
6016}
6017
6018void LLVoiceClient::filePlaybackStop()
6019{
6020// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
6021
6022 if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
4001 { 6023 {
4002 result = participant->mServiceType; 6024 std::ostringstream stream;
6025 stream
6026 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
6027 << "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
6028 << "<RecordingControlType>Stop</RecordingControlType>"
6029 << "</Request>\n\n\n";
6030
6031 writeString(stream.str());
6032 }
6033}
6034
6035void LLVoiceClient::filePlaybackSetPaused(bool paused)
6036{
6037 // TODO: Implement once Vivox gives me a sample
6038}
6039
6040void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
6041{
6042 // TODO: Implement once Vivox gives me a sample
6043}
6044
6045LLVoiceClient::sessionState::sessionState() :
6046 mMediaStreamState(streamStateUnknown),
6047 mTextStreamState(streamStateUnknown),
6048 mCreateInProgress(false),
6049 mMediaConnectInProgress(false),
6050 mVoiceInvitePending(false),
6051 mTextInvitePending(false),
6052 mSynthesizedCallerID(false),
6053 mIsChannel(false),
6054 mIsSpatial(false),
6055 mIsP2P(false),
6056 mIncoming(false),
6057 mVoiceEnabled(false),
6058 mReconnect(false),
6059 mVolumeDirty(false),
6060 mParticipantsChanged(false)
6061{
6062}
6063
6064LLVoiceClient::sessionState::~sessionState()
6065{
6066 removeAllParticipants();
6067}
6068
6069LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void)
6070{
6071 return mSessions.begin();
6072}
6073
6074LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void)
6075{
6076 return mSessions.end();
6077}
6078
6079
6080LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle)
6081{
6082 sessionState *result = NULL;
6083 sessionMap::iterator iter = mSessionsByHandle.find(&handle);
6084 if(iter != mSessionsByHandle.end())
6085 {
6086 result = iter->second;
4003 } 6087 }
4004 6088
4005 return result; 6089 return result;
4006} 6090}
4007 6091
4008std::string LLVoiceClient::getGroupID(const LLUUID& id) 6092LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
6093{
6094 sessionState *result = NULL;
6095 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6096 {
6097 sessionState *session = *iter;
6098 if(session->mCreateInProgress && (session->mSIPURI == uri))
6099 {
6100 result = session;
6101 break;
6102 }
6103 }
6104
6105 return result;
6106}
6107
6108LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id)
4009{ 6109{
4010 std::string result; 6110 sessionState *result = NULL;
6111
6112 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6113 {
6114 sessionState *session = *iter;
6115 if(session->mCallerID == participant_id)
6116 {
6117 result = session;
6118 break;
6119 }
6120 }
6121
6122 return result;
6123}
4011 6124
4012 participantState *participant = findParticipantByID(id); 6125LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle)
4013 if(participant) 6126{
6127 sessionState *result = NULL;
6128
6129 if(handle.empty())
4014 { 6130 {
4015 result = participant->mGroupID; 6131 // No handle supplied.
6132 // Check whether there's already a session with this URI
6133 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6134 {
6135 sessionState *s = *iter;
6136 if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
6137 {
6138 // TODO: I need to think about this logic... it's possible that this case should raise an internal error.
6139 result = s;
6140 break;
6141 }
6142 }
6143 }
6144 else // (!handle.empty())
6145 {
6146 // Check for an existing session with this handle
6147 sessionMap::iterator iter = mSessionsByHandle.find(&handle);
6148
6149 if(iter != mSessionsByHandle.end())
6150 {
6151 result = iter->second;
6152 }
6153 }
6154
6155 if(!result)
6156 {
6157 // No existing session found.
6158
6159 LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
6160 result = new sessionState();
6161 result->mSIPURI = uri;
6162 result->mHandle = handle;
6163
6164 mSessions.insert(result);
6165
6166 if(!result->mHandle.empty())
6167 {
6168 mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result));
6169 }
6170 }
6171 else
6172 {
6173 // Found an existing session
6174
6175 if(uri != result->mSIPURI)
6176 {
6177 // TODO: Should this be an internal error?
6178 LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
6179 setSessionURI(result, uri);
6180 }
6181
6182 if(handle != result->mHandle)
6183 {
6184 // TODO: Should this be an internal error?
6185 LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
6186 setSessionHandle(result, handle);
6187 }
6188
6189 LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
6190 }
6191
6192 verifySessionState();
6193
6194 return result;
6195}
6196
6197void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
6198{
6199 // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
6200
6201 if(!session->mHandle.empty())
6202 {
6203 // Remove session from the map if it should have been there.
6204 sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
6205 if(iter != mSessionsByHandle.end())
6206 {
6207 if(iter->second != session)
6208 {
6209 LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
6210 }
6211
6212 mSessionsByHandle.erase(iter);
6213 }
6214 else
6215 {
6216 LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
6217 }
6218 }
6219
6220 session->mHandle = handle;
6221
6222 if(!handle.empty())
6223 {
6224 mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session));
6225 }
6226
6227 verifySessionState();
6228}
6229
6230void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
6231{
6232 // There used to be a map of session URIs to sessions, which made this complex....
6233 session->mSIPURI = uri;
6234
6235 verifySessionState();
6236}
6237
6238void LLVoiceClient::deleteSession(sessionState *session)
6239{
6240 // Remove the session from the handle map
6241 if(!session->mHandle.empty())
6242 {
6243 sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
6244 if(iter != mSessionsByHandle.end())
6245 {
6246 if(iter->second != session)
6247 {
6248 LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL
6249 }
6250 mSessionsByHandle.erase(iter);
6251 }
6252 }
6253
6254 // Remove the session from the URI map
6255 mSessions.erase(session);
6256
6257 // At this point, the session should be unhooked from all lists and all state should be consistent.
6258 verifySessionState();
6259
6260 // If this is the current audio session, clean up the pointer which will soon be dangling.
6261 if(mAudioSession == session)
6262 {
6263 mAudioSession = NULL;
6264 mAudioSessionChanged = true;
6265 }
6266
6267 // ditto for the next audio session
6268 if(mNextAudioSession == session)
6269 {
6270 mNextAudioSession = NULL;
6271 }
6272
6273 // delete the session
6274 delete session;
6275}
6276
6277void LLVoiceClient::deleteAllSessions()
6278{
6279 LL_DEBUGS("Voice") << "called" << LL_ENDL;
6280
6281 while(!mSessions.empty())
6282 {
6283 deleteSession(*(sessionsBegin()));
6284 }
6285
6286 if(!mSessionsByHandle.empty())
6287 {
6288 LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL
6289 }
6290}
6291
6292void LLVoiceClient::verifySessionState(void)
6293{
6294 // This is mostly intended for debugging problems with session state management.
6295 LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
6296
6297 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6298 {
6299 sessionState *session = *iter;
6300
6301 LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
6302
6303 if(!session->mHandle.empty())
6304 {
6305 // every session with a non-empty handle needs to be in the handle map
6306 sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle));
6307 if(i2 == mSessionsByHandle.end())
6308 {
6309 LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
6310 }
6311 else
6312 {
6313 if(i2->second != session)
6314 {
6315 LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
6316 }
6317 }
6318 }
6319 }
6320
6321 // check that every entry in the handle map points to a valid session in the session set
6322 for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
6323 {
6324 sessionState *session = iter->second;
6325 sessionIterator i2 = mSessions.find(session);
6326 if(i2 == mSessions.end())
6327 {
6328 LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
6329 }
6330 else
6331 {
6332 if(session->mHandle != (*i2)->mHandle)
6333 {
6334 LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
6335 }
6336 }
6337 }
6338}
6339
6340LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
6341 mURI(uri)
6342{
6343 mOnlineSL = false;
6344 mOnlineSLim = false;
6345 mCanSeeMeOnline = true;
6346 mHasBlockListEntry = false;
6347 mHasAutoAcceptListEntry = false;
6348 mNameResolved = false;
6349 mInVivoxBuddies = false;
6350 mInSLFriends = false;
6351 mNeedsNameUpdate = false;
6352}
6353
6354void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
6355{
6356 buddyListEntry *buddy = addBuddy(uri, displayName);
6357 buddy->mInVivoxBuddies = true;
6358}
6359
6360LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri)
6361{
6362 std::string empty;
6363 buddyListEntry *buddy = addBuddy(uri, empty);
6364 if(buddy->mDisplayName.empty())
6365 {
6366 buddy->mNameResolved = false;
6367 }
6368 return buddy;
6369}
6370
6371LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
6372{
6373 buddyListEntry *result = NULL;
6374 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6375
6376 if(iter != mBuddyListMap.end())
6377 {
6378 // Found a matching buddy already in the map.
6379 LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
6380 result = iter->second;
6381 }
6382
6383 if(!result)
6384 {
6385 // participant isn't already in one list or the other.
6386 LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
6387 result = new buddyListEntry(uri);
6388 result->mDisplayName = displayName;
6389
6390 if(IDFromName(uri, result->mUUID))
6391 {
6392 // Extracted UUID from name successfully.
6393 }
6394 else
6395 {
6396 LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL;
6397 }
6398
6399 mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result));
4016 } 6400 }
4017 6401
4018 return result; 6402 return result;
4019} 6403}
4020 6404
4021BOOL LLVoiceClient::getAreaVoiceDisabled() 6405LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri)
4022{ 6406{
4023 return mAreaVoiceDisabled; 6407 buddyListEntry *result = NULL;
6408 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6409 if(iter != mBuddyListMap.end())
6410 {
6411 result = iter->second;
6412 }
6413
6414 return result;
6415}
6416
6417LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id)
6418{
6419 buddyListEntry *result = NULL;
6420 buddyListMap::iterator iter;
6421
6422 for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
6423 {
6424 if(iter->second->mUUID == id)
6425 {
6426 result = iter->second;
6427 break;
6428 }
6429 }
6430
6431 return result;
6432}
6433
6434LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name)
6435{
6436 buddyListEntry *result = NULL;
6437 buddyListMap::iterator iter;
6438
6439 for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
6440 {
6441 if(iter->second->mDisplayName == name)
6442 {
6443 result = iter->second;
6444 break;
6445 }
6446 }
6447
6448 return result;
6449}
6450
6451void LLVoiceClient::deleteBuddy(const std::string &uri)
6452{
6453 buddyListMap::iterator iter = mBuddyListMap.find(&uri);
6454 if(iter != mBuddyListMap.end())
6455 {
6456 LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
6457 buddyListEntry *buddy = iter->second;
6458 mBuddyListMap.erase(iter);
6459 delete buddy;
6460 }
6461 else
6462 {
6463 LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
6464 }
6465
6466}
6467
6468void LLVoiceClient::deleteAllBuddies(void)
6469{
6470 while(!mBuddyListMap.empty())
6471 {
6472 deleteBuddy(*(mBuddyListMap.begin()->first));
6473 }
6474
6475 // Don't want to correlate with friends list when we've emptied the buddy list.
6476 mBuddyListMapPopulated = false;
6477
6478 // Don't want to correlate with friends list when we've reset the block rules.
6479 mBlockRulesListReceived = false;
6480 mAutoAcceptRulesListReceived = false;
6481}
6482
6483void LLVoiceClient::deleteAllBlockRules(void)
6484{
6485 // Clear the block list entry flags from all local buddy list entries
6486 buddyListMap::iterator buddy_it;
6487 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
6488 {
6489 buddy_it->second->mHasBlockListEntry = false;
6490 }
6491}
6492
6493void LLVoiceClient::deleteAllAutoAcceptRules(void)
6494{
6495 // Clear the auto-accept list entry flags from all local buddy list entries
6496 buddyListMap::iterator buddy_it;
6497 for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
6498 {
6499 buddy_it->second->mHasAutoAcceptListEntry = false;
6500 }
6501}
6502
6503void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
6504{
6505 buddyListEntry *buddy = NULL;
6506
6507 // blockMask is the SIP URI of a friends list entry
6508 buddyListMap::iterator iter = mBuddyListMap.find(&blockMask);
6509 if(iter != mBuddyListMap.end())
6510 {
6511 LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
6512 buddy = iter->second;
6513 }
6514
6515 if(buddy == NULL)
6516 {
6517 LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
6518 buddy = addBuddy(blockMask);
6519 }
6520
6521 if(buddy != NULL)
6522 {
6523 buddy->mHasBlockListEntry = true;
6524 }
6525}
6526
6527void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
6528{
6529 buddyListEntry *buddy = NULL;
6530
6531 // blockMask is the SIP URI of a friends list entry
6532 buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask);
6533 if(iter != mBuddyListMap.end())
6534 {
6535 LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
6536 buddy = iter->second;
6537 }
6538
6539 if(buddy == NULL)
6540 {
6541 LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
6542 buddy = addBuddy(autoAcceptMask);
6543 }
6544
6545 if(buddy != NULL)
6546 {
6547 buddy->mHasAutoAcceptListEntry = true;
6548 }
6549}
6550
6551void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
6552{
6553 // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done.
6554 mBlockRulesListReceived = true;
6555}
6556
6557void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
6558{
6559 // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done.
6560 mAutoAcceptRulesListReceived = true;
4024} 6561}
4025 6562
4026void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) 6563void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
4027{ 6564{
4028 mObservers.insert(observer); 6565 mParticipantObservers.insert(observer);
4029} 6566}
4030 6567
4031void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) 6568void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
4032{ 6569{
4033 mObservers.erase(observer); 6570 mParticipantObservers.erase(observer);
4034} 6571}
4035 6572
4036void LLVoiceClient::notifyObservers() 6573void LLVoiceClient::notifyParticipantObservers()
4037{ 6574{
4038 for (observer_set_t::iterator it = mObservers.begin(); 6575 for (observer_set_t::iterator it = mParticipantObservers.begin();
4039 it != mObservers.end(); 6576 it != mParticipantObservers.end();
4040 ) 6577 )
4041 { 6578 {
4042 LLVoiceClientParticipantObserver* observer = *it; 6579 LLVoiceClientParticipantObserver* observer = *it;
4043 observer->onChange(); 6580 observer->onChange();
4044 // In case onChange() deleted an entry. 6581 // In case onChange() deleted an entry.
4045 it = mObservers.upper_bound(observer); 6582 it = mParticipantObservers.upper_bound(observer);
4046 } 6583 }
4047} 6584}
4048 6585
4049void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer) 6586void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
4050{ 6587{
4051 mStatusObservers.insert(observer); 6588 mStatusObservers.insert(observer);
4052} 6589}
4053 6590
4054void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer) 6591void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
4055{ 6592{
4056 mStatusObservers.erase(observer); 6593 mStatusObservers.erase(observer);
4057} 6594}
4058 6595
4059void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) 6596void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
4060{ 6597{
4061 if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) 6598 if(mAudioSession)
4062 { 6599 {
4063 switch(mVivoxErrorStatusCode) 6600 if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
4064 { 6601 {
4065 case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; 6602 switch(mAudioSession->mErrorStatusCode)
4066 case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; 6603 {
4067 case 20715: 6604 case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break;
4068 //invalid channel, we may be using a set of poorly cached 6605 case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break;
4069 //info 6606 case 20715:
4070 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; 6607 //invalid channel, we may be using a set of poorly cached
4071 break; 6608 //info
4072 case 1009: 6609 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4073 //invalid username and password 6610 break;
4074 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; 6611 case 1009:
4075 break; 6612 //invalid username and password
4076 } 6613 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4077 6614 break;
4078 // Reset the error code to make sure it won't be reused later by accident. 6615 }
4079 mVivoxErrorStatusCode = 0;
4080 }
4081
4082 if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
4083 //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
4084 && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408))
4085 {
4086 // call failed because other user was not available
4087 // treat this as an error case
4088 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
4089 6616
4090 // Reset the error code to make sure it won't be reused later by accident. 6617 // Reset the error code to make sure it won't be reused later by accident.
4091 mVivoxErrorStatusCode = 0; 6618 mAudioSession->mErrorStatusCode = 0;
6619 }
6620 else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
6621 {
6622 switch(mAudioSession->mErrorStatusCode)
6623 {
6624 case 404: // NOT_FOUND
6625 case 480: // TEMPORARILY_UNAVAILABLE
6626 case 408: // REQUEST_TIMEOUT
6627 // call failed because other user was not available
6628 // treat this as an error case
6629 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
6630
6631 // Reset the error code to make sure it won't be reused later by accident.
6632 mAudioSession->mErrorStatusCode = 0;
6633 break;
6634 }
6635 }
4092 } 6636 }
4093 6637
4094 LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status) << ", session URI " << mSessionURI << LL_ENDL; 6638 LL_DEBUGS("Voice")
6639 << " " << LLVoiceClientStatusObserver::status2string(status)
6640 << ", session URI " << getAudioSessionURI()
6641 << (inSpatialChannel()?", proximal is true":", proximal is false")
6642 << LL_ENDL;
4095 6643
4096 for (status_observer_set_t::iterator it = mStatusObservers.begin(); 6644 for (status_observer_set_t::iterator it = mStatusObservers.begin();
4097 it != mStatusObservers.end(); 6645 it != mStatusObservers.end();
4098 ) 6646 )
4099 { 6647 {
4100 LLVoiceClientStatusObserver* observer = *it; 6648 LLVoiceClientStatusObserver* observer = *it;
4101 observer->onChange(status, mSessionURI, !mNonSpatialChannel); 6649 observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
4102 // In case onError() deleted an entry. 6650 // In case onError() deleted an entry.
4103 it = mStatusObservers.upper_bound(observer); 6651 it = mStatusObservers.upper_bound(observer);
4104 } 6652 }
4105 6653
4106} 6654}
4107 6655
6656void LLVoiceClient::addObserver(LLFriendObserver* observer)
6657{
6658 mFriendObservers.insert(observer);
6659}
6660
6661void LLVoiceClient::removeObserver(LLFriendObserver* observer)
6662{
6663 mFriendObservers.erase(observer);
6664}
6665
6666void LLVoiceClient::notifyFriendObservers()
6667{
6668 for (friend_observer_set_t::iterator it = mFriendObservers.begin();
6669 it != mFriendObservers.end();
6670 )
6671 {
6672 LLFriendObserver* observer = *it;
6673 it++;
6674 // The only friend-related thing we notify on is online/offline transitions.
6675 observer->changed(LLFriendObserver::ONLINE);
6676 }
6677}
6678
6679void LLVoiceClient::lookupName(const LLUUID &id)
6680{
6681 gCacheName->getName(id, onAvatarNameLookup);
6682}
6683
4108//static 6684//static
4109// void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data) 6685void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data)
4110// { 6686{
4111// participantState* statep = gVoiceClient->findParticipantByID(id); 6687 if(gVoiceClient)
6688 {
6689 std::string name = llformat("%s %s", first.c_str(), last.c_str());
6690 gVoiceClient->avatarNameResolved(id, name);
6691 }
6692}
4112 6693
4113// if (statep) 6694void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
4114// { 6695{
4115// statep->mDisplayName = first + " " + last; 6696 // If the avatar whose name just resolved is on our friends list, resync the friends list.
4116// } 6697 if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
6698 {
6699 mFriendsListDirty = true;
6700 }
4117 6701
4118// gVoiceClient->notifyObservers(); 6702 // Iterate over all sessions.
4119// } 6703 for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
6704 {
6705 sessionState *session = *iter;
6706
6707 // Check for this user as a participant in this session
6708 participantState *participant = session->findParticipantByID(id);
6709 if(participant)
6710 {
6711 // Found -- fill in the name
6712 participant->mAccountName = name;
6713 // and post a "participants updated" message to listeners later.
6714 session->mParticipantsChanged = true;
6715 }
6716
6717 // Check whether this is a p2p session whose caller name just resolved
6718 if(session->mCallerID == id)
6719 {
6720 // this session's "caller ID" just resolved. Fill in the name.
6721 session->mName = name;
6722 if(session->mTextInvitePending)
6723 {
6724 session->mTextInvitePending = false;
6725
6726 // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel.
6727 }
6728 if(session->mVoiceInvitePending)
6729 {
6730 session->mVoiceInvitePending = false;
6731
6732 gIMMgr->inviteToSession(
6733 session->mIMSessionID,
6734 session->mName,
6735 session->mCallerID,
6736 session->mName,
6737 IM_SESSION_P2P_INVITE,
6738 LLIMMgr::INVITATION_TYPE_VOICE,
6739 session->mHandle,
6740 session->mSIPURI);
6741 }
6742
6743 }
6744 }
6745}
4120 6746
4121class LLViewerParcelVoiceInfo : public LLHTTPNode 6747class LLViewerParcelVoiceInfo : public LLHTTPNode
4122{ 6748{