diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7891add9b87e50640c8ba3d5495f1cbaf01a8d0e..cf28d9da5ed085b7ca2fe453dd83b0ad36735a68 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -515,8 +515,10 @@ set(viewer_SOURCE_FILES
     llvoground.cpp
     llvoicechannel.cpp
     llvoiceclient.cpp
+    llvoicedw.cpp
     llvoiceremotectrl.cpp
     llvoicevisualizer.cpp
+    llvoicevivox.cpp
     llvoinventorylistener.cpp
     llvopartgroup.cpp
     llvosky.cpp
@@ -1018,8 +1020,10 @@ set(viewer_HEADER_FILES
     llvoground.h
     llvoicechannel.h
     llvoiceclient.h
+    llvoicedw.h
     llvoiceremotectrl.h
     llvoicevisualizer.h
+    llvoicevivox.h
     llvoinventorylistener.h
     llvopartgroup.h
     llvosky.h
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index 0eb98e3311ecd1fbe78859ed5c840641a55078ba..229f15852d634aff7a3766606032cc705c8fa850 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -19,8 +19,7 @@
 						</array>
 					<key>tags</key>
 						<array>
-							<string>AppInit</string>
-							<string>LLLogin</string>
+							<string>Voice</string>
 						</array>
 				</map>
 				<map>
@@ -36,8 +35,7 @@
 						</array>
 					<key>tags</key>
 						<array>
-							<string>AppInit</string>
-							<string>LLLogin</string>
+							<string>Voice</string>
 						</array>
 				</map>
 			</array>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1641ab0ce1b8064be844dc9b128d9399a6bd5df3..8ca330ca33723bb11b0204342718bbfd30fddf3c 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10312,6 +10312,17 @@
       <key>Value</key>
       <string></string>
     </map>
+    <key>VivoxDebugSIPURIHostName</key>
+    <map>
+      <key>Comment</key>
+      <string>Hostname portion of vivox SIP URIs (empty string for the default).</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>
     <key>VivoxDebugVoiceAccountServerURI</key>
     <map>
       <key>Comment</key>
@@ -10323,6 +10334,28 @@
       <key>Value</key>
       <string></string>
     </map>
+    <key>VivoxVoiceHost</key>
+    <map>
+      <key>Comment</key>
+      <string>Client SLVoice host to connect to</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>127.0.0.1</string>
+    </map>
+    <key>VivoxVoicePort</key>
+    <map>
+      <key>Comment</key>
+      <string>Client SLVoice port to connect to</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>U32</string>
+      <key>Value</key>
+      <integer>44125</integer>
+    </map>
     <key>VoiceCallsFriendsOnly</key>
     <map>
       <key>Comment</key>
@@ -10455,6 +10488,17 @@
       <key>Value</key>
       <string>Default</string>
     </map>
+    <key>VoiceLogFile</key>
+    <map>
+      <key>Comment</key>
+      <string>Log file to use when launching the voice daemon</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>
     <key>VoiceOutputAudioDevice</key>
     <map>
       <key>Comment</key>
@@ -10499,6 +10543,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>VoiceServerType</key>
+    <map>
+      <key>Comment</key>
+      <string>The type of voice server to connect to.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>vivox</string>
+    </map>
     <key>WLSkyDetail</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 5e2e374df65727a51af2d73023872ae1f211659b..b5cad710a8feeda09d07b184bfcfe6bd638c8163 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -5972,7 +5972,7 @@ bool LLAgent::teleportCore(bool is_local)
 	
 	// MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.
 	// This was breaking the case of teleporting within a single sim.  Backing it out for now.
-//	gVoiceClient->leaveChannel();
+//	LLVoiceClient::getInstance()->leaveChannel();
 	
 	return true;
 }
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index c2f8487aa93a37dd73452caede2501079551b6e9..c1d1734cfb1c09d3c575e2aeb93ae2e711ab9297 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -920,8 +920,7 @@ bool LLAppViewer::mainLoop()
 	
 	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
 
-	LLVoiceChannel::initClass();
-	LLVoiceClient::init(gServicePump);
+	LLVoiceClient::getInstance()->init(gServicePump);
 				
 	LLTimer frameTimer,idleTimer;
 	LLTimer debugTime;
@@ -1241,7 +1240,7 @@ bool LLAppViewer::cleanup()
 	// to ensure shutdown order
 	LLMortician::setZealous(TRUE);
 
-	LLVoiceClient::terminate();
+	LLVoiceClient::getInstance()->terminate();
 	
 	disconnectViewer();
 
@@ -3807,7 +3806,7 @@ void LLAppViewer::sendLogoutRequest()
 		gLogoutMaxTime = LOGOUT_REQUEST_TIME;
 		mLogoutRequestSent = TRUE;
 		
-		gVoiceClient->leaveChannel();
+		LLVoiceClient::getInstance()->leaveChannel();
 
 		//Set internal status variables and marker files
 		gLogoutInProgress = TRUE;
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index c3deb602eec9042cef8b426f72eabaabb23a8d16..118a546ada0bcc2d385a8a775187e19edfb4e9c9 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -290,7 +290,7 @@ bool LLAvatarActions::canCall(const LLUUID &id)
 		// can be only ONLINE. There is no way to see "usual resident" in OFFLINE status. If we see "usual
 		// resident" it automatically means that the resident is ONLINE. So to make a call to the "usual resident"
 		// we need to check only that "our" voice is enabled.
-		return LLVoiceClient::voiceEnabled();
+		return LLVoiceClient::getInstance()->voiceEnabled();
 	}
 
 }
diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp
index 976b31250935be56f769cb3ac3d15631994b4d0a..8722ffd152186d16f0f1bcc78e373fdee8b9dece 100644
--- a/indra/newview/llbottomtray.cpp
+++ b/indra/newview/llbottomtray.cpp
@@ -365,7 +365,7 @@ BOOL LLBottomTray::postBuild()
 	mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") );
 
 	// Registering Chat Bar to receive Voice client status change notifications.
-	gVoiceClient->addObserver(this);
+	LLVoiceClient::getInstance()->addObserver(this);
 
 	mObjectDefaultWidthMap[RS_BUTTON_GESTURES] = mGesturePanel->getRect().getWidth();
 	mObjectDefaultWidthMap[RS_BUTTON_MOVEMENT] = mMovementPanel->getRect().getWidth();
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
index 1468f6d584f5b628f36cba6bbc4aa3f829956f94..38782226d6cf4a4704685eb960894c3c8045c018 100644
--- a/indra/newview/llcallfloater.cpp
+++ b/indra/newview/llcallfloater.cpp
@@ -143,9 +143,9 @@ LLCallFloater::~LLCallFloater()
 
 	// Don't use LLVoiceClient::getInstance() here 
 	// singleton MAY have already been destroyed.
-	if(gVoiceClient)
+	if(LLVoiceClient::getInstance())
 	{
-		gVoiceClient->removeObserver(this);
+		LLVoiceClient::getInstance()->removeObserver(this);
 	}
 	LLTransientFloaterMgr::getInstance()->removeControlView(this);
 }
@@ -194,7 +194,7 @@ void LLCallFloater::draw()
 	// Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent)
 //	onChange();
 
-	bool is_moderator_muted = gVoiceClient->getIsModeratorMuted(gAgentID);
+	bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID);
 
 	if (mIsModeratorMutedVoice != is_moderator_muted)
 	{
@@ -212,7 +212,6 @@ void LLCallFloater::draw()
 void LLCallFloater::onChange()
 {
 	if (NULL == mParticipants) return;
-
 	updateParticipantsVoiceState();
 
 	// Add newly joined participants.
@@ -252,11 +251,11 @@ void LLCallFloater::updateSession()
 	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
 	if (voice_channel)
 	{
-		lldebugs << "Current voice channel: " << voice_channel->getSessionID() << llendl;
+		LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL;
 
 		if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID())
 		{
-			lldebugs << "Speaker manager is already set for session: " << voice_channel->getSessionID() << llendl;
+			LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL;
 			return;
 		}
 		else
@@ -266,7 +265,7 @@ void LLCallFloater::updateSession()
 	}
 
 	const LLUUID& session_id = voice_channel->getSessionID();
-	lldebugs << "Set speaker manager for session: " << session_id << llendl;
+	LL_DEBUGS("Voice") << "Set speaker manager for session: " << session_id << LL_ENDL;
 
 	LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
 	if (im_session)
@@ -306,7 +305,7 @@ void LLCallFloater::updateSession()
 	{
 		// by default let show nearby chat participants
 		mSpeakerManager = LLLocalSpeakerMgr::getInstance();
-		lldebugs << "Set DEFAULT speaker manager" << llendl;
+		LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;
 		mVoiceType = VC_LOCAL_CHAT;
 	}
 
@@ -482,16 +481,14 @@ void LLCallFloater::updateAgentModeratorState()
 static void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids)
 {
 	// Get a list of participants from VoiceClient
-	LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList();
-	if (voice_map)
+	std::vector<LLUUID> participants = LLVoiceClient::getInstance()->getParticipantList();
+	
+	for (std::vector<LLUUID>::const_iterator iter = participants.begin();
+		 iter != participants.end(); ++iter)
 	{
-		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin();
-			iter != voice_map->end(); ++iter)
-		{
-			LLUUID id = (*iter).second->mAvatarID;
-			speakers_uuids.push_back(id);
-		}
+		speakers_uuids.push_back(*iter);
 	}
+
 }
 
 void LLCallFloater::initParticipantsVoiceState()
@@ -567,7 +564,7 @@ void LLCallFloater::updateParticipantsVoiceState()
 
 		std::vector<LLUUID>::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id);
 
-		lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl;
+		LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL;
 
 		// If an avatarID assigned to a panel is found in a speakers list
 		// obtained from VoiceClient we assign the JOINED status to the owner
@@ -611,7 +608,7 @@ void LLCallFloater::updateParticipantsVoiceState()
 			}
 			else
 			{
-				llwarns << "Unsupported (" << getState(participant_id) << ") state: " << item->getAvatarName()  << llendl;
+				llwarns << "Unsupported (" << getState(participant_id) << ") state: " << item->getAvatarName()  << LL_ENDL;
 			}
 		}
 	}
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
index aa343b2f6972260b26cf926c452e619083648b4f..775ccfa0d807747a562449b4e049965908fa4e72 100644
--- a/indra/newview/llfloaterabout.cpp
+++ b/indra/newview/llfloaterabout.cpp
@@ -266,8 +266,18 @@ LLSD LLFloaterAbout::getInfo()
 	info["J2C_VERSION"] = LLImageJ2C::getEngineInfo();
 	bool want_fullname = true;
 	info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : LLSD();
-	info["VIVOX_VERSION"] = gVoiceClient ? gVoiceClient->getAPIVersion() : LLTrans::getString("NotConnected");
-
+	if(LLVoiceClient::getInstance()->voiceEnabled())
+	{
+		LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion();
+		std::ostringstream version_string;
+		version_string << version.serverType << " " << version.serverVersion << std::endl;
+		info["VOICE_VERSION"] = version_string.str();
+	}
+	else 
+	{
+		info["VOICE_VERSION"] = LLTrans::getString("NotConnected");
+	}
+	
 	// TODO: Implement media plugin version query
 	info["QT_WEBKIT_VERSION"] = "4.5.2 (version number hard-coded)";
 
diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp
index 9108cfb72bd0f2da607583e3f90d9f3b8b1d6fa7..5a5d88b0587ef1de55178da42105a92ff6618e59 100644
--- a/indra/newview/llfloaterchatterbox.cpp
+++ b/indra/newview/llfloaterchatterbox.cpp
@@ -343,7 +343,7 @@ LLFloaterChatterBox* LLFloaterChatterBox::getInstance()
 //static 
 LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater()
 {
-	if (!LLVoiceClient::voiceEnabled())
+	if (!LLVoiceClient::getInstance()->voiceEnabled())
 	{
 		return NULL;
 	}
diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp
index 43024a4bd03fd3ae7178fda4dc29feb774d0cf73..0e437fb9b3fea1e75e2b5ddb8adb15ad5ddd48bb 100644
--- a/indra/newview/llfloatervoicedevicesettings.cpp
+++ b/indra/newview/llfloatervoicedevicesettings.cpp
@@ -64,9 +64,6 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()
 	// grab "live" mic volume level
 	mMicVolume = gSavedSettings.getF32("AudioLevelMic");
 
-	// ask for new device enumeration
-	// now do this in onOpen() instead...
-	//gVoiceClient->refreshDeviceLists();
 }
 
 LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings()
@@ -105,12 +102,12 @@ void LLPanelVoiceDeviceSettings::draw()
 	refresh();
 
 	// let user know that volume indicator is not yet available
-	bool is_in_tuning_mode = gVoiceClient->inTuningMode();
+	bool is_in_tuning_mode = LLVoiceClient::getInstance()->inTuningMode();
 	childSetVisible("wait_text", !is_in_tuning_mode);
 
 	LLPanel::draw();
 
-	F32 voice_power = gVoiceClient->tuningGetEnergy();
+	F32 voice_power = LLVoiceClient::getInstance()->tuningGetEnergy();
 	S32 discrete_power = 0;
 
 	if (!is_in_tuning_mode)
@@ -193,13 +190,13 @@ void LLPanelVoiceDeviceSettings::refresh()
 	LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider");
 	// set mic volume tuning slider based on last mic volume setting
 	F32 current_volume = (F32)volume_slider->getValue().asReal();
-	gVoiceClient->tuningSetMicVolume(current_volume);
+	LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume);
 
 	// Fill in popup menus
 	mCtrlInputDevices = getChild<LLComboBox>("voice_input_device");
 	mCtrlOutputDevices = getChild<LLComboBox>("voice_output_device");
 
-	if(!gVoiceClient->deviceSettingsAvailable())
+	if(!LLVoiceClient::getInstance()->deviceSettingsAvailable())
 	{
 		// The combo boxes are disabled, since we can't get the device settings from the daemon just now.
 		// Put the currently set default (ONLY) in the box, and select it.
@@ -218,17 +215,16 @@ void LLPanelVoiceDeviceSettings::refresh()
 	}
 	else if (!mDevicesUpdated)
 	{
-		LLVoiceClient::deviceList *devices;
-		
-		LLVoiceClient::deviceList::iterator iter;
+		LLVoiceDeviceList::const_iterator iter;
 		
 		if(mCtrlInputDevices)
 		{
 			mCtrlInputDevices->removeall();
 			mCtrlInputDevices->add( getString("default_text"), ADD_BOTTOM );
 
-			devices = gVoiceClient->getCaptureDevices();
-			for(iter=devices->begin(); iter != devices->end(); iter++)
+			for(iter=LLVoiceClient::getInstance()->getCaptureDevices().begin(); 
+				iter != LLVoiceClient::getInstance()->getCaptureDevices().end();
+				iter++)
 			{
 				mCtrlInputDevices->add( *iter, ADD_BOTTOM );
 			}
@@ -244,8 +240,8 @@ void LLPanelVoiceDeviceSettings::refresh()
 			mCtrlOutputDevices->removeall();
 			mCtrlOutputDevices->add( getString("default_text"), ADD_BOTTOM );
 
-			devices = gVoiceClient->getRenderDevices();
-			for(iter=devices->begin(); iter != devices->end(); iter++)
+			for(iter= LLVoiceClient::getInstance()->getRenderDevices().begin(); 
+				iter !=  LLVoiceClient::getInstance()->getRenderDevices().end(); iter++)
 			{
 				mCtrlOutputDevices->add( *iter, ADD_BOTTOM );
 			}
@@ -267,34 +263,34 @@ void LLPanelVoiceDeviceSettings::initialize()
 	mDevicesUpdated = FALSE;
 
 	// ask for new device enumeration
-	gVoiceClient->refreshDeviceLists();
+	LLVoiceClient::getInstance()->refreshDeviceLists();
 
 	// put voice client in "tuning" mode
-	gVoiceClient->tuningStart();
+	LLVoiceClient::getInstance()->tuningStart();
 	LLVoiceChannel::suspend();
 }
 
 void LLPanelVoiceDeviceSettings::cleanup()
 {
-	gVoiceClient->tuningStop();
+	LLVoiceClient::getInstance()->tuningStop();
 	LLVoiceChannel::resume();
 }
 
 // static
 void LLPanelVoiceDeviceSettings::onCommitInputDevice(LLUICtrl* ctrl, void* user_data)
 {
-	if(gVoiceClient)
+	if(LLVoiceClient::getInstance())
 	{
-		gVoiceClient->setCaptureDevice(ctrl->getValue().asString());
+		LLVoiceClient::getInstance()->setCaptureDevice(ctrl->getValue().asString());
 	}
 }
 
 // static
 void LLPanelVoiceDeviceSettings::onCommitOutputDevice(LLUICtrl* ctrl, void* user_data)
 {
-	if(gVoiceClient)
+	if(LLVoiceClient::getInstance())
 	{
-		gVoiceClient->setRenderDevice(ctrl->getValue().asString());
+		LLVoiceClient::getInstance()->setRenderDevice(ctrl->getValue().asString());
 	}
 }
 
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
index 029ddbaf2c756cb00e593e3bf3ebf96cfae7ed28..74e0a06a346429c023eb6cff296d998af9f9209f 100644
--- a/indra/newview/llimpanel.cpp
+++ b/indra/newview/llimpanel.cpp
@@ -300,7 +300,7 @@ void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data)
 	LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
 	if (floaterp)
 	{
-		gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());
+		LLVoiceClient::getInstance()->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());
 	}
 }
 
@@ -312,7 +312,7 @@ void LLFloaterIMPanel::draw()
 	
 	BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "")
 					  && mSessionInitialized
-					  && LLVoiceClient::voiceEnabled()
+					  && LLVoiceClient::getInstance()->voiceEnabled()
 					  && mCallBackEnabled;
 
 	// hide/show start call and end call buttons
@@ -320,8 +320,8 @@ void LLFloaterIMPanel::draw()
 	if (!voice_channel)
 		return;
 
-	childSetVisible("end_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED);
-	childSetVisible("start_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED);
+	childSetVisible("end_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED);
+	childSetVisible("start_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED);
 	childSetEnabled("start_call_btn", enable_connect);
 	childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty());
 	
@@ -384,11 +384,11 @@ void LLFloaterIMPanel::draw()
 	else
 	{
 		// refresh volume and mute checkbox
-		childSetVisible("speaker_volume", LLVoiceClient::voiceEnabled() && voice_channel->isActive());
-		childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID));
+		childSetVisible("speaker_volume", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive());
+		childSetValue("speaker_volume", LLVoiceClient::getInstance()->getUserVolume(mOtherParticipantUUID));
 
 		childSetValue("mute_btn", LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat));
-		childSetVisible("mute_btn", LLVoiceClient::voiceEnabled() && voice_channel->isActive());
+		childSetVisible("mute_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive());
 	}
 	LLFloater::draw();
 }
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index daabf1f7178361308155ca4232624ca673192c2f..15e657c8664978be8ffa37a7297bf9516ff1baae 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -331,13 +331,13 @@ LLIMModel::LLIMSession::~LLIMSession()
 	mSpeakers = NULL;
 
 	// End the text IM session if necessary
-	if(gVoiceClient && mOtherParticipantID.notNull())
+	if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull())
 	{
 		switch(mType)
 		{
 		case IM_NOTHING_SPECIAL:
 		case IM_SESSION_P2P_INVITE:
-			gVoiceClient->endUserIMSession(mOtherParticipantID);
+			LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID);
 			break;
 
 		default:
@@ -857,7 +857,7 @@ void LLIMModel::sendMessage(const std::string& utf8_text,
 	if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
 	{
 		// User is online through the OOW connector, but not with a regular viewer.  Try to send the message via SLVoice.
-		sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text);
+		sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);
 	}
 	
 	if(!sent)
@@ -1723,7 +1723,11 @@ bool LLIncomingCallDialog::lifetimeHasExpired()
 void LLIncomingCallDialog::onLifetimeExpired()
 {
 	// check whether a call is valid or not
-	if (LLVoiceClient::getInstance()->findSession(mPayload["caller_id"].asUUID()))
+	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mPayload["session_id"].asUUID());
+	if(channelp &&
+	   (channelp->getState() != LLVoiceChannel::STATE_NO_CHANNEL_INFO) &&
+	   (channelp->getState() != LLVoiceChannel::STATE_ERROR) &&
+	   (channelp->getState() != LLVoiceChannel::STATE_HUNG_UP))
 	{
 		// restart notification's timer if call is still valid
 		mLifetimeTimer.start();
@@ -1952,10 +1956,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response)
 	{
 		if (type == IM_SESSION_P2P_INVITE)
 		{
-			if(gVoiceClient)
+			if(LLVoiceClient::getInstance())
 			{
 				std::string s = mPayload["session_handle"].asString();
-				gVoiceClient->declineInvite(s);
+				LLVoiceClient::getInstance()->declineInvite(s);
 			}
 		}
 		else
@@ -2043,11 +2047,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response)
 	{
 		if (type == IM_SESSION_P2P_INVITE)
 		{
-			if(gVoiceClient)
-			{
-				std::string s = payload["session_handle"].asString();
-				gVoiceClient->declineInvite(s);
-			}
+		  std::string s = payload["session_handle"].asString();
+		  LLVoiceClient::getInstance()->declineInvite(s);
 		}
 		else
 		{
@@ -3156,7 +3157,7 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 				return;
 			}
 			
-			if(!LLVoiceClient::voiceEnabled())
+			if(!LLVoiceClient::getInstance()->voiceEnabled())
 			{
 				// Don't display voice invites unless the user has voice enabled.
 				return;
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index a2b3a54f51f80f920705557a5d812acf8106d18c..ea55d25106f61dae5658801bbf0e6b2be2455d75 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -517,7 +517,7 @@ void LLInspectAvatar::updateVolumeSlider()
 	// By convention, we only display and toggle voice mutes, not all mutes
 	bool is_muted = LLMuteList::getInstance()->
 						isMuted(mAvatarID, LLMute::flagVoiceChat);
-	bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID);
+	bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID);
 	bool is_self = (mAvatarID == gAgent.getID());
 
 	LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn");
@@ -544,7 +544,7 @@ void LLInspectAvatar::updateVolumeSlider()
 	else
 	{
 		// actual volume
-		volume = gVoiceClient->getUserVolume(mAvatarID);
+		volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID);
 
 		// *HACK: Voice client doesn't have any data until user actually
 		// says something.
@@ -578,7 +578,7 @@ void LLInspectAvatar::onClickMuteVolume()
 void LLInspectAvatar::onVolumeChange(const LLSD& data)
 {
 	F32 volume = (F32)data.asReal();
-	gVoiceClient->setUserVolume(mAvatarID, volume);
+	LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume);
 }
 
 void LLInspectAvatar::nameUpdatedCallback(
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 04e5cef62e3b83d2415a391fac775a8e488c93a7..c58a220ee8ae50e35e400df20c6289d82c60b641 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -150,6 +150,7 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia
 	requested_options.append("buddy-list");
 	requested_options.append("ui-config");
 #endif
+	requested_options.append("voice-config");
 	requested_options.append("tutorial_setting");
 	requested_options.append("login-flags");
 	requested_options.append("global-textures");
diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp
index 63803469dd1519e25dcfa9b686fc1eea81d5801e..953121bd6f5172dcd5e93629d3afb2f12fe699d5 100644
--- a/indra/newview/lloutputmonitorctrl.cpp
+++ b/indra/newview/lloutputmonitorctrl.cpp
@@ -119,7 +119,7 @@ void LLOutputMonitorCtrl::draw()
 {
 	// Copied from llmediaremotectrl.cpp
 	// *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then
-	// call directly into gVoiceClient to ask if that agent-id is muted, is
+	// call directly into LLVoiceClient::getInstance() to ask if that agent-id is muted, is
 	// speaking, and what power.  This avoids duplicating data, which can get
 	// out of sync.
 	const F32 LEVEL_0 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL / 3.f;
@@ -128,14 +128,14 @@ void LLOutputMonitorCtrl::draw()
 
 	if (getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull())
 	{
-		setPower(gVoiceClient->getCurrentPower(mSpeakerId));
+		setPower(LLVoiceClient::getInstance()->getCurrentPower(mSpeakerId));
 		if(mIsAgentControl)
 		{
-			setIsTalking(gVoiceClient->getUserPTTState());
+			setIsTalking(LLVoiceClient::getInstance()->getUserPTTState());
 		}
 		else
 		{
-			setIsTalking(gVoiceClient->getIsSpeaking(mSpeakerId));
+			setIsTalking(LLVoiceClient::getInstance()->getIsSpeaking(mSpeakerId));
 		}
 	}
 
diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h
index 85ea552a572b049fbf796b0f914fcabb9481f7d5..e6dbde41db02c35f2f4f97408aabe61570b3e09a 100644
--- a/indra/newview/lloutputmonitorctrl.h
+++ b/indra/newview/lloutputmonitorctrl.h
@@ -112,7 +112,7 @@ class LLOutputMonitorCtrl
 	LLPointer<LLUIImage> mImageLevel2;
 	LLPointer<LLUIImage> mImageLevel3;
 
-	/** whether to deal with gVoiceClient directly */
+	/** whether to deal with LLVoiceClient::getInstance() directly */
 	bool			mAutoUpdate;
 
 	/** uuid of a speaker being monitored */
diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp
index 67e048885f76cbc2c5756408a0346355272f3b5f..3f1b23ba14542546666911986ef0569830b0e4b2 100644
--- a/indra/newview/lloverlaybar.cpp
+++ b/indra/newview/lloverlaybar.cpp
@@ -258,7 +258,7 @@ void LLOverlayBar::refresh()
 	{
 		// update "remotes"
 		childSetVisible("media_remote_container", TRUE);
-		childSetVisible("voice_remote_container", LLVoiceClient::voiceEnabled());
+		childSetVisible("voice_remote_container", LLVoiceClient::getInstance()->voiceEnabled());
 		childSetVisible("state_buttons", TRUE);
 	}
 
diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp
index b547997e7aaf75854db77704e59e22fbfde69e19..0111a200e81bf1460a3c7f54f1e35c0dc6b6c948 100644
--- a/indra/newview/llpanelimcontrolpanel.cpp
+++ b/indra/newview/llpanelimcontrolpanel.cpp
@@ -92,7 +92,7 @@ BOOL LLPanelChatControlPanel::postBuild()
 void LLPanelChatControlPanel::draw()
 {
 	// hide/show start call and end call buttons
-	bool voice_enabled = LLVoiceClient::voiceEnabled();
+	bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled();
 
 	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId);
 	if (!session) return;
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index e2da4c44758626666d1ba10a35d4f43d0a9e10bd..ce946121dff529e60e4189359acc1632795a1850 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -336,7 +336,6 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
 {
 	if (mExcludeAgent && gAgent.getID() == avatar_id) return;
 	if (mAvatarList->contains(avatar_id)) return;
-
 	mAvatarList->getIDs().push_back(avatar_id);
 	mAvatarList->setDirty();
 	adjustParticipant(avatar_id);
@@ -622,7 +621,7 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD&
 	}
 	else if (item == "can_call")
 	{
-		return LLVoiceClient::voiceEnabled();
+		return LLVoiceClient::getInstance()->voiceEnabled();
 	}
 
 	return true;
diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp
index 8f2c877c7a856662bfa4d2bce64cdc68e07f73c6..068ff34b363aabe3ebf7b9936af8e6e02b622e21 100644
--- a/indra/newview/llspeakbutton.cpp
+++ b/indra/newview/llspeakbutton.cpp
@@ -59,9 +59,9 @@ LLSpeakButton::Params::Params()
 
 void LLSpeakButton::draw()
 {
-	// gVoiceClient is the authoritative global source of info regarding our open-mic state, we merely reflect that state.
-	bool openmic = gVoiceClient->getUserPTTState();
-	bool voiceenabled = gVoiceClient->voiceEnabled();
+	// LLVoiceClient::getInstance() is the authoritative global source of info regarding our open-mic state, we merely reflect that state.
+	bool openmic = LLVoiceClient::getInstance()->getUserPTTState();
+	bool voiceenabled = LLVoiceClient::getInstance()->voiceEnabled();
 	mSpeakBtn->setToggleState(openmic && voiceenabled);
 	mOutputMonitor->setIsMuted(!voiceenabled);
 	LLUICtrl::draw();
@@ -166,11 +166,11 @@ void LLSpeakButton::setLabelVisible(bool visible)
 void LLSpeakButton::onMouseDown_SpeakBtn()
 {
 	bool down = true;
-	gVoiceClient->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk
+	LLVoiceClient::getInstance()->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk
 }
 void LLSpeakButton::onMouseUp_SpeakBtn()
 {
 	bool down = false;
-	gVoiceClient->inputUserControlState(down);
+	LLVoiceClient::getInstance()->inputUserControlState(down);
 }
 
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp
index 010dfd1b331e13f9a07c870f2865f38a289255f5..a45c51784a9078a1eb6d4f943aad9131f00bdb2b 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -72,7 +72,7 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy
 		mDisplayName = name;
 	}
 
-	gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id));
+	LLVoiceClient::getInstance()->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id));
 
 	mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
 }
@@ -210,7 +210,7 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin
 
 void LLSpeakerMgr::update(BOOL resort_ok)
 {
-	if (!gVoiceClient)
+	if (!LLVoiceClient::getInstance())
 	{
 		return;
 	}
@@ -224,7 +224,7 @@ void LLSpeakerMgr::update(BOOL resort_ok)
 	}
 
 	// update status of all current speakers
-	BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
+	BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
 	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)
 	{
 		LLUUID speaker_id = speaker_it->first;
@@ -232,21 +232,21 @@ void LLSpeakerMgr::update(BOOL resort_ok)
 		
 		speaker_map_t::iterator  cur_speaker_it = speaker_it++;
 
-		if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id))
+		if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))
 		{
-			speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id);
-			BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id);
+			speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id);
+			BOOL moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id);
 			if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
 			{
 				speakerp->mModeratorMutedVoice = moderator_muted_voice;
 				speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
 			}
 
-			if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
+			if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
 			{
 				speakerp->mStatus = LLSpeaker::STATUS_MUTED;
 			}
-			else if (gVoiceClient->getIsSpeaking(speaker_id))
+			else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id))
 			{
 				// reset inactivity expiration
 				if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
@@ -341,19 +341,20 @@ void LLSpeakerMgr::update(BOOL resort_ok)
 void LLSpeakerMgr::updateSpeakerList()
 {
 	// are we bound to the currently active voice channel?
-	if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
+	if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
 	{
-		LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList();
-		if(participants)
+		std::vector<LLUUID> participants = LLVoiceClient::getInstance()->getParticipantList();
+		// add new participants to our list of known speakers
+		for (std::vector<LLUUID>::iterator participant_it = participants.begin();
+			 participant_it != participants.end(); 
+			 ++participant_it)
 		{
-			LLVoiceClient::participantMap::iterator participant_it;
+				setSpeaker(*participant_it, 
+						   LLVoiceClient::getInstance()->getDisplayName(*participant_it),
+						   LLSpeaker::STATUS_VOICE_ACTIVE, 
+						   (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
+
 
-			// add new participants to our list of known speakers
-			for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it)
-			{
-				LLVoiceClient::participantState* participantp = participant_it->second;
-				setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
-			}
 		}
 	}
 }
@@ -414,7 +415,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
 BOOL LLSpeakerMgr::isVoiceActive()
 {
 	// mVoiceChannel = NULL means current voice channel, whatever it is
-	return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
+	return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
 }
 
 
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index d1c6fca0636f32ac2fc55d3770621744b56bbdee..95158a9ae325ecb38fc2cbd4f4cc1c9096b10c00 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -189,6 +189,7 @@
 #include "llinventorybridge.h"
 #include "llappearancemgr.h"
 #include "llavatariconctrl.h"
+#include "llvoicechannel.h"
 
 #include "lllogin.h"
 #include "llevents.h"
@@ -1157,7 +1158,11 @@ bool idle_startup()
 			if(process_login_success_response())
 			{
 				// Pass the user information to the voice chat server interface.
-				gVoiceClient->userAuthorized(gUserCredential->userID(), gAgentID);
+				LLVoiceClient::getInstance()->userAuthorized(gUserCredential->userID(), gAgentID);
+				// create the default proximal channel
+				LLVoiceChannel::initClass();
+				// update the voice settings
+				LLVoiceClient::getInstance()->updateSettings();
 				LLGridManager::getInstance()->setFavorite(); 
 				LLStartUp::setStartupState( STATE_WORLD_INIT);
 			}
@@ -3008,7 +3013,44 @@ bool process_login_success_response()
 		//setup map of datetime strings to codes and slt & local time offset from utc
 		LLStringOps::setupDatetimeInfo(pacific_daylight_time);
 	}
-
+	
+	static const char* CONFIG_OPTIONS[] = {"voice-config"};
+	for (int i = 0; i < sizeof(CONFIG_OPTIONS)/sizeof(CONFIG_OPTIONS[0]); i++)
+	{
+		LLSD options = response[CONFIG_OPTIONS[i]];
+		if (!options.isArray() && (options.size() < 1) && !options[0].isMap())
+		{
+			continue;
+		}
+		llinfos << "config option " << CONFIG_OPTIONS[i][0] << "response " << options << llendl;
+		for(LLSD::map_iterator option_it = options[0].beginMap();
+			option_it != options[0].endMap();
+			option_it++)
+		{
+			llinfos << "trying option " << option_it->first << llendl;
+			LLPointer<LLControlVariable> control = gSavedSettings.getControl(option_it->first);
+			if(control.notNull())
+			{
+				if(control->isType(TYPE_BOOLEAN))
+				{
+					llinfos << "Setting BOOL from login " << option_it->first << " " << option_it->second << llendl;
+					
+					gSavedSettings.setBOOL(option_it->first, !((option_it->second == "F") ||
+															   (option_it->second == "false") ||
+															   (!option_it->second)));
+				}
+				else if (control->isType(TYPE_STRING))
+				{
+					llinfos << "Setting String from login " << option_it->first << " " << option_it->second << llendl;
+					gSavedSettings.setString(option_it->first, option_it->second);
+				}
+				// we don't support other types now                                                                                                            
+				
+			}
+			
+		}
+	}
+	
 	LLSD initial_outfit = response["initial-outfit"][0];
 	if(initial_outfit.size())
 	{
diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp
index ab65ead4c51fa5a8c64fe471694e550224827556..83a5839a93fc11237ff2bbacb0bae3a74beba990 100644
--- a/indra/newview/llurl.cpp
+++ b/indra/newview/llurl.cpp
@@ -286,5 +286,11 @@ const char * LLURL::getFullPath()
 	return(sReturnString);
 }
 
+const char * LLURL::getAuthority()
+{
+	strncpy(LLURL::sReturnString,mAuthority, LL_MAX_PATH -1);               /* Flawfinder: ignore */
+	LLURL::sReturnString[LL_MAX_PATH -1] = '\0';
+	return(sReturnString);
+}
 
 char LLURL::sReturnString[LL_MAX_PATH] = "";
diff --git a/indra/newview/llurl.h b/indra/newview/llurl.h
index 9a089dd835a75dfb2573ec673733f64cc6ff258d..e41b83d29fe7ef168ff9111e04641f6d52173b11 100644
--- a/indra/newview/llurl.h
+++ b/indra/newview/llurl.h
@@ -79,6 +79,7 @@ class LLURL
 
 	virtual const char *getFQURL() const;
 	virtual const char *getFullPath();
+	virtual const char *getAuthority();
 
 	virtual const char *updateRelativePath(const LLURL &url);
 
diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp
index 38103f9e41c4d486b5300050717d3f93a0ec0f2a..49caaf18f3402ad3b0c1cc39ffb805d1359a8af1 100644
--- a/indra/newview/llvieweraudio.cpp
+++ b/indra/newview/llvieweraudio.cpp
@@ -156,21 +156,21 @@ void audio_update_volume(bool force_update)
 	LLViewerMedia::setVolume( media_muted ? 0.0f : media_volume );
 
 	// Voice
-	if (gVoiceClient)
+	if (LLVoiceClient::getInstance())
 	{
 		F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice");
 		voice_volume = mute_volume * master_volume * voice_volume;
 		BOOL voice_mute = gSavedSettings.getBOOL("MuteVoice");
-		gVoiceClient->setVoiceVolume(voice_mute ? 0.f : voice_volume);
-		gVoiceClient->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic"));
+		LLVoiceClient::getInstance()->setVoiceVolume(voice_mute ? 0.f : voice_volume);
+		LLVoiceClient::getInstance()->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic"));
 
 		if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized")))
 		{
-			gVoiceClient->setMuteMic(true);
+			LLVoiceClient::getInstance()->setMuteMic(true);
 		}
 		else
 		{
-			gVoiceClient->setMuteMic(false);
+			LLVoiceClient::getInstance()->setMuteMic(false);
 		}
 	}
 }
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 57434bd1e4c58faffe7f8a3f237ced3efb2d1121..7be45c649c53da2aa310731e0fae5c577f614fea 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -441,9 +441,9 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue)
 
 bool handleVoiceClientPrefsChanged(const LLSD& newvalue)
 {
-	if(gVoiceClient)
+	if(LLVoiceClient::getInstance())
 	{
-		gVoiceClient->updateSettings();
+		LLVoiceClient::getInstance()->updateSettings();
 	}
 	return true;
 }
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index ee6fb8120cf43670223226a5cc5f12d59e00e2e6..660b33a1ffaa8fa42c41e7045a31a59dbc9a7040 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -813,7 +813,7 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window,  LLCoordGL pos, MASK m
 BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window,  LLCoordGL pos, MASK mask)
 {
 	BOOL down = TRUE;
-	gVoiceClient->middleMouseState(true);
+	LLVoiceClient::getInstance()->middleMouseState(true);
  	handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);
   
   	// Always handled as far as the OS is concerned.
@@ -823,7 +823,7 @@ BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window,  LLCoordGL pos, MAS
 BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window,  LLCoordGL pos, MASK mask)
 {
 	BOOL down = FALSE;
-	gVoiceClient->middleMouseState(false);
+	LLVoiceClient::getInstance()->middleMouseState(false);
  	handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);
   
   	// Always handled as far as the OS is concerned.
@@ -939,7 +939,7 @@ void LLViewerWindow::handleFocusLost(LLWindow *window)
 BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key,  MASK mask, BOOL repeated)
 {
 	// Let the voice chat code check for its PTT key.  Note that this never affects event processing.
-	gVoiceClient->keyDown(key, mask);
+	LLVoiceClient::getInstance()->keyDown(key, mask);
 	
 	if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
 	{
@@ -961,7 +961,7 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key,  MASK mask, BOOL repeated)
 BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key,  MASK mask)
 {
 	// Let the voice chat code check for its PTT key.  Note that this never affects event processing.
-	gVoiceClient->keyUp(key, mask);
+	LLVoiceClient::getInstance()->keyUp(key, mask);
 
 	return FALSE;
 }
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 6e93bf1bf272dbb391f796d6ea84424baf39f516..341ea91c6d44079eb09386c8471498b89aebc619 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1271,7 +1271,7 @@ void LLVOAvatar::initInstance(void)
 	
 	//VTPause();  // VTune
 	
-	mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) );
+	mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) );
 }
 
 const LLVector3 LLVOAvatar::getRenderPosition() const
@@ -2192,7 +2192,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 	// store off last frame's root position to be consistent with camera position
 	LLVector3 root_pos_last = mRoot.getWorldPosition();
 	BOOL detailed_update = updateCharacter(agent);
-	BOOL voice_enabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel();
+	BOOL voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled( mID ) && LLVoiceClient::getInstance()->inProximalChannel();
 
 	if (gNoRender)
 	{
@@ -2260,7 +2260,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
 		// Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been
 		// "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking. 
 		//-----------------------------------------------------------------------------------------------------------------
-		if (gVoiceClient->getIsSpeaking( mID ))
+		if (LLVoiceClient::getInstance()->getIsSpeaking( mID ))
 		{		
 			if (!mVoiceVisualizer->getCurrentlySpeaking())
 			{
@@ -2269,7 +2269,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
 				//printf( "gAwayTimer.reset();\n" );
 			}
 			
-			mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) );
+			mVoiceVisualizer->setSpeakingAmplitude( LLVoiceClient::getInstance()->getCurrentPower( mID ) );
 			
 			if( isSelf() )
 			{
@@ -2498,7 +2498,7 @@ F32 LLVOAvatar::calcMorphAmount()
 void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)
 {
 	// Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync
-	if ( voice_enabled && (gVoiceClient->lipSyncEnabled()) && gVoiceClient->getIsSpeaking( mID ) )
+	if ( voice_enabled && (LLVoiceClient::getInstance()->lipSyncEnabled()) && LLVoiceClient::getInstance()->getIsSpeaking( mID ) )
 	{
 		F32 ooh_morph_amount = 0.0f;
 		F32 aah_morph_amount = 0.0f;
diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp
index 917d69fe16d7dd828ea38c3a073a926cb2e1c580..4388f473b48fafd37cc7160fa965e31ec09eddfc 100644
--- a/indra/newview/llvoicechannel.cpp
+++ b/indra/newview/llvoicechannel.cpp
@@ -72,9 +72,9 @@ class LLVoiceCallCapResponder : public LLHTTPClient::Responder
 
 void LLVoiceCallCapResponder::error(U32 status, const std::string& reason)
 {
-	llwarns << "LLVoiceCallCapResponder::error("
+	LL_WARNS("Voice") << "LLVoiceCallCapResponder::error("
 		<< status << ": " << reason << ")"
-		<< llendl;
+		<< LL_ENDL;
 	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
 	if ( channelp )
 	{
@@ -104,8 +104,8 @@ void LLVoiceCallCapResponder::result(const LLSD& content)
 		LLSD::map_const_iterator iter;
 		for(iter = content.beginMap(); iter != content.endMap(); ++iter)
 		{
-			llinfos << "LLVoiceCallCapResponder::result got " 
-				<< iter->first << llendl;
+			LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " 
+				<< iter->first << LL_ENDL;
 		}
 
 		channelp->setChannelInfo(
@@ -130,18 +130,16 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& sess
 	{
 		// a voice channel already exists for this session id, so this instance will be orphaned
 		// the end result should simply be the failure to make voice calls
-		llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl;
+		LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL;
 	}
-
-	LLVoiceClient::getInstance()->addObserver(this);
 }
 
 LLVoiceChannel::~LLVoiceChannel()
 {
 	// Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed.
-	if(gVoiceClient)
+	if(LLVoiceClient::getInstance())
 	{
-		gVoiceClient->removeObserver(this);
+		LLVoiceClient::getInstance()->removeObserver(this);
 	}
 	
 	sVoiceChannelMap.erase(mSessionID);
@@ -161,13 +159,13 @@ void LLVoiceChannel::setChannelInfo(
 		if (mURI.empty())
 		{
 			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
-			llwarns << "Received empty URI for channel " << mSessionName << llendl;
+			LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL;
 			deactivate();
 		}
 		else if (mCredentials.empty())
 		{
 			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
-			llwarns << "Received empty credentials for channel " << mSessionName << llendl;
+			LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL;
 			deactivate();
 		}
 		else
@@ -282,13 +280,14 @@ void LLVoiceChannel::deactivate()
 		//Default mic is OFF when leaving voice calls
 		if (gSavedSettings.getBOOL("AutoDisengageMic") && 
 			sCurrentVoiceChannel == this &&
-			gVoiceClient->getUserPTTState())
+			LLVoiceClient::getInstance()->getUserPTTState())
 		{
 			gSavedSettings.setBOOL("PTTCurrentlyEnabled", true);
-			gVoiceClient->inputUserControlState(true);
+			LLVoiceClient::getInstance()->inputUserControlState(true);
 		}
 	}
-
+	LLVoiceClient::getInstance()->removeObserver(this);
+	
 	if (sCurrentVoiceChannel == this)
 	{
 		// default channel is proximal channel
@@ -328,7 +327,9 @@ void LLVoiceChannel::activate()
 	{
 		setState(STATE_CALL_STARTED);
 	}
-
+	
+	LLVoiceClient::getInstance()->addObserver(this);
+	
 	//do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state
 	sCurrentVoiceChannelChangedSignal(this->mSessionID);
 }
@@ -370,6 +371,11 @@ LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri)
 	}
 }
 
+LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel()
+{
+	return sCurrentVoiceChannel;
+}
+
 void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
 {
 	sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID));
@@ -418,7 +424,6 @@ void LLVoiceChannel::initClass()
 	sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
 }
 
-
 //static 
 void LLVoiceChannel::suspend()
 {
@@ -434,7 +439,7 @@ void LLVoiceChannel::resume()
 {
 	if (sSuspended)
 	{
-		if (gVoiceClient->voiceEnabled())
+		if (LLVoiceClient::getInstance()->voiceEnabled())
 		{
 			if (sSuspendedVoiceChannel)
 			{
@@ -504,9 +509,9 @@ void LLVoiceChannelGroup::activate()
 #endif
 
 		//Mic default state is OFF on initiating/joining Ad-Hoc/Group calls
-		if (gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle())
+		if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
 		{
-			gVoiceClient->inputUserControlState(true);
+			LLVoiceClient::getInstance()->inputUserControlState(true);
 		}
 		
 	}
@@ -553,7 +558,7 @@ void LLVoiceChannelGroup::setChannelInfo(
 		else
 		{
 			//*TODO: notify user
-			llwarns << "Received invalid credentials for channel " << mSessionName << llendl;
+			LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL;
 			deactivate();
 		}
 	}
@@ -651,7 +656,6 @@ void LLVoiceChannelGroup::setState(EState state)
 LLVoiceChannelProximal::LLVoiceChannelProximal() : 
 	LLVoiceChannel(LLUUID::null, LLStringUtil::null)
 {
-	activate();
 }
 
 BOOL LLVoiceChannelProximal::isActive()
@@ -663,13 +667,13 @@ void LLVoiceChannelProximal::activate()
 {
 	if (callStarted()) return;
 
-	LLVoiceChannel::activate();
-
-	if (callStarted())
+	if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED))
 	{
-		// this implicitly puts you back in the spatial channel
-		LLVoiceClient::getInstance()->leaveNonSpatialChannel();
+		// we're connected to a non-spatial channel, so disconnect.
+		LLVoiceClient::getInstance()->leaveNonSpatialChannel();	
 	}
+	LLVoiceChannel::activate();
+	
 }
 
 void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal)
@@ -754,7 +758,7 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string
 
 void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
 {
-	llinfos << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << llendl;
+	LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL;
 
 	// status updates
 	switch(type)
@@ -824,9 +828,9 @@ void LLVoiceChannelP2P::activate()
 		LLRecentPeople::instance().add(mOtherUserID);
 
 		//Default mic is ON on initiating/joining P2P calls
-		if (!gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle())
+		if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
 		{
-			gVoiceClient->inputUserControlState(true);
+			LLVoiceClient::getInstance()->inputUserControlState(true);
 		}
 	}
 }
@@ -889,7 +893,7 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s
 
 void LLVoiceChannelP2P::setState(EState state)
 {
-	llinfos << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << llendl;
+	LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL;
 
 	if (mReceivedCall) // incoming call
 	{
diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h
index cb866713051e9d0f94dbbd6de8dbdfa9535a371d..4538bfc10ccfd9d78126502599cd64444f2743e5 100644
--- a/indra/newview/llvoicechannel.h
+++ b/indra/newview/llvoicechannel.h
@@ -98,7 +98,8 @@ class LLVoiceChannel : public LLVoiceClientStatusObserver
 
 	static LLVoiceChannel* getChannelByID(const LLUUID& session_id);
 	static LLVoiceChannel* getChannelByURI(std::string uri);
-	static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; }
+	static LLVoiceChannel* getCurrentVoiceChannel();
+	
 	static void initClass();
 	
 	static void suspend();
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index fc264a6fcf043084933af1b2a6b08c6eefc5b3ad..0105e584c5d295ecc31e59707811604550f17872 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -1,6 +1,6 @@
  /** 
  * @file llvoiceclient.cpp
- * @brief Implementation of LLVoiceClient class which is the interface to the voice client process.
+ * @brief Voice client delegation class implementation.
  *
  * $LicenseInfo:firstyear=2001&license=viewergpl$
  * 
@@ -17,8 +17,7 @@
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -32,6991 +31,709 @@
 
 #include "llviewerprecompiledheaders.h"
 #include "llvoiceclient.h"
+#include "llviewercontrol.h"
+#include "llviewerwindow.h"
+#include "llvoicedw.h"
+#include "llvoicevivox.h"
+#include "llviewernetwork.h"
+#include "llhttpnode.h"
+#include "llnotificationsutil.h"
 
-#include <boost/tokenizer.hpp>
-
-// library includes
-#include "llnotificationsutil.h"
-#include "llsdutil.h"
-
-// project includes
-#include "llvoavatar.h"
-#include "llbufferstream.h"
-#include "llfile.h"
-#ifdef LL_STANDALONE
-# include "expat.h"
-#else
-# include "expat/expat.h"
-#endif
-#include "llcallbacklist.h"
-#include "llcallingcard.h"   // for LLFriendObserver
-#include "llviewerregion.h"
-#include "llviewernetwork.h"		// for gGridChoice
-#include "llbase64.h"
-#include "llviewercontrol.h"
-#include "llkeyboard.h"
-#include "llappviewer.h"	// for gDisconnected, gDisableVoice
-#include "llmutelist.h"  // to check for muted avatars
-#include "llagent.h"
-#include "llcachename.h"
-#include "llimview.h" // for LLIMMgr
-#include "llparcel.h"
-#include "llviewerparcelmgr.h"
-#include "llfirstuse.h"
-#include "lltrans.h"
-#include "llviewerwindow.h"
-#include "llviewercamera.h"
-#include "llvoavatarself.h"
-#include "llvoicechannel.h"
-
-#include "llfloaterfriends.h"  //VIVOX, inorder to refresh communicate panel
-#include "llfloaterchat.h"		// for LLFloaterChat::addChat()
-
-// for base64 decoding
-#include "apr_base64.h"
-
-// for SHA1 hash
-#include "apr_sha1.h"
-
-// for MD5 hash
-#include "llmd5.h"
-
-#define USE_SESSION_GROUPS 0
-
-static bool sConnectingToAgni = false;
-F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
-
-const F32 SPEAKING_TIMEOUT = 1.f;
-
-const int VOICE_MAJOR_VERSION = 1;
-const int VOICE_MINOR_VERSION = 0;
-
-LLVoiceClient *gVoiceClient = NULL;
-
-// Don't retry connecting to the daemon more frequently than this:
-const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
-
-// Don't send positional updates more frequently than this:
-const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
-
-const F32 LOGIN_RETRY_SECONDS = 10.0f;
-const int MAX_LOGIN_RETRIES = 12;
-
-static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
-{
-	LLMD5 md5_uuid;
-	md5_uuid.update((const unsigned char*)str.data(), str.size());
-	md5_uuid.finalize();
-	md5_uuid.raw_digest(uuid.mData);
-}
-
-static int scale_mic_volume(float volume)
-{
-	// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
-	// Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
-	return 30 + (int)(volume * 20.0f);
-}
-
-static int scale_speaker_volume(float volume)
-{
-	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
-	// Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
-	return 30 + (int)(volume * 40.0f);
-}
-
-class LLViewerVoiceAccountProvisionResponder :
-	public LLHTTPClient::Responder
-{
-public:
-	LLViewerVoiceAccountProvisionResponder(int retries)
-	{
-		mRetries = retries;
-	}
-
-	virtual void error(U32 status, const std::string& reason)
-	{
-		if ( mRetries > 0 )
-		{
-			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
-			if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
-				mRetries - 1);
-		}
-		else
-		{
-			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
-			if ( gVoiceClient ) gVoiceClient->giveUp();
-		}
-	}
-
-	virtual void result(const LLSD& content)
-	{
-		if ( gVoiceClient )
-		{
-			std::string voice_sip_uri_hostname;
-			std::string voice_account_server_uri;
-			
-			LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
-			
-			if(content.has("voice_sip_uri_hostname"))
-				voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
-			
-			// this key is actually misnamed -- it will be an entire URI, not just a hostname.
-			if(content.has("voice_account_server_name"))
-				voice_account_server_uri = content["voice_account_server_name"].asString();
-			
-			gVoiceClient->login(
-				content["username"].asString(),
-				content["password"].asString(),
-				voice_sip_uri_hostname,
-				voice_account_server_uri);
-		}
-	}
-
-private:
-	int mRetries;
-};
-
-/** 
- * @class LLVivoxProtocolParser
- * @brief This class helps construct new LLIOPipe specializations
- * @see LLIOPipe
- *
- * THOROUGH_DESCRIPTION
- */
-class LLVivoxProtocolParser : public LLIOPipe
-{
-	LOG_CLASS(LLVivoxProtocolParser);
-public:
-	LLVivoxProtocolParser();
-	virtual ~LLVivoxProtocolParser();
-
-protected:
-	/* @name LLIOPipe virtual implementations
-	 */
-	//@{
-	/** 
-	 * @brief Process the data in buffer
-	 */
-	virtual EStatus process_impl(
-		const LLChannelDescriptors& channels,
-		buffer_ptr_t& buffer,
-		bool& eos,
-		LLSD& context,
-		LLPumpIO* pump);
-	//@}
-	
-	std::string 	mInput;
-	
-	// Expat control members
-	XML_Parser		parser;
-	int				responseDepth;
-	bool			ignoringTags;
-	bool			isEvent;
-	int				ignoreDepth;
-
-	// Members for processing responses. The values are transient and only valid within a call to processResponse().
-	bool			squelchDebugOutput;
-	int				returnCode;
-	int				statusCode;
-	std::string		statusString;
-	std::string		requestId;
-	std::string		actionString;
-	std::string		connectorHandle;
-	std::string		versionID;
-	std::string		accountHandle;
-	std::string		sessionHandle;
-	std::string		sessionGroupHandle;
-	std::string		alias;
-	std::string		applicationString;
-
-	// Members for processing events. The values are transient and only valid within a call to processResponse().
-	std::string		eventTypeString;
-	int				state;
-	std::string		uriString;
-	bool			isChannel;
-	bool			incoming;
-	bool			enabled;
-	std::string		nameString;
-	std::string		audioMediaString;
-	std::string		displayNameString;
-	std::string		deviceString;
-	int				participantType;
-	bool			isLocallyMuted;
-	bool			isModeratorMuted;
-	bool			isSpeaking;
-	int				volume;
-	F32				energy;
-	std::string		messageHeader;
-	std::string		messageBody;
-	std::string		notificationType;
-	bool			hasText;
-	bool			hasAudio;
-	bool			hasVideo;
-	bool			terminated;
-	std::string		blockMask;
-	std::string		presenceOnly;
-	std::string		autoAcceptMask;
-	std::string		autoAddAsBuddy;
-	int				numberOfAliases;
-	std::string		subscriptionHandle;
-	std::string		subscriptionType;
-		
-
-	// Members for processing text between tags
-	std::string		textBuffer;
-	bool			accumulateText;
-	
-	void			reset();
-
-	void			processResponse(std::string tag);
-
-static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
-static void XMLCALL ExpatEndTag(void *data, const char *el);
-static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
-
-	void			StartTag(const char *tag, const char **attr);
-	void			EndTag(const char *tag);
-	void			CharData(const char *buffer, int length);
-	
-};
-
-LLVivoxProtocolParser::LLVivoxProtocolParser()
-{
-	parser = NULL;
-	parser = XML_ParserCreate(NULL);
-	
-	reset();
-}
-
-void LLVivoxProtocolParser::reset()
-{
-	responseDepth = 0;
-	ignoringTags = false;
-	accumulateText = false;
-	energy = 0.f;
-	ignoreDepth = 0;
-	isChannel = false;
-	isEvent = false;
-	isLocallyMuted = false;
-	isModeratorMuted = false;
-	isSpeaking = false;
-	participantType = 0;
-	squelchDebugOutput = false;
-	returnCode = -1;
-	state = 0;
-	statusCode = 0;
-	volume = 0;
-	textBuffer.clear();
-	alias.clear();
-	numberOfAliases = 0;
-	applicationString.clear();
-}
-
-//virtual 
-LLVivoxProtocolParser::~LLVivoxProtocolParser()
-{
-	if (parser)
-		XML_ParserFree(parser);
-}
-
-// virtual
-LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
-	const LLChannelDescriptors& channels,
-	buffer_ptr_t& buffer,
-	bool& eos,
-	LLSD& context,
-	LLPumpIO* pump)
-{
-	LLBufferStream istr(channels, buffer.get());
-	std::ostringstream ostr;
-	while (istr.good())
-	{
-		char buf[1024];
-		istr.read(buf, sizeof(buf));
-		mInput.append(buf, istr.gcount());
-	}
-	
-	// Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
-	int start = 0;
-	int delim;
-	while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
-	{	
-		
-		// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
-		reset();
-		
-		XML_ParserReset(parser, NULL);
-		XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
-		XML_SetCharacterDataHandler(parser, ExpatCharHandler);
-		XML_SetUserData(parser, this);	
-		XML_Parse(parser, mInput.data() + start, delim - start, false);
-		
-		// If this message isn't set to be squelched, output the raw XML received.
-		if(!squelchDebugOutput)
-		{
-			LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
-		}
-		
-		start = delim + 3;
-	}
-	
-	if(start != 0)
-		mInput = mInput.substr(start);
-
-	LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
-	
-	if(!gVoiceClient->mConnected)
-	{
-		// If voice has been disabled, we just want to close the socket.  This does so.
-		LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL;
-		return STATUS_STOP;
-	}
-	
-	return STATUS_OK;
-}
-
-void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
-{
-	if (data)
-	{
-		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
-		object->StartTag(el, attr);
-	}
-}
-
-// --------------------------------------------------------------------------------
-
-void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
-{
-	if (data)
-	{
-		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
-		object->EndTag(el);
-	}
-}
-
-// --------------------------------------------------------------------------------
-
-void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
-{
-	if (data)
-	{
-		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
-		object->CharData(s, len);
-	}
-}
-
-// --------------------------------------------------------------------------------
-
-
-void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
-{
-	// Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
-	textBuffer.clear();
-	// only accumulate text if we're not ignoring tags.
-	accumulateText = !ignoringTags;
-	
-	if (responseDepth == 0)
-	{	
-		isEvent = !stricmp("Event", tag);
-		
-		if (!stricmp("Response", tag) || isEvent)
-		{
-			// Grab the attributes
-			while (*attr)
-			{
-				const char	*key = *attr++;
-				const char	*value = *attr++;
-				
-				if (!stricmp("requestId", key))
-				{
-					requestId = value;
-				}
-				else if (!stricmp("action", key))
-				{
-					actionString = value;
-				}
-				else if (!stricmp("type", key))
-				{
-					eventTypeString = value;
-				}
-			}
-		}
-		LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
-	}
-	else
-	{
-		if (ignoringTags)
-		{
-			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
-		}
-		else
-		{
-			LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
-	
-			// Ignore the InputXml stuff so we don't get confused
-			if (!stricmp("InputXml", tag))
-			{
-				ignoringTags = true;
-				ignoreDepth = responseDepth;
-				accumulateText = false;
-
-				LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
-			}
-			else if (!stricmp("CaptureDevices", tag))
-			{
-				gVoiceClient->clearCaptureDevices();
-			}
-			else if (!stricmp("RenderDevices", tag))
-			{
-				gVoiceClient->clearRenderDevices();
-			}
-			else if (!stricmp("CaptureDevice", tag))
-			{
-				deviceString.clear();
-			}
-			else if (!stricmp("RenderDevice", tag))
-			{
-				deviceString.clear();
-			}
-			else if (!stricmp("Buddies", tag))
-			{
-				gVoiceClient->deleteAllBuddies();
-			}
-			else if (!stricmp("BlockRules", tag))
-			{
-				gVoiceClient->deleteAllBlockRules();
-			}
-			else if (!stricmp("AutoAcceptRules", tag))
-			{
-				gVoiceClient->deleteAllAutoAcceptRules();
-			}
-			
-		}
-	}
-	responseDepth++;
-}
-
-// --------------------------------------------------------------------------------
-
-void LLVivoxProtocolParser::EndTag(const char *tag)
-{
-	const std::string& string = textBuffer;
-
-	responseDepth--;
-
-	if (ignoringTags)
-	{
-		if (ignoreDepth == responseDepth)
-		{
-			LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
-			ignoringTags = false;
-		}
-		else
-		{
-			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
-		}
-	}
-	
-	if (!ignoringTags)
-	{
-		LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
-
-		// Closing a tag. Finalize the text we've accumulated and reset
-		if (!stricmp("ReturnCode", tag))
-			returnCode = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("SessionHandle", tag))
-			sessionHandle = string;
-		else if (!stricmp("SessionGroupHandle", tag))
-			sessionGroupHandle = string;
-		else if (!stricmp("StatusCode", tag))
-			statusCode = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("StatusString", tag))
-			statusString = string;
-		else if (!stricmp("ParticipantURI", tag))
-			uriString = string;
-		else if (!stricmp("Volume", tag))
-			volume = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("Energy", tag))
-			energy = (F32)strtod(string.c_str(), NULL);
-		else if (!stricmp("IsModeratorMuted", tag))
-			isModeratorMuted = !stricmp(string.c_str(), "true");
-		else if (!stricmp("IsSpeaking", tag))
-			isSpeaking = !stricmp(string.c_str(), "true");
-		else if (!stricmp("Alias", tag))
-			alias = string;
-		else if (!stricmp("NumberOfAliases", tag))
-			numberOfAliases = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("Application", tag))
-			applicationString = string;
-		else if (!stricmp("ConnectorHandle", tag))
-			connectorHandle = string;
-		else if (!stricmp("VersionID", tag))
-			versionID = string;
-		else if (!stricmp("AccountHandle", tag))
-			accountHandle = string;
-		else if (!stricmp("State", tag))
-			state = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("URI", tag))
-			uriString = string;
-		else if (!stricmp("IsChannel", tag))
-			isChannel = !stricmp(string.c_str(), "true");
-		else if (!stricmp("Incoming", tag))
-			incoming = !stricmp(string.c_str(), "true");
-		else if (!stricmp("Enabled", tag))
-			enabled = !stricmp(string.c_str(), "true");
-		else if (!stricmp("Name", tag))
-			nameString = string;
-		else if (!stricmp("AudioMedia", tag))
-			audioMediaString = string;
-		else if (!stricmp("ChannelName", tag))
-			nameString = string;
-		else if (!stricmp("DisplayName", tag))
-			displayNameString = string;
-		else if (!stricmp("Device", tag))
-			deviceString = string;
-		else if (!stricmp("AccountName", tag))
-			nameString = string;
-		else if (!stricmp("ParticipantType", tag))
-			participantType = strtol(string.c_str(), NULL, 10);
-		else if (!stricmp("IsLocallyMuted", tag))
-			isLocallyMuted = !stricmp(string.c_str(), "true");
-		else if (!stricmp("MicEnergy", tag))
-			energy = (F32)strtod(string.c_str(), NULL);
-		else if (!stricmp("ChannelName", tag))
-			nameString = string;
-		else if (!stricmp("ChannelURI", tag))
-			uriString = string;
-		else if (!stricmp("BuddyURI", tag))
-			uriString = string;
-		else if (!stricmp("Presence", tag))
-			statusString = string;
-		else if (!stricmp("CaptureDevice", tag))
-		{
-			gVoiceClient->addCaptureDevice(deviceString);
-		}
-		else if (!stricmp("RenderDevice", tag))
-		{
-			gVoiceClient->addRenderDevice(deviceString);
-		}
-		else if (!stricmp("Buddy", tag))
-		{
-			gVoiceClient->processBuddyListEntry(uriString, displayNameString);
-		}
-		else if (!stricmp("BlockRule", tag))
-		{
-			gVoiceClient->addBlockRule(blockMask, presenceOnly);
-		}
-		else if (!stricmp("BlockMask", tag))
-			blockMask = string;
-		else if (!stricmp("PresenceOnly", tag))
-			presenceOnly = string;
-		else if (!stricmp("AutoAcceptRule", tag))
-		{
-			gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
-		}
-		else if (!stricmp("AutoAcceptMask", tag))
-			autoAcceptMask = string;
-		else if (!stricmp("AutoAddAsBuddy", tag))
-			autoAddAsBuddy = string;
-		else if (!stricmp("MessageHeader", tag))
-			messageHeader = string;
-		else if (!stricmp("MessageBody", tag))
-			messageBody = string;
-		else if (!stricmp("NotificationType", tag))
-			notificationType = string;
-		else if (!stricmp("HasText", tag))
-			hasText = !stricmp(string.c_str(), "true");
-		else if (!stricmp("HasAudio", tag))
-			hasAudio = !stricmp(string.c_str(), "true");
-		else if (!stricmp("HasVideo", tag))
-			hasVideo = !stricmp(string.c_str(), "true");
-		else if (!stricmp("Terminated", tag))
-			terminated = !stricmp(string.c_str(), "true");
-		else if (!stricmp("SubscriptionHandle", tag))
-			subscriptionHandle = string;
-		else if (!stricmp("SubscriptionType", tag))
-			subscriptionType = string;
-		
-		textBuffer.clear();
-		accumulateText= false;
-		
-		if (responseDepth == 0)
-		{
-			// We finished all of the XML, process the data
-			processResponse(tag);
-		}
-	}
-}
-
-// --------------------------------------------------------------------------------
-
-void LLVivoxProtocolParser::CharData(const char *buffer, int length)
-{
-	/*
-		This method is called for anything that isn't a tag, which can be text you
-		want that lies between tags, and a lot of stuff you don't want like file formatting
-		(tabs, spaces, CR/LF, etc).
-		
-		Only copy text if we are in accumulate mode...
-	*/
-	if (accumulateText)
-		textBuffer.append(buffer, length);
-}
-
-// --------------------------------------------------------------------------------
-
-void LLVivoxProtocolParser::processResponse(std::string tag)
-{
-	LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
-
-	// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs.
-	// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
-	// so I believe this will give correct behavior.
-	
-	if(returnCode == 0)
-		statusCode = 0;
-		
-	if (isEvent)
-	{
-		const char *eventTypeCstr = eventTypeString.c_str();
-		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
-		{
-			gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
-		}
-		else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
-		{
-			/*
-			<Event type="SessionAddedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
-				<Uri>sip:confctl-1408789@bhr.vivox.com</Uri>
-				<IsChannel>true</IsChannel>
-				<Incoming>false</Incoming>
-				<ChannelName />
-			</Event>
-			*/
-			gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
-		}
-		else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
-		{
-			gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
-		}
-		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
-		{
-			gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle);
-		}
-		else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
-		{
-			/*
-			<Event type="MediaStreamUpdatedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
-				<StatusCode>200</StatusCode>
-				<StatusString>OK</StatusString>
-				<State>2</State>
-				<Incoming>false</Incoming>
-			</Event>
-			*/
-			gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
-		}		
-		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
-		{
-			/*
-			<Event type="TextStreamUpdatedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
-				<Enabled>true</Enabled>
-				<State>1</State>
-				<Incoming>true</Incoming>
-			</Event>
-			*/
-			gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
-		}
-		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
-		{
-			/* 
-			<Event type="ParticipantAddedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
-				<ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
-				<AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
-				<DisplayName />
-				<ParticipantType>0</ParticipantType>
-			</Event>
-			*/
-			gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
-		}
-		else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
-		{
-			/*
-			<Event type="ParticipantRemovedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
-				<ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
-				<AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
-			</Event>
-			*/
-			gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
-		}
-		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
-		{
-			/*
-			<Event type="ParticipantUpdatedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
-				<ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
-				<IsModeratorMuted>false</IsModeratorMuted>
-				<IsSpeaking>true</IsSpeaking>
-				<Volume>44</Volume>
-				<Energy>0.0879437</Energy>
-			</Event>
-			*/
-			
-			// These happen so often that logging them is pretty useless.
-			squelchDebugOutput = true;
-			
-			gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
-		}
-		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
-		{
-			gVoiceClient->auxAudioPropertiesEvent(energy);
-		}
-		else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
-		{
-			gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString);
-		}
-		else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
-		{
-			// The buddy list was updated during parsing.
-			// Need to recheck against the friends list.
-			gVoiceClient->buddyListChanged();
-		}
-		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
-		{
-			/*
-			<Event type="BuddyChangedEvent">
-				<AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
-				<BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
-				<DisplayName>Monroe Tester</DisplayName>
-				<BuddyData />
-				<GroupID>0</GroupID>
-				<ChangeType>Set</ChangeType>
-			</Event>
-			*/		
-			// TODO: Question: Do we need to process this at all?
-		}
-		else if (!stricmp(eventTypeCstr, "MessageEvent"))  
-		{
-			gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
-		}
-		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))  
-		{
-			gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType);
-		}
-		else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))  
-		{
-			gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
-		}
-		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))  
-		{
-			/*
-			<Event type="SessionUpdatedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
-				<Uri>sip:confctl-9@bhd.vivox.com</Uri>
-				<IsMuted>0</IsMuted>
-				<Volume>50</Volume>
-				<TransmitEnabled>1</TransmitEnabled>
-				<IsFocused>0</IsFocused>
-				<SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
-				<SessionFontID>0</SessionFontID>
-			</Event>
-			*/
-			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
-		}
-		
-		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))  
-		{
-			/*
-			<Event type="SessionGroupRemovedEvent">
-				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-			</Event>
-			*/
-			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
-		}
-		else
-		{
-			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
-		}
-	}
-	else
-	{
-		const char *actionCstr = actionString.c_str();
-		if (!stricmp(actionCstr, "Connector.Create.1"))
-		{
-			gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
-		}
-		else if (!stricmp(actionCstr, "Account.Login.1"))
-		{
-			gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
-		}
-		else if (!stricmp(actionCstr, "Session.Create.1"))
-		{
-			gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);			
-		}
-		else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
-		{
-			gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);			
-		}
-		else if (!stricmp(actionCstr, "Session.Connect.1"))
-		{
-			gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);			
-		}
-		else if (!stricmp(actionCstr, "Account.Logout.1"))
-		{
-			gVoiceClient->logoutResponse(statusCode, statusString);			
-		}
-		else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
-		{
-			gVoiceClient->connectorShutdownResponse(statusCode, statusString);			
-		}
-		else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
-		{
-			gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);						
-		}
-		else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
-		{
-			gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);						
-		}
-		else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
-		{
-			// We don't need to process these, but they're so spammy we don't want to log them.
-			squelchDebugOutput = true;
-		}
-/*
-		else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
-		{
-			gVoiceClient->channelGetListResponse(statusCode, statusString);
-		}
-		else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
-		{
-			
-		}
-		else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
-		{
-			
-		}
-*/
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-class LLVoiceClientMuteListObserver : public LLMuteListObserver
-{
-	/* virtual */ void onChange()  { gVoiceClient->muteListChanged();}
-};
-
-class LLVoiceClientFriendsObserver : public LLFriendObserver
-{
-public:
-	/* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);}
-};
-
-static LLVoiceClientMuteListObserver mutelist_listener;
-static bool sMuteListListener_listening = false;
-
-static LLVoiceClientFriendsObserver *friendslist_listener = NULL;
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-class LLVoiceClientCapResponder : public LLHTTPClient::Responder
-{
-public:
-	LLVoiceClientCapResponder(void){};
-
-	virtual void error(U32 status, const std::string& reason);	// called with bad status codes
-	virtual void result(const LLSD& content);
-
-private:
-};
-
-void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
-{
-	LL_WARNS("Voice") << "LLVoiceClientCapResponder::error("
-		<< status << ": " << reason << ")"
-		<< LL_ENDL;
-}
-
-void LLVoiceClientCapResponder::result(const LLSD& content)
-{
-	LLSD::map_const_iterator iter;
-	
-	LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
-
-	if ( content.has("voice_credentials") )
-	{
-		LLSD voice_credentials = content["voice_credentials"];
-		std::string uri;
-		std::string credentials;
-
-		if ( voice_credentials.has("channel_uri") )
-		{
-			uri = voice_credentials["channel_uri"].asString();
-		}
-		if ( voice_credentials.has("channel_credentials") )
-		{
-			credentials =
-				voice_credentials["channel_credentials"].asString();
-		}
-
-		gVoiceClient->setSpatialChannel(uri, credentials);
-	}
-}
-
-
-
-#if LL_WINDOWS
-static HANDLE sGatewayHandle = 0;
-
-static bool isGatewayRunning()
-{
-	bool result = false;
-	if(sGatewayHandle != 0)		
-	{
-		DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
-		if(waitresult != WAIT_OBJECT_0)
-		{
-			result = true;
-		}			
-	}
-	return result;
-}
-static void killGateway()
-{
-	if(sGatewayHandle != 0)
-	{
-		TerminateProcess(sGatewayHandle,0);
-	}
-}
-
-#else // Mac and linux
-
-static pid_t sGatewayPID = 0;
-static bool isGatewayRunning()
-{
-	bool result = false;
-	if(sGatewayPID != 0)
-	{
-		// A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
-		if(kill(sGatewayPID, 0) == 0)
-		{
-			result = true;
-		}
-	}
-	return result;
-}
-
-static void killGateway()
-{
-	if(sGatewayPID != 0)
-	{
-		kill(sGatewayPID, SIGTERM);
-	}
-}
-
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-LLVoiceClient::LLVoiceClient() :
-	mState(stateDisabled),
-	mSessionTerminateRequested(false),
-	mRelogRequested(false),
-	mConnected(false),
-	mPump(NULL),
-	
-	mTuningMode(false),
-	mTuningEnergy(0.0f),
-	mTuningMicVolume(0),
-	mTuningMicVolumeDirty(true),
-	mTuningSpeakerVolume(0),
-	mTuningSpeakerVolumeDirty(true),
-	mTuningExitState(stateDisabled),
-	
-	mAreaVoiceDisabled(false),
-	mAudioSession(NULL),
-	mAudioSessionChanged(false),
-	mNextAudioSession(NULL),
-	
-	mCurrentParcelLocalID(0),
-	mNumberOfAliases(0),
-	mCommandCookie(0),
-	mLoginRetryCount(0),
-	
-	mBuddyListMapPopulated(false),
-	mBlockRulesListReceived(false),
-	mAutoAcceptRulesListReceived(false),
-	mCaptureDeviceDirty(false),
-	mRenderDeviceDirty(false),
-	mSpatialCoordsDirty(false),
-
-	mPTTDirty(true),
-	mPTT(true),
-	mUsePTT(true),
-	mPTTIsMiddleMouse(false),
-	mPTTKey(0),
-	mPTTIsToggle(false),
-	mUserPTTState(false),
-	mMuteMic(false),
-	mFriendsListDirty(true),
-	
-	mEarLocation(0),
-	mSpeakerVolumeDirty(true),
-	mSpeakerMuteDirty(true),
-	mSpeakerVolume(0),
-	mMicVolume(0),
-	mMicVolumeDirty(true),
-	
-	mVoiceEnabled(false),
-	mWriteInProgress(false),
-	
-	mLipSyncEnabled(false)
-{	
-	gVoiceClient = this;
-	
-	mAPIVersion = LLTrans::getString("NotConnected");
-
-#if LL_DARWIN || LL_LINUX || LL_SOLARIS
-		// HACK: THIS DOES NOT BELONG HERE
-		// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
-		// This should cause us to ignore SIGPIPE and handle the error through proper channels.
-		// This should really be set up elsewhere.  Where should it go?
-		signal(SIGPIPE, SIG_IGN);
-		
-		// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
-		// Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that.
-		signal(SIGCHLD, SIG_IGN);
-#endif
-
-	// set up state machine
-	setState(stateDisabled);
-	
-	gIdleCallbacks.addFunction(idle, this);
-}
-
-//---------------------------------------------------
-
-LLVoiceClient::~LLVoiceClient()
-{
-}
-
-//----------------------------------------------
-
-void LLVoiceClient::init(LLPumpIO *pump)
-{
-	// constructor will set up gVoiceClient
-	LLVoiceClient::getInstance()->mPump = pump;
-	LLVoiceClient::getInstance()->updateSettings();
-}
-
-void LLVoiceClient::terminate()
-{
-	if(gVoiceClient)
-	{
-//		gVoiceClient->leaveAudioSession();
-		gVoiceClient->logout();
-		// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to.
-		// ms_sleep(2000);
-		gVoiceClient->connectorShutdown();
-		gVoiceClient->closeSocket();		// Need to do this now -- bad things happen if the destructor does it later.
-		
-		// This will do unpleasant things on windows.
-//		killGateway();
-		
-		// Don't do this anymore -- LLSingleton will take care of deleting the object.		
-//		delete gVoiceClient;
-		
-		// Hint to other code not to access the voice client anymore.
-		gVoiceClient = NULL;
-	}
-}
-
-//---------------------------------------------------
-
-void LLVoiceClient::updateSettings()
-{
-	setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
-	setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled"));
-	std::string keyString = gSavedSettings.getString("PushToTalkButton");
-	setPTTKey(keyString);
-	setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
-	setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
-
-	std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
-	setCaptureDevice(inputDevice);
-	std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
-	setRenderDevice(outputDevice);
-	F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
-	setMicGain(mic_level);
-	setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
-}
-
-/////////////////////////////
-// utility functions
-
-bool LLVoiceClient::writeString(const std::string &str)
-{
-	bool result = false;
-	if(mConnected)
-	{
-		apr_status_t err;
-		apr_size_t size = (apr_size_t)str.size();
-		apr_size_t written = size;
-	
-		//MARK: Turn this on to log outgoing XML
-//		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
-
-		// check return code - sockets will fail (broken, etc.)
-		err = apr_socket_send(
-				mSocket->getSocket(),
-				(const char*)str.data(),
-				&written);
-		
-		if(err == 0)
-		{
-			// Success.
-			result = true;
-		}
-		// TODO: handle partial writes (written is number of bytes written)
-		// Need to set socket to non-blocking before this will work.
-//		else if(APR_STATUS_IS_EAGAIN(err))
-//		{
-//			// 
-//		}
-		else
-		{
-			// Assume any socket error means something bad.  For now, just close the socket.
-			char buf[MAX_STRING];
-			LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
-			daemonDied();
-		}
-	}
-		
-	return result;
-}
-
-
-/////////////////////////////
-// session control messages
-void LLVoiceClient::connectorCreate()
-{
-	std::ostringstream stream;
-	std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
-	std::string loglevel = "0";
-	
-	// Transition to stateConnectorStarted when the connector handle comes back.
-	setState(stateConnectorStarting);
-
-	std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
-		
-	if(savedLogLevel != "-1")
-	{
-		LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
-		loglevel = "10";
-	}
-	
-	stream 
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
-		<< "<ClientName>V2 SDK</ClientName>"
-		<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
-		<< "<Mode>Normal</Mode>"
-		<< "<Logging>"
-			<< "<Folder>" << logpath << "</Folder>"
-			<< "<FileNamePrefix>Connector</FileNamePrefix>"
-			<< "<FileNameSuffix>.log</FileNameSuffix>"
-			<< "<LogLevel>" << loglevel << "</LogLevel>"
-		<< "</Logging>"
-		<< "<Application>SecondLifeViewer.1</Application>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::connectorShutdown()
-{
-	setState(stateConnectorStopping);
-	
-	if(!mConnectorHandle.empty())
-	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
-			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-		<< "</Request>"
-		<< "\n\n\n";
-		
-		mConnectorHandle.clear();
-		
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)
-{
-	LL_INFOS("Voice") << "name \"" << user_id << "\" , ID " << agentID << LL_ENDL;
-
-	sConnectingToAgni = LLGridManager::getInstance()->isInProductionGrid();
-
-	mAccountName = nameFromID(agentID);
-}
-
-void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
-{
-	if ( gAgent.getRegion() && mVoiceEnabled )
-	{
-		std::string url = 
-			gAgent.getRegion()->getCapability(
-				"ProvisionVoiceAccountRequest");
-
-		if ( url == "" ) return;
-
-		LLHTTPClient::post(
-			url,
-			LLSD(),
-			new LLViewerVoiceAccountProvisionResponder(retries));
-	}
-}
-
-void LLVoiceClient::login(
-	const std::string& account_name,
-	const std::string& password,
-	const std::string& voice_sip_uri_hostname,
-	const std::string& voice_account_server_uri)
-{
-	mVoiceSIPURIHostName = voice_sip_uri_hostname;
-	mVoiceAccountServerURI = voice_account_server_uri;
-
-	if(!mAccountHandle.empty())
-	{
-		// Already logged in.
-		LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
-		
-		// Don't process another login.
-		return;
-	}
-	else if ( account_name != mAccountName )
-	{
-		//TODO: error?
-		LL_WARNS("Voice") << "Wrong account name! " << account_name
-				<< " instead of " << mAccountName << LL_ENDL;
-	}
-	else
-	{
-		mAccountPassword = password;
-	}
-
-	std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
-	
-	if( !debugSIPURIHostName.empty() )
-	{
-		mVoiceSIPURIHostName = debugSIPURIHostName;
-	}
-	
-	if( mVoiceSIPURIHostName.empty() )
-	{
-		// we have an empty account server name
-		// so we fall back to hardcoded defaults
-
-		if(sConnectingToAgni)
-		{
-			// Use the release account server
-			mVoiceSIPURIHostName = "bhr.vivox.com";
-		}
-		else
-		{
-			// Use the development account server
-			mVoiceSIPURIHostName = "bhd.vivox.com";
-		}
-	}
-	
-	std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
-
-	if( !debugAccountServerURI.empty() )
-	{
-		mVoiceAccountServerURI = debugAccountServerURI;
-	}
-	
-	if( mVoiceAccountServerURI.empty() )
-	{
-		// If the account server URI isn't specified, construct it from the SIP URI hostname
-		mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";		
-	}
-}
-
-void LLVoiceClient::idle(void* user_data)
-{
-	LLVoiceClient* self = (LLVoiceClient*)user_data;
-	self->stateMachine();
-}
-
-std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
-{
-	std::string result = "UNKNOWN";
-	
-		// Prevent copy-paste errors when updating this list...
-#define CASE(x)  case x:  result = #x;  break
-
-	switch(inState)
-	{
-		CASE(stateDisableCleanup);
-		CASE(stateDisabled);
-		CASE(stateStart);
-		CASE(stateDaemonLaunched);
-		CASE(stateConnecting);
-		CASE(stateConnected);
-		CASE(stateIdle);
-		CASE(stateMicTuningStart);
-		CASE(stateMicTuningRunning);
-		CASE(stateMicTuningStop);
-		CASE(stateConnectorStart);
-		CASE(stateConnectorStarting);
-		CASE(stateConnectorStarted);
-		CASE(stateLoginRetry);
-		CASE(stateLoginRetryWait);
-		CASE(stateNeedsLogin);
-		CASE(stateLoggingIn);
-		CASE(stateLoggedIn);
-		CASE(stateCreatingSessionGroup);
-		CASE(stateNoChannel);
-		CASE(stateJoiningSession);
-		CASE(stateSessionJoined);
-		CASE(stateRunning);
-		CASE(stateLeavingSession);
-		CASE(stateSessionTerminated);
-		CASE(stateLoggingOut);
-		CASE(stateLoggedOut);
-		CASE(stateConnectorStopping);
-		CASE(stateConnectorStopped);
-		CASE(stateConnectorFailed);
-		CASE(stateConnectorFailedWaiting);
-		CASE(stateLoginFailed);
-		CASE(stateLoginFailedWaiting);
-		CASE(stateJoinSessionFailed);
-		CASE(stateJoinSessionFailedWaiting);
-		CASE(stateJail);
-	}
-
-#undef CASE
-	
-	return result;
-}
-
-std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
-{
-	std::string result = "UNKNOWN";
-	
-		// Prevent copy-paste errors when updating this list...
-#define CASE(x)  case x:  result = #x;  break
-
-	switch(inStatus)
-	{
-		CASE(STATUS_LOGIN_RETRY);
-		CASE(STATUS_LOGGED_IN);
-		CASE(STATUS_JOINING);
-		CASE(STATUS_JOINED);
-		CASE(STATUS_LEFT_CHANNEL);
-		CASE(STATUS_VOICE_DISABLED);
-		CASE(STATUS_VOICE_ENABLED);
-		CASE(BEGIN_ERROR_STATUS);
-		CASE(ERROR_CHANNEL_FULL);
-		CASE(ERROR_CHANNEL_LOCKED);
-		CASE(ERROR_NOT_AVAILABLE);
-		CASE(ERROR_UNKNOWN);
-	default:
-		break;
-	}
-
-#undef CASE
-	
-	return result;
-}
-
-void LLVoiceClient::setState(state inState)
-{
-	LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
-	
-	mState = inState;
-}
-
-void LLVoiceClient::stateMachine()
-{
-	if(gDisconnected)
-	{
-		// The viewer has been disconnected from the sim.  Disable voice.
-		setVoiceEnabled(false);
-	}
-	
-	if(mVoiceEnabled)
-	{
-		updatePosition();
-	}
-	else if(mTuningMode)
-	{
-		// Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
-	}
-	else
-	{
-		if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
-		{
-			// User turned off voice support.  Send the cleanup messages, close the socket, and reset.
-			if(!mConnected)
-			{
-				// if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
-				LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
-				killGateway();
-			}
-			
-			logout();
-			connectorShutdown();
-			
-			setState(stateDisableCleanup);
-		}
-	}
-	
-	// Check for parcel boundary crossing
-	{
-		LLViewerRegion *region = gAgent.getRegion();
-		LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
-		
-		if(region && parcel)
-		{
-			S32 parcelLocalID = parcel->getLocalID();
-			std::string regionName = region->getName();
-			std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
-		
-//			LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL;
-
-			// The region name starts out empty and gets filled in later.  
-			// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
-			// If either is empty, wait for the next time around.
-			if(!regionName.empty())
-			{
-				if(!capURI.empty())
-				{
-					if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
-					{
-						// We have changed parcels.  Initiate a parcel channel lookup.
-						mCurrentParcelLocalID = parcelLocalID;
-						mCurrentRegionName = regionName;
-						
-						parcelChanged();
-					}
-				}
-				else
-				{
-					LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability.  This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL;
-				}
-			}
-		}
-	}
-
-	switch(getState())
-	{
-		//MARK: stateDisableCleanup
-		case stateDisableCleanup:
-			// Clean up and reset everything. 
-			closeSocket();
-			deleteAllSessions();
-			deleteAllBuddies();		
-			
-			mConnectorHandle.clear();
-			mAccountHandle.clear();
-			mAccountPassword.clear();
-			mVoiceAccountServerURI.clear();
-			
-			setState(stateDisabled);	
-		break;
-		
-		//MARK: stateDisabled
-		case stateDisabled:
-			if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
-			{
-				setState(stateStart);
-			}
-		break;
-		
-		//MARK: stateStart
-		case stateStart:
-			if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
-			{
-				// Voice is locked out, we must not launch the vivox daemon.
-				setState(stateJail);
-			}
-			else if(!isGatewayRunning())
-			{
-				if(true)
-				{
-					// Launch the voice daemon
-					
-					// *FIX:Mani - Using the executable dir instead 
-					// of mAppRODataDir, the working directory from which the app
-					// is launched.
-					//std::string exe_path = gDirUtilp->getAppRODataDir();
-					std::string exe_path = gDirUtilp->getExecutableDir();
-					exe_path += gDirUtilp->getDirDelimiter();
-#if LL_WINDOWS
-					exe_path += "SLVoice.exe";
-#elif LL_DARWIN
-					exe_path += "../Resources/SLVoice";
-#else
-					exe_path += "SLVoice";
-#endif
-					// See if the vivox executable exists
-					llstat s;
-					if(!LLFile::stat(exe_path, &s))
-					{
-						// vivox executable exists.  Build the command line and launch the daemon.
-						// SLIM SDK: these arguments are no longer necessary.
-//						std::string args = " -p tcp -h -c";
-						std::string args;
-						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
-						
-						if(loglevel.empty())
-						{
-							loglevel = "-1";	// turn logging off completely
-						}
-						
-						args += " -ll ";
-						args += loglevel;
-						
-						LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
-
-#if LL_WINDOWS
-						PROCESS_INFORMATION pinfo;
-						STARTUPINFOW sinfo;
-						memset(&sinfo, 0, sizeof(sinfo));
-
-						std::string exe_dir = gDirUtilp->getExecutableDir();
-
-						llutf16string exe_path16 = utf8str_to_utf16str(exe_path);
-						llutf16string exe_dir16 = utf8str_to_utf16str(exe_dir);
-						llutf16string args16 = utf8str_to_utf16str(args);
-						// Create a writeable copy to keep Windows happy.
-						U16 *argscpy_16 = new U16[args16.size() + 1];
-						wcscpy_s(argscpy_16,args16.size()+1,args16.c_str());
-						if(!CreateProcessW(exe_path16.c_str(), argscpy_16, NULL, NULL, FALSE, 0, NULL, exe_dir16.c_str(), &sinfo, &pinfo))
-						{
-//							DWORD dwErr = GetLastError();
-						}
-						else
-						{
-							// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
-							// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
-							sGatewayHandle = pinfo.hProcess;
-							CloseHandle(pinfo.hThread); // stops leaks - nothing else
-						}		
-						
-						delete[] argscpy_16;
-#else	// LL_WINDOWS
-						// This should be the same for mac and linux
-						{
-							std::vector<std::string> arglist;
-							arglist.push_back(exe_path);
-							
-							// Split the argument string into separate strings for each argument
-							typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-							boost::char_separator<char> sep(" ");
-							tokenizer tokens(args, sep);
-							tokenizer::iterator token_iter;
-
-							for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
-							{
-								arglist.push_back(*token_iter);
-							}
-							
-							// create an argv vector for the child process
-							char **fakeargv = new char*[arglist.size() + 1];
-							int i;
-							for(i=0; i < arglist.size(); i++)
-								fakeargv[i] = const_cast<char*>(arglist[i].c_str());
-
-							fakeargv[i] = NULL;
-							
-							fflush(NULL); // flush all buffers before the child inherits them
-							pid_t id = vfork();
-							if(id == 0)
-							{
-								// child
-								execv(exe_path.c_str(), fakeargv);
-								
-								// If we reach this point, the exec failed.
-								// Use _exit() instead of exit() per the vfork man page.
-								_exit(0);
-							}
-
-							// parent
-							delete[] fakeargv;
-							sGatewayPID = id;
-						}
-#endif	// LL_WINDOWS
-						mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort"));
-					}	
-					else
-					{
-						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
-					}	
-				}
-				else
-				{		
-					// SLIM SDK: port changed from 44124 to 44125.
-					// We can connect to a client gateway running on another host.  This is useful for testing.
-					// To do this, launch the gateway on a nearby host like this:
-					//  vivox-gw.exe -p tcp -i 0.0.0.0:44125
-					// and put that host's IP address here.
-					mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort"));
-				}
-
-				mUpdateTimer.start();
-				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
-
-				setState(stateDaemonLaunched);
-				
-				// Dirty the states we'll need to sync with the daemon when it comes up.
-				mPTTDirty = true;
-				mMicVolumeDirty = true;
-				mSpeakerVolumeDirty = true;
-				mSpeakerMuteDirty = true;
-				// These only need to be set if they're not default (i.e. empty string).
-				mCaptureDeviceDirty = !mCaptureDevice.empty();
-				mRenderDeviceDirty = !mRenderDevice.empty();
-				
-				mMainSessionGroupHandle.clear();
-			}
-		break;
-
-		//MARK: stateDaemonLaunched
-		case stateDaemonLaunched:
-			if(mUpdateTimer.hasExpired())
-			{
-				LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
-
-				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
-
-				if(!mSocket)
-				{
-					mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);	
-				}
-				
-				mConnected = mSocket->blockingConnect(mDaemonHost);
-				if(mConnected)
-				{
-					setState(stateConnecting);
-				}
-				else
-				{
-					// If the connect failed, the socket may have been put into a bad state.  Delete it.
-					closeSocket();
-				}
-			}
-		break;
-
-		//MARK: stateConnecting
-		case stateConnecting:
-		// Can't do this until we have the pump available.
-		if(mPump)
-		{
-			// MBW -- Note to self: pumps and pipes examples in
-			//  indra/test/io.cpp
-			//  indra/test/llpipeutil.{cpp|h}
-
-			// Attach the pumps and pipes
-				
-			LLPumpIO::chain_t readChain;
-
-			readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
-			readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
-
-			mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
-
-			setState(stateConnected);
-		}
-
-		break;
-		
-		//MARK: stateConnected
-		case stateConnected:
-			// Initial devices query
-			getCaptureDevicesSendMessage();
-			getRenderDevicesSendMessage();
-
-			mLoginRetryCount = 0;
-
-			setState(stateIdle);
-		break;
-
-		//MARK: stateIdle
-		case stateIdle:
-			// This is the idle state where we're connected to the daemon but haven't set up a connector yet.
-			if(mTuningMode)
-			{
-				mTuningExitState = stateIdle;
-				setState(stateMicTuningStart);
-			}
-			else if(!mVoiceEnabled)
-			{
-				// We never started up the connector.  This will shut down the daemon.
-				setState(stateConnectorStopped);
-			}
-			else if(!mAccountName.empty())
-			{
-				LLViewerRegion *region = gAgent.getRegion();
-				
-				if(region)
-				{
-					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
-					{
-						if ( mAccountPassword.empty() )
-						{
-							requestVoiceAccountProvision();
-						}
-						setState(stateConnectorStart);
-					}
-					else
-					{
-						LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL;
-					}
-				}
-			}
-		break;
-
-		//MARK: stateMicTuningStart
-		case stateMicTuningStart:
-			if(mUpdateTimer.hasExpired())
-			{
-				if(mCaptureDeviceDirty || mRenderDeviceDirty)
-				{
-					// These can't be changed while in tuning mode.  Set them before starting.
-					std::ostringstream stream;
-					
-					buildSetCaptureDevice(stream);
-					buildSetRenderDevice(stream);
-
-					if(!stream.str().empty())
-					{
-						writeString(stream.str());
-					}				
-
-					// This will come around again in the same state and start the capture, after the timer expires.
-					mUpdateTimer.start();
-					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-				}
-				else
-				{
-					// duration parameter is currently unused, per Mike S.
-					tuningCaptureStartSendMessage(10000);
-
-					setState(stateMicTuningRunning);
-				}
-			}
-			
-		break;
-		
-		//MARK: stateMicTuningRunning
-		case stateMicTuningRunning:
-			if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
-			{
-				// All of these conditions make us leave tuning mode.
-				setState(stateMicTuningStop);
-			}
-			else
-			{
-				// process mic/speaker volume changes
-				if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
-				{
-					std::ostringstream stream;
-					
-					if(mTuningMicVolumeDirty)
-					{
-						LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
-						stream
-						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
-						<< "<Level>" << mTuningMicVolume << "</Level>"
-						<< "</Request>\n\n\n";
-					}
-					
-					if(mTuningSpeakerVolumeDirty)
-					{
-						stream
-						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
-						<< "<Level>" << mTuningSpeakerVolume << "</Level>"
-						<< "</Request>\n\n\n";
-					}
-					
-					mTuningMicVolumeDirty = false;
-					mTuningSpeakerVolumeDirty = false;
-
-					if(!stream.str().empty())
-					{
-						writeString(stream.str());
-					}
-				}
-			}
-		break;
-		
-		//MARK: stateMicTuningStop
-		case stateMicTuningStop:
-		{
-			// transition out of mic tuning
-			tuningCaptureStopSendMessage();
-			
-			setState(mTuningExitState);
-			
-			// if we exited just to change devices, this will keep us from re-entering too fast.
-			mUpdateTimer.start();
-			mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-			
-		}
-		break;
-												
-		//MARK: stateConnectorStart
-		case stateConnectorStart:
-			if(!mVoiceEnabled)
-			{
-				// We were never logged in.  This will shut down the connector.
-				setState(stateLoggedOut);
-			}
-			else if(!mVoiceAccountServerURI.empty())
-			{
-				connectorCreate();
-			}
-		break;
-		
-		//MARK: stateConnectorStarting
-		case stateConnectorStarting:	// waiting for connector handle
-			// connectorCreateResponse() will transition from here to stateConnectorStarted.
-		break;
-		
-		//MARK: stateConnectorStarted
-		case stateConnectorStarted:		// connector handle received
-			if(!mVoiceEnabled)
-			{
-				// We were never logged in.  This will shut down the connector.
-				setState(stateLoggedOut);
-			}
-			else
-			{
-				// The connector is started.  Send a login message.
-				setState(stateNeedsLogin);
-			}
-		break;
-				
-		//MARK: stateLoginRetry
-		case stateLoginRetry:
-			if(mLoginRetryCount == 0)
-			{
-				// First retry -- display a message to the user
-				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
-			}
-			
-			mLoginRetryCount++;
-			
-			if(mLoginRetryCount > MAX_LOGIN_RETRIES)
-			{
-				LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
-				setState(stateLoginFailed);
-			}
-			else
-			{
-				LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
-				mUpdateTimer.start();
-				mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
-				setState(stateLoginRetryWait);
-			}
-		break;
-		
-		//MARK: stateLoginRetryWait
-		case stateLoginRetryWait:
-			if(mUpdateTimer.hasExpired())
-			{
-				setState(stateNeedsLogin);
-			}
-		break;
-		
-		//MARK: stateNeedsLogin
-		case stateNeedsLogin:
-			if(!mAccountPassword.empty())
-			{
-				setState(stateLoggingIn);
-				loginSendMessage();
-			}		
-		break;
-		
-		//MARK: stateLoggingIn
-		case stateLoggingIn:			// waiting for account handle
-			// loginResponse() will transition from here to stateLoggedIn.
-		break;
-		
-		//MARK: stateLoggedIn
-		case stateLoggedIn:				// account handle received
-
-			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
-
-			// request the current set of block rules (we'll need them when updating the friends list)
-			accountListBlockRulesSendMessage();
-			
-			// request the current set of auto-accept rules
-			accountListAutoAcceptRulesSendMessage();
-			
-			// Set up the mute list observer if it hasn't been set up already.
-			if((!sMuteListListener_listening))
-			{
-				LLMuteList::getInstance()->addObserver(&mutelist_listener);
-				sMuteListListener_listening = true;
-			}
-
-			// Set up the friends list observer if it hasn't been set up already.
-			if(friendslist_listener == NULL)
-			{
-				friendslist_listener = new LLVoiceClientFriendsObserver;
-				LLAvatarTracker::instance().addObserver(friendslist_listener);
-			}
-			
-			// Set the initial state of mic mute, local speaker volume, etc.
-			{
-				std::ostringstream stream;
-				
-				buildLocalAudioUpdates(stream);
-				
-				if(!stream.str().empty())
-				{
-					writeString(stream.str());
-				}
-			}
-			
-#if USE_SESSION_GROUPS			
-			// create the main session group
-			sessionGroupCreateSendMessage();
-			
-			setState(stateCreatingSessionGroup);
-#else
-			// Not using session groups -- skip the stateCreatingSessionGroup state.
-			setState(stateNoChannel);
-
-			// Initial kick-off of channel lookup logic
-			parcelChanged();		
-#endif
-		break;
-		
-		//MARK: stateCreatingSessionGroup
-		case stateCreatingSessionGroup:
-			if(mSessionTerminateRequested || !mVoiceEnabled)
-			{
-				// TODO: Question: is this the right way out of this state
-				setState(stateSessionTerminated);
-			}
-			else if(!mMainSessionGroupHandle.empty())
-			{
-				setState(stateNoChannel);
-				
-				// Start looped recording (needed for "panic button" anti-griefing tool)
-				recordingLoopStart();
-
-				// Initial kick-off of channel lookup logic
-				parcelChanged();		
-			}
-		break;
-					
-		//MARK: stateNoChannel
-		case stateNoChannel:
-			// Do this here as well as inside sendPositionalUpdate().  
-			// 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.
-			sendFriendsListUpdates();
-			
-			if(mSessionTerminateRequested || !mVoiceEnabled)
-			{
-				// TODO: Question: Is this the right way out of this state?
-				setState(stateSessionTerminated);
-			}
-			else if(mTuningMode)
-			{
-				mTuningExitState = stateNoChannel;
-				setState(stateMicTuningStart);
-			}
-			else if(sessionNeedsRelog(mNextAudioSession))
-			{
-				requestRelog();
-				setState(stateSessionTerminated);
-			}
-			else if(mNextAudioSession)
-			{				
-				sessionState *oldSession = mAudioSession;
-
-				mAudioSession = mNextAudioSession;
-				if(!mAudioSession->mReconnect)	
-				{
-					mNextAudioSession = NULL;
-				}
-				
-				// The old session may now need to be deleted.
-				reapSession(oldSession);
-				
-				if(!mAudioSession->mHandle.empty())
-				{
-					// Connect to a session by session handle
-
-					sessionMediaConnectSendMessage(mAudioSession);
-				}
-				else
-				{
-					// Connect to a session by URI
-					sessionCreateSendMessage(mAudioSession, true, false);
-				}
-
-				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
-				setState(stateJoiningSession);
-			}
-			else if(!mSpatialSessionURI.empty())
-			{
-				// If we're not headed elsewhere and have a spatial URI, return to spatial.
-				switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
-			}
-		break;
-
-		//MARK: stateJoiningSession
-		case stateJoiningSession:		// waiting for session handle
-			// joinedAudioSession() will transition from here to stateSessionJoined.
-			if(!mVoiceEnabled)
-			{
-				// User bailed out during connect -- jump straight to teardown.
-				setState(stateSessionTerminated);
-			}
-			else if(mSessionTerminateRequested)
-			{
-				if(mAudioSession && !mAudioSession->mHandle.empty())
-				{
-					// Only allow direct exits from this state in p2p calls (for cancelling an invite).
-					// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
-					if(mAudioSession->mIsP2P)
-					{
-						sessionMediaDisconnectSendMessage(mAudioSession);
-						setState(stateSessionTerminated);
-					}
-				}
-			}
-		break;
-		
-		//MARK: stateSessionJoined
-		case stateSessionJoined:		// session handle received
-			// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
-			// before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
-			// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
-			// This is a cheap way to make sure both have happened before proceeding.
-			if(mAudioSession && mAudioSession->mVoiceEnabled)
-			{
-				// Dirty state that may need to be sync'ed with the daemon.
-				mPTTDirty = true;
-				mSpeakerVolumeDirty = true;
-				mSpatialCoordsDirty = true;
-				
-				setState(stateRunning);
-				
-				// Start the throttle timer
-				mUpdateTimer.start();
-				mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-
-				// Events that need to happen when a session is joined could go here.
-				// Maybe send initial spatial data?
-				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
-
-			}
-			else if(!mVoiceEnabled)
-			{
-				// User bailed out during connect -- jump straight to teardown.
-				setState(stateSessionTerminated);
-			}
-			else if(mSessionTerminateRequested)
-			{
-				// Only allow direct exits from this state in p2p calls (for cancelling an invite).
-				// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
-				if(mAudioSession && mAudioSession->mIsP2P)
-				{
-					sessionMediaDisconnectSendMessage(mAudioSession);
-					setState(stateSessionTerminated);
-				}
-			}
-		break;
-		
-		//MARK: stateRunning
-		case stateRunning:				// steady state
-			// Disabling voice or disconnect requested.
-			if(!mVoiceEnabled || mSessionTerminateRequested)
-			{
-				leaveAudioSession();
-			}
-			else
-			{
-				
-				// Figure out whether the PTT state needs to change
-				{
-					bool newPTT;
-					if(mUsePTT)
-					{
-						// If configured to use PTT, track the user state.
-						newPTT = mUserPTTState;
-					}
-					else
-					{
-						// If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
-						newPTT = true;
-					}
-					
-					if(mMuteMic)
-					{
-						// This always overrides any other PTT setting.
-						newPTT = false;
-					}
-					
-					// Dirty if state changed.
-					if(newPTT != mPTT)
-					{
-						mPTT = newPTT;
-						mPTTDirty = true;
-					}
-				}
-				
-				if(!inSpatialChannel())
-				{
-					// When in a non-spatial channel, never send positional updates.
-					mSpatialCoordsDirty = false;
-				}
-				else
-				{
-					// Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
-					enforceTether();
-				}
-				
-				// 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)
-				// or every 10hz, whichever is sooner.
-				if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
-				{
-					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-					sendPositionalUpdate();
-				}
-			}
-		break;
-		
-		//MARK: stateLeavingSession
-		case stateLeavingSession:		// waiting for terminate session response
-			// The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
-		break;
-
-		//MARK: stateSessionTerminated
-		case stateSessionTerminated:
-			
-			// Must do this first, since it uses mAudioSession.
-			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
-			
-			if(mAudioSession)
-			{
-				sessionState *oldSession = mAudioSession;
-
-				mAudioSession = NULL;
-				// We just notified status observers about this change.  Don't do it again.
-				mAudioSessionChanged = false;
-
-				// The old session may now need to be deleted.
-				reapSession(oldSession);
-			}
-			else
-			{
-				LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
-			}
-	
-			// Always reset the terminate request flag when we get here.
-			mSessionTerminateRequested = false;
-
-			if(mVoiceEnabled && !mRelogRequested)
-			{				
-				// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
-				setState(stateNoChannel);
-			}
-			else
-			{
-				// Shutting down voice, continue with disconnecting.
-				logout();
-				
-				// The state machine will take it from here
-				mRelogRequested = false;
-			}
-			
-		break;
-		
-		//MARK: stateLoggingOut
-		case stateLoggingOut:			// waiting for logout response
-			// The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
-		break;
-		
-		//MARK: stateLoggedOut
-		case stateLoggedOut:			// logout response received
-			
-			// Once we're logged out, all these things are invalid.
-			mAccountHandle.clear();
-			deleteAllSessions();
-			deleteAllBuddies();
-
-			if(mVoiceEnabled && !mRelogRequested)
-			{
-				// User was logged out, but wants to be logged in.  Send a new login request.
-				setState(stateNeedsLogin);
-			}
-			else
-			{
-				// shut down the connector
-				connectorShutdown();
-			}
-		break;
-		
-		//MARK: stateConnectorStopping
-		case stateConnectorStopping:	// waiting for connector stop
-			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
-		break;
-
-		//MARK: stateConnectorStopped
-		case stateConnectorStopped:		// connector stop received
-			setState(stateDisableCleanup);
-		break;
-
-		//MARK: stateConnectorFailed
-		case stateConnectorFailed:
-			setState(stateConnectorFailedWaiting);
-		break;
-		//MARK: stateConnectorFailedWaiting
-		case stateConnectorFailedWaiting:
-			if(!mVoiceEnabled)
-			{
-				setState(stateDisableCleanup);
-			}
-		break;
-
-		//MARK: stateLoginFailed
-		case stateLoginFailed:
-			setState(stateLoginFailedWaiting);
-		break;
-		//MARK: stateLoginFailedWaiting
-		case stateLoginFailedWaiting:
-			if(!mVoiceEnabled)
-			{
-				setState(stateDisableCleanup);
-			}
-		break;
-
-		//MARK: stateJoinSessionFailed
-		case stateJoinSessionFailed:
-			// Transition to error state.  Send out any notifications here.
-			if(mAudioSession)
-			{
-				LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
-			}
-			else
-			{
-				LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
-			}
-			
-			notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
-			setState(stateJoinSessionFailedWaiting);
-		break;
-		
-		//MARK: stateJoinSessionFailedWaiting
-		case stateJoinSessionFailedWaiting:
-			// Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
-			// Region crossings may leave this state and try the join again.
-			if(mSessionTerminateRequested)
-			{
-				setState(stateSessionTerminated);
-			}
-		break;
-		
-		//MARK: stateJail
-		case stateJail:
-			// We have given up.  Do nothing.
-		break;
-
-	}
-	
-	if(mAudioSession && mAudioSession->mParticipantsChanged)
-	{
-		mAudioSession->mParticipantsChanged = false;
-		mAudioSessionChanged = true;
-	}
-	
-	if(mAudioSessionChanged)
-	{
-		mAudioSessionChanged = false;
-		notifyParticipantObservers();
-	}
-}
-
-void LLVoiceClient::closeSocket(void)
-{
-	mSocket.reset();
-	mConnected = false;	
-}
-
-void LLVoiceClient::loginSendMessage()
-{
-	std::ostringstream stream;
-
-	bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps");
-
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
-		<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-		<< "<AccountName>" << mAccountName << "</AccountName>"
-		<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
-		<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
-		<< "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
-		<< "<BuddyManagementMode>Application</BuddyManagementMode>"
-		<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
-		<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::logout()
-{
-	// Ensure that we'll re-request provisioning before logging in again
-	mAccountPassword.clear();
-	mVoiceAccountServerURI.clear();
-	
-	setState(stateLoggingOut);
-	logoutSendMessage();
-}
-
-void LLVoiceClient::logoutSendMessage()
-{
-	if(!mAccountHandle.empty())
-	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
-			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-		<< "</Request>"
-		<< "\n\n\n";
-
-		mAccountHandle.clear();
-
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::accountListBlockRulesSendMessage()
-{
-	if(!mAccountHandle.empty())
-	{		
-		std::ostringstream stream;
-
-		LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
-
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
-			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-		<< "</Request>"
-		<< "\n\n\n";
-
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
-{
-	if(!mAccountHandle.empty())
-	{		
-		std::ostringstream stream;
-
-		LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
-
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
-			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-		<< "</Request>"
-		<< "\n\n\n";
-
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::sessionGroupCreateSendMessage()
-{
-	if(!mAccountHandle.empty())
-	{		
-		std::ostringstream stream;
-
-		LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
-
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
-			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-			<< "<Type>Normal</Type>"
-		<< "</Request>"
-		<< "\n\n\n";
-
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
-{
-	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
-	
-	session->mCreateInProgress = true;
-	if(startAudio)
-	{
-		session->mMediaConnectInProgress = true;
-	}
-
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
-		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-		<< "<URI>" << session->mSIPURI << "</URI>";
-
-	static const std::string allowed_chars =
-				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-				"0123456789"
-				"-._~";
-
-	if(!session->mHash.empty())
-	{
-		stream
-			<< "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
-			<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
-	}
-	
-	stream
-		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
-		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
-		<< "<Name>" << mChannelName << "</Name>"
-	<< "</Request>\n\n\n";
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
-{
-	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
-	
-	session->mCreateInProgress = true;
-	if(startAudio)
-	{
-		session->mMediaConnectInProgress = true;
-	}
-	
-	std::string password;
-	if(!session->mHash.empty())
-	{
-		static const std::string allowed_chars =
-					"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-					"0123456789"
-					"-._~"
-					;
-		password = LLURI::escape(session->mHash, allowed_chars);
-	}
-
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<URI>" << session->mSIPURI << "</URI>"
-		<< "<Name>" << mChannelName << "</Name>"
-		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
-		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
-		<< "<Password>" << password << "</Password>"
-		<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
-	<< "</Request>\n\n\n"
-	;
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
-{
-	LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
-
-	session->mMediaConnectInProgress = true;
-	
-	std::ostringstream stream;
-
-	stream
-	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-		<< "<Media>Audio</Media>"
-	<< "</Request>\n\n\n";
-
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
-{
-	LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
-	
-	std::ostringstream stream;
-
-	stream
-	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-	<< "</Request>\n\n\n";
-
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionTerminate()
-{
-	mSessionTerminateRequested = true;
-}
-
-void LLVoiceClient::requestRelog()
-{
-	mSessionTerminateRequested = true;
-	mRelogRequested = true;
-}
-
-
-void LLVoiceClient::leaveAudioSession()
-{
-	if(mAudioSession)
-	{
-		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
-
-		switch(getState())
-		{
-			case stateNoChannel:
-				// In this case, we want to pretend the join failed so our state machine doesn't get stuck.
-				// Skip the join failed transition state so we don't send out error notifications.
-				setState(stateJoinSessionFailedWaiting);
-			break;
-			case stateJoiningSession:
-			case stateSessionJoined:
-			case stateRunning:
-				if(!mAudioSession->mHandle.empty())
-				{
-
-#if RECORD_EVERYTHING
-					// HACK: for testing only
-					// Save looped recording
-					std::string savepath("/tmp/vivoxrecording");
-					{
-						time_t now = time(NULL);
-						const size_t BUF_SIZE = 64;
-						char time_str[BUF_SIZE];	/* Flawfinder: ignore */
-						
-						strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
-						savepath += time_str;
-					}
-					recordingLoopSave(savepath);
-#endif
-
-					sessionMediaDisconnectSendMessage(mAudioSession);
-					setState(stateLeavingSession);
-				}
-				else
-				{
-					LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	
-					setState(stateSessionTerminated);
-				}
-			break;
-			case stateJoinSessionFailed:
-			case stateJoinSessionFailedWaiting:
-				setState(stateSessionTerminated);
-			break;
-			
-			default:
-				LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
-			break;
-		}
-	}
-	else
-	{
-		LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
-		setState(stateSessionTerminated);
-	}
-}
-
-void LLVoiceClient::sessionTerminateSendMessage(sessionState *session)
-{
-	std::ostringstream stream;
-	
-	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;	
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
-{
-	std::ostringstream stream;
-	
-	LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;	
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
-{
-	std::ostringstream stream;
-	
-	LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;	
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-		<< "<Media>Audio</Media>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-	
-}
-
-void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
-{
-	std::ostringstream stream;
-	
-	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::getCaptureDevicesSendMessage()
-{
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::getRenderDevicesSendMessage()
-{
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::clearCaptureDevices()
-{
-	LL_DEBUGS("Voice") << "called" << LL_ENDL;
-	mCaptureDevices.clear();
-}
-
-void LLVoiceClient::addCaptureDevice(const std::string& name)
-{
-	LL_DEBUGS("Voice") << name << LL_ENDL;
-
-	mCaptureDevices.push_back(name);
-}
-
-LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices()
-{
-	return &mCaptureDevices;
-}
-
-void LLVoiceClient::setCaptureDevice(const std::string& name)
-{
-	if(name == "Default")
-	{
-		if(!mCaptureDevice.empty())
-		{
-			mCaptureDevice.clear();
-			mCaptureDeviceDirty = true;	
-		}
-	}
-	else
-	{
-		if(mCaptureDevice != name)
-		{
-			mCaptureDevice = name;
-			mCaptureDeviceDirty = true;	
-		}
-	}
-}
-
-void LLVoiceClient::clearRenderDevices()
-{	
-	LL_DEBUGS("Voice") << "called" << LL_ENDL;
-	mRenderDevices.clear();
-}
-
-void LLVoiceClient::addRenderDevice(const std::string& name)
-{
-	LL_DEBUGS("Voice") << name << LL_ENDL;
-	mRenderDevices.push_back(name);
-}
-
-LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices()
-{
-	return &mRenderDevices;
-}
-
-void LLVoiceClient::setRenderDevice(const std::string& name)
-{
-	if(name == "Default")
-	{
-		if(!mRenderDevice.empty())
-		{
-			mRenderDevice.clear();
-			mRenderDeviceDirty = true;	
-		}
-	}
-	else
-	{
-		if(mRenderDevice != name)
-		{
-			mRenderDevice = name;
-			mRenderDeviceDirty = true;	
-		}
-	}
-	
-}
-
-void LLVoiceClient::tuningStart()
-{
-	mTuningMode = true;
-	if(getState() >= stateNoChannel)
-	{
-		sessionTerminate();
-	}
-}
-
-void LLVoiceClient::tuningStop()
-{
-	mTuningMode = false;
-}
-
-bool LLVoiceClient::inTuningMode()
-{
-	bool result = false;
-	switch(getState())
-	{
-	case stateMicTuningRunning:
-		result = true;
-		break;
-	default:
-		break;
-	}
-	return result;
-}
-
-void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
-{		
-	mTuningAudioFile = name;
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
-    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
-    << "<Loop>" << (loop?"1":"0") << "</Loop>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::tuningRenderStopSendMessage()
-{
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
-    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::tuningCaptureStartSendMessage(int duration)
-{
-	LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
-	
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
-    << "<Duration>" << duration << "</Duration>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-
-void LLVoiceClient::tuningCaptureStopSendMessage()
-{
-	LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
-	
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-
-	mTuningEnergy = 0.0f;
-}
-
-void LLVoiceClient::tuningSetMicVolume(float volume)
-{
-	int scaled_volume = scale_mic_volume(volume);
-
-	if(scaled_volume != mTuningMicVolume)
-	{
-		mTuningMicVolume = scaled_volume;
-		mTuningMicVolumeDirty = true;
-	}
-}
-
-void LLVoiceClient::tuningSetSpeakerVolume(float volume)
-{
-	int scaled_volume = scale_speaker_volume(volume);	
-
-	if(scaled_volume != mTuningSpeakerVolume)
-	{
-		mTuningSpeakerVolume = scaled_volume;
-		mTuningSpeakerVolumeDirty = true;
-	}
-}
-				
-float LLVoiceClient::tuningGetEnergy(void)
-{
-	return mTuningEnergy;
-}
-
-bool LLVoiceClient::deviceSettingsAvailable()
-{
-	bool result = true;
-	
-	if(!mConnected)
-		result = false;
-	
-	if(mRenderDevices.empty())
-		result = false;
-	
-	return result;
-}
-
-void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
-{
-	if(clearCurrentList)
-	{
-		clearCaptureDevices();
-		clearRenderDevices();
-	}
-	getCaptureDevicesSendMessage();
-	getRenderDevicesSendMessage();
-}
-
-void LLVoiceClient::daemonDied()
-{
-	// The daemon died, so the connection is gone.  Reset everything and start over.
-	LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL;
-
-	// Try to relaunch the daemon
-	setState(stateDisableCleanup);
-}
-
-void LLVoiceClient::giveUp()
-{
-	// All has failed.  Clean up and stop trying.
-	closeSocket();
-	deleteAllSessions();
-	deleteAllBuddies();
-	
-	setState(stateJail);
-}
-
-static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
-{
-	F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity
-	F64 npos[3];
-	
-	// The original XML command was sent like this:
-	/*
-			<< "<Position>"
-				<< "<X>" << pos[VX] << "</X>"
-				<< "<Y>" << pos[VZ] << "</Y>"
-				<< "<Z>" << pos[VY] << "</Z>"
-			<< "</Position>"
-			<< "<Velocity>"
-				<< "<X>" << mAvatarVelocity[VX] << "</X>"
-				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>"
-				<< "<Z>" << mAvatarVelocity[VY] << "</Z>"
-			<< "</Velocity>"
-			<< "<AtOrientation>"
-				<< "<X>" << l.mV[VX] << "</X>"
-				<< "<Y>" << u.mV[VX] << "</Y>"
-				<< "<Z>" << a.mV[VX] << "</Z>"
-			<< "</AtOrientation>"
-			<< "<UpOrientation>"
-				<< "<X>" << l.mV[VZ] << "</X>"
-				<< "<Y>" << u.mV[VY] << "</Y>"
-				<< "<Z>" << a.mV[VZ] << "</Z>"
-			<< "</UpOrientation>"
-			<< "<LeftOrientation>"
-				<< "<X>" << l.mV [VY] << "</X>"
-				<< "<Y>" << u.mV [VZ] << "</Y>"
-				<< "<Z>" << a.mV [VY] << "</Z>"
-			<< "</LeftOrientation>";
-	*/
-
-#if 1
-	// This was the original transform done when building the XML command
-	nat[0] = left.mV[VX];
-	nat[1] = up.mV[VX];
-	nat[2] = at.mV[VX];
-
-	nup[0] = left.mV[VZ];
-	nup[1] = up.mV[VY];
-	nup[2] = at.mV[VZ];
-
-	nl[0] = left.mV[VY];
-	nl[1] = up.mV[VZ];
-	nl[2] = at.mV[VY];
-
-	npos[0] = pos.mdV[VX];
-	npos[1] = pos.mdV[VZ];
-	npos[2] = pos.mdV[VY];
-
-	nvel[0] = vel.mV[VX];
-	nvel[1] = vel.mV[VZ];
-	nvel[2] = vel.mV[VY];
-
-	for(int i=0;i<3;++i) {
-		at.mV[i] = nat[i];
-		up.mV[i] = nup[i];
-		left.mV[i] = nl[i];
-		pos.mdV[i] = npos[i];
-	}
-	
-	// This was the original transform done in the SDK
-	nat[0] = at.mV[2];
-	nat[1] = 0; // y component of at vector is always 0, this was up[2]
-	nat[2] = -1 * left.mV[2];
-
-	// We override whatever the application gives us
-	nup[0] = 0; // x component of up vector is always 0
-	nup[1] = 1; // y component of up vector is always 1
-	nup[2] = 0; // z component of up vector is always 0
-
-	nl[0] = at.mV[0];
-	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
-	nl[2] = -1 * left.mV[0];
-
-	npos[2] = pos.mdV[2] * -1.0;
-	npos[1] = pos.mdV[1];
-	npos[0] = pos.mdV[0];
-
-	for(int i=0;i<3;++i) {
-		at.mV[i] = nat[i];
-		up.mV[i] = nup[i];
-		left.mV[i] = nl[i];
-		pos.mdV[i] = npos[i];
-	}
-#else
-	// This is the compose of the two transforms (at least, that's what I'm trying for)
-	nat[0] = at.mV[VX];
-	nat[1] = 0; // y component of at vector is always 0, this was up[2]
-	nat[2] = -1 * up.mV[VZ];
-
-	// We override whatever the application gives us
-	nup[0] = 0; // x component of up vector is always 0
-	nup[1] = 1; // y component of up vector is always 1
-	nup[2] = 0; // z component of up vector is always 0
-
-	nl[0] = left.mV[VX];
-	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
-	nl[2] = -1 * left.mV[VY];
-
-	npos[0] = pos.mdV[VX];
-	npos[1] = pos.mdV[VZ];
-	npos[2] = pos.mdV[VY] * -1.0;
-
-	nvel[0] = vel.mV[VX];
-	nvel[1] = vel.mV[VZ];
-	nvel[2] = vel.mV[VY];
-
-	for(int i=0;i<3;++i) {
-		at.mV[i] = nat[i];
-		up.mV[i] = nup[i];
-		left.mV[i] = nl[i];
-		pos.mdV[i] = npos[i];
-	}
-	
-#endif
-}
-
-void LLVoiceClient::sendPositionalUpdate(void)
-{	
-	std::ostringstream stream;
-	
-	if(mSpatialCoordsDirty)
-	{
-		LLVector3 l, u, a, vel;
-		LLVector3d pos;
-
-		mSpatialCoordsDirty = false;
-		
-		// Always send both speaker and listener positions together.
-		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"		
-			<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
-		
-		stream << "<SpeakerPosition>";
-
-//		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
-		l = mAvatarRot.getLeftRow();
-		u = mAvatarRot.getUpRow();
-		a = mAvatarRot.getFwdRow();
-		pos = mAvatarPosition;
-		vel = mAvatarVelocity;
-
-		// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
-		// The old transform is replicated by this function.
-		oldSDKTransform(l, u, a, pos, vel);
-		
-		stream 
-			<< "<Position>"
-				<< "<X>" << pos.mdV[VX] << "</X>"
-				<< "<Y>" << pos.mdV[VY] << "</Y>"
-				<< "<Z>" << pos.mdV[VZ] << "</Z>"
-			<< "</Position>"
-			<< "<Velocity>"
-				<< "<X>" << vel.mV[VX] << "</X>"
-				<< "<Y>" << vel.mV[VY] << "</Y>"
-				<< "<Z>" << vel.mV[VZ] << "</Z>"
-			<< "</Velocity>"
-			<< "<AtOrientation>"
-				<< "<X>" << a.mV[VX] << "</X>"
-				<< "<Y>" << a.mV[VY] << "</Y>"
-				<< "<Z>" << a.mV[VZ] << "</Z>"
-			<< "</AtOrientation>"
-			<< "<UpOrientation>"
-				<< "<X>" << u.mV[VX] << "</X>"
-				<< "<Y>" << u.mV[VY] << "</Y>"
-				<< "<Z>" << u.mV[VZ] << "</Z>"
-			<< "</UpOrientation>"
-			<< "<LeftOrientation>"
-				<< "<X>" << l.mV [VX] << "</X>"
-				<< "<Y>" << l.mV [VY] << "</Y>"
-				<< "<Z>" << l.mV [VZ] << "</Z>"
-			<< "</LeftOrientation>";
-
-		stream << "</SpeakerPosition>";
-
-		stream << "<ListenerPosition>";
-
-		LLVector3d	earPosition;
-		LLVector3	earVelocity;
-		LLMatrix3	earRot;
-		
-		switch(mEarLocation)
-		{
-			case earLocCamera:
-			default:
-				earPosition = mCameraPosition;
-				earVelocity = mCameraVelocity;
-				earRot = mCameraRot;
-			break;
-			
-			case earLocAvatar:
-				earPosition = mAvatarPosition;
-				earVelocity = mAvatarVelocity;
-				earRot = mAvatarRot;
-			break;
-			
-			case earLocMixed:
-				earPosition = mAvatarPosition;
-				earVelocity = mAvatarVelocity;
-				earRot = mCameraRot;
-			break;
-		}
-
-		l = earRot.getLeftRow();
-		u = earRot.getUpRow();
-		a = earRot.getFwdRow();
-		pos = earPosition;
-		vel = earVelocity;
-
-//		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
-		
-		oldSDKTransform(l, u, a, pos, vel);
-		
-		stream 
-			<< "<Position>"
-				<< "<X>" << pos.mdV[VX] << "</X>"
-				<< "<Y>" << pos.mdV[VY] << "</Y>"
-				<< "<Z>" << pos.mdV[VZ] << "</Z>"
-			<< "</Position>"
-			<< "<Velocity>"
-				<< "<X>" << vel.mV[VX] << "</X>"
-				<< "<Y>" << vel.mV[VY] << "</Y>"
-				<< "<Z>" << vel.mV[VZ] << "</Z>"
-			<< "</Velocity>"
-			<< "<AtOrientation>"
-				<< "<X>" << a.mV[VX] << "</X>"
-				<< "<Y>" << a.mV[VY] << "</Y>"
-				<< "<Z>" << a.mV[VZ] << "</Z>"
-			<< "</AtOrientation>"
-			<< "<UpOrientation>"
-				<< "<X>" << u.mV[VX] << "</X>"
-				<< "<Y>" << u.mV[VY] << "</Y>"
-				<< "<Z>" << u.mV[VZ] << "</Z>"
-			<< "</UpOrientation>"
-			<< "<LeftOrientation>"
-				<< "<X>" << l.mV [VX] << "</X>"
-				<< "<Y>" << l.mV [VY] << "</Y>"
-				<< "<Z>" << l.mV [VZ] << "</Z>"
-			<< "</LeftOrientation>";
-
-
-		stream << "</ListenerPosition>";
-
-		stream << "</Request>\n\n\n";
-	}	
-	
-	if(mAudioSession && mAudioSession->mVolumeDirty)
-	{
-		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
-
-		mAudioSession->mVolumeDirty = false;
-		
-		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
-		{
-			participantState *p = iter->second;
-			
-			if(p->mVolumeDirty)
-			{
-				// Can't set volume/mute for yourself
-				if(!p->mIsSelf)
-				{
-					int volume = 56; // nominal default value
-					bool mute = p->mOnMuteList;
-					
-					if(p->mUserVolume != -1)
-					{
-						// scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal")
-						if(p->mUserVolume < 100)
-							volume = (p->mUserVolume * 56) / 100;
-						else
-							volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56;
-					}
-					else if(p->mVolume != -1)
-					{
-						// Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent)
-						volume = p->mVolume;
-					}
-										
-
-					if(mute)
-					{
-						// SetParticipantMuteForMe doesn't work in p2p sessions.
-						// If we want the user to be muted, set their volume to 0 as well.
-						// This isn't perfect, but it will at least reduce their volume to a minimum.
-						volume = 0;
-					}
-					
-					if(volume == 0)
-						mute = true;
-
-					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
-					
-					// SLIM SDK: Send both volume and mute commands.
-					
-					// Send a "volume for me" command for the user.
-					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
-						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
-						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
-						<< "<Volume>" << volume << "</Volume>"
-						<< "</Request>\n\n\n";
-
-					// Send a "mute for me" command for the user
-					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
-						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
-						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
-						<< "<Mute>" << (mute?"1":"0") << "</Mute>"
-						<< "</Request>\n\n\n";
-				}
-				
-				p->mVolumeDirty = false;
-			}
-		}
-	}
-			
-	buildLocalAudioUpdates(stream);
-	
-	if(!stream.str().empty())
-	{
-		writeString(stream.str());
-	}
-	
-	// Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
-	// Batching them all together can choke SLVoice, so send them in separate writes.
-	sendFriendsListUpdates();
-}
-
-void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
-{
-	if(mCaptureDeviceDirty)
-	{
-		LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
-	
-		stream 
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
-			<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
-		<< "</Request>"
-		<< "\n\n\n";
-		
-		mCaptureDeviceDirty = false;
-	}
-}
-
-void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
-{
-	if(mRenderDeviceDirty)
-	{
-		LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
-
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
-			<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
-		<< "</Request>"
-		<< "\n\n\n";
-		mRenderDeviceDirty = false;
-	}
-}
-
-void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
-{
-	buildSetCaptureDevice(stream);
-
-	buildSetRenderDevice(stream);
-
-	if(mPTTDirty)
-	{
-		mPTTDirty = false;
-
-		// Send a local mute command.
-		// NOTE that the state of "PTT" is the inverse of "local mute".
-		//   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
-		
-		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL;
-
-		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
-			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-			<< "<Value>" << (mPTT?"false":"true") << "</Value>"
-			<< "</Request>\n\n\n";
-		
-	}
-
-	if(mSpeakerMuteDirty)
-	{
-		const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
-
-		mSpeakerMuteDirty = false;
-
-		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
-		
-		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
-			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-			<< "<Value>" << muteval << "</Value>"
-			<< "</Request>\n\n\n";	
-		
-	}
-	
-	if(mSpeakerVolumeDirty)
-	{
-		mSpeakerVolumeDirty = false;
-
-		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
-
-		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
-			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-			<< "<Value>" << mSpeakerVolume << "</Value>"
-			<< "</Request>\n\n\n";
-			
-	}
-	
-	if(mMicVolumeDirty)
-	{
-		mMicVolumeDirty = false;
-
-		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
-
-		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
-			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-			<< "<Value>" << mMicVolume << "</Value>"
-			<< "</Request>\n\n\n";				
-	}
-
-	
-}
-
-void LLVoiceClient::checkFriend(const LLUUID& id)
-{
-	std::string name;
-	buddyListEntry *buddy = findBuddy(id);
-
-	// Make sure we don't add a name before it's been looked up.
-	if(gCacheName->getFullName(id, name))
-	{
-
-		const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
-		bool canSeeMeOnline = false;
-		if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
-			canSeeMeOnline = true;
-		
-		// When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary.
-		
-		if(buddy)
-		{
-			// This buddy is already in both lists.
-
-			if(name != buddy->mDisplayName)
-			{
-				// The buddy is in the list with the wrong name.  Update it with the correct name.
-				LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL;
-				buddy->mDisplayName = name;
-				buddy->mNeedsNameUpdate = true;		// This will cause the buddy to be resent.
-			}
-		}
-		else
-		{
-			// This buddy was not in the vivox list, needs to be added.
-			buddy = addBuddy(sipURIFromID(id), name);
-			buddy->mUUID = id;
-		}
-		
-		// In all the above cases, the buddy is in the SL friends list (which is how we got here).
-		buddy->mInSLFriends = true;
-		buddy->mCanSeeMeOnline = canSeeMeOnline;
-		buddy->mNameResolved = true;
-		
-	}
-	else
-	{
-		// This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has.
-		if(buddy)
-		{
-			buddy->mNameResolved = false;
-		}
-		
-		// Initiate a lookup.
-		// The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
-		lookupName(id);
-	}
-}
-
-void LLVoiceClient::clearAllLists()
-{
-	// FOR TESTING ONLY
-	
-	// This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
-	buddyListMap::iterator buddy_it;
-	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
-	{
-		buddyListEntry *buddy = buddy_it->second;
-		buddy_it++;
-		
-		std::ostringstream stream;
-
-		if(buddy->mInVivoxBuddies)
-		{
-			// delete this entry from the vivox buddy list
-			buddy->mInVivoxBuddies = false;
-			LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
-			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
-				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
-				<< "</Request>\n\n\n";		
-		}
-
-		if(buddy->mHasBlockListEntry)
-		{
-			// Delete the associated block list entry (so the block list doesn't fill up with junk)
-			buddy->mHasBlockListEntry = false;
-			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
-				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-				<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
-				<< "</Request>\n\n\n";								
-		}
-		if(buddy->mHasAutoAcceptListEntry)
-		{
-			// Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
-			buddy->mHasAutoAcceptListEntry = false;
-			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
-				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-				<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
-				<< "</Request>\n\n\n";
-		}
-
-		writeString(stream.str());
-
-	}
-}
-
-void LLVoiceClient::sendFriendsListUpdates()
-{
-	if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
-	{
-		mFriendsListDirty = false;
-		
-		if(0)
-		{
-			// FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
-			clearAllLists();
-			return;
-		}
-		
-		LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
-		
-		buddyListMap::iterator buddy_it;
-		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
-		{
-			// reset the temp flags in the local buddy list
-			buddy_it->second->mInSLFriends = false;
-		}
-		
-		// correlate with the friends list
-		{
-			LLCollectAllBuddies collect;
-			LLAvatarTracker::instance().applyFunctor(collect);
-			LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
-			LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
-			
-			for ( ; it != end; ++it)
-			{
-				checkFriend(it->second);
-			}
-			it = collect.mOffline.begin();
-			end = collect.mOffline.end();
-			for ( ; it != end; ++it)
-			{
-				checkFriend(it->second);
-			}
-		}
-				
-		LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
-
-		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
-		{
-			buddyListEntry *buddy = buddy_it->second;
-			buddy_it++;
-			
-			// Ignore entries that aren't resolved yet.
-			if(buddy->mNameResolved)
-			{
-				std::ostringstream stream;
-
-				if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
-				{					
-					if(mNumberOfAliases > 0)
-					{
-						// Add (or update) this entry in the vivox buddy list
-						buddy->mInVivoxBuddies = true;
-						buddy->mNeedsNameUpdate = false;
-						LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
-						stream 
-							<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">"
-								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-								<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
-								<< "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
-								<< "<BuddyData></BuddyData>"	// Without this, SLVoice doesn't seem to parse the command.
-								<< "<GroupID>0</GroupID>"
-							<< "</Request>\n\n\n";	
-					}
-				}
-				else if(!buddy->mInSLFriends)
-				{
-					// This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list.
- 					if(buddy->mInVivoxBuddies)
-					{
-						// delete this entry from the vivox buddy list
-						buddy->mInVivoxBuddies = false;
-						LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
-						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
-							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-							<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
-							<< "</Request>\n\n\n";		
-					}
-
-					if(buddy->mHasBlockListEntry)
-					{
-						// Delete the associated block list entry, if any
-						buddy->mHasBlockListEntry = false;
-						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
-							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-							<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
-							<< "</Request>\n\n\n";								
-					}
-					if(buddy->mHasAutoAcceptListEntry)
-					{
-						// Delete the associated auto-accept list entry, if any
-						buddy->mHasAutoAcceptListEntry = false;
-						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
-							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-							<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
-							<< "</Request>\n\n\n";
-					}
-				}
-				
-				if(buddy->mInSLFriends)
-				{
-
-					if(buddy->mCanSeeMeOnline)
-					{
-						// Buddy should not be blocked.
-
-						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
-						
-						// If the buddy has a block list entry, delete it.
-						if(buddy->mHasBlockListEntry)
-						{
-							buddy->mHasBlockListEntry = false;
-							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
-								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-								<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
-								<< "</Request>\n\n\n";		
-							
-							
-							// If we just deleted a block list entry, add an auto-accept entry.
-							if(!buddy->mHasAutoAcceptListEntry)
-							{
-								buddy->mHasAutoAcceptListEntry = true;								
-								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">"
-									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-									<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
-									<< "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
-									<< "</Request>\n\n\n";
-							}
-						}
-					}
-					else
-					{
-						// Buddy should be blocked.
-						
-						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
-
-						// If this buddy has an autoaccept entry, delete it
-						if(buddy->mHasAutoAcceptListEntry)
-						{
-							buddy->mHasAutoAcceptListEntry = false;
-							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
-								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-								<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
-								<< "</Request>\n\n\n";
-						
-							// If we just deleted an auto-accept entry, add a block list entry.
-							if(!buddy->mHasBlockListEntry)
-							{
-								buddy->mHasBlockListEntry = true;
-								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">"
-									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-									<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
-									<< "<PresenceOnly>1</PresenceOnly>"
-									<< "</Request>\n\n\n";								
-							}
-						}
-					}
-
-					if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
-					{
-						// Delete this entry from the local buddy list.  This should NOT invalidate the iterator,
-						// since it has already been incremented to the next entry.
-						deleteBuddy(buddy->mURI);
-					}
-
-				}
-				writeString(stream.str());
-			}
-		}
-	}
-}
-
-/////////////////////////////
-// Response/Event handlers
-
-void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
-{	
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
-		setState(stateConnectorFailed);
-	}
-	else
-	{
-		// Connector created, move forward.
-		LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
-		mAPIVersion = versionID;
-		mConnectorHandle = connectorHandle;
-		if(getState() == stateConnectorStarting)
-		{
-			setState(stateConnectorStarted);
-		}
-	}
-}
-
-void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
-{ 
-	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
-	
-	// Status code of 20200 means "bad password".  We may want to special-case that at some point.
-	
-	if ( statusCode == 401 )
-	{
-		// Login failure which is probably caused by the delay after a user's password being updated.
-		LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		setState(stateLoginRetry);
-	}
-	else if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		setState(stateLoginFailed);
-	}
-	else
-	{
-		// Login succeeded, move forward.
-		mAccountHandle = accountHandle;
-		mNumberOfAliases = numberOfAliases;
-		// This needs to wait until the AccountLoginStateChangeEvent is received.
-//		if(getState() == stateLoggingIn)
-//		{
-//			setState(stateLoggedIn);
-//		}
-	}
-}
-
-void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
-{	
-	sessionState *session = findSessionBeingCreatedByURI(requestId);
-	
-	if(session)
-	{
-		session->mCreateInProgress = false;
-	}
-	
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		if(session)
-		{
-			session->mErrorStatusCode = statusCode;		
-			session->mErrorStatusString = statusString;
-			if(session == mAudioSession)
-			{
-				setState(stateJoinSessionFailed);
-			}
-			else
-			{
-				reapSession(session);
-			}
-		}
-	}
-	else
-	{
-		LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
-		if(session)
-		{
-			setSessionHandle(session, sessionHandle);
-		}
-	}
-}
-
-void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
-{	
-	sessionState *session = findSessionBeingCreatedByURI(requestId);
-	
-	if(session)
-	{
-		session->mCreateInProgress = false;
-	}
-	
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		if(session)
-		{
-			session->mErrorStatusCode = statusCode;		
-			session->mErrorStatusString = statusString;
-			if(session == mAudioSession)
-			{
-				setState(stateJoinSessionFailed);
-			}
-			else
-			{
-				reapSession(session);
-			}
-		}
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
-		if(session)
-		{
-			setSessionHandle(session, sessionHandle);
-		}
-	}
-}
-
-void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
-{
-	sessionState *session = findSession(requestId);
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		if(session)
-		{
-			session->mMediaConnectInProgress = false;
-			session->mErrorStatusCode = statusCode;		
-			session->mErrorStatusString = statusString;
-			if(session == mAudioSession)
-				setState(stateJoinSessionFailed);
-		}
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL;
-	}
-}
-
-void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
-{	
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
-		// Should this ever fail?  do we care if it does?
-	}
-}
-
-void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
-{
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
-		// Should this ever fail?  do we care if it does?
-	}
-	
-	mConnected = false;
-	
-	if(getState() == stateConnectorStopping)
-	{
-		setState(stateConnectorStopped);
-	}
-}
-
-void LLVoiceClient::sessionAddedEvent(
-		std::string &uriString, 
-		std::string &alias, 
-		std::string &sessionHandle, 
-		std::string &sessionGroupHandle, 
-		bool isChannel, 
-		bool incoming,
-		std::string &nameString,
-		std::string &applicationString)
-{
-	sessionState *session = NULL;
-
-	LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
-	
-	session = addSession(uriString, sessionHandle);
-	if(session)
-	{
-		session->mGroupHandle = sessionGroupHandle;
-		session->mIsChannel = isChannel;
-		session->mIncoming = incoming;
-		session->mAlias = alias;
-			
-		// Generate a caller UUID -- don't need to do this for channels
-		if(!session->mIsChannel)
-		{
-			if(IDFromName(session->mSIPURI, session->mCallerID))
-			{
-				// Normal URI(base64-encoded UUID) 
-			}
-			else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
-			{
-				// Wrong URI, but an alias is available.  Stash the incoming URI as an alternate
-				session->mAlternateSIPURI = session->mSIPURI;
-				
-				// and generate a proper URI from the ID.
-				setSessionURI(session, sipURIFromID(session->mCallerID));
-			}
-			else
-			{
-				LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
-				setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
-				session->mSynthesizedCallerID = true;
-				
-				// Can't look up the name in this case -- we have to extract it from the URI.
-				std::string namePortion = nameFromsipURI(session->mSIPURI);
-				if(namePortion.empty())
-				{
-					// Didn't seem to be a SIP URI, just use the whole provided name.
-					namePortion = nameString;
-				}
-				
-				// Some incoming names may be separated with an underscore instead of a space.  Fix this.
-				LLStringUtil::replaceChar(namePortion, '_', ' ');
-				
-				// Act like we just finished resolving the name (this stores it in all the right places)
-				avatarNameResolved(session->mCallerID, namePortion);
-			}
-		
-			LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
-
-			if(!session->mSynthesizedCallerID)
-			{
-				// If we got here, we don't have a proper name.  Initiate a lookup.
-				lookupName(session->mCallerID);
-			}
-		}
-	}
-}
-
-void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
-{
-	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
-	
-#if USE_SESSION_GROUPS
-	if(mMainSessionGroupHandle.empty())
-	{
-		// This is the first (i.e. "main") session group.  Save its handle.
-		mMainSessionGroupHandle = sessionGroupHandle;
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
-	}
-#endif
-}
-
-void LLVoiceClient::joinedAudioSession(sessionState *session)
-{
-	if(mAudioSession != session)
-	{
-		sessionState *oldSession = mAudioSession;
-
-		mAudioSession = session;
-		mAudioSessionChanged = true;
-
-		// The old session may now need to be deleted.
-		reapSession(oldSession);
-	}
-	
-	// This is the session we're joining.
-	if(getState() == stateJoiningSession)
-	{
-		setState(stateSessionJoined);
-		
-		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
-		// Add the current user as a participant here.
-		participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
-		if(participant)
-		{
-			participant->mIsSelf = true;
-			lookupName(participant->mAvatarID);
-
-			LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName 
-					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
-		}
-		
-		if(!session->mIsChannel)
-		{
-			// this is a p2p session.  Make sure the other end is added as a participant.
-			participantState *participant = session->addParticipant(session->mSIPURI);
-			if(participant)
-			{
-				if(participant->mAvatarIDValid)
-				{
-					lookupName(participant->mAvatarID);
-				}
-				else if(!session->mName.empty())
-				{
-					participant->mDisplayName = session->mName;
-					avatarNameResolved(participant->mAvatarID, session->mName);
-				}
-				
-				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
-				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName 
-						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
-			}
-		}
-	}
-}
-
-void LLVoiceClient::sessionRemovedEvent(
-	std::string &sessionHandle, 
-	std::string &sessionGroupHandle)
-{
-	LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
-	
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		leftAudioSession(session);
-
-		// This message invalidates the session's handle.  Set it to empty.
-		setSessionHandle(session);
-		
-		// This also means that the session's session group is now empty.
-		// Terminate the session group so it doesn't leak.
-		sessionGroupTerminateSendMessage(session);
-		
-		// Reset the media state (we now have no info)
-		session->mMediaStreamState = streamStateUnknown;
-		session->mTextStreamState = streamStateUnknown;
-		
-		// Conditionally delete the session
-		reapSession(session);
-	}
-	else
-	{
-		LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
-	}
-}
-
-void LLVoiceClient::reapSession(sessionState *session)
-{
-	if(session)
-	{
-		if(!session->mHandle.empty())
-		{
-			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
-		}
-		else if(session->mCreateInProgress)
-		{
-			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
-		}
-		else if(session->mMediaConnectInProgress)
-		{
-			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
-		}
-		else if(session == mAudioSession)
-		{
-			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
-		}
-		else if(session == mNextAudioSession)
-		{
-			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
-		}
-		else
-		{
-			// TODO: Question: Should we check for queued text messages here?
-			// We don't have a reason to keep tracking this session, so just delete it.
-			LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
-			deleteSession(session);
-			session = NULL;
-		}	
-	}
-	else
-	{
-//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
-	}
-}
-
-// Returns true if the session seems to indicate we've moved to a region on a different voice server
-bool LLVoiceClient::sessionNeedsRelog(sessionState *session)
-{
-	bool result = false;
-	
-	if(session != NULL)
-	{
-		// Only make this check for spatial channels (so it won't happen for group or p2p calls)
-		if(session->mIsSpatial)
-		{
-			std::string::size_type atsign;
-			
-			atsign = session->mSIPURI.find("@");
-			
-			if(atsign != std::string::npos)
-			{
-				std::string urihost = session->mSIPURI.substr(atsign + 1);
-				if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
-				{
-					// The hostname in this URI is different from what we expect.  This probably means we need to relog.
-					
-					// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
-					// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
-					
-					result = true;
-				}
-			}
-		}
-	}
-	
-	return result;
-}
-
-void LLVoiceClient::leftAudioSession(
-	sessionState *session)
-{
-	if(mAudioSession == session)
-	{
-		switch(getState())
-		{
-			case stateJoiningSession:
-			case stateSessionJoined:
-			case stateRunning:
-			case stateLeavingSession:
-			case stateJoinSessionFailed:
-			case stateJoinSessionFailedWaiting:
-				// normal transition
-				LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
-				setState(stateSessionTerminated);
-			break;
-			
-			case stateSessionTerminated:
-				// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
-				LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
-			break;
-			
-			default:
-				LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
-				setState(stateSessionTerminated);
-			break;
-		}
-	}
-}
-
-void LLVoiceClient::accountLoginStateChangeEvent(
-		std::string &accountHandle, 
-		int statusCode, 
-		std::string &statusString, 
-		int state)
-{
-	LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
-	/*
-		According to Mike S., status codes for this event are:
-		login_state_logged_out=0,
-        login_state_logged_in = 1,
-        login_state_logging_in = 2,
-        login_state_logging_out = 3,
-        login_state_resetting = 4,
-        login_state_error=100	
-	*/
-	
-	switch(state)
-	{
-		case 1:
-		if(getState() == stateLoggingIn)
-		{
-			setState(stateLoggedIn);
-		}
-		break;
-
-		case 3:
-			// The user is in the process of logging out.
-			setState(stateLoggingOut);
-		break;
-
-		case 0:
-			// The user has been logged out.  
-			setState(stateLoggedOut);
-		break;
-		
-		default:
-			//Used to be a commented out warning
-			LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
-		break;
-	}
-}
-
-void LLVoiceClient::mediaStreamUpdatedEvent(
-	std::string &sessionHandle, 
-	std::string &sessionGroupHandle, 
-	int statusCode, 
-	std::string &statusString, 
-	int state, 
-	bool incoming)
-{
-	sessionState *session = findSession(sessionHandle);
-	
-	LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
-	
-	if(session)
-	{
-		// We know about this session
-		
-		// Save the state for later use
-		session->mMediaStreamState = state;
-		
-		switch(statusCode)
-		{
-			case 0:
-			case 200:
-				// generic success
-				// Don't change the saved error code (it may have been set elsewhere)
-			break;
-			default:
-				// save the status code for later
-				session->mErrorStatusCode = statusCode;
-			break;
-		}
-		
-		switch(state)
-		{
-			case streamStateIdle:
-				// Standard "left audio session"
-				session->mVoiceEnabled = false;
-				session->mMediaConnectInProgress = false;
-				leftAudioSession(session);
-			break;
-
-			case streamStateConnected:
-				session->mVoiceEnabled = true;
-				session->mMediaConnectInProgress = false;
-				joinedAudioSession(session);
-			break;
-			
-			case streamStateRinging:
-				if(incoming)
-				{
-					// Send the voice chat invite to the GUI layer
-					// *TODO: Question: Should we correlate with the mute list here?
-					session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
-					session->mVoiceInvitePending = true;
-					if(session->mName.empty())
-					{
-						lookupName(session->mCallerID);
-					}
-					else
-					{
-						// Act like we just finished resolving the name
-						avatarNameResolved(session->mCallerID, session->mName);
-					}
-				}
-			break;
-			
-			default:
-				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
-			break;
-			
-		}
-		
-	}
-	else
-	{
-		LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
-	}
-}
-
-void LLVoiceClient::textStreamUpdatedEvent(
-	std::string &sessionHandle, 
-	std::string &sessionGroupHandle, 
-	bool enabled,
-	int state, 
-	bool incoming)
-{
-	sessionState *session = findSession(sessionHandle);
-	
-	if(session)
-	{
-		// Save the state for later use
-		session->mTextStreamState = state;
-		
-		// We know about this session
-		switch(state)
-		{
-			case 0:	// We see this when the text stream closes
-				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
-			break;
-			
-			case 1:	// We see this on an incoming call from the Connector
-				// Try to send any text messages queued for this session.
-				sendQueuedTextMessages(session);
-
-				// Send the text chat invite to the GUI layer
-				// TODO: Question: Should we correlate with the mute list here?
-				session->mTextInvitePending = true;
-				if(session->mName.empty())
-				{
-					lookupName(session->mCallerID);
-				}
-				else
-				{
-					// Act like we just finished resolving the name
-					avatarNameResolved(session->mCallerID, session->mName);
-				}
-			break;
-
-			default:
-				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
-			break;
-			
-		}
-	}
-}
-
-void LLVoiceClient::participantAddedEvent(
-		std::string &sessionHandle, 
-		std::string &sessionGroupHandle, 
-		std::string &uriString, 
-		std::string &alias, 
-		std::string &nameString, 
-		std::string &displayNameString, 
-		int participantType)
-{
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		participantState *participant = session->addParticipant(uriString);
-		if(participant)
-		{
-			participant->mAccountName = nameString;
-
-			LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName 
-					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
-
-			if(participant->mAvatarIDValid)
-			{
-				// Initiate a lookup
-				lookupName(participant->mAvatarID);
-			}
-			else
-			{
-				// If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
-				std::string namePortion = nameFromsipURI(uriString);
-				if(namePortion.empty())
-				{
-					// Problem with the SIP URI, fall back to the display name
-					namePortion = displayNameString;
-				}
-				if(namePortion.empty())
-				{
-					// Problems with both of the above, fall back to the account name
-					namePortion = nameString;
-				}
-				
-				// Set the display name (which is a hint to the active speakers window not to do its own lookup)
-				participant->mDisplayName = namePortion;
-				avatarNameResolved(participant->mAvatarID, namePortion);
-			}
-		}
-	}
-}
-
-void LLVoiceClient::participantRemovedEvent(
-		std::string &sessionHandle, 
-		std::string &sessionGroupHandle, 
-		std::string &uriString, 
-		std::string &alias, 
-		std::string &nameString)
-{
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		participantState *participant = session->findParticipant(uriString);
-		if(participant)
-		{
-			session->removeParticipant(participant);
-		}
-		else
-		{
-			LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
-		}
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
-	}
-}
-
-
-void LLVoiceClient::participantUpdatedEvent(
-		std::string &sessionHandle, 
-		std::string &sessionGroupHandle, 
-		std::string &uriString, 
-		std::string &alias, 
-		bool isModeratorMuted, 
-		bool isSpeaking, 
-		int volume, 
-		F32 energy)
-{
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		participantState *participant = session->findParticipant(uriString);
-		
-		if(participant)
-		{
-			participant->mIsSpeaking = isSpeaking;
-			participant->mIsModeratorMuted = isModeratorMuted;
-
-			// SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
-			if (isSpeaking)
-			{
-				participant->mSpeakingTimeout.reset();
-				participant->mPower = energy;
-			}
-			else
-			{
-				participant->mPower = 0.0f;
-			}
-			participant->mVolume = volume;
-
-			
-			// *HACK: mantipov: added while working on EXT-3544
-			/*
-			Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE 
-			LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.
-			
-			participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted
-			Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.
-			Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.
-			
-			But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()
-			voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
-			and event is not fired.
-
-			So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it
-			in LLCallFloater::draw()
-			*/
-			LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
-
-			// ignore session ID of local chat
-			if (voice_cnl && voice_cnl->getSessionID().notNull())
-			{
-				LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID());
-				if (speaker_manager)
-				{
-					speaker_manager->update(true);
-				}
-			}
-		}
-		else
-		{
-			LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
-		}
-	}
-	else
-	{
-		LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
-	}
-}
-
-void LLVoiceClient::buddyPresenceEvent(
-		std::string &uriString, 
-		std::string &alias, 
-		std::string &statusString,
-		std::string &applicationString)
-{
-	buddyListEntry *buddy = findBuddy(uriString);
-	
-	if(buddy)
-	{
-		LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL;
-		LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
-
-		if(applicationString.empty())
-		{
-			// This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way.
-			// NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
-
-			if ( stricmp("Unknown", statusString.c_str())== 0) 
-			{
-				// User went offline with a non-SLim-enabled viewer.
-				buddy->mOnlineSL = false;
-			}
-			else if ( stricmp("Online", statusString.c_str())== 0) 
-			{
-				// User came online with a non-SLim-enabled viewer.
-				buddy->mOnlineSL = true;
-			}
-			else
-			{
-				// If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
-				// 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.
-				buddy->mOnlineSLim = true;
-			} 
-		}
-		else if(applicationString.find("SecondLifeViewer") != std::string::npos)
-		{
-			// This presence event is from a viewer that sets the application string
-			if ( stricmp("Unknown", statusString.c_str())== 0) 
-			{
-				// Viewer says they're offline
-				buddy->mOnlineSL = false;
-			}
-			else
-			{
-				// Viewer says they're online
-				buddy->mOnlineSL = true;
-			}
-		}
-		else
-		{
-			// This presence event is from something which is NOT the SL viewer (assume it's SLim).
-			if ( stricmp("Unknown", statusString.c_str())== 0) 
-			{
-				// SLim says they're offline
-				buddy->mOnlineSLim = false;
-			}
-			else
-			{
-				// SLim says they're online
-				buddy->mOnlineSLim = true;
-			}
-		} 
-
-		LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
-		
-		// HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
-		LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
-
-		notifyFriendObservers();
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
-	}	
-}
-
-void LLVoiceClient::messageEvent(
-		std::string &sessionHandle, 
-		std::string &uriString, 
-		std::string &alias, 
-		std::string &messageHeader, 
-		std::string &messageBody,
-		std::string &applicationString)
-{
-	LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
-//	LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
-	
-	if(messageHeader.find("text/html") != std::string::npos)
-	{
-		std::string message;
-
-		{
-			const std::string startMarker = "<body";
-			const std::string startMarker2 = ">";
-			const std::string endMarker = "</body>";
-			const std::string startSpan = "<span";
-			const std::string endSpan = "</span>";
-			std::string::size_type start;
-			std::string::size_type end;
-			
-			// Default to displaying the raw string, so the message gets through.
-			message = messageBody;
-
-			// Find the actual message text within the XML fragment
-			start = messageBody.find(startMarker);
-			start = messageBody.find(startMarker2, start);
-			end = messageBody.find(endMarker);
-
-			if(start != std::string::npos)
-			{
-				start += startMarker2.size();
-				
-				if(end != std::string::npos)
-					end -= start;
-					
-				message.assign(messageBody, start, end);
-			}
-			else 
-			{
-				// Didn't find a <body>, try looking for a <span> instead.
-				start = messageBody.find(startSpan);
-				start = messageBody.find(startMarker2, start);
-				end = messageBody.find(endSpan);
-				
-				if(start != std::string::npos)
-				{
-					start += startMarker2.size();
-					
-					if(end != std::string::npos)
-						end -= start;
-					
-					message.assign(messageBody, start, end);
-				}			
-			}
-		}	
-		
-//		LL_DEBUGS("Voice") << "    raw message = \n" << message << LL_ENDL;
-
-		// strip formatting tags
-		{
-			std::string::size_type start;
-			std::string::size_type end;
-			
-			while((start = message.find('<')) != std::string::npos)
-			{
-				if((end = message.find('>', start + 1)) != std::string::npos)
-				{
-					// Strip out the tag
-					message.erase(start, (end + 1) - start);
-				}
-				else
-				{
-					// Avoid an infinite loop
-					break;
-				}
-			}
-		}
-		
-		// Decode ampersand-escaped chars
-		{
-			std::string::size_type mark = 0;
-
-			// The text may contain text encoded with &lt;, &gt;, and &amp;
-			mark = 0;
-			while((mark = message.find("&lt;", mark)) != std::string::npos)
-			{
-				message.replace(mark, 4, "<");
-				mark += 1;
-			}
-			
-			mark = 0;
-			while((mark = message.find("&gt;", mark)) != std::string::npos)
-			{
-				message.replace(mark, 4, ">");
-				mark += 1;
-			}
-			
-			mark = 0;
-			while((mark = message.find("&amp;", mark)) != std::string::npos)
-			{
-				message.replace(mark, 5, "&");
-				mark += 1;
-			}
-		}
-		
-		// strip leading/trailing whitespace (since we always seem to get a couple newlines)
-		LLStringUtil::trim(message);
-		
-//		LL_DEBUGS("Voice") << "    stripped message = \n" << message << LL_ENDL;
-		
-		sessionState *session = findSession(sessionHandle);
-		if(session)
-		{
-			bool is_busy = gAgent.getBusy();
-			bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
-			bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
-			bool quiet_chat = false;
-			LLChat chat;
-
-			chat.mMuted = is_muted && !is_linden;
-			
-			if(!chat.mMuted)
-			{
-				chat.mFromID = session->mCallerID;
-				chat.mFromName = session->mName;
-				chat.mSourceType = CHAT_SOURCE_AGENT;
-
-				if(is_busy && !is_linden)
-				{
-					quiet_chat = true;
-					// TODO: Question: Return busy mode response here?  Or maybe when session is started instead?
-				}
-								
-				LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
-				gIMMgr->addMessage(session->mIMSessionID,
-						session->mCallerID,
-						session->mName.c_str(),
-						message.c_str(),
-						LLStringUtil::null,		// default arg
-						IM_NOTHING_SPECIAL,		// default arg
-						0,						// default arg
-						LLUUID::null,			// default arg
-						LLVector3::zero,		// default arg
-						true);					// prepend name and make it a link to the user's profile
-
-				chat.mText = std::string("IM: ") + session->mName + std::string(": ") + message;
-				// If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
-				LLFloaterChat::addChat( chat, TRUE, quiet_chat );
-			}
-		}		
-	}
-}
-
-void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
-{
-	sessionState *session = findSession(sessionHandle);
-	
-	if(session)
-	{
-		participantState *participant = session->findParticipant(uriString);
-		if(participant)
-		{
-			if (!stricmp(notificationType.c_str(), "Typing"))
-			{
-				// Other end started typing
-				// TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
-				// It requires an LLIMInfo for the message, which we don't have here.
-			}
-			else if (!stricmp(notificationType.c_str(), "NotTyping"))
-			{
-				// Other end stopped typing
-				// TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
-				// It requires an LLIMInfo for the message, which we don't have here.
-			}
-			else
-			{
-				LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
-			}
-		}
-		else
-		{
-			LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
-		}
-	}
-	else
-	{
-		LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
-	}
-}
-
-void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
-{
-	buddyListEntry *buddy = findBuddy(buddyURI);
-	
-	if(!buddy)
-	{
-		// Couldn't find buddy by URI, try converting the alias...
-		if(!alias.empty())
-		{
-			LLUUID id;
-			if(IDFromName(alias, id))
-			{
-				buddy = findBuddy(id);
-			}
-		}
-	}
-	
-	if(buddy)
-	{
-		std::ostringstream stream;
-		
-		if(buddy->mCanSeeMeOnline)
-		{
-			// Sending the response will create an auto-accept rule
-			buddy->mHasAutoAcceptListEntry = true;
-		}
-		else
-		{
-			// Sending the response will create a block rule
-			buddy->mHasBlockListEntry = true;
-		}
-		
-		if(buddy->mInSLFriends)
-		{
-			buddy->mInVivoxBuddies = true;
-		}
-		
-		stream
-			<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">"
-				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
-				<< "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
-				<< "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
-				<< "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
-			<< "</Request>"
-			<< "\n\n\n";
-			
-		writeString(stream.str());
-	}
-}
-
-void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
-{
-	LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL;
-	mTuningEnergy = energy;
-}
-
-void LLVoiceClient::buddyListChanged()
-{
-	// This is called after we receive a BuddyAndGroupListChangedEvent.
-	mBuddyListMapPopulated = true;
-	mFriendsListDirty = true;
-}
-
-void LLVoiceClient::muteListChanged()
-{
-	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
-	if(mAudioSession)
-	{
-		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
-		
-		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
-		{
-			participantState *p = iter->second;
-			
-			// Check to see if this participant is on the mute list already
-			if(p->updateMuteState())
-				mAudioSession->mVolumeDirty = true;
-		}
-	}
-}
-
-void LLVoiceClient::updateFriends(U32 mask)
-{
-	if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
-	{
-		// Just resend the whole friend list to the daemon
-		mFriendsListDirty = true;
-	}
-}
-
-/////////////////////////////
-// Managing list of participants
-LLVoiceClient::participantState::participantState(const std::string &uri) : 
-	 mURI(uri), 
-	 mPTT(false), 
-	 mIsSpeaking(false), 
-	 mIsModeratorMuted(false), 
-	 mLastSpokeTimestamp(0.f), 
-	 mPower(0.f), 
-	 mVolume(-1), 
-	 mOnMuteList(false), 
-	 mUserVolume(-1), 
-	 mVolumeDirty(false), 
-	 mAvatarIDValid(false),
-	 mIsSelf(false)
-{
-}
-
-LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri)
-{
-	participantState *result = NULL;
-	bool useAlternateURI = false;
-	
-	// Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it
-	// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
-	{
-		participantMap::iterator iter = mParticipantsByURI.find(&uri);
-
-		if(iter == mParticipantsByURI.end())
-		{
-			if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
-			{
-				// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
-				// Use mSIPURI instead, since it will be properly encoded.
-				iter = mParticipantsByURI.find(&(mSIPURI));
-				useAlternateURI = true;
-			}
-		}
-
-		if(iter != mParticipantsByURI.end())
-		{
-			result = iter->second;
-		}
-	}
-		
-	if(!result)
-	{
-		// participant isn't already in one list or the other.
-		result = new participantState(useAlternateURI?mSIPURI:uri);
-		mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result));
-		mParticipantsChanged = true;
-		
-		// Try to do a reverse transform on the URI to get the GUID back.
-		{
-			LLUUID id;
-			if(IDFromName(result->mURI, id))
-			{
-				result->mAvatarIDValid = true;
-				result->mAvatarID = id;
-
-				if(result->updateMuteState())
-					mVolumeDirty = true;
-			}
-			else
-			{
-				// Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
-				// This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
-				setUUIDFromStringHash(result->mAvatarID, uri);
-			}
-		}
-		
-		mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
-		
-		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
-	}
-	
-	return result;
-}
-
-bool LLVoiceClient::participantState::updateMuteState()
-{
-	bool result = false;
-	
-	if(mAvatarIDValid)
-	{
-		bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
-		if(mOnMuteList != isMuted)
-		{
-			mOnMuteList = isMuted;
-			mVolumeDirty = true;
-			result = true;
-		}
-	}
-	return result;
-}
-
-bool LLVoiceClient::participantState::isAvatar()
-{
-	return mAvatarIDValid;
-}
-
-void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant)
-{
-	if(participant)
-	{
-		participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI));
-		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID));
-		
-		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
-		
-		if(iter == mParticipantsByURI.end())
-		{
-			LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
-		}
-		else if(iter2 == mParticipantsByUUID.end())
-		{
-			LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
-		}
-		else if(iter->second != iter2->second)
-		{
-			LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
-		}
-		else
-		{
-			mParticipantsByURI.erase(iter);
-			mParticipantsByUUID.erase(iter2);
-			
-			delete participant;
-			mParticipantsChanged = true;
-		}
-	}
-}
-
-void LLVoiceClient::sessionState::removeAllParticipants()
-{
-	LL_DEBUGS("Voice") << "called" << LL_ENDL;
-
-	while(!mParticipantsByURI.empty())
-	{
-		removeParticipant(mParticipantsByURI.begin()->second);
-	}
-	
-	if(!mParticipantsByUUID.empty())
-	{
-		LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL;
-	}
-}
-
-LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
-{
-	participantMap *result = NULL;
-	if(mAudioSession)
-	{
-		result = &(mAudioSession->mParticipantsByURI);
-	}
-	return result;
-}
-
-
-LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
-{
-	participantState *result = NULL;
-	
-	participantMap::iterator iter = mParticipantsByURI.find(&uri);
-
-	if(iter == mParticipantsByURI.end())
-	{
-		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
-		{
-			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
-			// Look up the other URI
-			iter = mParticipantsByURI.find(&(mSIPURI));
-		}
-	}
-
-	if(iter != mParticipantsByURI.end())
-	{
-		result = iter->second;
-	}
-		
-	return result;
-}
-
-LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
-{
-	participantState * result = NULL;
-	participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id);
-
-	if(iter != mParticipantsByUUID.end())
-	{
-		result = iter->second;
-	}
-
-	return result;
-}
-
-LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
-{
-	participantState * result = NULL;
-	
-	if(mAudioSession)
-	{
-		result = mAudioSession->findParticipantByID(id);
-	}
-	
-	return result;
-}
-
-
-void LLVoiceClient::parcelChanged()
-{
-	if(getState() >= stateNoChannel)
-	{
-		// If the user is logged in, start a channel lookup.
-		LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
-
-		std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
-		LLSD data;
-		LLHTTPClient::post(
-			url,
-			data,
-			new LLVoiceClientCapResponder);
-	}
-	else
-	{
-		// The transition to stateNoChannel needs to kick this off again.
-		LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
-	}
-}
-
-void LLVoiceClient::switchChannel(
-	std::string uri,
-	bool spatial,
-	bool no_reconnect,
-	bool is_p2p,
-	std::string hash)
-{
-	bool needsSwitch = false;
-	
-	LL_DEBUGS("Voice") 
-		<< "called in state " << state2string(getState()) 
-		<< " with uri \"" << uri << "\"" 
-		<< (spatial?", spatial is true":", spatial is false")
-		<< LL_ENDL;
-	
-	switch(getState())
-	{
-		case stateJoinSessionFailed:
-		case stateJoinSessionFailedWaiting:
-		case stateNoChannel:
-			// Always switch to the new URI from these states.
-			needsSwitch = true;
-		break;
-
-		default:
-			if(mSessionTerminateRequested)
-			{
-				// If a terminate has been requested, we need to compare against where the URI we're already headed to.
-				if(mNextAudioSession)
-				{
-					if(mNextAudioSession->mSIPURI != uri)
-						needsSwitch = true;
-				}
-				else
-				{
-					// mNextAudioSession is null -- this probably means we're on our way back to spatial.
-					if(!uri.empty())
-					{
-						// We do want to process a switch in this case.
-						needsSwitch = true;
-					}
-				}
-			}
-			else
-			{
-				// Otherwise, compare against the URI we're in now.
-				if(mAudioSession)
-				{
-					if(mAudioSession->mSIPURI != uri)
-					{
-						needsSwitch = true;
-					}
-				}
-				else
-				{
-					if(!uri.empty())
-					{
-						// mAudioSession is null -- it's not clear what case would cause this.
-						// For now, log it as a warning and see if it ever crops up.
-						LL_WARNS("Voice") << "No current audio session." << LL_ENDL;
-					}
-				}
-			}
-		break;
-	}
-	
-	if(needsSwitch)
-	{
-		if(uri.empty())
-		{
-			// Leave any channel we may be in
-			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
-
-			sessionState *oldSession = mNextAudioSession;
-			mNextAudioSession = NULL;
-
-			// The old session may now need to be deleted.
-			reapSession(oldSession);
-
-			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
-		}
-		else
-		{
-			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
-
-			mNextAudioSession = addSession(uri);
-			mNextAudioSession->mHash = hash;
-			mNextAudioSession->mIsSpatial = spatial;
-			mNextAudioSession->mReconnect = !no_reconnect;
-			mNextAudioSession->mIsP2P = is_p2p;
-		}
-		
-		if(getState() <= stateNoChannel)
-		{
-			// We're already set up to join a channel, just needed to fill in the session URI
-		}
-		else
-		{
-			// State machine will come around and rejoin if uri/handle is not empty.
-			sessionTerminate();
-		}
-	}
-}
-
-void LLVoiceClient::joinSession(sessionState *session)
-{
-	mNextAudioSession = session;
-	
-	if(getState() <= stateNoChannel)
-	{
-		// We're already set up to join a channel, just needed to fill in the session handle
-	}
-	else
-	{
-		// State machine will come around and rejoin if uri/handle is not empty.
-		sessionTerminate();
-	}
-}
-
-void LLVoiceClient::setNonSpatialChannel(
-	const std::string &uri,
-	const std::string &credentials)
-{
-	switchChannel(uri, false, false, false, credentials);
-}
-
-void LLVoiceClient::setSpatialChannel(
-	const std::string &uri,
-	const std::string &credentials)
-{
-	mSpatialSessionURI = uri;
-	mSpatialSessionCredentials = credentials;
-	mAreaVoiceDisabled = mSpatialSessionURI.empty();
-
-	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
-	
-	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
-	{
-		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
-		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
-	}
-	else
-	{
-		switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
-	}
-}
-
-void LLVoiceClient::callUser(const LLUUID &uuid)
-{
-	std::string userURI = sipURIFromID(uuid);
-
-	switchChannel(userURI, false, true, true);
-}
-
-LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid)
-{
-	// Figure out if a session with the user already exists
-	sessionState *session = findSession(uuid);
-	if(!session)
-	{
-		// No session with user, need to start one.
-		std::string uri = sipURIFromID(uuid);
-		session = addSession(uri);
-		session->mIsSpatial = false;
-		session->mReconnect = false;	
-		session->mIsP2P = true;
-		session->mCallerID = uuid;
-	}
-	
-	if(session)
-	{
-		if(session->mHandle.empty())
-		{
-			// Session isn't active -- start it up.
-			sessionCreateSendMessage(session, false, true);
-		}
-		else
-		{	
-			// Session is already active -- start up text.
-			sessionTextConnectSendMessage(session);
-		}
-	}
-	
-	return session;
-}
-
-bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
-{
-	bool result = false;
-
-	// Attempt to locate the indicated session
-	sessionState *session = startUserIMSession(participant_id);
-	if(session)
-	{
-		// found the session, attempt to send the message
-		session->mTextMsgQueue.push(message);
-		
-		// Try to send queued messages (will do nothing if the session is not open yet)
-		sendQueuedTextMessages(session);
-
-		// The message is queued, so we succeed.
-		result = true;
-	}	
-	else
-	{
-		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
-	}
-	
-	return result;
-}
-
-void LLVoiceClient::sendQueuedTextMessages(sessionState *session)
-{
-	if(session->mTextStreamState == 1)
-	{
-		if(!session->mTextMsgQueue.empty())
-		{
-			std::ostringstream stream;
-			
-			while(!session->mTextMsgQueue.empty())
-			{
-				std::string message = session->mTextMsgQueue.front();
-				session->mTextMsgQueue.pop();
-				stream
-				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
-					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-					<< "<MessageHeader>text/HTML</MessageHeader>"
-					<< "<MessageBody>" << message << "</MessageBody>"
-				<< "</Request>"
-				<< "\n\n\n";
-			}		
-			writeString(stream.str());
-		}
-	}
-	else
-	{
-		// Session isn't connected yet, defer until later.
-	}
-}
-
-void LLVoiceClient::endUserIMSession(const LLUUID &uuid)
-{
-	// Figure out if a session with the user exists
-	sessionState *session = findSession(uuid);
-	if(session)
-	{
-		// found the session
-		if(!session->mHandle.empty())
-		{
-			sessionTextDisconnectSendMessage(session);
-		}
-	}	
-	else
-	{
-		LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
-	}
-}
-
-bool LLVoiceClient::answerInvite(std::string &sessionHandle)
-{
-	// this is only ever used to answer incoming p2p call invites.
-	
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		session->mIsSpatial = false;
-		session->mReconnect = false;	
-		session->mIsP2P = true;
-
-		joinSession(session);
-		return true;
-	}
-	
-	return false;
-}
-
-bool LLVoiceClient::isOnlineSIP(const LLUUID &id)
-{
-	bool result = false;
-	buddyListEntry *buddy = findBuddy(id);
-	if(buddy)
-	{
-		result = buddy->mOnlineSLim;
-		LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
-	}
-
-	if(!result)
-	{
-		// 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.
-		sessionState *session = findSession(id);
-		if(session && !session->mHandle.empty())
-		{
-			if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
-			{
-				LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
-				// we have a p2p text session open with this user, so by definition they're online.
-				result = true;
-			}
-		}
-	}
-	
-	return result;
-}
-
-// Returns true if the indicated participant in the current audio session is really an SL avatar.
-// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls.
-bool LLVoiceClient::isParticipantAvatar(const LLUUID &id)
-{
-	bool result = true; 
-	sessionState *session = findSession(id);
-	
-	if(session != NULL)
-	{
-		// this is a p2p session with the indicated caller, or the session with the specified UUID.
-		if(session->mSynthesizedCallerID)
-			result = false;
-	}
-	else
-	{
-		// Didn't find a matching session -- check the current audio session for a matching participant
-		if(mAudioSession != NULL)
-		{
-			participantState *participant = findParticipantByID(id);
-			if(participant != NULL)
-			{
-				result = participant->isAvatar();
-			}
-		}
-	}
-	
-	return result;
-}
-
-// Returns true if calling back the session URI after the session has closed is possible.
-// Currently this will be false only for PSTN P2P calls.		
-bool LLVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
-{
-	bool result = true; 
-	sessionState *session = findSession(session_id);
-	
-	if(session != NULL)
-	{
-		result = session->isCallBackPossible();
-	}
-	
-	return result;
-}
-
-// Returns true if the session can accepte text IM's.
-// Currently this will be false only for PSTN P2P calls.
-bool LLVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
-{
-	bool result = true; 
-	sessionState *session = findSession(session_id);
-	
-	if(session != NULL)
-	{
-		result = session->isTextIMPossible();
-	}
-	
-	return result;
-}
-		
-
-void LLVoiceClient::declineInvite(std::string &sessionHandle)
-{
-	sessionState *session = findSession(sessionHandle);
-	if(session)
-	{
-		sessionMediaDisconnectSendMessage(session);
-	}
-}
-
-void LLVoiceClient::leaveNonSpatialChannel()
-{
-	LL_DEBUGS("Voice") 
-		<< "called in state " << state2string(getState()) 
-		<< LL_ENDL;
-	
-	// Make sure we don't rejoin the current session.	
-	sessionState *oldNextSession = mNextAudioSession;
-	mNextAudioSession = NULL;
-	
-	// Most likely this will still be the current session at this point, but check it anyway.
-	reapSession(oldNextSession);
-	
-	verifySessionState();
-	
-	sessionTerminate();
-}
-
-std::string LLVoiceClient::getCurrentChannel()
-{
-	std::string result;
-	
-	if((getState() == stateRunning) && !mSessionTerminateRequested)
-	{
-		result = getAudioSessionURI();
-	}
-	
-	return result;
-}
-
-bool LLVoiceClient::inProximalChannel()
-{
-	bool result = false;
-	
-	if((getState() == stateRunning) && !mSessionTerminateRequested)
-	{
-		result = inSpatialChannel();
-	}
-	
-	return result;
-}
-
-std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
-{
-	std::string result;
-	result = "sip:";
-	result += nameFromID(id);
-	result += "@";
-	result += mVoiceSIPURIHostName;
-	
-	return result;
-}
-
-std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
-{
-	std::string result;
-	if(avatar)
-	{
-		result = "sip:";
-		result += nameFromID(avatar->getID());
-		result += "@";
-		result += mVoiceSIPURIHostName;
-	}
-	
-	return result;
-}
-
-std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
-{
-	std::string result;
-	if(avatar)
-	{
-		result = nameFromID(avatar->getID());
-	}	
-	return result;
-}
-
-std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
-{
-	std::string result;
-	
-	if (uuid.isNull()) {
-		//VIVOX, the uuid emtpy look for the mURIString and return that instead.
-		//result.assign(uuid.mURIStringName);
-		LLStringUtil::replaceChar(result, '_', ' ');
-		return result;
-	}
-	// Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
-	result = "x";
-	
-	// Base64 encode and replace the pieces of base64 that are less compatible 
-	// with e-mail local-parts.
-	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
-	result += LLBase64::encode(uuid.mData, UUID_BYTES);
-	LLStringUtil::replaceChar(result, '+', '-');
-	LLStringUtil::replaceChar(result, '/', '_');
-	
-	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
-	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
-	
-	// The reverse transform can be done with:
-	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
-	
-	return result;
-}
+const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
 
-bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
+std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
 {
-	bool result = false;
-	
-	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
-	// If it is, convert to a bare name before doing the transform.
-	std::string name = nameFromsipURI(inName);
-	
-	// Doesn't look like a SIP URI, assume it's an actual name.
-	if(name.empty())
-		name = inName;
-
-	// This will only work if the name is of the proper form.
-	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
-	// "xFnPP04IpREWNkuw1cOXlhw=="
-	
-	if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
-	{
-		// The name appears to have the right form.
-
-		// Reverse the transforms done by nameFromID
-		std::string temp = name;
-		LLStringUtil::replaceChar(temp, '-', '+');
-		LLStringUtil::replaceChar(temp, '_', '/');
-
-		U8 rawuuid[UUID_BYTES + 1]; 
-		int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
-		if(len == UUID_BYTES)
-		{
-			// The decode succeeded.  Stuff the bits into the result's UUID
-			memcpy(uuid.mData, rawuuid, UUID_BYTES);
-			result = true;
-		}
-	} 
+	std::string result = "UNKNOWN";
 	
-	if(!result)
-	{
-		// VIVOX:  not a standard account name, just copy the URI name mURIString field
-		// and hope for the best.  bpj
-		uuid.setNull();  // VIVOX, set the uuid field to nulls
-	}
+	// Prevent copy-paste errors when updating this list...
+#define CASE(x)  case x:  result = #x;  break
 	
-	return result;
-}
-
-std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
-{
-	return avatar->getFullname();
-}
-
-std::string LLVoiceClient::sipURIFromName(std::string &name)
-{
-	std::string result;
-	result = "sip:";
-	result += name;
-	result += "@";
-	result += mVoiceSIPURIHostName;
-
-//	LLStringUtil::toLower(result);
-
-	return result;
-}
-
-std::string LLVoiceClient::nameFromsipURI(const std::string &uri)
-{
-	std::string result;
-
-	std::string::size_type sipOffset, atOffset;
-	sipOffset = uri.find("sip:");
-	atOffset = uri.find("@");
-	if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
+	switch(inStatus)
 	{
-		result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
+			CASE(STATUS_LOGIN_RETRY);
+			CASE(STATUS_LOGGED_IN);
+			CASE(STATUS_JOINING);
+			CASE(STATUS_JOINED);
+			CASE(STATUS_LEFT_CHANNEL);
+			CASE(STATUS_VOICE_DISABLED);
+			CASE(BEGIN_ERROR_STATUS);
+			CASE(ERROR_CHANNEL_FULL);
+			CASE(ERROR_CHANNEL_LOCKED);
+			CASE(ERROR_NOT_AVAILABLE);
+			CASE(ERROR_UNKNOWN);
+		default:
+			break;
 	}
 	
-	return result;
-}
-
-bool LLVoiceClient::inSpatialChannel(void)
-{
-	bool result = false;
+#undef CASE
 	
-	if(mAudioSession)
-		result = mAudioSession->mIsSpatial;
-		
 	return result;
 }
 
-std::string LLVoiceClient::getAudioSessionURI()
-{
-	std::string result;
-	
-	if(mAudioSession)
-		result = mAudioSession->mSIPURI;
-		
-	return result;
-}
 
-std::string LLVoiceClient::getAudioSessionHandle()
-{
-	std::string result;
-	
-	if(mAudioSession)
-		result = mAudioSession->mHandle;
-		
-	return result;
-}
 
 
-/////////////////////////////
-// Sending updates of current state
+///////////////////////////////////////////////////////////////////////////////////////////////
 
-void LLVoiceClient::enforceTether(void)
+LLVoiceClient::LLVoiceClient()
 {
-	LLVector3d tethered	= mCameraRequestedPosition;
-
-	// constrain 'tethered' to within 50m of mAvatarPosition.
-	{
-		F32 max_dist = 50.0f;
-		LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
-		F32 camera_distance = (F32)camera_offset.magVec();
-		if(camera_distance > max_dist)
-		{
-			tethered = mAvatarPosition + 
-				(max_dist / camera_distance) * camera_offset;
-		}
-	}
-	
-	if(dist_vec(mCameraPosition, tethered) > 0.1)
-	{
-		mCameraPosition = tethered;
-		mSpatialCoordsDirty = true;
-	}
+	mVoiceModule = NULL;
 }
 
-void LLVoiceClient::updatePosition(void)
-{
-	
-	if(gVoiceClient)
-	{
-		LLVOAvatar *agent = gAgent.getAvatarObject();
-		LLViewerRegion *region = gAgent.getRegion();
-		if(region && agent)
-		{
-			LLMatrix3 rot;
-			LLVector3d pos;
-
-			// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
-			// They're currently always set to zero.
-
-			// Send the current camera position to the voice code
-			rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());		
-			pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin());
-			
-			gVoiceClient->setCameraPosition(
-					pos,				// position
-					LLVector3::zero, 	// velocity
-					rot);				// rotation matrix
-					
-			// Send the current avatar position to the voice code
-			rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
-	
-			pos = agent->getPositionGlobal();
-			// TODO: Can we get the head offset from outside the LLVOAvatar?
-//			pos += LLVector3d(mHeadOffset);
-			pos += LLVector3d(0.f, 0.f, 1.f);
-		
-			gVoiceClient->setAvatarPosition(
-					pos,				// position
-					LLVector3::zero, 	// velocity
-					rot);				// rotation matrix
-		}
-	}
-}
+//---------------------------------------------------
+// Basic setup/shutdown
 
-void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+LLVoiceClient::~LLVoiceClient()
 {
-	mCameraRequestedPosition = position;
-	
-	if(mCameraVelocity != velocity)
-	{
-		mCameraVelocity = velocity;
-		mSpatialCoordsDirty = true;
-	}
-	
-	if(mCameraRot != rot)
-	{
-		mCameraRot = rot;
-		mSpatialCoordsDirty = true;
-	}
 }
 
-void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+void LLVoiceClient::init(LLPumpIO *pump)
 {
-	if(dist_vec(mAvatarPosition, position) > 0.1)
-	{
-		mAvatarPosition = position;
-		mSpatialCoordsDirty = true;
-	}
-	
-	if(mAvatarVelocity != velocity)
-	{
-		mAvatarVelocity = velocity;
-		mSpatialCoordsDirty = true;
-	}
-	
-	if(mAvatarRot != rot)
-	{
-		mAvatarRot = rot;
-		mSpatialCoordsDirty = true;
-	}
+	// Initialize all of the voice modules
+	m_servicePump = pump;
 }
 
-bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
+void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)
 {
-	bool result = false;
-	
-	if(region)
+	// In the future, we should change this to allow voice module registration
+	// with a table lookup of sorts.
+	std::string voice_server = gSavedSettings.getString("VoiceServerType");
+	LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL;
+	if(voice_server == "diamondware")
 	{
-		name = region->getName();
+		mVoiceModule = (LLVoiceModuleInterface *)LLDiamondwareVoiceClient::getInstance();
 	}
-	
-	if(!name.empty())
-		result = true;
-	
-	return result;
-}
-
-void LLVoiceClient::leaveChannel(void)
-{
-	if(getState() == stateRunning)
+	else if(voice_server == "vivox")
 	{
-		LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;
-		mChannelName.clear();
-		sessionTerminate();
+		mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance();
 	}
-}
-
-void LLVoiceClient::setMuteMic(bool muted)
-{
-	mMuteMic = muted;
-}
-
-bool LLVoiceClient::getMuteMic() const
-{
-	return mMuteMic;
-}
-
-void LLVoiceClient::setUserPTTState(bool ptt)
-{
-	mUserPTTState = ptt;
-}
-
-bool LLVoiceClient::getUserPTTState()
-{
-	return mUserPTTState;
-}
-
-void LLVoiceClient::toggleUserPTTState(void)
-{
-	mUserPTTState = !mUserPTTState;
-}
-
-void LLVoiceClient::setVoiceEnabled(bool enabled)
-{
-	if (enabled != mVoiceEnabled)
+	else
 	{
-		mVoiceEnabled = enabled;
-		LLVoiceClientStatusObserver::EStatusType status;
-
-		if (enabled)
-		{
-			LLVoiceChannel::getCurrentVoiceChannel()->activate();
-			status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
-		}
-		else
-		{
-			// Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it.
-			LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
-			status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
-		}
-
-		notifyStatusObservers(status);
+		mVoiceModule = NULL;
+		return; 
 	}
+	mVoiceModule->init(m_servicePump);	
+	mVoiceModule->userAuthorized(user_id, agentID);
 }
 
-bool LLVoiceClient::voiceEnabled()
-{
-	return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
-}
 
-void LLVoiceClient::setLipSyncEnabled(BOOL enabled)
+void LLVoiceClient::terminate()
 {
-	mLipSyncEnabled = enabled;
+	if (mVoiceModule) mVoiceModule->terminate();
+	mVoiceModule = NULL;
 }
 
-BOOL LLVoiceClient::lipSyncEnabled()
+const LLVoiceVersionInfo LLVoiceClient::getVersion()
 {
-	   
-	if ( mVoiceEnabled && stateDisabled != getState() )
+	if (mVoiceModule) 
 	{
-		return mLipSyncEnabled;
+		return mVoiceModule->getVersion();
 	}
 	else
 	{
-		return FALSE;
+		LLVoiceVersionInfo result;
+		result.serverVersion = std::string();
+		result.serverType = std::string();
+		return result;
 	}
 }
 
-void LLVoiceClient::setUsePTT(bool usePTT)
+void LLVoiceClient::updateSettings()
 {
-	if(usePTT && !mUsePTT)
-	{
-		// When the user turns on PTT, reset the current state.
-		mUserPTTState = false;
-	}
-	mUsePTT = usePTT;
+	if (mVoiceModule) mVoiceModule->updateSettings();
 }
 
-void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
+//--------------------------------------------------
+// tuning
+
+void LLVoiceClient::tuningStart()
 {
-	if(!PTTIsToggle && mPTTIsToggle)
-	{
-		// When the user turns off toggle, reset the current state.
-		mUserPTTState = false;
-	}
-	
-	mPTTIsToggle = PTTIsToggle;
+	if (mVoiceModule) mVoiceModule->tuningStart();
 }
 
-bool LLVoiceClient::getPTTIsToggle()
+void LLVoiceClient::tuningStop()
 {
-	return mPTTIsToggle;
+	if (mVoiceModule) mVoiceModule->tuningStop();
 }
 
-void LLVoiceClient::setPTTKey(std::string &key)
+bool LLVoiceClient::inTuningMode()
 {
-	if(key == "MiddleMouse")
+	if (mVoiceModule) 
 	{
-		mPTTIsMiddleMouse = true;
+		return mVoiceModule->inTuningMode();
 	}
 	else
 	{
-		mPTTIsMiddleMouse = false;
-		if(!LLKeyboard::keyFromString(key, &mPTTKey))
-		{
-			// If the call failed, don't match any key.
-			key = KEY_NONE;
-		}
+		return false;
 	}
 }
 
-void LLVoiceClient::setEarLocation(S32 loc)
+void LLVoiceClient::tuningSetMicVolume(float volume)
 {
-	if(mEarLocation != loc)
-	{
-		LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
-		
-		mEarLocation = loc;
-		mSpatialCoordsDirty = true;
-	}
+	if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume);
 }
 
-void LLVoiceClient::setVoiceVolume(F32 volume)
+void LLVoiceClient::tuningSetSpeakerVolume(float volume)
 {
-	int scaled_volume = scale_speaker_volume(volume);	
-
-	if(scaled_volume != mSpeakerVolume)
-	{
-		if((scaled_volume == 0) || (mSpeakerVolume == 0))
-		{
-			mSpeakerMuteDirty = true;
-		}
-
-		mSpeakerVolume = scaled_volume;
-		mSpeakerVolumeDirty = true;
-	}
+	if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume);
 }
 
-void LLVoiceClient::setMicGain(F32 volume)
+float LLVoiceClient::tuningGetEnergy(void)
 {
-	int scaled_volume = scale_mic_volume(volume);
-	
-	if(scaled_volume != mMicVolume)
-	{
-		mMicVolume = scaled_volume;
-		mMicVolumeDirty = true;
-	}
-}
-
-void LLVoiceClient::keyDown(KEY key, MASK mask)
-{	
-	if (gKeyboard->getKeyRepeated(key))
-	{
-		// ignore auto-repeat keys
-		return;
-	}
-
-	if(!mPTTIsMiddleMouse)
+	if (mVoiceModule) 
 	{
-		bool down = (mPTTKey != KEY_NONE)
-			&& gKeyboard->getKeyDown(mPTTKey);
-		inputUserControlState(down);
+		return mVoiceModule->tuningGetEnergy();
 	}
-}
-void LLVoiceClient::keyUp(KEY key, MASK mask)
-{
-	if(!mPTTIsMiddleMouse)
+	else
 	{
-		bool down = (mPTTKey != KEY_NONE)
-			&& gKeyboard->getKeyDown(mPTTKey);
-		inputUserControlState(down);
+		return 0.0;
 	}
 }
-void LLVoiceClient::inputUserControlState(bool down)
+
+
+//------------------------------------------------
+// devices
+
+bool LLVoiceClient::deviceSettingsAvailable()
 {
-	if(mPTTIsToggle)
+	if (mVoiceModule) 
 	{
-		if(down) // toggle open-mic state on 'down'
-		{
-			toggleUserPTTState();
-		}
+		return mVoiceModule->deviceSettingsAvailable();
 	}
-	else // set open-mic state as an absolute
+	else
 	{
-		setUserPTTState(down);
+		return false;
 	}
 }
-void LLVoiceClient::middleMouseState(bool down)
+
+void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
 {
-	if(mPTTIsMiddleMouse)
-	{
-		inputUserControlState(down);
-	}
+	if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList);
 }
 
-/////////////////////////////
-// Accessors for data related to nearby speakers
-BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
+void LLVoiceClient::setCaptureDevice(const std::string& name)
 {
-	BOOL result = FALSE;
-	participantState *participant = findParticipantByID(id);
-	if(participant)
-	{
-		// I'm not sure what the semantics of this should be.
-		// For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
-		result = TRUE;
-	}
+	if (mVoiceModule) mVoiceModule->setCaptureDevice(name);
 	
-	return result;
 }
 
-BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
+void LLVoiceClient::setRenderDevice(const std::string& name)
 {
-	BOOL result = FALSE;
-
-	participantState *participant = findParticipantByID(id);
-	if(participant)
-	{
-		if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
-		{
-			participant->mIsSpeaking = FALSE;
-		}
-		result = participant->mIsSpeaking;
-	}
-	
-	return result;
+	if (mVoiceModule) mVoiceModule->setRenderDevice(name);	
 }
 
-BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
+const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices()
 {
-	BOOL result = FALSE;
-
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	static LLVoiceDeviceList nullCaptureDevices;
+	if (mVoiceModule) 
 	{
-		result = participant->mIsModeratorMuted;
+		return mVoiceModule->getCaptureDevices();
 	}
-	
-	return result;
-}
-
-F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
-{		
-	F32 result = 0;
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	else
 	{
-		result = participant->mPower;
+		return nullCaptureDevices;
 	}
-	
-	return result;
 }
 
 
-std::string LLVoiceClient::getDisplayName(const LLUUID& id)
+const LLVoiceDeviceList& LLVoiceClient::getRenderDevices()
 {
-	std::string result;
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	static LLVoiceDeviceList nullRenderDevices;	
+	if (mVoiceModule) 
 	{
-		result = participant->mDisplayName;
+		return mVoiceModule->getRenderDevices();
 	}
-	
-	return result;
-}
-
-
-BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
-{
-	BOOL result = FALSE;
-
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	else
 	{
-		// I'm not sure what the semantics of this should be.
-		// Does "using PTT" mean they're configured with a push-to-talk button?
-		// For now, we know there's no PTT mechanism in place, so nobody is using it.
+		return nullRenderDevices;
 	}
-	
-	return result;
 }
 
-BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
-{
-	BOOL result = FALSE;
-	
-	participantState *participant = findParticipantByID(id);
-	if(participant)
-	{
-		result = participant->mOnMuteList;
-	}
 
-	return result;
-}
+//--------------------------------------------------
+// participants
 
-// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
-// internal = 400 * external^2
-F32 LLVoiceClient::getUserVolume(const LLUUID& id)
+std::vector<LLUUID> LLVoiceClient::getParticipantList(void)
 {
-	F32 result = 0.0f;
-	
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	if (mVoiceModule) 
 	{
-		S32 ires = 100; // nominal default volume
-		
-		if(participant->mIsSelf)
-		{
-			// Always make it look like the user's own volume is set at the default.
-		}
-		else if(participant->mUserVolume != -1)
-		{
-			// Use the internal volume
-			ires = participant->mUserVolume;
-			
-			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
-//			LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL;
-		}
-		else if(participant->mVolume != -1)
-		{
-			// Map backwards from vivox volume 
-
-			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
-//			LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL;
-
-			if(participant->mVolume < 56)
-			{
-				ires = (participant->mVolume * 100) / 56;
-			}
-			else
-			{
-				ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100;
-			}
-		}
-		result = sqrtf(((F32)ires) / 400.f);
+		return mVoiceModule->getParticipantList();
 	}
-
-	// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
-//	LL_DEBUGS("Voice") << "returning " << result << LL_ENDL;
-
-	return result;
-}
-
-void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
-{
-	if(mAudioSession)
+	else
 	{
-		participantState *participant = findParticipantByID(id);
-		if (participant)
-		{
-			// volume can amplify by as much as 4x!
-			S32 ivol = (S32)(400.f * volume * volume);
-			participant->mUserVolume = llclamp(ivol, 0, 400);
-			participant->mVolumeDirty = TRUE;
-			mAudioSession->mVolumeDirty = TRUE;
-		}
+		return std::vector<LLUUID>();
 	}
 }
 
-std::string LLVoiceClient::getGroupID(const LLUUID& id)
-{
-	std::string result;
-
-	participantState *participant = findParticipantByID(id);
-	if(participant)
-	{
-		result = participant->mGroupID;
-	}
-	
-	return result;
-}
+//--------------------------------------------------
+// text chat
 
-BOOL LLVoiceClient::getAreaVoiceDisabled()
-{
-	return mAreaVoiceDisabled;
-}
 
-void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
+BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id)
 {
-//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
-	
-	if(!mMainSessionGroupHandle.empty())
+	if (mVoiceModule) 
 	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
-		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
-		<< "<RecordingControlType>Start</RecordingControlType>" 
-		<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
-		<< "<Filename>" << "" << "</Filename>"
-		<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
-		<< "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
-		<< "</Request>\n\n\n";
-
-
-		writeString(stream.str());
+		return mVoiceModule->isSessionTextIMPossible(id);
 	}
-}
-
-void LLVoiceClient::recordingLoopSave(const std::string& filename)
-{
-//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
-
-	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	else
 	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
-		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
-		<< "<RecordingControlType>Flush</RecordingControlType>" 
-		<< "<Filename>" << filename << "</Filename>"
-		<< "</Request>\n\n\n";
-
-		writeString(stream.str());
-	}
+		return FALSE;
+	}	
 }
 
-void LLVoiceClient::recordingStop()
+BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id)
 {
-//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
-
-	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	if (mVoiceModule) 
 	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
-		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
-		<< "<RecordingControlType>Stop</RecordingControlType>" 
-		<< "</Request>\n\n\n";
-
-		writeString(stream.str());
+		return mVoiceModule->isSessionCallBackPossible(id);
 	}
+	else
+	{
+		return FALSE;
+	}	
 }
 
-void LLVoiceClient::filePlaybackStart(const std::string& filename)
+BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
 {
-//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
-
-	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	if (mVoiceModule) 
 	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
-		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
-		<< "<RecordingControlType>Start</RecordingControlType>" 
-		<< "<Filename>" << filename << "</Filename>"
-		<< "</Request>\n\n\n";
-
-		writeString(stream.str());
+		return mVoiceModule->sendTextMessage(participant_id, message);
 	}
+	else
+	{
+		return FALSE;
+	}	
 }
 
-void LLVoiceClient::filePlaybackStop()
+void LLVoiceClient::endUserIMSession(const LLUUID& participant_id)
 {
-//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
-
-	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	if (mVoiceModule) 
 	{
-		std::ostringstream stream;
-		stream
-		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
-		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
-		<< "<RecordingControlType>Stop</RecordingControlType>" 
-		<< "</Request>\n\n\n";
-
-		writeString(stream.str());
+		mVoiceModule->endUserIMSession(participant_id);
 	}
 }
 
-void LLVoiceClient::filePlaybackSetPaused(bool paused)
-{
-	// TODO: Implement once Vivox gives me a sample
-}
-
-void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
-{
-	// TODO: Implement once Vivox gives me a sample
-}
-
-LLVoiceClient::sessionState::sessionState() :
-	mMediaStreamState(streamStateUnknown),
-	mTextStreamState(streamStateUnknown),
-	mCreateInProgress(false),
-	mMediaConnectInProgress(false),
-	mVoiceInvitePending(false),
-	mTextInvitePending(false),
-	mSynthesizedCallerID(false),
-	mIsChannel(false),
-	mIsSpatial(false),
-	mIsP2P(false),
-	mIncoming(false),
-	mVoiceEnabled(false),
-	mReconnect(false),
-	mVolumeDirty(false),
-	mParticipantsChanged(false)
-{
-}
+//----------------------------------------------
+// channels
 
-LLVoiceClient::sessionState::~sessionState()
+bool LLVoiceClient::inProximalChannel()
 {
-	removeAllParticipants();
+	if (mVoiceModule) 
+	{
+		return mVoiceModule->inProximalChannel();
+	}
+	else
+	{
+		return false;
+	}
 }
 
-bool LLVoiceClient::sessionState::isCallBackPossible()
+void LLVoiceClient::setNonSpatialChannel(
+	const std::string &uri,
+	const std::string &credentials)
 {
-	// This may change to be explicitly specified by vivox in the future...
-	// Currently, only PSTN P2P calls cannot be returned.
-	// Conveniently, this is also the only case where we synthesize a caller UUID.
-	return !mSynthesizedCallerID;
+	if (mVoiceModule) mVoiceModule->setNonSpatialChannel(uri, credentials);
 }
 
-bool LLVoiceClient::sessionState::isTextIMPossible()
+void LLVoiceClient::setSpatialChannel(
+	const std::string &uri,
+	const std::string &credentials)
 {
-	// This may change to be explicitly specified by vivox in the future...
-	return !mSynthesizedCallerID;
+	if (mVoiceModule) mVoiceModule->setSpatialChannel(uri, credentials);
 }
 
-
-LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void)
+void LLVoiceClient::leaveNonSpatialChannel()
 {
-	return mSessions.begin();
+	if (mVoiceModule) mVoiceModule->leaveNonSpatialChannel();
 }
 
-LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void)
+void LLVoiceClient::leaveChannel(void)
 {
-	return mSessions.end();
+	if (mVoiceModule) mVoiceModule->leaveChannel();
 }
 
-
-LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle)
+std::string LLVoiceClient::getCurrentChannel()
 {
-	sessionState *result = NULL;
-	sessionMap::iterator iter = mSessionsByHandle.find(&handle);
-	if(iter != mSessionsByHandle.end())
+	if (mVoiceModule) 
 	{
-		result = iter->second;
+		return mVoiceModule->getCurrentChannel();
 	}
-	
-	return result;
-}
-
-LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
-{	
-	sessionState *result = NULL;
-	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	else
 	{
-		sessionState *session = *iter;
-		if(session->mCreateInProgress && (session->mSIPURI == uri))
-		{
-			result = session;
-			break;
-		}
+		return std::string();
 	}
-	
-	return result;
 }
 
-LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id)
+
+//---------------------------------------
+// invitations
+
+void LLVoiceClient::callUser(const LLUUID &uuid)
 {
-	sessionState *result = NULL;
-	
-	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
-	{
-		sessionState *session = *iter;
-		if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id))
-		{
-			result = session;
-			break;
-		}
-	}
-	
-	return result;
+	if (mVoiceModule) mVoiceModule->callUser(uuid);
 }
 
-LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle)
+bool LLVoiceClient::answerInvite(std::string &channelHandle)
 {
-	sessionState *result = NULL;
-	
-	if(handle.empty())
-	{
-		// No handle supplied.
-		// Check whether there's already a session with this URI
-		for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
-		{
-			sessionState *s = *iter;
-			if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
-			{
-				// TODO: I need to think about this logic... it's possible that this case should raise an internal error.
-				result = s;
-				break;
-			}
-		}
-	}
-	else // (!handle.empty())
-	{
-		// Check for an existing session with this handle
-		sessionMap::iterator iter = mSessionsByHandle.find(&handle);
-		
-		if(iter != mSessionsByHandle.end())
-		{
-			result = iter->second;
-		}
-	}
-
-	if(!result)
+	if (mVoiceModule) 
 	{
-		// No existing session found.
-		
-		LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
-		result = new sessionState();
-		result->mSIPURI = uri;
-		result->mHandle = handle;
-		
-		mSessions.insert(result);
-
-		if(!result->mHandle.empty())
-		{
-			mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result));
-		}
+		return mVoiceModule->answerInvite(channelHandle);
 	}
 	else
 	{
-		// Found an existing session
-		
-		if(uri != result->mSIPURI)
-		{
-			// TODO: Should this be an internal error?
-			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
-			setSessionURI(result, uri);
-		}
-
-		if(handle != result->mHandle)
-		{
-			if(handle.empty())
-			{
-				// There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break.
-				LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL;
-			}
-			else
-			{
-				// TODO: Should this be an internal error?
-				LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
-				setSessionHandle(result, handle);
-			}
-		}
-		
-		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
+		return false;
 	}
-
-	verifySessionState();
-		
-	return result;
 }
 
-void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
+void LLVoiceClient::declineInvite(std::string &channelHandle)
 {
-	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
-	
-	if(!session->mHandle.empty())
-	{
-		// Remove session from the map if it should have been there.
-		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
-		if(iter != mSessionsByHandle.end())
-		{
-			if(iter->second != session)
-			{
-				LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
-			}
+	if (mVoiceModule) mVoiceModule->declineInvite(channelHandle);
+}
 
-			mSessionsByHandle.erase(iter);
-		}
-		else
-		{
-			LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
-		}
-	}
-			
-	session->mHandle = handle;
 
-	if(!handle.empty())
-	{
-		mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session));
-	}
+//------------------------------------------
+// Volume/gain
 
-	verifySessionState();
-}
 
-void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
+void LLVoiceClient::setVoiceVolume(F32 volume)
 {
-	// There used to be a map of session URIs to sessions, which made this complex....
-	session->mSIPURI = uri;
-
-	verifySessionState();
+	if (mVoiceModule) mVoiceModule->setVoiceVolume(volume);
 }
 
-void LLVoiceClient::deleteSession(sessionState *session)
+void LLVoiceClient::setMicGain(F32 volume)
 {
-	// Remove the session from the handle map
-	if(!session->mHandle.empty())
-	{
-		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
-		if(iter != mSessionsByHandle.end())
-		{
-			if(iter->second != session)
-			{
-				LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL;
-			}
-			mSessionsByHandle.erase(iter);
-		}
-	}
+	if (mVoiceModule) mVoiceModule->setMicGain(volume);
+}
 
-	// Remove the session from the URI map
-	mSessions.erase(session);
-	
-	// At this point, the session should be unhooked from all lists and all state should be consistent.
-	verifySessionState();
 
-	// If this is the current audio session, clean up the pointer which will soon be dangling.
-	if(mAudioSession == session)
+//------------------------------------------
+// enable/disable voice features
+
+bool LLVoiceClient::voiceEnabled()
+{
+	if (mVoiceModule) 
 	{
-		mAudioSession = NULL;
-		mAudioSessionChanged = true;
+		return mVoiceModule->voiceEnabled();
 	}
-
-	// ditto for the next audio session
-	if(mNextAudioSession == session)
+	else
 	{
-		mNextAudioSession = NULL;
+		return false;
 	}
+}
 
-	// delete the session
-	delete session;
+void LLVoiceClient::setVoiceEnabled(bool enabled)
+{
+	if (mVoiceModule) mVoiceModule->setVoiceEnabled(enabled);
 }
 
-void LLVoiceClient::deleteAllSessions()
+void LLVoiceClient::setLipSyncEnabled(BOOL enabled)
 {
-	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+	if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled);
+}
 
-	while(!mSessions.empty())
+BOOL LLVoiceClient::lipSyncEnabled()
+{
+	if (mVoiceModule) 
 	{
-		deleteSession(*(sessionsBegin()));
+		return mVoiceModule->lipSyncEnabled();
 	}
-	
-	if(!mSessionsByHandle.empty())
+	else
 	{
-		LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL;
+		return false;
 	}
 }
 
-void LLVoiceClient::verifySessionState(void)
+void LLVoiceClient::setMuteMic(bool muted)
 {
-	// This is mostly intended for debugging problems with session state management.
-	LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
+	if (mVoiceModule) mVoiceModule->setMuteMic(muted);
+}
 
-	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
-	{
-		sessionState *session = *iter;
 
-		LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
-		
-		if(!session->mHandle.empty())
-		{
-			// every session with a non-empty handle needs to be in the handle map
-			sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle));
-			if(i2 == mSessionsByHandle.end())
-			{
-				LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
-			}
-			else
-			{
-				if(i2->second != session)
-				{
-					LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
-				}
-			}
-		}
+// ----------------------------------------------
+// PTT
+
+void LLVoiceClient::setUserPTTState(bool ptt)
+{
+	if (mVoiceModule) mVoiceModule->setUserPTTState(ptt);
+}
+
+bool LLVoiceClient::getUserPTTState()
+{
+	if (mVoiceModule) 
+	{
+		return mVoiceModule->getUserPTTState();
 	}
-		
-	// check that every entry in the handle map points to a valid session in the session set
-	for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
+	else
 	{
-		sessionState *session = iter->second;
-		sessionIterator i2 = mSessions.find(session);
-		if(i2 == mSessions.end())
-		{
-			LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
-		}
-		else
-		{
-			if(session->mHandle != (*i2)->mHandle)
-			{
-				LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
-			}
-		}
+		return false;
 	}
 }
 
-LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
-	mURI(uri)
+void LLVoiceClient::setUsePTT(bool usePTT)
 {
-	mOnlineSL = false;
-	mOnlineSLim = false;
-	mCanSeeMeOnline = true;
-	mHasBlockListEntry = false;
-	mHasAutoAcceptListEntry = false;
-	mNameResolved = false;
-	mInVivoxBuddies = false;
-	mInSLFriends = false;
-	mNeedsNameUpdate = false;
+	if (mVoiceModule) mVoiceModule->setUsePTT(usePTT);
 }
 
-void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
+void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
 {
-	buddyListEntry *buddy = addBuddy(uri, displayName);
-	buddy->mInVivoxBuddies = true;	
+	if (mVoiceModule) mVoiceModule->setPTTIsToggle(PTTIsToggle);
 }
 
-LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri)
+bool LLVoiceClient::getPTTIsToggle()
 {
-	std::string empty;
-	buddyListEntry *buddy = addBuddy(uri, empty);
-	if(buddy->mDisplayName.empty())
+	if (mVoiceModule) 
 	{
-		buddy->mNameResolved = false;
+		return mVoiceModule->getPTTIsToggle();
 	}
-	return buddy;
-}
-
-LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
-{
-	buddyListEntry *result = NULL;
-	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
-	
-	if(iter != mBuddyListMap.end())
-	{
-		// Found a matching buddy already in the map.
-		LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
-		result = iter->second;
+	else {
+		return false;
 	}
 
-	if(!result)
-	{
-		// participant isn't already in one list or the other.
-		LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
-		result = new buddyListEntry(uri);
-		result->mDisplayName = displayName;
-
-		if(IDFromName(uri, result->mUUID)) 
-		{
-			// Extracted UUID from name successfully.
-		}
-		else
-		{
-			LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL;
-		}
-
-		mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result));
-	}
-	
-	return result;
 }
 
-LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri)
+void LLVoiceClient::inputUserControlState(bool down)
 {
-	buddyListEntry *result = NULL;
-	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
-	if(iter != mBuddyListMap.end())
-	{
-		result = iter->second;
-	}
-	
-	return result;
+	if (mVoiceModule) mVoiceModule->inputUserControlState(down);	
 }
 
-LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id)
+void LLVoiceClient::toggleUserPTTState(void)
 {
-	buddyListEntry *result = NULL;
-	buddyListMap::iterator iter;
-
-	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
-	{
-		if(iter->second->mUUID == id)
-		{
-			result = iter->second;
-			break;
-		}
-	}
-	
-	return result;
+	if (mVoiceModule) mVoiceModule->toggleUserPTTState();
 }
 
-LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name)
+void LLVoiceClient::keyDown(KEY key, MASK mask)
+{	
+	if (mVoiceModule) mVoiceModule->keyDown(key, mask);
+}
+void LLVoiceClient::keyUp(KEY key, MASK mask)
 {
-	buddyListEntry *result = NULL;
-	buddyListMap::iterator iter;
+	if (mVoiceModule) mVoiceModule->keyUp(key, mask);
+}
+void LLVoiceClient::middleMouseState(bool down)
+{
+	if (mVoiceModule) mVoiceModule->middleMouseState(down);
+}
 
-	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
+
+//-------------------------------------------
+// nearby speaker accessors
+
+BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
+{
+	if (mVoiceModule) 
 	{
-		if(iter->second->mDisplayName == name)
-		{
-			result = iter->second;
-			break;
-		}
+		return mVoiceModule->getVoiceEnabled(id);
+	} 
+	else
+	{
+		return FALSE;
 	}
-	
-	return result;
 }
 
-void LLVoiceClient::deleteBuddy(const std::string &uri)
+std::string LLVoiceClient::getDisplayName(const LLUUID& id)
 {
-	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
-	if(iter != mBuddyListMap.end())
+	if (mVoiceModule) 
 	{
-		LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
-		buddyListEntry *buddy = iter->second;
-		mBuddyListMap.erase(iter);
-		delete buddy;
+		return mVoiceModule->getDisplayName(id);
 	}
 	else
 	{
-		LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
+	  return std::string();
 	}
-	
 }
 
-void LLVoiceClient::deleteAllBuddies(void)
+BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id)
 {
-	while(!mBuddyListMap.empty())
+	if (mVoiceModule) 
 	{
-		deleteBuddy(*(mBuddyListMap.begin()->first));
+		return mVoiceModule->isParticipantAvatar(id);
 	}
-	
-	// Don't want to correlate with friends list when we've emptied the buddy list.
-	mBuddyListMapPopulated = false;
-	
-	// Don't want to correlate with friends list when we've reset the block rules.
-	mBlockRulesListReceived = false;
-	mAutoAcceptRulesListReceived = false;
-}
-
-void LLVoiceClient::deleteAllBlockRules(void)
-{
-	// Clear the block list entry flags from all local buddy list entries
-	buddyListMap::iterator buddy_it;
-	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	else
 	{
-		buddy_it->second->mHasBlockListEntry = false;
+		return FALSE;
 	}
 }
 
-void LLVoiceClient::deleteAllAutoAcceptRules(void)
+BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id)
 {
-	// Clear the auto-accept list entry flags from all local buddy list entries
-	buddyListMap::iterator buddy_it;
-	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	if (mVoiceModule) 
 	{
-		buddy_it->second->mHasAutoAcceptListEntry = false;
+		return mVoiceModule->isOnlineSIP(id);
 	}
-}
-
-void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
-{
-	buddyListEntry *buddy = NULL;
-
-	// blockMask is the SIP URI of a friends list entry
-	buddyListMap::iterator iter = mBuddyListMap.find(&blockMask);
-	if(iter != mBuddyListMap.end())
+	else
 	{
-		LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
-		buddy = iter->second;
+		return FALSE;
 	}
+}
 
-	if(buddy == NULL)
+BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
+{
+	if (mVoiceModule) 
 	{
-		LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
-		buddy = addBuddy(blockMask);
+		return mVoiceModule->getIsSpeaking(id);
 	}
-	
-	if(buddy != NULL)
+	else
 	{
-		buddy->mHasBlockListEntry = true;
+		return FALSE;
 	}
 }
 
-void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
+BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
 {
-	buddyListEntry *buddy = NULL;
-
-	// blockMask is the SIP URI of a friends list entry
-	buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask);
-	if(iter != mBuddyListMap.end())
+	if (mVoiceModule) 
 	{
-		LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
-		buddy = iter->second;
+		return mVoiceModule->getIsModeratorMuted(id);
 	}
-
-	if(buddy == NULL)
+	else
 	{
-		LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
-		buddy = addBuddy(autoAcceptMask);
+		return FALSE;
 	}
+}
 
-	if(buddy != NULL)
+F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
+{		
+	if (mVoiceModule) 
 	{
-		buddy->mHasAutoAcceptListEntry = true;
+		return mVoiceModule->getCurrentPower(id);
+	}
+	else
+	{
+		return 0.0;
 	}
 }
 
-void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
-{
-	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
-	mBlockRulesListReceived = true;
-}
-
-void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
+BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
 {
-	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
-	mAutoAcceptRulesListReceived = true;
+	if (mVoiceModule) 
+	{
+		return mVoiceModule->getOnMuteList(id);
+	}
+	else
+	{
+		return FALSE;
+	}
 }
 
-void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
+F32 LLVoiceClient::getUserVolume(const LLUUID& id)
 {
-	mParticipantObservers.insert(observer);
+	if (mVoiceModule) 
+	{
+		return mVoiceModule->getUserVolume(id);
+	}
+	else
+	{
+		return 0.0;
+	}
 }
 
-void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
+void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
 {
-	mParticipantObservers.erase(observer);
+	if (mVoiceModule) mVoiceModule->setUserVolume(id, volume);
 }
 
-void LLVoiceClient::notifyParticipantObservers()
-{
-	for (observer_set_t::iterator it = mParticipantObservers.begin();
-		it != mParticipantObservers.end();
-		)
-	{
-		LLVoiceClientParticipantObserver* observer = *it;
-		observer->onChange();
-		// In case onChange() deleted an entry.
-		it = mParticipantObservers.upper_bound(observer);
-	}
-}
+//--------------------------------------------------
+// status observers
 
 void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
 {
-	mStatusObservers.insert(observer);
+	if (mVoiceModule) mVoiceModule->addObserver(observer);
 }
 
 void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
 {
-	mStatusObservers.erase(observer);
-}
-
-void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
-{
-	if(mAudioSession)
-	{
-		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
-		{
-			switch(mAudioSession->mErrorStatusCode)
-			{
-				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break;
-				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break;
-				case 20715:
-					//invalid channel, we may be using a set of poorly cached
-					//info
-					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
-					break;
-				case 1009:
-					//invalid username and password
-					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
-					break;
-			}
-
-			// Reset the error code to make sure it won't be reused later by accident.
-			mAudioSession->mErrorStatusCode = 0;
-		}
-		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
-		{
-			switch(mAudioSession->mErrorStatusCode)
-			{
-				case 404:	// NOT_FOUND
-				case 480:	// TEMPORARILY_UNAVAILABLE
-				case 408:	// REQUEST_TIMEOUT
-					// call failed because other user was not available
-					// treat this as an error case
-					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
-
-					// Reset the error code to make sure it won't be reused later by accident.
-					mAudioSession->mErrorStatusCode = 0;
-				break;
-			}
-		}
-	}
-		
-	LL_DEBUGS("Voice") 
-		<< " " << LLVoiceClientStatusObserver::status2string(status)  
-		<< ", session URI " << getAudioSessionURI() 
-		<< (inSpatialChannel()?", proximal is true":", proximal is false")
-	<< LL_ENDL;
-
-	for (status_observer_set_t::iterator it = mStatusObservers.begin();
-		it != mStatusObservers.end();
-		)
-	{
-		LLVoiceClientStatusObserver* observer = *it;
-		observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
-		// In case onError() deleted an entry.
-		it = mStatusObservers.upper_bound(observer);
-	}
-
+	if (mVoiceModule) mVoiceModule->removeObserver(observer);
 }
 
 void LLVoiceClient::addObserver(LLFriendObserver* observer)
 {
-	mFriendObservers.insert(observer);
+	if (mVoiceModule) mVoiceModule->addObserver(observer);
 }
 
 void LLVoiceClient::removeObserver(LLFriendObserver* observer)
 {
-	mFriendObservers.erase(observer);
+	if (mVoiceModule) mVoiceModule->removeObserver(observer);
 }
 
-void LLVoiceClient::notifyFriendObservers()
+void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
 {
-	for (friend_observer_set_t::iterator it = mFriendObservers.begin();
-		it != mFriendObservers.end();
-		)
-	{
-		LLFriendObserver* observer = *it;
-		it++;
-		// The only friend-related thing we notify on is online/offline transitions.
-		observer->changed(LLFriendObserver::ONLINE);
-	}
+	if (mVoiceModule) mVoiceModule->addObserver(observer);
 }
 
-void LLVoiceClient::lookupName(const LLUUID &id)
+void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
 {
-	BOOL is_group = FALSE;
-	gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup);
+	if (mVoiceModule) mVoiceModule->removeObserver(observer);
 }
 
-//static
-void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
+std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
 {
-	if(gVoiceClient)
+	if (mVoiceModule) 
+	{
+		return mVoiceModule->sipURIFromID(id);
+	}
+	else
 	{
-		std::string name = llformat("%s %s", first.c_str(), last.c_str());
-		gVoiceClient->avatarNameResolved(id, name);
+		return std::string();
 	}
 }
 
-void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
+
+///////////////////
+// version checking
+
+class LLViewerRequiredVoiceVersion : public LLHTTPNode
 {
-	// If the avatar whose name just resolved is on our friends list, resync the friends list.
-	if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
-	{
-		mFriendsListDirty = true;
-	}
-	
-	// Iterate over all sessions.
-	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	static BOOL sAlertedUser;
+	virtual void post(
+					  LLHTTPNode::ResponsePtr response,
+					  const LLSD& context,
+					  const LLSD& input) const
 	{
-		sessionState *session = *iter;
-
-		// Check for this user as a participant in this session
-		participantState *participant = session->findParticipantByID(id);
-		if(participant)
-		{
-			// Found -- fill in the name
-			participant->mAccountName = name;
-			// and post a "participants updated" message to listeners later.
-			session->mParticipantsChanged = true;
-		}
-		
-		// Check whether this is a p2p session whose caller name just resolved
-		if(session->mCallerID == id)
+		//You received this messsage (most likely on region cross or
+		//teleport)
+		if ( input.has("body") && input["body"].has("major_version") )
 		{
-			// this session's "caller ID" just resolved.  Fill in the name.
-			session->mName = name;
-			if(session->mTextInvitePending)
-			{
-				session->mTextInvitePending = false;
-
-				// We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.				
-			}
-			if(session->mVoiceInvitePending)
+			int major_voice_version =
+			input["body"]["major_version"].asInteger();
+			// 			int minor_voice_version =
+			// 				input["body"]["minor_version"].asInteger();
+			LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion();
+			
+			if (major_voice_version > 1)
 			{
-				session->mVoiceInvitePending = false;
-
-				gIMMgr->inviteToSession(
-					session->mIMSessionID,
-					session->mName,
-					session->mCallerID, 
-					session->mName, 
-					IM_SESSION_P2P_INVITE, 
-					LLIMMgr::INVITATION_TYPE_VOICE,
-					session->mHandle,
-					session->mSIPURI);
+				if (!sAlertedUser)
+				{
+					//sAlertedUser = TRUE;
+					LLNotificationsUtil::add("VoiceVersionMismatch");
+					gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
+				}
 			}
-			
 		}
 	}
-}
+};
 
 class LLViewerParcelVoiceInfo : public LLHTTPNode
 {
 	virtual void post(
-		LLHTTPNode::ResponsePtr response,
-		const LLSD& context,
-		const LLSD& input) const
+					  LLHTTPNode::ResponsePtr response,
+					  const LLSD& context,
+					  const LLSD& input) const
 	{
 		//the parcel you are in has changed something about its
 		//voice information
-
+		
 		//this is a misnomer, as it can also be when you are not in
 		//a parcel at all.  Should really be something like
 		//LLViewerVoiceInfoChanged.....
 		if ( input.has("body") )
 		{
 			LLSD body = input["body"];
-
+			
 			//body has "region_name" (str), "parcel_local_id"(int),
 			//"voice_credentials" (map).
-
+			
 			//body["voice_credentials"] has "channel_uri" (str),
 			//body["voice_credentials"] has "channel_credentials" (str)
-
+			
 			//if we really wanted to be extra careful,
 			//we'd check the supplied
 			//local parcel id to make sure it's for the same parcel
@@ -7026,7 +743,7 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode
 				LLSD voice_credentials = body["voice_credentials"];
 				std::string uri;
 				std::string credentials;
-
+				
 				if ( voice_credentials.has("channel_uri") )
 				{
 					uri = voice_credentials["channel_uri"].asString();
@@ -7034,51 +751,21 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode
 				if ( voice_credentials.has("channel_credentials") )
 				{
 					credentials =
-						voice_credentials["channel_credentials"].asString();
+					voice_credentials["channel_credentials"].asString();
 				}
-
-				gVoiceClient->setSpatialChannel(uri, credentials);
+				
+				LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials);
 			}
 		}
 	}
 };
 
-class LLViewerRequiredVoiceVersion : public LLHTTPNode
-{
-	static BOOL sAlertedUser;
-	virtual void post(
-		LLHTTPNode::ResponsePtr response,
-		const LLSD& context,
-		const LLSD& input) const
-	{
-		//You received this messsage (most likely on region cross or
-		//teleport)
-		if ( input.has("body") && input["body"].has("major_version") )
-		{
-			int major_voice_version =
-				input["body"]["major_version"].asInteger();
-// 			int minor_voice_version =
-// 				input["body"]["minor_version"].asInteger();
-
-			if (gVoiceClient &&
-				(major_voice_version > VOICE_MAJOR_VERSION) )
-			{
-				if (!sAlertedUser)
-				{
-					//sAlertedUser = TRUE;
-					LLNotificationsUtil::add("VoiceVersionMismatch");
-					gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
-				}
-			}
-		}
-	}
-};
 BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
 
 LLHTTPRegistration<LLViewerParcelVoiceInfo>
-    gHTTPRegistrationMessageParcelVoiceInfo(
-		"/message/ParcelVoiceInfo");
+gHTTPRegistrationMessageParcelVoiceInfo(
+										"/message/ParcelVoiceInfo");
 
 LLHTTPRegistration<LLViewerRequiredVoiceVersion>
-    gHTTPRegistrationMessageRequiredVoiceVersion(
-		"/message/RequiredVoiceVersion");
+gHTTPRegistrationMessageRequiredVoiceVersion(
+											 "/message/RequiredVoiceVersion");
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 075834f7e03a6a8d4ff7a1ab991a52192fe8142d..8af12f9d3881ae6853362d8cbc752a8dc11aae01 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -17,8 +17,7 @@
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -33,7 +32,6 @@
 #define LL_VOICE_CLIENT_H
 
 class LLVOAvatar;
-class LLVivoxProtocolParser;
 
 #include "lliopipe.h"
 #include "llpumpio.h"
@@ -42,9 +40,14 @@ class LLVivoxProtocolParser;
 #include "v3math.h"
 #include "llframetimer.h"
 #include "llviewerregion.h"
-#include "m3math.h"			// LLMatrix3
+#include "llcallingcard.h"   // for LLFriendObserver
+#include "llsecapi.h"
+
+// devices
+
+typedef std::vector<std::string> LLVoiceDeviceList;	
+
 
-class LLFriendObserver;
 class LLVoiceClientParticipantObserver
 {
 public:
@@ -52,6 +55,9 @@ class LLVoiceClientParticipantObserver
 	virtual void onChange() = 0;
 };
 
+
+///////////////////////////////////
+/// @class LLVoiceClientStatusObserver
 class LLVoiceClientStatusObserver
 {
 public:
@@ -65,11 +71,7 @@ class LLVoiceClientStatusObserver
 		STATUS_JOINED,
 		STATUS_LEFT_CHANNEL,
 		STATUS_VOICE_DISABLED,
-
-		// Adding STATUS_VOICE_ENABLED as pair status for STATUS_VOICE_DISABLED
-		// See LLVoiceClient::setVoiceEnabled()
 		STATUS_VOICE_ENABLED,
-
 		BEGIN_ERROR_STATUS,
 		ERROR_CHANNEL_FULL,
 		ERROR_CHANNEL_LOCKED,
@@ -83,683 +85,319 @@ class LLVoiceClientStatusObserver
 	static std::string status2string(EStatusType inStatus);
 };
 
-class LLVoiceClient: public LLSingleton<LLVoiceClient>
+struct LLVoiceVersionInfo
 {
-	LOG_CLASS(LLVoiceClient);
-	public:
-		LLVoiceClient();	
-		~LLVoiceClient();
-		
-	public:
-		static void init(LLPumpIO *pump);	// Call this once at application startup (creates connector)
-		static void terminate();	// Call this to clean up during shutdown
-						
-	protected:
-		bool writeString(const std::string &str);
-
-	public:
-		
-		static F32 OVERDRIVEN_POWER_LEVEL;
-
-		void updateSettings(); // call after loading settings and whenever they change
-	
-		void getCaptureDevicesSendMessage();
-		void getRenderDevicesSendMessage();
-		
-		void clearCaptureDevices();
-		void addCaptureDevice(const std::string& name);
-		void setCaptureDevice(const std::string& name);
-		
-		void clearRenderDevices();
-		void addRenderDevice(const std::string& name);
-		void setRenderDevice(const std::string& name);
-
-		void tuningStart();
-		void tuningStop();
-		bool inTuningMode();
-		bool inTuningStates();
-		
-		void tuningRenderStartSendMessage(const std::string& name, bool loop);
-		void tuningRenderStopSendMessage();
-
-		void tuningCaptureStartSendMessage(int duration);
-		void tuningCaptureStopSendMessage();
-		
-		void tuningSetMicVolume(float volume);
-		void tuningSetSpeakerVolume(float volume);
-		float tuningGetEnergy(void);
-				
-		// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
-		// i.e. when the daemon is running and connected, and the device lists are populated.
-		bool deviceSettingsAvailable();
-		
-		// Requery the vivox daemon for the current list of input/output devices.
-		// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
-		// (use this if you want to know when it's done).
-		// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
-		void refreshDeviceLists(bool clearCurrentList = true);
-		
-		// Call this if the connection to the daemon terminates unexpectedly.  It will attempt to reset everything and relaunch.
-		void daemonDied();
-
-		// Call this if we're just giving up on voice (can't provision an account, etc.).  It will clean up and go away.
-		void giveUp();
-		
-		/////////////////////////////
-		// Response/Event handlers
-		void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID);
-		void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases);
-		void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
-		void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
-		void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString);
-		void logoutResponse(int statusCode, std::string &statusString);
-		void connectorShutdownResponse(int statusCode, std::string &statusString);
-
-		void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
-		void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming);
-		void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming);
-		void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString);
-		void sessionGroupAddedEvent(std::string &sessionGroupHandle);
-		void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle);
-		void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType);
-		void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString);
-		void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
-		void auxAudioPropertiesEvent(F32 energy);
-		void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString);
-		void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString);
-		void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType);
-		void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType);
-		
-		void buddyListChanged();
-		void muteListChanged();
-		void updateFriends(U32 mask);
-		
-		/////////////////////////////
-		// Sending updates of current state
-static	void updatePosition(void);
-		void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
-		void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
-		bool channelFromRegion(LLViewerRegion *region, std::string &name);
-		void leaveChannel(void);		// call this on logout or teleport begin
-
-		
-		void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
-		bool getMuteMic() const;
-		void setUserPTTState(bool ptt);
-		bool getUserPTTState();
-		void toggleUserPTTState(void);
-		void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs
-		void setVoiceEnabled(bool enabled);
-		static bool voiceEnabled();
-		void setUsePTT(bool usePTT);
-		void setPTTIsToggle(bool PTTIsToggle);
-		bool getPTTIsToggle();
-		void setPTTKey(std::string &key);
-		void setEarLocation(S32 loc);
-		void setVoiceVolume(F32 volume);
-		void setMicGain(F32 volume);
-		void setUserVolume(const LLUUID& id, F32 volume); // sets volume for specified agent, from 0-1 (where .5 is nominal)
-		void setLipSyncEnabled(BOOL enabled);
-		BOOL lipSyncEnabled();
-
-		// PTT key triggering
-		void keyDown(KEY key, MASK mask);
-		void keyUp(KEY key, MASK mask);
-		void middleMouseState(bool down);
-
-		// Return the version of the Vivox library
-		std::string getAPIVersion() const { return mAPIVersion; }
-		
-		/////////////////////////////
-		// Accessors for data related to nearby speakers
-		BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar
-		BOOL getIsSpeaking(const LLUUID& id);
-		BOOL getIsModeratorMuted(const LLUUID& id);
-		F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
-		BOOL getOnMuteList(const LLUUID& id);
-		F32 getUserVolume(const LLUUID& id);
-		std::string getDisplayName(const LLUUID& id);
-		
-		// MBW -- XXX -- Not sure how to get this data out of the TVC
-		BOOL getUsingPTT(const LLUUID& id);
-		std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable)
-
-		/////////////////////////////
-		BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled.
-											// Use this to determine whether to show a "no speech" icon in the menu bar.
-		
-		/////////////////////////////
-		// Recording controls
-		void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200);
-		void recordingLoopSave(const std::string& filename);
-		void recordingStop();
-		
-		// Playback controls
-		void filePlaybackStart(const std::string& filename);
-		void filePlaybackStop();
-		void filePlaybackSetPaused(bool paused);
-		void filePlaybackSetMode(bool vox = false, float speed = 1.0f);
-		
-		
-		// This is used by the string-keyed maps below, to avoid storing the string twice.
-		// The 'const std::string *' in the key points to a string actually stored in the object referenced by the map.
-		// The add and delete operations for each map allocate and delete in the right order to avoid dangling references.
-		// The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead.
-		struct stringMapComparitor
-		{
-			bool operator()(const std::string* a, const std::string * b) const
-			{
-				return a->compare(*b) < 0;
-			}
-		};
-
-		struct uuidMapComparitor
-		{
-			bool operator()(const LLUUID* a, const LLUUID * b) const
-			{
-				return *a < *b;
-			}
-		};
-		
-		struct participantState
-		{
-		public:
-			participantState(const std::string &uri);
-
-			bool updateMuteState();
-			bool isAvatar();
-
-			std::string mURI;
-			LLUUID mAvatarID;
-			std::string mAccountName;
-			std::string mDisplayName;
-			LLFrameTimer mSpeakingTimeout;
-			F32	mLastSpokeTimestamp;
-			F32 mPower;
-			int mVolume;
-			std::string mGroupID;
-			int mUserVolume;
-			bool mPTT;
-			bool mIsSpeaking;
-			bool mIsModeratorMuted;
-			bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted)
-			bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
-			bool mAvatarIDValid;
-			bool mIsSelf;
-		};
-		typedef std::map<const std::string *, participantState*, stringMapComparitor> participantMap;
-
-		typedef std::map<const LLUUID *, participantState*, uuidMapComparitor> participantUUIDMap;
-	
-		enum streamState
-		{
-			streamStateUnknown = 0,
-			streamStateIdle = 1,
-			streamStateConnected = 2,
-			streamStateRinging = 3,
-		};
-		
-		struct sessionState
-		{
-		public:
-			sessionState();
-			~sessionState();
-
-			participantState *addParticipant(const std::string &uri);
-			// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
-			// Take care not to use the pointer again after that.
-			void removeParticipant(participantState *participant);
-			void removeAllParticipants();
-
-			participantState *findParticipant(const std::string &uri);
-			participantState *findParticipantByID(const LLUUID& id);
-
-			bool isCallBackPossible();
-			bool isTextIMPossible();
-
-			std::string mHandle;
-			std::string mGroupHandle;
-			std::string mSIPURI;
-			std::string mAlias;
-			std::string mName;
-			std::string mAlternateSIPURI;
-			std::string mHash;			// Channel password
-			std::string mErrorStatusString;
-			std::queue<std::string> mTextMsgQueue;
-			
-			LLUUID		mIMSessionID;
-			LLUUID		mCallerID;
-			int			mErrorStatusCode;
-			int			mMediaStreamState;
-			int			mTextStreamState;
-			bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet.
-			bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet.
-			bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup)
-			bool		mTextInvitePending;		// True if a text invite is pending for this session (usually waiting on a name lookup)
-			bool		mSynthesizedCallerID;	// True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup.
-			bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN)
-			bool		mIsSpatial;	// True for spatial channels
-			bool		mIsP2P;
-			bool		mIncoming;
-			bool		mVoiceEnabled;
-			bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped
-			// Set to true when the mute state of someone in the participant list changes.
-			// The code will have to walk the list to find the changed participant(s).
-			bool		mVolumeDirty;
-
-			bool		mParticipantsChanged;
-			participantMap mParticipantsByURI;
-			participantUUIDMap mParticipantsByUUID;
-		};
-
-		participantState *findParticipantByID(const LLUUID& id);
-		participantMap *getParticipantList(void);
-		
-		typedef std::map<const std::string*, sessionState*, stringMapComparitor> sessionMap;
-		typedef std::set<sessionState*> sessionSet;
-				
-		typedef sessionSet::iterator sessionIterator;
-		sessionIterator sessionsBegin(void);
-		sessionIterator sessionsEnd(void);
-
-		sessionState *findSession(const std::string &handle);
-		sessionState *findSessionBeingCreatedByURI(const std::string &uri);
-		sessionState *findSession(const LLUUID &participant_id);
-		sessionState *findSessionByCreateID(const std::string &create_id);
-		
-		sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null);
-		void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null);
-		void setSessionURI(sessionState *session, const std::string &uri);
-		void deleteSession(sessionState *session);
-		void deleteAllSessions(void);
-
-		void verifySessionState(void);
-
-		void joinedAudioSession(sessionState *session);
-		void leftAudioSession(sessionState *session);
-
-		// This is called in several places where the session _may_ need to be deleted.
-		// It contains logic for whether to delete the session or keep it around.
-		void reapSession(sessionState *session);
-		
-		// Returns true if the session seems to indicate we've moved to a region on a different voice server
-		bool sessionNeedsRelog(sessionState *session);
-		
-		struct buddyListEntry
-		{
-			buddyListEntry(const std::string &uri);
-			std::string mURI;
-			std::string mDisplayName;
-			LLUUID	mUUID;
-			bool mOnlineSL;
-			bool mOnlineSLim;
-			bool mCanSeeMeOnline;
-			bool mHasBlockListEntry;
-			bool mHasAutoAcceptListEntry;
-			bool mNameResolved;
-			bool mInSLFriends;
-			bool mInVivoxBuddies;
-			bool mNeedsNameUpdate;
-		};
-
-		typedef std::map<const std::string*, buddyListEntry*, stringMapComparitor> buddyListMap;
-		
-		// This should be called when parsing a buddy list entry sent by SLVoice.		
-		void processBuddyListEntry(const std::string &uri, const std::string &displayName);
-
-		buddyListEntry *addBuddy(const std::string &uri);
-		buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName);
-		buddyListEntry *findBuddy(const std::string &uri);
-		buddyListEntry *findBuddy(const LLUUID &id);
-		buddyListEntry *findBuddyByDisplayName(const std::string &name);
-		void deleteBuddy(const std::string &uri);
-		void deleteAllBuddies(void);
-
-		void deleteAllBlockRules(void);
-		void addBlockRule(const std::string &blockMask, const std::string &presenceOnly);
-		void deleteAllAutoAcceptRules(void);
-		void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy);
-		void accountListBlockRulesResponse(int statusCode, const std::string &statusString);						
-		void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);						
-		
-		/////////////////////////////
-		// session control messages
-		void connectorCreate();
-		void connectorShutdown();
-
-		void requestVoiceAccountProvision(S32 retries = 3);
-	void userAuthorized(const std::string& user_id,
-						const LLUUID &agentID);
-		void login(
-			const std::string& account_name,
-			const std::string& password,
-			const std::string& voice_sip_uri_hostname,
-			const std::string& voice_account_server_uri);
-		void loginSendMessage();
-		void logout();
-		void logoutSendMessage();
-
-		void accountListBlockRulesSendMessage();
-		void accountListAutoAcceptRulesSendMessage();
-		
-		void sessionGroupCreateSendMessage();
-		void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
-		void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
-		void sessionMediaConnectSendMessage(sessionState *session);		// just joins the audio session
-		void sessionTextConnectSendMessage(sessionState *session);		// just joins the text session
-		void sessionTerminateSendMessage(sessionState *session);
-		void sessionGroupTerminateSendMessage(sessionState *session);
-		void sessionMediaDisconnectSendMessage(sessionState *session);
-		void sessionTextDisconnectSendMessage(sessionState *session);
-
-		// Pokes the state machine to leave the audio session next time around.
-		void sessionTerminate();	
-		
-		// Pokes the state machine to shut down the connector and restart it.
-		void requestRelog();
-		
-		// Does the actual work to get out of the audio session
-		void leaveAudioSession();
-		
-		void addObserver(LLVoiceClientParticipantObserver* observer);
-		void removeObserver(LLVoiceClientParticipantObserver* observer);
-
-		void addObserver(LLVoiceClientStatusObserver* observer);
-		void removeObserver(LLVoiceClientStatusObserver* observer);
-
-		void addObserver(LLFriendObserver* observer);
-		void removeObserver(LLFriendObserver* observer);
-		
-		void lookupName(const LLUUID &id);
-		static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group);
-		void avatarNameResolved(const LLUUID &id, const std::string &name);
-		
-		typedef std::vector<std::string> deviceList;
-
-		deviceList *getCaptureDevices();
-		deviceList *getRenderDevices();
-		
-		void setNonSpatialChannel(
-			const std::string &uri,
-			const std::string &credentials);
-		void setSpatialChannel(
-			const std::string &uri,
-			const std::string &credentials);
-		// start a voice session with the specified user
-		void callUser(const LLUUID &uuid);
-		
-		// Send a text message to the specified user, initiating the session if necessary.
-		bool sendTextMessage(const LLUUID& participant_id, const std::string& message);
-		
-		// close any existing text IM session with the specified user
-		void endUserIMSession(const LLUUID &uuid);
-		
-		bool answerInvite(std::string &sessionHandle);
-		void declineInvite(std::string &sessionHandle);
-		void leaveNonSpatialChannel();
+	std::string serverType;
+	std::string serverVersion;
+};
 
-		// Returns the URI of the current channel, or an empty string if not currently in a channel.
-		// NOTE that it will return an empty string if it's in the process of joining a channel.
-		std::string getCurrentChannel();
-		
-		// returns true iff the user is currently in a proximal (local spatial) channel.
-		// Note that gestures should only fire if this returns true.
-		bool inProximalChannel();
+//////////////////////////////////
+/// @class LLVoiceModuleInterface
+/// @brief Voice module interface
+///
+/// Voice modules should provide an implementation for this interface.
+/////////////////////////////////
 
-		std::string sipURIFromID(const LLUUID &id);
-				
-		// Returns true if the indicated user is online via SIP presence according to SLVoice.
-		// Note that we only get SIP presence data for other users that are in our vivox buddy list.
-		bool isOnlineSIP(const LLUUID &id);
-
-		// Returns true if the indicated participant is really an SL avatar.
-		// This should be used to control the state of the "profile" button.
-		// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls.
-		bool isParticipantAvatar(const LLUUID &id);
-		
-		// Returns true if calling back the session URI after the session has closed is possible.
-		// Currently this will be false only for PSTN P2P calls.		
-		// NOTE: this will return true if the session can't be found. 
-		bool isSessionCallBackPossible(const LLUUID &session_id);
-		
-		// Returns true if the session can accepte text IM's.
-		// Currently this will be false only for PSTN P2P calls.
-		// NOTE: this will return true if the session can't be found. 
-		bool isSessionTextIMPossible(const LLUUID &session_id);
-		
-	private:
-
-		// internal state for a simple state machine.  This is used to deal with the asynchronous nature of some of the messages.
-		// Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string().
-		enum state
-		{
-			stateDisableCleanup,
-			stateDisabled,				// Voice is turned off.
-			stateStart,					// Class is initialized, socket is created
-			stateDaemonLaunched,		// Daemon has been launched
-			stateConnecting,			// connect() call has been issued
-			stateConnected,				// connection to the daemon has been made, send some initial setup commands.
-			stateIdle,					// socket is connected, ready for messaging
-			stateMicTuningStart,
-			stateMicTuningRunning,		
-			stateMicTuningStop,
-			stateConnectorStart,		// connector needs to be started
-			stateConnectorStarting,		// waiting for connector handle
-			stateConnectorStarted,		// connector handle received
-			stateLoginRetry,			// need to retry login (failed due to changing password)
-			stateLoginRetryWait,		// waiting for retry timer
-			stateNeedsLogin,			// send login request
-			stateLoggingIn,				// waiting for account handle
-			stateLoggedIn,				// account handle received
-			stateCreatingSessionGroup,	// Creating the main session group
-			stateNoChannel,				// 
-			stateJoiningSession,		// waiting for session handle
-			stateSessionJoined,			// session handle received
-			stateRunning,				// in session, steady state
-			stateLeavingSession,		// waiting for terminate session response
-			stateSessionTerminated,		// waiting for terminate session response
-
-			stateLoggingOut,			// waiting for logout response
-			stateLoggedOut,				// logout response received
-			stateConnectorStopping,		// waiting for connector stop
-			stateConnectorStopped,		// connector stop received
-			
-			// We go to this state if the login fails because the account needs to be provisioned.
-			
-			// error states.  No way to recover from these yet.
-			stateConnectorFailed,
-			stateConnectorFailedWaiting,
-			stateLoginFailed,
-			stateLoginFailedWaiting,
-			stateJoinSessionFailed,
-			stateJoinSessionFailedWaiting,
-
-			stateJail					// Go here when all else has failed.  Nothing will be retried, we're done.
-		};
-		
-		state mState;
-		bool mSessionTerminateRequested;
-		bool mRelogRequested;
-		
-		void setState(state inState);
-		state getState(void)  { return mState; };
-		static std::string state2string(state inState);
-		
-		void stateMachine();
-		static void idle(void *user_data);
-		
-		LLHost mDaemonHost;
-		LLSocket::ptr_t mSocket;
-		bool mConnected;
-		
-		void closeSocket(void);
-		
-		LLPumpIO *mPump;
-		friend class LLVivoxProtocolParser;
-		
-		std::string mAccountName;
-		std::string mAccountPassword;
-		std::string mAccountDisplayName;
-		std::string mAccountFirstName;
-		std::string mAccountLastName;
-				
-		bool mTuningMode;
-		float mTuningEnergy;
-		std::string mTuningAudioFile;
-		int mTuningMicVolume;
-		bool mTuningMicVolumeDirty;
-		int mTuningSpeakerVolume;
-		bool mTuningSpeakerVolumeDirty;
-		state mTuningExitState;					// state to return to when we leave tuning mode.
-		
-		std::string mSpatialSessionURI;
-		std::string mSpatialSessionCredentials;
+class LLVoiceModuleInterface
+{
+public:
+	LLVoiceModuleInterface() {}
+	virtual ~LLVoiceModuleInterface() {}
+	
+	virtual void init(LLPumpIO *pump)=0;	// Call this once at application startup (creates connector)
+	virtual void terminate()=0;	// Call this to clean up during shutdown
+	
+	virtual void updateSettings()=0; // call after loading settings and whenever they change
+	
+	virtual const LLVoiceVersionInfo& getVersion()=0;
+	
+	/////////////////////
+	/// @name Tuning
+	//@{
+	virtual void tuningStart()=0;
+	virtual void tuningStop()=0;
+	virtual bool inTuningMode()=0;
+	
+	virtual void tuningSetMicVolume(float volume)=0;
+	virtual void tuningSetSpeakerVolume(float volume)=0;
+	virtual float tuningGetEnergy(void)=0;
+	//@}
+	
+	/////////////////////
+	/// @name Devices
+	//@{
+	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+	// i.e. when the daemon is running and connected, and the device lists are populated.
+	virtual bool deviceSettingsAvailable()=0;
+	
+	// Requery the vivox daemon for the current list of input/output devices.
+	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+	// (use this if you want to know when it's done).
+	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+	virtual void refreshDeviceLists(bool clearCurrentList = true)=0;
+	
+	virtual void setCaptureDevice(const std::string& name)=0;
+	virtual void setRenderDevice(const std::string& name)=0;
+	
+	virtual LLVoiceDeviceList& getCaptureDevices()=0;
+	virtual LLVoiceDeviceList& getRenderDevices()=0;
+	
+	virtual std::vector<LLUUID> getParticipantList(void)=0;
+	//@}
+	
+	////////////////////////////
+	/// @ name Channel stuff
+	//@{
+	// returns true iff the user is currently in a proximal (local spatial) channel.
+	// Note that gestures should only fire if this returns true.
+	virtual bool inProximalChannel()=0;
+	
+	virtual void setNonSpatialChannel(const std::string &uri,
+									  const std::string &credentials)=0;
+	
+	virtual void setSpatialChannel(const std::string &uri,
+								   const std::string &credentials)=0;
+	
+	virtual void leaveNonSpatialChannel()=0;
+	
+	virtual void leaveChannel(void)=0;	
+	
+	// Returns the URI of the current channel, or an empty string if not currently in a channel.
+	// NOTE that it will return an empty string if it's in the process of joining a channel.
+	virtual std::string getCurrentChannel()=0;
+	//@}
+	
+	
+	//////////////////////////
+	/// @name invitations
+	//@{
+	// start a voice channel with the specified user
+	virtual void callUser(const LLUUID &uuid)=0;
+	virtual bool answerInvite(std::string &channelHandle)=0;
+	virtual void declineInvite(std::string &channelHandle)=0;
+	//@}
+	
+	/////////////////////////
+	/// @name Volume/gain
+	//@{
+	virtual void setVoiceVolume(F32 volume)=0;
+	virtual void setMicGain(F32 volume)=0;
+	//@}
+	
+	/////////////////////////
+	/// @name enable disable voice and features
+	//@{
+	virtual bool voiceEnabled()=0;
+	virtual void setVoiceEnabled(bool enabled)=0;
+	virtual void setLipSyncEnabled(BOOL enabled)=0;
+	virtual BOOL lipSyncEnabled()=0;	
+	virtual void setMuteMic(bool muted)=0;		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
+	//@}
+	
+	////////////////////////
+	/// @name PTT
+	//@{
+	virtual void setUserPTTState(bool ptt)=0;
+	virtual bool getUserPTTState()=0;
+	virtual void setUsePTT(bool usePTT)=0;
+	virtual void setPTTIsToggle(bool PTTIsToggle)=0;
+	virtual bool getPTTIsToggle()=0;	
+	virtual void toggleUserPTTState(void)=0;
+	virtual void inputUserControlState(bool down)=0;  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs
+	
+	virtual void keyDown(KEY key, MASK mask)=0;
+	virtual void keyUp(KEY key, MASK mask)=0;
+	virtual void middleMouseState(bool down)=0;
+	//@}
+	
+	//////////////////////////
+	/// @name nearby speaker accessors
+	//@{
+
+
+	virtual BOOL getVoiceEnabled(const LLUUID& id)=0;		// true if we've received data for this avatar
+	virtual std::string getDisplayName(const LLUUID& id)=0;
+	virtual BOOL isOnlineSIP(const LLUUID &id)=0;	
+	virtual BOOL isParticipantAvatar(const LLUUID &id)=0;
+	virtual BOOL getIsSpeaking(const LLUUID& id)=0;
+	virtual BOOL getIsModeratorMuted(const LLUUID& id)=0;
+	virtual F32 getCurrentPower(const LLUUID& id)=0;		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
+	virtual BOOL getOnMuteList(const LLUUID& id)=0;
+	virtual F32 getUserVolume(const LLUUID& id)=0;
+	virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal)	
+	//@}
+	
+	//////////////////////////
+	/// @name text chat
+	//@{
+	virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0;
+	virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0;
+	virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0;
+	virtual void endUserIMSession(const LLUUID &uuid)=0;	
+	//@}
+	
+	// authorize the user
+	virtual void userAuthorized(const std::string& user_id,
+								const LLUUID &agentID)=0;
+	
+	//////////////////////////////
+	/// @name Status notification
+	//@{
+	virtual void addObserver(LLVoiceClientStatusObserver* observer)=0;
+	virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0;
+	virtual void addObserver(LLFriendObserver* observer)=0;
+	virtual void removeObserver(LLFriendObserver* observer)=0;	
+	virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0;
+	virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0;	
+	//@}
+	
+	virtual std::string sipURIFromID(const LLUUID &id)=0;
+	//@}
+	
+};
 
-		std::string mMainSessionGroupHandle; // handle of the "main" session group.
-		
-		std::string mChannelName;			// Name of the channel to be looked up 
-		bool mAreaVoiceDisabled;
-		sessionState *mAudioSession;		// Session state for the current audio session
-		bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified.
 
-		sessionState *mNextAudioSession;	// Session state for the audio session we're trying to join
+class LLVoiceClient: public LLSingleton<LLVoiceClient>
+{
+	LOG_CLASS(LLVoiceClient);
+public:
+	LLVoiceClient();	
+	~LLVoiceClient();
 
-//		std::string mSessionURI;			// URI of the session we're in.
-//		std::string mSessionHandle;		// returned by ?
-		
-		S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings
-		std::string mCurrentRegionName;		// Used to detect parcel boundary crossings
-		
-		std::string mConnectorHandle;	// returned by "Create Connector" message
-		std::string mAccountHandle;		// returned by login message		
-		int 		mNumberOfAliases;
-		U32 mCommandCookie;
+	void init(LLPumpIO *pump);	// Call this once at application startup (creates connector)
+	void terminate();	// Call this to clean up during shutdown
 	
-		std::string mVoiceAccountServerURI;
-		std::string mVoiceSIPURIHostName;
-		
-		int mLoginRetryCount;
-		
-		sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map.
-		sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list.
-		
-		bool mBuddyListMapPopulated;
-		bool mBlockRulesListReceived;
-		bool mAutoAcceptRulesListReceived;
-		buddyListMap mBuddyListMap;
-		
-		deviceList mCaptureDevices;
-		deviceList mRenderDevices;
+	const LLVoiceVersionInfo getVersion();
+	
+static const F32 OVERDRIVEN_POWER_LEVEL;
 
-		std::string mCaptureDevice;
-		std::string mRenderDevice;
-		bool mCaptureDeviceDirty;
-		bool mRenderDeviceDirty;
-		
-		// This should be called when the code detects we have changed parcels.
-		// It initiates the call to the server that gets the parcel channel.
-		void parcelChanged();
-		
-	void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
-		void joinSession(sessionState *session);
-		
-static 	std::string nameFromAvatar(LLVOAvatar *avatar);
-static	std::string nameFromID(const LLUUID &id);
-static	bool IDFromName(const std::string name, LLUUID &uuid);
-static	std::string displayNameFromAvatar(LLVOAvatar *avatar);
-		std::string sipURIFromAvatar(LLVOAvatar *avatar);
-		std::string sipURIFromName(std::string &name);
-		
-		// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not.
-static	std::string nameFromsipURI(const std::string &uri);		
+	void updateSettings(); // call after loading settings and whenever they change
 
-		bool inSpatialChannel(void);
-		std::string getAudioSessionURI();
-		std::string getAudioSessionHandle();
-				
-		void sendPositionalUpdate(void);
-		
-		void buildSetCaptureDevice(std::ostringstream &stream);
-		void buildSetRenderDevice(std::ostringstream &stream);
-		void buildLocalAudioUpdates(std::ostringstream &stream);
+	// tuning
+	void tuningStart();
+	void tuningStop();
+	bool inTuningMode();
 		
-		void clearAllLists();
-		void checkFriend(const LLUUID& id);
-		void sendFriendsListUpdates();
-
-		// start a text IM session with the specified user
-		// This will be asynchronous, the session may be established at a future time.
-		sessionState* startUserIMSession(const LLUUID& uuid);
-		void sendQueuedTextMessages(sessionState *session);
-		
-		void enforceTether(void);
-		
-		bool		mSpatialCoordsDirty;
-		
-		LLVector3d	mCameraPosition;
-		LLVector3d	mCameraRequestedPosition;
-		LLVector3	mCameraVelocity;
-		LLMatrix3	mCameraRot;
-
-		LLVector3d	mAvatarPosition;
-		LLVector3	mAvatarVelocity;
-		LLMatrix3	mAvatarRot;
-		
-		bool		mPTTDirty;
-		bool		mPTT;
-		
-		bool		mUsePTT;
-		bool		mPTTIsMiddleMouse;
-		KEY			mPTTKey;
-		bool		mPTTIsToggle;
-		bool		mUserPTTState;
-		bool		mMuteMic;
+	void tuningSetMicVolume(float volume);
+	void tuningSetSpeakerVolume(float volume);
+	float tuningGetEnergy(void);
 				
-		// Set to true when the friends list is known to have changed.
-		bool		mFriendsListDirty;
-		
-		enum
-		{
-			earLocCamera = 0,		// ear at camera
-			earLocAvatar,			// ear at avatar
-			earLocMixed				// ear at avatar location/camera direction
-		};
-		
-		S32			mEarLocation;  
+	// devices
+	
+	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+	// i.e. when the daemon is running and connected, and the device lists are populated.
+	bool deviceSettingsAvailable();
 		
-		bool		mSpeakerVolumeDirty;
-		bool		mSpeakerMuteDirty;
-		int			mSpeakerVolume;
+	// Requery the vivox daemon for the current list of input/output devices.
+	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+	// (use this if you want to know when it's done).
+	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+	void refreshDeviceLists(bool clearCurrentList = true);
 
-		int			mMicVolume;
-		bool		mMicVolumeDirty;
-		
-		bool		mVoiceEnabled;
-		bool		mWriteInProgress;
-		std::string mWriteString;
-		
-		LLTimer		mUpdateTimer;
-		
-		BOOL		mLipSyncEnabled;
+	void setCaptureDevice(const std::string& name);
+	void setRenderDevice(const std::string& name);
 
-		std::string	mAPIVersion;
+	const LLVoiceDeviceList& getCaptureDevices();
+	const LLVoiceDeviceList& getRenderDevices();
 
-		typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
-		observer_set_t mParticipantObservers;
+	////////////////////////////
+	// Channel stuff
+	//
+	
+	// returns true iff the user is currently in a proximal (local spatial) channel.
+	// Note that gestures should only fire if this returns true.
+	bool inProximalChannel();
+	void setNonSpatialChannel(
+							  const std::string &uri,
+							  const std::string &credentials);
+	void setSpatialChannel(
+						   const std::string &uri,
+						   const std::string &credentials);
+	void leaveNonSpatialChannel();
+	
+	// Returns the URI of the current channel, or an empty string if not currently in a channel.
+	// NOTE that it will return an empty string if it's in the process of joining a channel.
+	std::string getCurrentChannel();
+	// start a voice channel with the specified user
+	void callUser(const LLUUID &uuid);	
+	bool answerInvite(std::string &channelHandle);
+	void declineInvite(std::string &channelHandle);	
+	void leaveChannel(void);		// call this on logout or teleport begin
+	
+	
+	/////////////////////////////
+	// Sending updates of current state
+	
 
-		void notifyParticipantObservers();
+	void setVoiceVolume(F32 volume);
+	void setMicGain(F32 volume);
+	void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)		
+	bool voiceEnabled();
+	void setLipSyncEnabled(BOOL enabled);
+	void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
+	void setUserPTTState(bool ptt);
+	bool getUserPTTState();
+	void toggleUserPTTState(void);
+	void inputUserControlState(bool down);  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs	
+	void setVoiceEnabled(bool enabled);
+
+	void setUsePTT(bool usePTT);
+	void setPTTIsToggle(bool PTTIsToggle);
+	bool getPTTIsToggle();	
+	
+	BOOL lipSyncEnabled();
+	
+	// PTT key triggering
+	void keyDown(KEY key, MASK mask);
+	void keyUp(KEY key, MASK mask);
+	void middleMouseState(bool down);
+	
+	
+	/////////////////////////////
+	// Accessors for data related to nearby speakers
+	BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar
+	std::string getDisplayName(const LLUUID& id);	
+	BOOL isOnlineSIP(const LLUUID &id);
+	BOOL isParticipantAvatar(const LLUUID &id);
+	BOOL getIsSpeaking(const LLUUID& id);
+	BOOL getIsModeratorMuted(const LLUUID& id);
+	F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
+	BOOL getOnMuteList(const LLUUID& id);
+	F32 getUserVolume(const LLUUID& id);
+
+	/////////////////////////////
+	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled.
+													  // Use this to determine whether to show a "no speech" icon in the menu bar.
+	std::vector<LLUUID> getParticipantList(void);
+	
+	//////////////////////////
+	/// @name text chat
+	//@{
+	BOOL isSessionTextIMPossible(const LLUUID& id);
+	BOOL isSessionCallBackPossible(const LLUUID& id);
+	BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message);
+	void endUserIMSession(const LLUUID &uuid);	
+	//@}
+	
 
-		typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
-		status_observer_set_t mStatusObservers;
+	void userAuthorized(const std::string& user_id,
+			const LLUUID &agentID);
+	
+	void addObserver(LLVoiceClientStatusObserver* observer);
+	void removeObserver(LLVoiceClientStatusObserver* observer);
+	void addObserver(LLFriendObserver* observer);
+	void removeObserver(LLFriendObserver* observer);
+	void addObserver(LLVoiceClientParticipantObserver* observer);
+	void removeObserver(LLVoiceClientParticipantObserver* observer);
+	
+	std::string sipURIFromID(const LLUUID &id);	
 		
-		void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
-
-		typedef std::set<LLFriendObserver*> friend_observer_set_t;
-		friend_observer_set_t mFriendObservers;
-		void notifyFriendObservers();
+protected:
+	LLVoiceModuleInterface* mVoiceModule;
+	LLPumpIO *m_servicePump;
+	
 };
 
-extern LLVoiceClient *gVoiceClient;
 
 #endif //LL_VOICE_CLIENT_H
 
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..41c20597b13b7c637a968c4981ddc3159e06fc15
--- /dev/null
+++ b/indra/newview/llvoicevivox.cpp
@@ -0,0 +1,6900 @@
+ /** 
+ * @file LLVivoxVoiceClient.cpp
+ * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llvoicevivox.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llsdutil.h"
+
+#include "llvoavatarself.h"
+#include "llbufferstream.h"
+#include "llfile.h"
+#ifdef LL_STANDALONE
+# include "expat.h"
+#else
+# include "expat/expat.h"
+#endif
+#include "llcallbacklist.h"
+#include "llviewerregion.h"
+#include "llviewernetwork.h"		// for gGridChoice
+#include "llbase64.h"
+#include "llviewercontrol.h"
+#include "llkeyboard.h"
+#include "llappviewer.h"	// for gDisconnected, gDisableVoice
+#include "llmutelist.h"  // to check for muted avatars
+#include "llagent.h"
+#include "llcachename.h"
+#include "llimview.h" // for LLIMMgr
+#include "llparcel.h"
+#include "llviewerparcelmgr.h"
+#include "llfirstuse.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+
+#include "llfloaterfriends.h"  //VIVOX, inorder to refresh communicate panel
+#include "llfloaterchat.h"		// for LLFloaterChat::addChat()
+#include "llviewernetwork.h"
+#include "llnotificationsutil.h"
+
+// for base64 decoding
+#include "apr_base64.h"
+
+// for SHA1 hash
+#include "apr_sha1.h"
+
+// for MD5 hash
+#include "llmd5.h"
+
+#define USE_SESSION_GROUPS 0
+
+const F32 SPEAKING_TIMEOUT = 1.f;
+
+static const std::string VOICE_SERVER_TYPE = "Vivox";
+
+// Don't retry connecting to the daemon more frequently than this:
+const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
+
+// Don't send positional updates more frequently than this:
+const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
+
+const F32 LOGIN_RETRY_SECONDS = 10.0f;
+const int MAX_LOGIN_RETRIES = 12;
+
+static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
+{
+	LLMD5 md5_uuid;
+	md5_uuid.update((const unsigned char*)str.data(), str.size());
+	md5_uuid.finalize();
+	md5_uuid.raw_digest(uuid.mData);
+}
+
+static int scale_mic_volume(float volume)
+{
+	// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.                                                
+	// Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70                                                   
+	return 30 + (int)(volume * 20.0f);
+}
+
+static int scale_speaker_volume(float volume)
+{
+	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.                                                
+	// Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70                                                   
+	return 30 + (int)(volume * 40.0f);
+	
+}
+
+class LLVivoxVoiceAccountProvisionResponder :
+	public LLHTTPClient::Responder
+{
+public:
+	LLVivoxVoiceAccountProvisionResponder(int retries)
+	{
+		mRetries = retries;
+	}
+
+	virtual void error(U32 status, const std::string& reason)
+	{
+		if ( mRetries > 0 )
+		{
+			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
+			LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision(
+				mRetries - 1);
+		}
+		else
+		{
+			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
+			LLVivoxVoiceClient::getInstance()->giveUp();
+		}
+	}
+
+	virtual void result(const LLSD& content)
+	{
+
+		std::string voice_sip_uri_hostname;
+		std::string voice_account_server_uri;
+		
+		LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
+		
+		if(content.has("voice_sip_uri_hostname"))
+			voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
+		
+		// this key is actually misnamed -- it will be an entire URI, not just a hostname.
+		if(content.has("voice_account_server_name"))
+			voice_account_server_uri = content["voice_account_server_name"].asString();
+		
+		LLVivoxVoiceClient::getInstance()->login(
+			content["username"].asString(),
+			content["password"].asString(),
+			voice_sip_uri_hostname,
+			voice_account_server_uri);
+
+	}
+
+private:
+	int mRetries;
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver
+{
+	/* virtual */ void onChange()  { LLVivoxVoiceClient::getInstance()->muteListChanged();}
+};
+
+class LLVivoxVoiceClientFriendsObserver : public LLFriendObserver
+{
+public:
+	/* virtual */ void changed(U32 mask) { LLVivoxVoiceClient::getInstance()->updateFriends(mask);}
+};
+
+static LLVivoxVoiceClientMuteListObserver mutelist_listener;
+static bool sMuteListListener_listening = false;
+
+static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL;
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder
+{
+public:
+	LLVivoxVoiceClientCapResponder(void){};
+
+	virtual void error(U32 status, const std::string& reason);	// called with bad status codes
+	virtual void result(const LLSD& content);
+
+private:
+};
+
+void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason)
+{
+	LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder::error("
+		<< status << ": " << reason << ")"
+		<< LL_ENDL;
+}
+
+void LLVivoxVoiceClientCapResponder::result(const LLSD& content)
+{
+	LLSD::map_const_iterator iter;
+	
+	LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
+
+	if ( content.has("voice_credentials") )
+	{
+		LLSD voice_credentials = content["voice_credentials"];
+		std::string uri;
+		std::string credentials;
+
+		if ( voice_credentials.has("channel_uri") )
+		{
+			uri = voice_credentials["channel_uri"].asString();
+		}
+		if ( voice_credentials.has("channel_credentials") )
+		{
+			credentials =
+				voice_credentials["channel_credentials"].asString();
+		}
+
+		LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials);
+	}
+}
+
+
+
+#if LL_WINDOWS
+static HANDLE sGatewayHandle = 0;
+
+static bool isGatewayRunning()
+{
+	bool result = false;
+	if(sGatewayHandle != 0)		
+	{
+		DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
+		if(waitresult != WAIT_OBJECT_0)
+		{
+			result = true;
+		}			
+	}
+	return result;
+}
+static void killGateway()
+{
+	if(sGatewayHandle != 0)
+	{
+		TerminateProcess(sGatewayHandle,0);
+	}
+}
+
+#else // Mac and linux
+
+static pid_t sGatewayPID = 0;
+static bool isGatewayRunning()
+{
+	bool result = false;
+	if(sGatewayPID != 0)
+	{
+		// A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
+		if(kill(sGatewayPID, 0) == 0)
+		{
+			result = true;
+		}
+	}
+	return result;
+}
+
+static void killGateway()
+{
+	if(sGatewayPID != 0)
+	{
+		kill(sGatewayPID, SIGTERM);
+	}
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+LLVivoxVoiceClient::LLVivoxVoiceClient() :
+	mState(stateDisabled),
+	mSessionTerminateRequested(false),
+	mRelogRequested(false),
+	mConnected(false),
+	mPump(NULL),
+
+	mTuningMode(false),
+	mTuningEnergy(0.0f),
+	mTuningMicVolume(0),
+	mTuningMicVolumeDirty(true),
+	mTuningSpeakerVolume(0),
+	mTuningSpeakerVolumeDirty(true),
+	mTuningExitState(stateDisabled),
+
+	mAreaVoiceDisabled(false),
+	mAudioSession(NULL),
+	mAudioSessionChanged(false),
+	mNextAudioSession(NULL),
+
+	mCurrentParcelLocalID(0),
+	mNumberOfAliases(0),
+	mCommandCookie(0),
+	mLoginRetryCount(0),
+
+	mBuddyListMapPopulated(false),
+	mBlockRulesListReceived(false),
+	mAutoAcceptRulesListReceived(false),
+	mCaptureDeviceDirty(false),
+	mRenderDeviceDirty(false),
+	mSpatialCoordsDirty(false),
+
+	mPTTDirty(true),
+	mPTT(true),
+	mUsePTT(true),
+	mPTTIsMiddleMouse(false),
+	mPTTKey(0),
+	mPTTIsToggle(false),
+	mUserPTTState(false),
+	mMuteMic(false),
+	mFriendsListDirty(true),
+
+	mEarLocation(0),
+	mSpeakerVolumeDirty(true),
+	mSpeakerMuteDirty(true),
+	mMicVolume(0),
+	mMicVolumeDirty(true),
+
+	mVoiceEnabled(false),
+	mWriteInProgress(false),
+
+	mLipSyncEnabled(false)
+
+
+
+{	
+	mSpeakerVolume = scale_speaker_volume(0);
+
+	mVoiceVersion.serverVersion = "";
+	mVoiceVersion.serverType = VOICE_SERVER_TYPE;
+	
+	//  gMuteListp isn't set up at this point, so we defer this until later.
+//	gMuteListp->addObserver(&mutelist_listener);
+	
+	
+#if LL_DARWIN || LL_LINUX || LL_SOLARIS
+		// HACK: THIS DOES NOT BELONG HERE
+		// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
+		// This should cause us to ignore SIGPIPE and handle the error through proper channels.
+		// This should really be set up elsewhere.  Where should it go?
+		signal(SIGPIPE, SIG_IGN);
+		
+		// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
+		// Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that.
+		signal(SIGCHLD, SIG_IGN);
+#endif
+
+	// set up state machine
+	setState(stateDisabled);
+	
+	gIdleCallbacks.addFunction(idle, this);
+}
+
+//---------------------------------------------------
+
+LLVivoxVoiceClient::~LLVivoxVoiceClient()
+{
+}
+
+//----------------------------------------------
+
+void LLVivoxVoiceClient::init(LLPumpIO *pump)
+{
+	// constructor will set up LLVoiceClient::getInstance()
+	LLVivoxVoiceClient::getInstance()->mPump = pump;
+}
+
+void LLVivoxVoiceClient::terminate()
+{
+
+//	leaveAudioSession();
+	logout();
+	// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to.
+	// ms_sleep(2000);
+	connectorShutdown();
+	closeSocket();		// Need to do this now -- bad things happen if the destructor does it later.
+	
+	// This will do unpleasant things on windows.
+//	killGateway();
+	
+
+
+}
+
+const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion()
+{
+	return mVoiceVersion;
+}
+
+//---------------------------------------------------
+
+void LLVivoxVoiceClient::updateSettings()
+{
+	setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
+	setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled"));
+	std::string keyString = gSavedSettings.getString("PushToTalkButton");
+	setPTTKey(keyString);
+	setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
+	setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
+
+	std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+	setCaptureDevice(inputDevice);
+	std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+	setRenderDevice(outputDevice);
+	F32 mic_level = gSavedSettings.getF32("AudioLevelMic");
+	setMicGain(mic_level);
+	setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled"));
+}
+
+/////////////////////////////
+// utility functions
+
+bool LLVivoxVoiceClient::writeString(const std::string &str)
+{
+	bool result = false;
+	if(mConnected)
+	{
+		apr_status_t err;
+		apr_size_t size = (apr_size_t)str.size();
+		apr_size_t written = size;
+	
+		//MARK: Turn this on to log outgoing XML
+//		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
+
+		// check return code - sockets will fail (broken, etc.)
+		err = apr_socket_send(
+				mSocket->getSocket(),
+				(const char*)str.data(),
+				&written);
+		
+		if(err == 0)
+		{
+			// Success.
+			result = true;
+		}
+		// TODO: handle partial writes (written is number of bytes written)
+		// Need to set socket to non-blocking before this will work.
+//		else if(APR_STATUS_IS_EAGAIN(err))
+//		{
+//			// 
+//		}
+		else
+		{
+			// Assume any socket error means something bad.  For now, just close the socket.
+			char buf[MAX_STRING];
+			LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
+			daemonDied();
+		}
+	}
+		
+	return result;
+}
+
+
+/////////////////////////////
+// session control messages
+void LLVivoxVoiceClient::connectorCreate()
+{
+	std::ostringstream stream;
+	std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+	std::string loglevel = "0";
+	
+	// Transition to stateConnectorStarted when the connector handle comes back.
+	setState(stateConnectorStarting);
+
+	std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
+		
+	if(savedLogLevel != "-1")
+	{
+		LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
+		loglevel = "10";
+	}
+	
+	stream 
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
+		<< "<ClientName>V2 SDK</ClientName>"
+		<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
+		<< "<Mode>Normal</Mode>"
+		<< "<Logging>"
+			<< "<Folder>" << logpath << "</Folder>"
+			<< "<FileNamePrefix>Connector</FileNamePrefix>"
+			<< "<FileNameSuffix>.log</FileNameSuffix>"
+			<< "<LogLevel>" << loglevel << "</LogLevel>"
+		<< "</Logging>"
+		<< "<Application>SecondLifeViewer.1</Application>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::connectorShutdown()
+{
+	setState(stateConnectorStopping);
+	
+	if(!mConnectorHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
+			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+		
+		mConnectorHandle.clear();
+		
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)
+{
+
+	mAccountDisplayName = user_id;
+
+	LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL;
+
+	mAccountName = nameFromID(agentID);
+}
+
+void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
+{
+	if ( gAgent.getRegion() && mVoiceEnabled )
+	{
+		std::string url = 
+			gAgent.getRegion()->getCapability(
+				"ProvisionVoiceAccountRequest");
+
+		if ( url == "" ) return;
+
+		LLHTTPClient::post(
+			url,
+			LLSD(),
+			new LLVivoxVoiceAccountProvisionResponder(retries));
+	}
+}
+
+void LLVivoxVoiceClient::login(
+	const std::string& account_name,
+	const std::string& password,
+	const std::string& voice_sip_uri_hostname,
+	const std::string& voice_account_server_uri)
+{
+	mVoiceSIPURIHostName = voice_sip_uri_hostname;
+	mVoiceAccountServerURI = voice_account_server_uri;
+
+	if(!mAccountHandle.empty())
+	{
+		// Already logged in.
+		LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL;
+		
+		// Don't process another login.
+		return;
+	}
+	else if ( account_name != mAccountName )
+	{
+		//TODO: error?
+		LL_WARNS("Voice") << "Wrong account name! " << account_name
+				<< " instead of " << mAccountName << LL_ENDL;
+	}
+	else
+	{
+		mAccountPassword = password;
+	}
+
+	std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
+	
+	if( !debugSIPURIHostName.empty() )
+	{
+		mVoiceSIPURIHostName = debugSIPURIHostName;
+	}
+	
+	if( mVoiceSIPURIHostName.empty() )
+	{
+		// we have an empty account server name
+		// so we fall back to hardcoded defaults
+
+		if(LLGridManager::getInstance()->isInProductionGrid())
+		{
+			// Use the release account server
+			mVoiceSIPURIHostName = "bhr.vivox.com";
+		}
+		else
+		{
+			// Use the development account server
+			mVoiceSIPURIHostName = "bhd.vivox.com";
+		}
+	}
+	
+	std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
+
+	if( !debugAccountServerURI.empty() )
+	{
+		mVoiceAccountServerURI = debugAccountServerURI;
+	}
+	
+	if( mVoiceAccountServerURI.empty() )
+	{
+		// If the account server URI isn't specified, construct it from the SIP URI hostname
+		mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";		
+	}
+}
+
+void LLVivoxVoiceClient::idle(void* user_data)
+{
+	LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data;
+	self->stateMachine();
+}
+
+std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState)
+{
+	std::string result = "UNKNOWN";
+	
+		// Prevent copy-paste errors when updating this list...
+#define CASE(x)  case x:  result = #x;  break
+
+	switch(inState)
+	{
+		CASE(stateDisableCleanup);
+		CASE(stateDisabled);
+		CASE(stateStart);
+		CASE(stateDaemonLaunched);
+		CASE(stateConnecting);
+		CASE(stateConnected);
+		CASE(stateIdle);
+		CASE(stateMicTuningStart);
+		CASE(stateMicTuningRunning);
+		CASE(stateMicTuningStop);
+		CASE(stateConnectorStart);
+		CASE(stateConnectorStarting);
+		CASE(stateConnectorStarted);
+		CASE(stateLoginRetry);
+		CASE(stateLoginRetryWait);
+		CASE(stateNeedsLogin);
+		CASE(stateLoggingIn);
+		CASE(stateLoggedIn);
+		CASE(stateCreatingSessionGroup);
+		CASE(stateNoChannel);
+		CASE(stateJoiningSession);
+		CASE(stateSessionJoined);
+		CASE(stateRunning);
+		CASE(stateLeavingSession);
+		CASE(stateSessionTerminated);
+		CASE(stateLoggingOut);
+		CASE(stateLoggedOut);
+		CASE(stateConnectorStopping);
+		CASE(stateConnectorStopped);
+		CASE(stateConnectorFailed);
+		CASE(stateConnectorFailedWaiting);
+		CASE(stateLoginFailed);
+		CASE(stateLoginFailedWaiting);
+		CASE(stateJoinSessionFailed);
+		CASE(stateJoinSessionFailedWaiting);
+		CASE(stateJail);
+	}
+
+#undef CASE
+	
+	return result;
+}
+
+
+
+void LLVivoxVoiceClient::setState(state inState)
+{
+	LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
+	
+	mState = inState;
+}
+
+void LLVivoxVoiceClient::stateMachine()
+{
+	if(gDisconnected)
+	{
+		// The viewer has been disconnected from the sim.  Disable voice.
+		setVoiceEnabled(false);
+	}
+	
+	if(mVoiceEnabled)
+	{
+		updatePosition();
+	}
+	else if(mTuningMode)
+	{
+		// Tuning mode is special -- it needs to launch SLVoice even if voice is disabled.
+	}
+	else
+	{
+		if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
+		{
+			// User turned off voice support.  Send the cleanup messages, close the socket, and reset.
+			if(!mConnected)
+			{
+				// if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
+				LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
+				killGateway();
+			}
+			
+			logout();
+			connectorShutdown();
+			
+			setState(stateDisableCleanup);
+		}
+	}
+	
+	// Check for parcel boundary crossing
+	{
+		LLViewerRegion *region = gAgent.getRegion();
+		LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+		
+		if(region && parcel)
+		{
+			S32 parcelLocalID = parcel->getLocalID();
+			std::string regionName = region->getName();
+			std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
+		
+//			LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL;
+
+			// The region name starts out empty and gets filled in later.  
+			// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
+			// If either is empty, wait for the next time around.
+			if(!regionName.empty())
+			{
+				if(!capURI.empty())
+				{
+					if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
+					{
+						// We have changed parcels.  Initiate a parcel channel lookup.
+						mCurrentParcelLocalID = parcelLocalID;
+						mCurrentRegionName = regionName;
+						
+						parcelChanged();
+					}
+				}
+				else
+				{
+					LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability.  This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL;
+				}
+			}
+		}
+	}
+
+	switch(getState())
+	{
+		//MARK: stateDisableCleanup
+		case stateDisableCleanup:
+			// Clean up and reset everything. 
+			closeSocket();
+			deleteAllSessions();
+			deleteAllBuddies();		
+			
+			mConnectorHandle.clear();
+			mAccountHandle.clear();
+			mAccountPassword.clear();
+			mVoiceAccountServerURI.clear();
+			
+			setState(stateDisabled);	
+		break;
+		
+		//MARK: stateDisabled
+		case stateDisabled:
+			if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
+			{
+				setState(stateStart);
+			}
+		break;
+		
+		//MARK: stateStart
+		case stateStart:
+			if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
+			{
+				// Voice is locked out, we must not launch the vivox daemon.
+				setState(stateJail);
+			}
+			else if(!isGatewayRunning())
+			{
+				if(true)
+				{
+					// Launch the voice daemon
+					
+					// *FIX:Mani - Using the executable dir instead 
+					// of mAppRODataDir, the working directory from which the app
+					// is launched.
+					//std::string exe_path = gDirUtilp->getAppRODataDir();
+					std::string exe_path = gDirUtilp->getExecutableDir();
+					exe_path += gDirUtilp->getDirDelimiter();
+#if LL_WINDOWS
+					exe_path += "SLVoice.exe";
+#elif LL_DARWIN
+					exe_path += "../Resources/SLVoice";
+#else
+					exe_path += "SLVoice";
+#endif
+					// See if the vivox executable exists
+					llstat s;
+					if(!LLFile::stat(exe_path, &s))
+					{
+						// vivox executable exists.  Build the command line and launch the daemon.
+						// SLIM SDK: these arguments are no longer necessary.
+//						std::string args = " -p tcp -h -c";
+						std::string args;
+						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+						
+						if(loglevel.empty())
+						{
+							loglevel = "-1";	// turn logging off completely
+						}
+						
+						args += " -ll ";
+						args += loglevel;
+						
+						LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
+
+#if LL_WINDOWS
+						PROCESS_INFORMATION pinfo;
+						STARTUPINFOW sinfo;
+						
+						memset(&sinfo, 0, sizeof(sinfo));
+						
+						std::string exe_dir = gDirUtilp->getExecutableDir();
+						
+						llutf16string exe_path16 = utf8str_to_utf16str(exe_path);
+						llutf16string exe_dir16 = utf8str_to_utf16str(exe_dir);
+						llutf16string args16 = utf8str_to_utf16str(args);
+						// Create a writeable copy to keep Windows happy.                               
+						U16 *argscpy_16 = new U16[args16.size() + 1];
+						wcscpy_s(argscpy_16,args16.size()+1,args16.c_str());
+
+						if(!CreateProcessW(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
+						{
+//							DWORD dwErr = GetLastError();
+						}
+						else
+						{
+							// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
+							// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
+							sGatewayHandle = pinfo.hProcess;
+							CloseHandle(pinfo.hThread); // stops leaks - nothing else
+						}		
+						
+						delete[] argscpy_16;
+#else	// LL_WINDOWS
+						// This should be the same for mac and linux
+						{
+							std::vector<std::string> arglist;
+							arglist.push_back(exe_path);
+							
+							// Split the argument string into separate strings for each argument
+							typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+							boost::char_separator<char> sep(" ");
+							tokenizer tokens(args, sep);
+							tokenizer::iterator token_iter;
+
+							for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+							{
+								arglist.push_back(*token_iter);
+							}
+							
+							// create an argv vector for the child process
+							char **fakeargv = new char*[arglist.size() + 1];
+							int i;
+							for(i=0; i < arglist.size(); i++)
+								fakeargv[i] = const_cast<char*>(arglist[i].c_str());
+
+							fakeargv[i] = NULL;
+							
+							fflush(NULL); // flush all buffers before the child inherits them
+							pid_t id = vfork();
+							if(id == 0)
+							{
+								// child
+								execv(exe_path.c_str(), fakeargv);
+								
+								// If we reach this point, the exec failed.
+								// Use _exit() instead of exit() per the vfork man page.
+								_exit(0);
+							}
+
+							// parent
+							delete[] fakeargv;
+							sGatewayPID = id;
+						}
+#endif	// LL_WINDOWS
+						mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
+					}	
+					else
+					{
+						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
+					}	
+				}
+				else
+				{		
+					// SLIM SDK: port changed from 44124 to 44125.
+					// We can connect to a client gateway running on another host.  This is useful for testing.
+					// To do this, launch the gateway on a nearby host like this:
+					//  vivox-gw.exe -p tcp -i 0.0.0.0:44125
+					// and put that host's IP address here.
+					mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
+				}
+
+				mUpdateTimer.start();
+				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+				setState(stateDaemonLaunched);
+				
+				// Dirty the states we'll need to sync with the daemon when it comes up.
+				mPTTDirty = true;
+				mMicVolumeDirty = true;
+				mSpeakerVolumeDirty = true;
+				mSpeakerMuteDirty = true;
+				// These only need to be set if they're not default (i.e. empty string).
+				mCaptureDeviceDirty = !mCaptureDevice.empty();
+				mRenderDeviceDirty = !mRenderDevice.empty();
+				
+				mMainSessionGroupHandle.clear();
+			}
+		break;
+
+		//MARK: stateDaemonLaunched
+		case stateDaemonLaunched:
+			if(mUpdateTimer.hasExpired())
+			{
+				LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
+
+				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+				if(!mSocket)
+				{
+					mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);	
+				}
+				
+				mConnected = mSocket->blockingConnect(mDaemonHost);
+				if(mConnected)
+				{
+					setState(stateConnecting);
+				}
+				else
+				{
+					// If the connect failed, the socket may have been put into a bad state.  Delete it.
+					closeSocket();
+				}
+			}
+		break;
+
+		//MARK: stateConnecting
+		case stateConnecting:
+		// Can't do this until we have the pump available.
+		if(mPump)
+		{
+			// MBW -- Note to self: pumps and pipes examples in
+			//  indra/test/io.cpp
+			//  indra/test/llpipeutil.{cpp|h}
+
+			// Attach the pumps and pipes
+				
+			LLPumpIO::chain_t readChain;
+
+			readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+			readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+
+			mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+
+			setState(stateConnected);
+		}
+
+		break;
+		
+		//MARK: stateConnected
+		case stateConnected:
+			// Initial devices query
+			getCaptureDevicesSendMessage();
+			getRenderDevicesSendMessage();
+
+			mLoginRetryCount = 0;
+
+			setState(stateIdle);
+		break;
+
+		//MARK: stateIdle
+		case stateIdle:
+			// This is the idle state where we're connected to the daemon but haven't set up a connector yet.
+			if(mTuningMode)
+			{
+				mTuningExitState = stateIdle;
+				setState(stateMicTuningStart);
+			}
+			else if(!mVoiceEnabled)
+			{
+				// We never started up the connector.  This will shut down the daemon.
+				setState(stateConnectorStopped);
+			}
+			else if(!mAccountName.empty())
+			{
+				LLViewerRegion *region = gAgent.getRegion();
+				
+				if(region)
+				{
+					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
+					{
+						if ( mAccountPassword.empty() )
+						{
+							requestVoiceAccountProvision();
+						}
+						setState(stateConnectorStart);
+					}
+					else
+					{
+						LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL;
+					}
+				}
+			}
+		break;
+
+		//MARK: stateMicTuningStart
+		case stateMicTuningStart:
+			if(mUpdateTimer.hasExpired())
+			{
+				if(mCaptureDeviceDirty || mRenderDeviceDirty)
+				{
+					// These can't be changed while in tuning mode.  Set them before starting.
+					std::ostringstream stream;
+					
+					buildSetCaptureDevice(stream);
+					buildSetRenderDevice(stream);
+
+					if(!stream.str().empty())
+					{
+						writeString(stream.str());
+					}				
+
+					// This will come around again in the same state and start the capture, after the timer expires.
+					mUpdateTimer.start();
+					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+				}
+				else
+				{
+					// duration parameter is currently unused, per Mike S.
+					tuningCaptureStartSendMessage(10000);
+
+					setState(stateMicTuningRunning);
+				}
+			}
+			
+		break;
+		
+		//MARK: stateMicTuningRunning
+		case stateMicTuningRunning:
+			if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
+			{
+				// All of these conditions make us leave tuning mode.
+				setState(stateMicTuningStop);
+			}
+			else
+			{
+				// process mic/speaker volume changes
+				if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
+				{
+					std::ostringstream stream;
+					
+					if(mTuningMicVolumeDirty)
+					{
+						LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
+						stream
+						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+						<< "<Level>" << mTuningMicVolume << "</Level>"
+						<< "</Request>\n\n\n";
+					}
+					
+					if(mTuningSpeakerVolumeDirty)
+					{
+						stream
+						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+						<< "<Level>" << mTuningSpeakerVolume << "</Level>"
+						<< "</Request>\n\n\n";
+					}
+					
+					mTuningMicVolumeDirty = false;
+					mTuningSpeakerVolumeDirty = false;
+
+					if(!stream.str().empty())
+					{
+						writeString(stream.str());
+					}
+				}
+			}
+		break;
+		
+		//MARK: stateMicTuningStop
+		case stateMicTuningStop:
+		{
+			// transition out of mic tuning
+			tuningCaptureStopSendMessage();
+			
+			setState(mTuningExitState);
+			
+			// if we exited just to change devices, this will keep us from re-entering too fast.
+			mUpdateTimer.start();
+			mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+			
+		}
+		break;
+												
+		//MARK: stateConnectorStart
+		case stateConnectorStart:
+			if(!mVoiceEnabled)
+			{
+				// We were never logged in.  This will shut down the connector.
+				setState(stateLoggedOut);
+			}
+			else if(!mVoiceAccountServerURI.empty())
+			{
+				connectorCreate();
+			}
+		break;
+		
+		//MARK: stateConnectorStarting
+		case stateConnectorStarting:	// waiting for connector handle
+			// connectorCreateResponse() will transition from here to stateConnectorStarted.
+		break;
+		
+		//MARK: stateConnectorStarted
+		case stateConnectorStarted:		// connector handle received
+			if(!mVoiceEnabled)
+			{
+				// We were never logged in.  This will shut down the connector.
+				setState(stateLoggedOut);
+			}
+			else
+			{
+				// The connector is started.  Send a login message.
+				setState(stateNeedsLogin);
+			}
+		break;
+				
+		//MARK: stateLoginRetry
+		case stateLoginRetry:
+			if(mLoginRetryCount == 0)
+			{
+				// First retry -- display a message to the user
+				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+			}
+			
+			mLoginRetryCount++;
+			
+			if(mLoginRetryCount > MAX_LOGIN_RETRIES)
+			{
+				LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
+				setState(stateLoginFailed);
+				LLSD args;
+				std::stringstream errs;
+				errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
+				args["HOSTID"] = errs.str();
+				if (LLGridManager::getInstance()->isSystemGrid())
+				{
+					LLNotificationsUtil::add("NoVoiceConnect", args);	
+				}
+				else
+				{
+					LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);	
+				}				
+			}
+			else
+			{
+				LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
+				mUpdateTimer.start();
+				mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
+				setState(stateLoginRetryWait);
+			}
+		break;
+		
+		//MARK: stateLoginRetryWait
+		case stateLoginRetryWait:
+			if(mUpdateTimer.hasExpired())
+			{
+				setState(stateNeedsLogin);
+			}
+		break;
+		
+		//MARK: stateNeedsLogin
+		case stateNeedsLogin:
+			if(!mAccountPassword.empty())
+			{
+				setState(stateLoggingIn);
+				loginSendMessage();
+			}		
+		break;
+		
+		//MARK: stateLoggingIn
+		case stateLoggingIn:			// waiting for account handle
+			// loginResponse() will transition from here to stateLoggedIn.
+		break;
+		
+		//MARK: stateLoggedIn
+		case stateLoggedIn:				// account handle received
+
+			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+			// request the current set of block rules (we'll need them when updating the friends list)
+			accountListBlockRulesSendMessage();
+			
+			// request the current set of auto-accept rules
+			accountListAutoAcceptRulesSendMessage();
+			
+			// Set up the mute list observer if it hasn't been set up already.
+			if((!sMuteListListener_listening))
+			{
+				LLMuteList::getInstance()->addObserver(&mutelist_listener);
+				sMuteListListener_listening = true;
+			}
+
+			// Set up the friends list observer if it hasn't been set up already.
+			if(friendslist_listener == NULL)
+			{
+				friendslist_listener = new LLVivoxVoiceClientFriendsObserver;
+				LLAvatarTracker::instance().addObserver(friendslist_listener);
+			}
+			
+			// Set the initial state of mic mute, local speaker volume, etc.
+			{
+				std::ostringstream stream;
+				
+				buildLocalAudioUpdates(stream);
+				
+				if(!stream.str().empty())
+				{
+					writeString(stream.str());
+				}
+			}
+			
+#if USE_SESSION_GROUPS			
+			// create the main session group
+			sessionGroupCreateSendMessage();
+			
+			setState(stateCreatingSessionGroup);
+#else
+			// Not using session groups -- skip the stateCreatingSessionGroup state.
+			setState(stateNoChannel);
+
+			// Initial kick-off of channel lookup logic
+			parcelChanged();		
+#endif
+		break;
+		
+		//MARK: stateCreatingSessionGroup
+		case stateCreatingSessionGroup:
+			if(mSessionTerminateRequested || !mVoiceEnabled)
+			{
+				// *TODO: Question: is this the right way out of this state
+				setState(stateSessionTerminated);
+			}
+			else if(!mMainSessionGroupHandle.empty())
+			{
+				setState(stateNoChannel);
+				
+				// Start looped recording (needed for "panic button" anti-griefing tool)
+				recordingLoopStart();
+
+				// Initial kick-off of channel lookup logic
+				parcelChanged();		
+			}
+		break;
+					
+		//MARK: stateNoChannel
+		case stateNoChannel:
+			
+			LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL;
+			// Do this here as well as inside sendPositionalUpdate().  
+			// 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.
+			sendFriendsListUpdates();
+			
+			if(mSessionTerminateRequested || !mVoiceEnabled)
+			{
+				// TODO: Question: Is this the right way out of this state?
+				setState(stateSessionTerminated);
+			}
+			else if(mTuningMode)
+			{
+				mTuningExitState = stateNoChannel;
+				setState(stateMicTuningStart);
+			}
+			else if(sessionNeedsRelog(mNextAudioSession))
+			{
+				requestRelog();
+				setState(stateSessionTerminated);
+			}
+			else if(mNextAudioSession)
+			{				
+				sessionState *oldSession = mAudioSession;
+
+				mAudioSession = mNextAudioSession;
+				if(!mAudioSession->mReconnect)	
+				{
+					mNextAudioSession = NULL;
+				}
+				
+				// The old session may now need to be deleted.
+				reapSession(oldSession);
+				
+				if(!mAudioSession->mHandle.empty())
+				{
+					// Connect to a session by session handle
+
+					sessionMediaConnectSendMessage(mAudioSession);
+				}
+				else
+				{
+					// Connect to a session by URI
+					sessionCreateSendMessage(mAudioSession, true, false);
+				}
+
+				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+				setState(stateJoiningSession);
+			}
+			else if(!mSpatialSessionURI.empty())
+			{
+				// If we're not headed elsewhere and have a spatial URI, return to spatial.
+				switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
+			}
+		break;
+
+		//MARK: stateJoiningSession
+		case stateJoiningSession:		// waiting for session handle
+			// joinedAudioSession() will transition from here to stateSessionJoined.
+			if(!mVoiceEnabled)
+			{
+				// User bailed out during connect -- jump straight to teardown.
+				setState(stateSessionTerminated);
+			}
+			else if(mSessionTerminateRequested)
+			{
+				if(mAudioSession && !mAudioSession->mHandle.empty())
+				{
+					// Only allow direct exits from this state in p2p calls (for cancelling an invite).
+					// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+					if(mAudioSession->mIsP2P)
+					{
+						sessionMediaDisconnectSendMessage(mAudioSession);
+						setState(stateSessionTerminated);
+					}
+				}
+			}
+		break;
+		
+		//MARK: stateSessionJoined
+		case stateSessionJoined:		// session handle received
+			// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
+			// before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
+			// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+			// This is a cheap way to make sure both have happened before proceeding.
+			if(mAudioSession && mAudioSession->mVoiceEnabled)
+			{
+				// Dirty state that may need to be sync'ed with the daemon.
+				mPTTDirty = true;
+				mSpeakerVolumeDirty = true;
+				mSpatialCoordsDirty = true;
+				
+				setState(stateRunning);
+				
+				// Start the throttle timer
+				mUpdateTimer.start();
+				mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+
+				// Events that need to happen when a session is joined could go here.
+				// Maybe send initial spatial data?
+				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
+			}
+			else if(!mVoiceEnabled)
+			{
+				// User bailed out during connect -- jump straight to teardown.
+				setState(stateSessionTerminated);
+			}
+			else if(mSessionTerminateRequested)
+			{
+				// Only allow direct exits from this state in p2p calls (for cancelling an invite).
+				// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+				if(mAudioSession && mAudioSession->mIsP2P)
+				{
+					sessionMediaDisconnectSendMessage(mAudioSession);
+					setState(stateSessionTerminated);
+				}
+			}
+		break;
+		
+		//MARK: stateRunning
+		case stateRunning:				// steady state
+			// Disabling voice or disconnect requested.
+			if(!mVoiceEnabled || mSessionTerminateRequested)
+			{
+				leaveAudioSession();
+			}
+			else
+			{
+				
+				// Figure out whether the PTT state needs to change
+				{
+					bool newPTT;
+					if(mUsePTT)
+					{
+						// If configured to use PTT, track the user state.
+						newPTT = mUserPTTState;
+					}
+					else
+					{
+						// If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
+						newPTT = true;
+					}
+					
+					if(mMuteMic)
+					{
+						// This always overrides any other PTT setting.
+						newPTT = false;
+					}
+					
+					// Dirty if state changed.
+					if(newPTT != mPTT)
+					{
+						mPTT = newPTT;
+						mPTTDirty = true;
+					}
+				}
+				
+				if(!inSpatialChannel())
+				{
+					// When in a non-spatial channel, never send positional updates.
+					mSpatialCoordsDirty = false;
+				}
+				else
+				{
+					// Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
+					enforceTether();
+				}
+				
+				// 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)
+				// or every 10hz, whichever is sooner.
+				if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
+				{
+					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+					sendPositionalUpdate();
+				}
+			}
+		break;
+		
+		//MARK: stateLeavingSession
+		case stateLeavingSession:		// waiting for terminate session response
+			// The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
+		break;
+
+		//MARK: stateSessionTerminated
+		case stateSessionTerminated:
+			
+			// Must do this first, since it uses mAudioSession.
+			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+			
+			if(mAudioSession)
+			{
+				sessionState *oldSession = mAudioSession;
+
+				mAudioSession = NULL;
+				// We just notified status observers about this change.  Don't do it again.
+				mAudioSessionChanged = false;
+
+				// The old session may now need to be deleted.
+				reapSession(oldSession);
+			}
+			else
+			{
+				LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
+			}
+	
+			// Always reset the terminate request flag when we get here.
+			mSessionTerminateRequested = false;
+
+			if(mVoiceEnabled && !mRelogRequested)
+			{				
+				// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
+				setState(stateNoChannel);
+			}
+			else
+			{
+				// Shutting down voice, continue with disconnecting.
+				logout();
+				
+				// The state machine will take it from here
+				mRelogRequested = false;
+			}
+			
+		break;
+		
+		//MARK: stateLoggingOut
+		case stateLoggingOut:			// waiting for logout response
+			// The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
+		break;
+		
+		//MARK: stateLoggedOut
+		case stateLoggedOut:			// logout response received
+			
+			// Once we're logged out, all these things are invalid.
+			mAccountHandle.clear();
+			deleteAllSessions();
+			deleteAllBuddies();
+
+			if(mVoiceEnabled && !mRelogRequested)
+			{
+				// User was logged out, but wants to be logged in.  Send a new login request.
+				setState(stateNeedsLogin);
+			}
+			else
+			{
+				// shut down the connector
+				connectorShutdown();
+			}
+		break;
+		
+		//MARK: stateConnectorStopping
+		case stateConnectorStopping:	// waiting for connector stop
+			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
+		break;
+
+		//MARK: stateConnectorStopped
+		case stateConnectorStopped:		// connector stop received
+			setState(stateDisableCleanup);
+		break;
+
+		//MARK: stateConnectorFailed
+		case stateConnectorFailed:
+			setState(stateConnectorFailedWaiting);
+		break;
+		//MARK: stateConnectorFailedWaiting
+		case stateConnectorFailedWaiting:
+			if(!mVoiceEnabled)
+			{
+				setState(stateDisableCleanup);
+			}
+		break;
+
+		//MARK: stateLoginFailed
+		case stateLoginFailed:
+			setState(stateLoginFailedWaiting);
+		break;
+		//MARK: stateLoginFailedWaiting
+		case stateLoginFailedWaiting:
+			if(!mVoiceEnabled)
+			{
+				setState(stateDisableCleanup);
+			}
+		break;
+
+		//MARK: stateJoinSessionFailed
+		case stateJoinSessionFailed:
+			// Transition to error state.  Send out any notifications here.
+			if(mAudioSession)
+			{
+				LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
+			}
+			else
+			{
+				LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
+			}
+			
+			notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+			setState(stateJoinSessionFailedWaiting);
+		break;
+		
+		//MARK: stateJoinSessionFailedWaiting
+		case stateJoinSessionFailedWaiting:
+			// Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
+			// Region crossings may leave this state and try the join again.
+			if(mSessionTerminateRequested)
+			{
+				setState(stateSessionTerminated);
+			}
+		break;
+		
+		//MARK: stateJail
+		case stateJail:
+			// We have given up.  Do nothing.
+		break;
+
+	}
+	
+	if(mAudioSession && mAudioSession->mParticipantsChanged)
+	{
+		mAudioSession->mParticipantsChanged = false;
+		mAudioSessionChanged = true;
+	}
+	
+	if(mAudioSessionChanged)
+	{
+		mAudioSessionChanged = false;
+		notifyParticipantObservers();
+	}
+}
+
+void LLVivoxVoiceClient::closeSocket(void)
+{
+	mSocket.reset();
+	mConnected = false;	
+}
+
+void LLVivoxVoiceClient::loginSendMessage()
+{
+	std::ostringstream stream;
+
+	bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps");
+
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
+		<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+		<< "<AccountName>" << mAccountName << "</AccountName>"
+		<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
+		<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
+		<< "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
+		<< "<BuddyManagementMode>Application</BuddyManagementMode>"
+		<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
+		<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::logout()
+{
+	// Ensure that we'll re-request provisioning before logging in again
+	mAccountPassword.clear();
+	mVoiceAccountServerURI.clear();
+	
+	setState(stateLoggingOut);
+	logoutSendMessage();
+}
+
+void LLVivoxVoiceClient::logoutSendMessage()
+{
+	if(!mAccountHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		mAccountHandle.clear();
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::accountListBlockRulesSendMessage()
+{
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
+
+		LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::accountListAutoAcceptRulesSendMessage()
+{
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
+
+		LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::sessionGroupCreateSendMessage()
+{
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
+
+		LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+			<< "<Type>Normal</Type>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
+{
+	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
+	
+	session->mCreateInProgress = true;
+	if(startAudio)
+	{
+		session->mMediaConnectInProgress = true;
+	}
+
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
+		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "<URI>" << session->mSIPURI << "</URI>";
+
+	static const std::string allowed_chars =
+				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+				"0123456789"
+				"-._~";
+
+	if(!session->mHash.empty())
+	{
+		stream
+			<< "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
+			<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
+	}
+	
+	stream
+		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
+		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
+		<< "<Name>" << mChannelName << "</Name>"
+	<< "</Request>\n\n\n";
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
+{
+	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
+	
+	session->mCreateInProgress = true;
+	if(startAudio)
+	{
+		session->mMediaConnectInProgress = true;
+	}
+	
+	std::string password;
+	if(!session->mHash.empty())
+	{
+		static const std::string allowed_chars =
+					"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+					"0123456789"
+					"-._~"
+					;
+		password = LLURI::escape(session->mHash, allowed_chars);
+	}
+
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<URI>" << session->mSIPURI << "</URI>"
+		<< "<Name>" << mChannelName << "</Name>"
+		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
+		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
+		<< "<Password>" << password << "</Password>"
+		<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
+	<< "</Request>\n\n\n"
+	;
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
+{
+	LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
+
+	session->mMediaConnectInProgress = true;
+	
+	std::ostringstream stream;
+
+	stream
+	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+		<< "<Media>Audio</Media>"
+	<< "</Request>\n\n\n";
+
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session)
+{
+	LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
+	
+	std::ostringstream stream;
+
+	stream
+	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+	<< "</Request>\n\n\n";
+
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionTerminate()
+{
+	mSessionTerminateRequested = true;
+}
+
+void LLVivoxVoiceClient::requestRelog()
+{
+	mSessionTerminateRequested = true;
+	mRelogRequested = true;
+}
+
+
+void LLVivoxVoiceClient::leaveAudioSession()
+{
+	if(mAudioSession)
+	{
+		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
+
+		switch(getState())
+		{
+			case stateNoChannel:
+				// In this case, we want to pretend the join failed so our state machine doesn't get stuck.
+				// Skip the join failed transition state so we don't send out error notifications.
+				setState(stateJoinSessionFailedWaiting);
+			break;
+			case stateJoiningSession:
+			case stateSessionJoined:
+			case stateRunning:
+				if(!mAudioSession->mHandle.empty())
+				{
+
+#if RECORD_EVERYTHING
+					// HACK: for testing only
+					// Save looped recording
+					std::string savepath("/tmp/vivoxrecording");
+					{
+						time_t now = time(NULL);
+						const size_t BUF_SIZE = 64;
+						char time_str[BUF_SIZE];	/* Flawfinder: ignore */
+						
+						strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+						savepath += time_str;
+					}
+					recordingLoopSave(savepath);
+#endif
+
+					sessionMediaDisconnectSendMessage(mAudioSession);
+					setState(stateLeavingSession);
+				}
+				else
+				{
+					LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	
+					setState(stateSessionTerminated);
+				}
+			break;
+			case stateJoinSessionFailed:
+			case stateJoinSessionFailedWaiting:
+				setState(stateSessionTerminated);
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
+			break;
+		}
+	}
+	else
+	{
+		LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
+		setState(stateSessionTerminated);
+	}
+}
+
+void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+		<< "<Media>Audio</Media>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+	
+}
+
+void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
+{
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::getRenderDevicesSendMessage()
+{
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::clearCaptureDevices()
+{
+	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+	mCaptureDevices.clear();
+}
+
+void LLVivoxVoiceClient::addCaptureDevice(const std::string& name)
+{
+	LL_DEBUGS("Voice") << name << LL_ENDL;
+
+	mCaptureDevices.push_back(name);
+}
+
+LLVoiceDeviceList& LLVivoxVoiceClient::getCaptureDevices()
+{
+	return mCaptureDevices;
+}
+
+void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
+{
+	if(name == "Default")
+	{
+		if(!mCaptureDevice.empty())
+		{
+			mCaptureDevice.clear();
+			mCaptureDeviceDirty = true;	
+		}
+	}
+	else
+	{
+		if(mCaptureDevice != name)
+		{
+			mCaptureDevice = name;
+			mCaptureDeviceDirty = true;	
+		}
+	}
+}
+
+void LLVivoxVoiceClient::clearRenderDevices()
+{	
+	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+	mRenderDevices.clear();
+}
+
+void LLVivoxVoiceClient::addRenderDevice(const std::string& name)
+{
+	LL_DEBUGS("Voice") << name << LL_ENDL;
+	mRenderDevices.push_back(name);
+}
+
+LLVoiceDeviceList& LLVivoxVoiceClient::getRenderDevices()
+{
+	return mRenderDevices;
+}
+
+void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
+{
+	if(name == "Default")
+	{
+		if(!mRenderDevice.empty())
+		{
+			mRenderDevice.clear();
+			mRenderDeviceDirty = true;	
+		}
+	}
+	else
+	{
+		if(mRenderDevice != name)
+		{
+			mRenderDevice = name;
+			mRenderDeviceDirty = true;	
+		}
+	}
+	
+}
+
+void LLVivoxVoiceClient::tuningStart()
+{
+	mTuningMode = true;
+	LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
+	if(getState() >= stateNoChannel)
+	{
+		LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
+		sessionTerminate();
+	}
+}
+
+void LLVivoxVoiceClient::tuningStop()
+{
+	mTuningMode = false;
+}
+
+bool LLVivoxVoiceClient::inTuningMode()
+{
+	bool result = false;
+	switch(getState())
+	{
+	case stateMicTuningRunning:
+		result = true;
+		break;
+	default:
+		break;
+	}
+	return result;
+}
+
+void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
+{		
+	mTuningAudioFile = name;
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
+    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+    << "<Loop>" << (loop?"1":"0") << "</Loop>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::tuningRenderStopSendMessage()
+{
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
+    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration)
+{
+	LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
+	
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
+    << "<Duration>" << duration << "</Duration>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVivoxVoiceClient::tuningCaptureStopSendMessage()
+{
+	LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
+	
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+
+	mTuningEnergy = 0.0f;
+}
+
+void LLVivoxVoiceClient::tuningSetMicVolume(float volume)
+{
+	int scaled_volume = scale_mic_volume(volume);
+
+	if(scaled_volume != mTuningMicVolume)
+	{
+		mTuningMicVolume = scaled_volume;
+		mTuningMicVolumeDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume)
+{
+	int scaled_volume = scale_speaker_volume(volume);	
+
+	if(scaled_volume != mTuningSpeakerVolume)
+	{
+		mTuningSpeakerVolume = scaled_volume;
+		mTuningSpeakerVolumeDirty = true;
+	}
+}
+				
+float LLVivoxVoiceClient::tuningGetEnergy(void)
+{
+	return mTuningEnergy;
+}
+
+bool LLVivoxVoiceClient::deviceSettingsAvailable()
+{
+	bool result = true;
+	
+	if(!mConnected)
+		result = false;
+	
+	if(mRenderDevices.empty())
+		result = false;
+	
+	return result;
+}
+
+void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
+{
+	if(clearCurrentList)
+	{
+		clearCaptureDevices();
+		clearRenderDevices();
+	}
+	getCaptureDevicesSendMessage();
+	getRenderDevicesSendMessage();
+}
+
+void LLVivoxVoiceClient::daemonDied()
+{
+	// The daemon died, so the connection is gone.  Reset everything and start over.
+	LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL;
+
+	// Try to relaunch the daemon
+	setState(stateDisableCleanup);
+}
+
+void LLVivoxVoiceClient::giveUp()
+{
+	// All has failed.  Clean up and stop trying.
+	closeSocket();
+	deleteAllSessions();
+	deleteAllBuddies();
+	
+	setState(stateJail);
+}
+
+static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
+{
+	F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity
+	F64 npos[3];
+	
+	// The original XML command was sent like this:
+	/*
+			<< "<Position>"
+				<< "<X>" << pos[VX] << "</X>"
+				<< "<Y>" << pos[VZ] << "</Y>"
+				<< "<Z>" << pos[VY] << "</Z>"
+			<< "</Position>"
+			<< "<Velocity>"
+				<< "<X>" << mAvatarVelocity[VX] << "</X>"
+				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>"
+				<< "<Z>" << mAvatarVelocity[VY] << "</Z>"
+			<< "</Velocity>"
+			<< "<AtOrientation>"
+				<< "<X>" << l.mV[VX] << "</X>"
+				<< "<Y>" << u.mV[VX] << "</Y>"
+				<< "<Z>" << a.mV[VX] << "</Z>"
+			<< "</AtOrientation>"
+			<< "<UpOrientation>"
+				<< "<X>" << l.mV[VZ] << "</X>"
+				<< "<Y>" << u.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
+			<< "</UpOrientation>"
+			<< "<LeftOrientation>"
+				<< "<X>" << l.mV [VY] << "</X>"
+				<< "<Y>" << u.mV [VZ] << "</Y>"
+				<< "<Z>" << a.mV [VY] << "</Z>"
+			<< "</LeftOrientation>";
+	*/
+
+#if 1
+	// This was the original transform done when building the XML command
+	nat[0] = left.mV[VX];
+	nat[1] = up.mV[VX];
+	nat[2] = at.mV[VX];
+
+	nup[0] = left.mV[VZ];
+	nup[1] = up.mV[VY];
+	nup[2] = at.mV[VZ];
+
+	nl[0] = left.mV[VY];
+	nl[1] = up.mV[VZ];
+	nl[2] = at.mV[VY];
+
+	npos[0] = pos.mdV[VX];
+	npos[1] = pos.mdV[VZ];
+	npos[2] = pos.mdV[VY];
+
+	nvel[0] = vel.mV[VX];
+	nvel[1] = vel.mV[VZ];
+	nvel[2] = vel.mV[VY];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+	
+	// This was the original transform done in the SDK
+	nat[0] = at.mV[2];
+	nat[1] = 0; // y component of at vector is always 0, this was up[2]
+	nat[2] = -1 * left.mV[2];
+
+	// We override whatever the application gives us
+	nup[0] = 0; // x component of up vector is always 0
+	nup[1] = 1; // y component of up vector is always 1
+	nup[2] = 0; // z component of up vector is always 0
+
+	nl[0] = at.mV[0];
+	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
+	nl[2] = -1 * left.mV[0];
+
+	npos[2] = pos.mdV[2] * -1.0;
+	npos[1] = pos.mdV[1];
+	npos[0] = pos.mdV[0];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+#else
+	// This is the compose of the two transforms (at least, that's what I'm trying for)
+	nat[0] = at.mV[VX];
+	nat[1] = 0; // y component of at vector is always 0, this was up[2]
+	nat[2] = -1 * up.mV[VZ];
+
+	// We override whatever the application gives us
+	nup[0] = 0; // x component of up vector is always 0
+	nup[1] = 1; // y component of up vector is always 1
+	nup[2] = 0; // z component of up vector is always 0
+
+	nl[0] = left.mV[VX];
+	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
+	nl[2] = -1 * left.mV[VY];
+
+	npos[0] = pos.mdV[VX];
+	npos[1] = pos.mdV[VZ];
+	npos[2] = pos.mdV[VY] * -1.0;
+
+	nvel[0] = vel.mV[VX];
+	nvel[1] = vel.mV[VZ];
+	nvel[2] = vel.mV[VY];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+	
+#endif
+}
+
+void LLVivoxVoiceClient::sendPositionalUpdate(void)
+{	
+	std::ostringstream stream;
+	
+	if(mSpatialCoordsDirty)
+	{
+		LLVector3 l, u, a, vel;
+		LLVector3d pos;
+
+		mSpatialCoordsDirty = false;
+		
+		// Always send both speaker and listener positions together.
+		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"		
+			<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
+		
+		stream << "<SpeakerPosition>";
+
+//		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
+		l = mAvatarRot.getLeftRow();
+		u = mAvatarRot.getUpRow();
+		a = mAvatarRot.getFwdRow();
+		pos = mAvatarPosition;
+		vel = mAvatarVelocity;
+
+		// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
+		// The old transform is replicated by this function.
+		oldSDKTransform(l, u, a, pos, vel);
+		
+		stream 
+			<< "<Position>"
+				<< "<X>" << pos.mdV[VX] << "</X>"
+				<< "<Y>" << pos.mdV[VY] << "</Y>"
+				<< "<Z>" << pos.mdV[VZ] << "</Z>"
+			<< "</Position>"
+			<< "<Velocity>"
+				<< "<X>" << vel.mV[VX] << "</X>"
+				<< "<Y>" << vel.mV[VY] << "</Y>"
+				<< "<Z>" << vel.mV[VZ] << "</Z>"
+			<< "</Velocity>"
+			<< "<AtOrientation>"
+				<< "<X>" << a.mV[VX] << "</X>"
+				<< "<Y>" << a.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
+			<< "</AtOrientation>"
+			<< "<UpOrientation>"
+				<< "<X>" << u.mV[VX] << "</X>"
+				<< "<Y>" << u.mV[VY] << "</Y>"
+				<< "<Z>" << u.mV[VZ] << "</Z>"
+			<< "</UpOrientation>"
+			<< "<LeftOrientation>"
+				<< "<X>" << l.mV [VX] << "</X>"
+				<< "<Y>" << l.mV [VY] << "</Y>"
+				<< "<Z>" << l.mV [VZ] << "</Z>"
+			<< "</LeftOrientation>";
+
+		stream << "</SpeakerPosition>";
+
+		stream << "<ListenerPosition>";
+
+		LLVector3d	earPosition;
+		LLVector3	earVelocity;
+		LLMatrix3	earRot;
+		
+		switch(mEarLocation)
+		{
+			case earLocCamera:
+			default:
+				earPosition = mCameraPosition;
+				earVelocity = mCameraVelocity;
+				earRot = mCameraRot;
+			break;
+			
+			case earLocAvatar:
+				earPosition = mAvatarPosition;
+				earVelocity = mAvatarVelocity;
+				earRot = mAvatarRot;
+			break;
+			
+			case earLocMixed:
+				earPosition = mAvatarPosition;
+				earVelocity = mAvatarVelocity;
+				earRot = mCameraRot;
+			break;
+		}
+
+		l = earRot.getLeftRow();
+		u = earRot.getUpRow();
+		a = earRot.getFwdRow();
+		pos = earPosition;
+		vel = earVelocity;
+
+//		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
+		
+		oldSDKTransform(l, u, a, pos, vel);
+		
+		stream 
+			<< "<Position>"
+				<< "<X>" << pos.mdV[VX] << "</X>"
+				<< "<Y>" << pos.mdV[VY] << "</Y>"
+				<< "<Z>" << pos.mdV[VZ] << "</Z>"
+			<< "</Position>"
+			<< "<Velocity>"
+				<< "<X>" << vel.mV[VX] << "</X>"
+				<< "<Y>" << vel.mV[VY] << "</Y>"
+				<< "<Z>" << vel.mV[VZ] << "</Z>"
+			<< "</Velocity>"
+			<< "<AtOrientation>"
+				<< "<X>" << a.mV[VX] << "</X>"
+				<< "<Y>" << a.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
+			<< "</AtOrientation>"
+			<< "<UpOrientation>"
+				<< "<X>" << u.mV[VX] << "</X>"
+				<< "<Y>" << u.mV[VY] << "</Y>"
+				<< "<Z>" << u.mV[VZ] << "</Z>"
+			<< "</UpOrientation>"
+			<< "<LeftOrientation>"
+				<< "<X>" << l.mV [VX] << "</X>"
+				<< "<Y>" << l.mV [VY] << "</Y>"
+				<< "<Z>" << l.mV [VZ] << "</Z>"
+			<< "</LeftOrientation>";
+
+
+		stream << "</ListenerPosition>";
+
+		stream << "</Request>\n\n\n";
+	}	
+	
+	if(mAudioSession && mAudioSession->mVolumeDirty)
+	{
+		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
+
+		mAudioSession->mVolumeDirty = false;
+		
+		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
+		{
+			participantState *p = iter->second;
+			
+			if(p->mVolumeDirty)
+			{
+				// Can't set volume/mute for yourself
+				if(!p->mIsSelf)
+				{
+					int volume = 56; // nominal default value
+					bool mute = p->mOnMuteList;
+					
+					if(p->mUserVolume != -1)
+					{
+						// scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal")
+						if(p->mUserVolume < 100)
+							volume = (p->mUserVolume * 56) / 100;
+						else
+							volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56;
+					}
+					else if(p->mVolume != -1)
+					{
+						// Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent)
+						volume = p->mVolume;
+					}
+										
+
+					if(mute)
+					{
+						// SetParticipantMuteForMe doesn't work in p2p sessions.
+						// If we want the user to be muted, set their volume to 0 as well.
+						// This isn't perfect, but it will at least reduce their volume to a minimum.
+						volume = 0;
+					}
+					
+					if(volume == 0)
+						mute = true;
+
+					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
+					
+					// SLIM SDK: Send both volume and mute commands.
+					
+					// Send a "volume for me" command for the user.
+					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
+						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
+						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+						<< "<Volume>" << volume << "</Volume>"
+						<< "</Request>\n\n\n";
+
+					// Send a "mute for me" command for the user
+					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
+						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
+						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+						<< "<Mute>" << (mute?"1":"0") << "</Mute>"
+						<< "</Request>\n\n\n";
+				}
+				
+				p->mVolumeDirty = false;
+			}
+		}
+	}
+			
+	buildLocalAudioUpdates(stream);
+	
+	if(!stream.str().empty())
+	{
+		writeString(stream.str());
+	}
+	
+	// Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
+	// Batching them all together can choke SLVoice, so send them in separate writes.
+	sendFriendsListUpdates();
+}
+
+void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
+{
+	if(mCaptureDeviceDirty)
+	{
+		LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
+	
+		stream 
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
+			<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
+		<< "</Request>"
+		<< "\n\n\n";
+		
+		mCaptureDeviceDirty = false;
+	}
+}
+
+void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
+{
+	if(mRenderDeviceDirty)
+	{
+		LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
+			<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
+		<< "</Request>"
+		<< "\n\n\n";
+		mRenderDeviceDirty = false;
+	}
+}
+
+void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
+{
+	buildSetCaptureDevice(stream);
+
+	buildSetRenderDevice(stream);
+
+	if(mPTTDirty)
+	{
+		mPTTDirty = false;
+
+		// Send a local mute command.
+		// NOTE that the state of "PTT" is the inverse of "local mute".
+		//   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
+		
+		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL;
+
+		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+			<< "<Value>" << (mPTT?"false":"true") << "</Value>"
+			<< "</Request>\n\n\n";
+		
+	}
+
+	if(mSpeakerMuteDirty)
+	{
+		const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
+
+		mSpeakerMuteDirty = false;
+
+		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
+		
+		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
+			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+			<< "<Value>" << muteval << "</Value>"
+			<< "</Request>\n\n\n";	
+		
+	}
+	
+	if(mSpeakerVolumeDirty)
+	{
+		mSpeakerVolumeDirty = false;
+
+		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
+
+		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
+			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+			<< "<Value>" << mSpeakerVolume << "</Value>"
+			<< "</Request>\n\n\n";
+			
+	}
+	
+	if(mMicVolumeDirty)
+	{
+		mMicVolumeDirty = false;
+
+		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
+
+		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
+			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+			<< "<Value>" << mMicVolume << "</Value>"
+			<< "</Request>\n\n\n";				
+	}
+
+	
+}
+
+void LLVivoxVoiceClient::checkFriend(const LLUUID& id)
+{
+	std::string name;
+	buddyListEntry *buddy = findBuddy(id);
+
+	// Make sure we don't add a name before it's been looked up.
+	if(gCacheName->getFullName(id, name))
+	{
+
+		const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
+		bool canSeeMeOnline = false;
+		if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+			canSeeMeOnline = true;
+		
+		// When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary.
+		
+		if(buddy)
+		{
+			// This buddy is already in both lists.
+
+			if(name != buddy->mDisplayName)
+			{
+				// The buddy is in the list with the wrong name.  Update it with the correct name.
+				LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL;
+				buddy->mDisplayName = name;
+				buddy->mNeedsNameUpdate = true;		// This will cause the buddy to be resent.
+			}
+		}
+		else
+		{
+			// This buddy was not in the vivox list, needs to be added.
+			buddy = addBuddy(sipURIFromID(id), name);
+			buddy->mUUID = id;
+		}
+		
+		// In all the above cases, the buddy is in the SL friends list (which is how we got here).
+		buddy->mInSLFriends = true;
+		buddy->mCanSeeMeOnline = canSeeMeOnline;
+		buddy->mNameResolved = true;
+		
+	}
+	else
+	{
+		// This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has.
+		if(buddy)
+		{
+			buddy->mNameResolved = false;
+		}
+		
+		// Initiate a lookup.
+		// The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
+		lookupName(id);
+	}
+}
+
+void LLVivoxVoiceClient::clearAllLists()
+{
+	// FOR TESTING ONLY
+	
+	// This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
+	{
+		buddyListEntry *buddy = buddy_it->second;
+		buddy_it++;
+		
+		std::ostringstream stream;
+
+		if(buddy->mInVivoxBuddies)
+		{
+			// delete this entry from the vivox buddy list
+			buddy->mInVivoxBuddies = false;
+			LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+				<< "</Request>\n\n\n";		
+		}
+
+		if(buddy->mHasBlockListEntry)
+		{
+			// Delete the associated block list entry (so the block list doesn't fill up with junk)
+			buddy->mHasBlockListEntry = false;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+				<< "</Request>\n\n\n";								
+		}
+		if(buddy->mHasAutoAcceptListEntry)
+		{
+			// Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
+			buddy->mHasAutoAcceptListEntry = false;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+				<< "</Request>\n\n\n";
+		}
+
+		writeString(stream.str());
+
+	}
+}
+
+void LLVivoxVoiceClient::sendFriendsListUpdates()
+{
+	if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
+	{
+		mFriendsListDirty = false;
+		
+		if(0)
+		{
+			// FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
+			clearAllLists();
+			return;
+		}
+		
+		LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
+		
+		buddyListMap::iterator buddy_it;
+		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+		{
+			// reset the temp flags in the local buddy list
+			buddy_it->second->mInSLFriends = false;
+		}
+		
+		// correlate with the friends list
+		{
+			LLCollectAllBuddies collect;
+			LLAvatarTracker::instance().applyFunctor(collect);
+			LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
+			LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
+			
+			for ( ; it != end; ++it)
+			{
+				checkFriend(it->second);
+			}
+			it = collect.mOffline.begin();
+			end = collect.mOffline.end();
+			for ( ; it != end; ++it)
+			{
+				checkFriend(it->second);
+			}
+		}
+				
+		LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
+
+		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
+		{
+			buddyListEntry *buddy = buddy_it->second;
+			buddy_it++;
+			
+			// Ignore entries that aren't resolved yet.
+			if(buddy->mNameResolved)
+			{
+				std::ostringstream stream;
+
+				if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
+				{					
+					if(mNumberOfAliases > 0)
+					{
+						// Add (or update) this entry in the vivox buddy list
+						buddy->mInVivoxBuddies = true;
+						buddy->mNeedsNameUpdate = false;
+						LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+						stream 
+							<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+								<< "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
+								<< "<BuddyData></BuddyData>"	// Without this, SLVoice doesn't seem to parse the command.
+								<< "<GroupID>0</GroupID>"
+							<< "</Request>\n\n\n";	
+					}
+				}
+				else if(!buddy->mInSLFriends)
+				{
+					// This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list.
+ 					if(buddy->mInVivoxBuddies)
+					{
+						// delete this entry from the vivox buddy list
+						buddy->mInVivoxBuddies = false;
+						LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+							<< "</Request>\n\n\n";		
+					}
+
+					if(buddy->mHasBlockListEntry)
+					{
+						// Delete the associated block list entry, if any
+						buddy->mHasBlockListEntry = false;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+							<< "</Request>\n\n\n";								
+					}
+					if(buddy->mHasAutoAcceptListEntry)
+					{
+						// Delete the associated auto-accept list entry, if any
+						buddy->mHasAutoAcceptListEntry = false;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+							<< "</Request>\n\n\n";
+					}
+				}
+				
+				if(buddy->mInSLFriends)
+				{
+
+					if(buddy->mCanSeeMeOnline)
+					{
+						// Buddy should not be blocked.
+
+						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
+						
+						// If the buddy has a block list entry, delete it.
+						if(buddy->mHasBlockListEntry)
+						{
+							buddy->mHasBlockListEntry = false;
+							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+								<< "</Request>\n\n\n";		
+							
+							
+							// If we just deleted a block list entry, add an auto-accept entry.
+							if(!buddy->mHasAutoAcceptListEntry)
+							{
+								buddy->mHasAutoAcceptListEntry = true;								
+								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">"
+									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+									<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+									<< "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
+									<< "</Request>\n\n\n";
+							}
+						}
+					}
+					else
+					{
+						// Buddy should be blocked.
+						
+						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
+
+						// If this buddy has an autoaccept entry, delete it
+						if(buddy->mHasAutoAcceptListEntry)
+						{
+							buddy->mHasAutoAcceptListEntry = false;
+							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+								<< "</Request>\n\n\n";
+						
+							// If we just deleted an auto-accept entry, add a block list entry.
+							if(!buddy->mHasBlockListEntry)
+							{
+								buddy->mHasBlockListEntry = true;
+								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">"
+									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+									<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+									<< "<PresenceOnly>1</PresenceOnly>"
+									<< "</Request>\n\n\n";								
+							}
+						}
+					}
+
+					if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
+					{
+						// Delete this entry from the local buddy list.  This should NOT invalidate the iterator,
+						// since it has already been incremented to the next entry.
+						deleteBuddy(buddy->mURI);
+					}
+
+				}
+				writeString(stream.str());
+			}
+		}
+	}
+}
+
+/////////////////////////////
+// Response/Event handlers
+
+void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
+{	
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
+		setState(stateConnectorFailed);
+		LLSD args;
+		std::stringstream errs;
+		errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
+		args["HOSTID"] = errs.str();
+		if (LLGridManager::getInstance()->isSystemGrid())
+		{
+			LLNotificationsUtil::add("NoVoiceConnect", args);	
+		}
+		else
+		{
+			LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);	
+		}
+	}
+	else
+	{
+		// Connector created, move forward.
+		LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
+		mVoiceVersion.serverVersion = versionID;
+		mConnectorHandle = connectorHandle;
+		if(getState() == stateConnectorStarting)
+		{
+			setState(stateConnectorStarted);
+		}
+	}
+}
+
+void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
+{ 
+	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
+	
+	// Status code of 20200 means "bad password".  We may want to special-case that at some point.
+	
+	if ( statusCode == 401 )
+	{
+		// Login failure which is probably caused by the delay after a user's password being updated.
+		LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		setState(stateLoginRetry);
+	}
+	else if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		setState(stateLoginFailed);
+	}
+	else
+	{
+		// Login succeeded, move forward.
+		mAccountHandle = accountHandle;
+		mNumberOfAliases = numberOfAliases;
+		// This needs to wait until the AccountLoginStateChangeEvent is received.
+//		if(getState() == stateLoggingIn)
+//		{
+//			setState(stateLoggedIn);
+//		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
+{	
+	sessionState *session = findSessionBeingCreatedByURI(requestId);
+	
+	if(session)
+	{
+		session->mCreateInProgress = false;
+	}
+	
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		if(session)
+		{
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+			{
+				setState(stateJoinSessionFailed);
+			}
+			else
+			{
+				reapSession(session);
+			}
+		}
+	}
+	else
+	{
+		LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
+		if(session)
+		{
+			setSessionHandle(session, sessionHandle);
+		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
+{	
+	sessionState *session = findSessionBeingCreatedByURI(requestId);
+	
+	if(session)
+	{
+		session->mCreateInProgress = false;
+	}
+	
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		if(session)
+		{
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+			{
+				setState(stateJoinSessionFailed);
+			}
+			else
+			{
+				reapSession(session);
+			}
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
+		if(session)
+		{
+			setSessionHandle(session, sessionHandle);
+		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
+{
+	sessionState *session = findSession(requestId);
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		if(session)
+		{
+			session->mMediaConnectInProgress = false;
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+				setState(stateJoinSessionFailed);
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString)
+{	
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
+		// Should this ever fail?  do we care if it does?
+	}
+}
+
+void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
+{
+	if(statusCode != 0)
+	{
+		LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
+		// Should this ever fail?  do we care if it does?
+	}
+	
+	mConnected = false;
+	
+	if(getState() == stateConnectorStopping)
+	{
+		setState(stateConnectorStopped);
+	}
+}
+
+void LLVivoxVoiceClient::sessionAddedEvent(
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
+		bool isChannel, 
+		bool incoming,
+		std::string &nameString,
+		std::string &applicationString)
+{
+	sessionState *session = NULL;
+
+	LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
+	
+	session = addSession(uriString, sessionHandle);
+	if(session)
+	{
+		session->mGroupHandle = sessionGroupHandle;
+		session->mIsChannel = isChannel;
+		session->mIncoming = incoming;
+		session->mAlias = alias;
+			
+		// Generate a caller UUID -- don't need to do this for channels
+		if(!session->mIsChannel)
+		{
+			if(IDFromName(session->mSIPURI, session->mCallerID))
+			{
+				// Normal URI(base64-encoded UUID) 
+			}
+			else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
+			{
+				// Wrong URI, but an alias is available.  Stash the incoming URI as an alternate
+				session->mAlternateSIPURI = session->mSIPURI;
+				
+				// and generate a proper URI from the ID.
+				setSessionURI(session, sipURIFromID(session->mCallerID));
+			}
+			else
+			{
+				LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
+				setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
+				session->mSynthesizedCallerID = true;
+				
+				// Can't look up the name in this case -- we have to extract it from the URI.
+				std::string namePortion = nameFromsipURI(session->mSIPURI);
+				if(namePortion.empty())
+				{
+					// Didn't seem to be a SIP URI, just use the whole provided name.
+					namePortion = nameString;
+				}
+				
+				// Some incoming names may be separated with an underscore instead of a space.  Fix this.
+				LLStringUtil::replaceChar(namePortion, '_', ' ');
+				
+				// Act like we just finished resolving the name (this stores it in all the right places)
+				avatarNameResolved(session->mCallerID, namePortion);
+			}
+		
+			LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
+
+			if(!session->mSynthesizedCallerID)
+			{
+				// If we got here, we don't have a proper name.  Initiate a lookup.
+				lookupName(session->mCallerID);
+			}
+		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
+{
+	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
+	
+#if USE_SESSION_GROUPS
+	if(mMainSessionGroupHandle.empty())
+	{
+		// This is the first (i.e. "main") session group.  Save its handle.
+		mMainSessionGroupHandle = sessionGroupHandle;
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
+	}
+#endif
+}
+
+void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
+{
+	LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL;
+	if(mAudioSession != session)
+	{
+		sessionState *oldSession = mAudioSession;
+
+		mAudioSession = session;
+		mAudioSessionChanged = true;
+
+		// The old session may now need to be deleted.
+		reapSession(oldSession);
+	}
+	
+	// This is the session we're joining.
+	if(getState() == stateJoiningSession)
+	{
+		setState(stateSessionJoined);
+		
+		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
+		// Add the current user as a participant here.
+		participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
+		if(participant)
+		{
+			participant->mIsSelf = true;
+			lookupName(participant->mAvatarID);
+
+			LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName 
+					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+		}
+		
+		if(!session->mIsChannel)
+		{
+			// this is a p2p session.  Make sure the other end is added as a participant.
+			participantState *participant = session->addParticipant(session->mSIPURI);
+			if(participant)
+			{
+				if(participant->mAvatarIDValid)
+				{
+					lookupName(participant->mAvatarID);
+				}
+				else if(!session->mName.empty())
+				{
+					participant->mDisplayName = session->mName;
+					avatarNameResolved(participant->mAvatarID, session->mName);
+				}
+				
+				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
+				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName 
+						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+			}
+		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionRemovedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle)
+{
+	LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
+	
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		leftAudioSession(session);
+
+		// This message invalidates the session's handle.  Set it to empty.
+		setSessionHandle(session);
+		
+		// This also means that the session's session group is now empty.
+		// Terminate the session group so it doesn't leak.
+		sessionGroupTerminateSendMessage(session);
+		
+		// Reset the media state (we now have no info)
+		session->mMediaStreamState = streamStateUnknown;
+		session->mTextStreamState = streamStateUnknown;
+		
+		// Conditionally delete the session
+		reapSession(session);
+	}
+	else
+	{
+		LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::reapSession(sessionState *session)
+{
+	if(session)
+	{
+		if(!session->mHandle.empty())
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
+		}
+		else if(session->mCreateInProgress)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
+		}
+		else if(session->mMediaConnectInProgress)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
+		}
+		else if(session == mAudioSession)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
+		}
+		else if(session == mNextAudioSession)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
+		}
+		else
+		{
+			// TODO: Question: Should we check for queued text messages here?
+			// We don't have a reason to keep tracking this session, so just delete it.
+			LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
+			deleteSession(session);
+			session = NULL;
+		}	
+	}
+	else
+	{
+//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
+	}
+}
+
+// Returns true if the session seems to indicate we've moved to a region on a different voice server
+bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session)
+{
+	bool result = false;
+	
+	if(session != NULL)
+	{
+		// Only make this check for spatial channels (so it won't happen for group or p2p calls)
+		if(session->mIsSpatial)
+		{	
+			std::string::size_type atsign;
+			
+			atsign = session->mSIPURI.find("@");
+			
+			if(atsign != std::string::npos)
+			{
+				std::string urihost = session->mSIPURI.substr(atsign + 1);
+				if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
+				{
+					// The hostname in this URI is different from what we expect.  This probably means we need to relog.
+					
+					// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
+					// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
+					
+					result = true;
+				}
+			}
+		}
+	}
+	
+	return result;
+}
+
+void LLVivoxVoiceClient::leftAudioSession(
+	sessionState *session)
+{
+	if(mAudioSession == session)
+	{
+		switch(getState())
+		{
+			case stateJoiningSession:
+			case stateSessionJoined:
+			case stateRunning:
+			case stateLeavingSession:
+			case stateJoinSessionFailed:
+			case stateJoinSessionFailedWaiting:
+				// normal transition
+				LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
+				setState(stateSessionTerminated);
+			break;
+			
+			case stateSessionTerminated:
+				// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
+				LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
+				setState(stateSessionTerminated);
+			break;
+		}
+	}
+}
+
+void LLVivoxVoiceClient::accountLoginStateChangeEvent(
+		std::string &accountHandle, 
+		int statusCode, 
+		std::string &statusString, 
+		int state)
+{
+	/*
+		According to Mike S., status codes for this event are:
+		login_state_logged_out=0,
+        login_state_logged_in = 1,
+        login_state_logging_in = 2,
+        login_state_logging_out = 3,
+        login_state_resetting = 4,
+        login_state_error=100	
+	*/
+	
+	LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL;
+	switch(state)
+	{
+		case 1:
+		if(getState() == stateLoggingIn)
+		{
+			setState(stateLoggedIn);
+		}
+		break;
+
+		case 3:
+			// The user is in the process of logging out.
+			setState(stateLoggingOut);
+		break;
+
+		case 0:
+			// The user has been logged out.  
+			setState(stateLoggedOut);
+		break;
+		
+		default:
+			//Used to be a commented out warning
+			LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
+		break;
+	}
+}
+
+void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle, 
+	int statusCode, 
+	std::string &statusString, 
+	int state, 
+	bool incoming)
+{
+	sessionState *session = findSession(sessionHandle);
+	
+	LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
+	
+	if(session)
+	{
+		// We know about this session
+		
+		// Save the state for later use
+		session->mMediaStreamState = state;
+		
+		switch(statusCode)
+		{
+			case 0:
+			case 200:
+				// generic success
+				// Don't change the saved error code (it may have been set elsewhere)
+			break;
+			default:
+				// save the status code for later
+				session->mErrorStatusCode = statusCode;
+			break;
+		}
+		
+		switch(state)
+		{
+			case streamStateIdle:
+				// Standard "left audio session"
+				session->mVoiceEnabled = false;
+				session->mMediaConnectInProgress = false;
+				leftAudioSession(session);
+			break;
+
+			case streamStateConnected:
+				session->mVoiceEnabled = true;
+				session->mMediaConnectInProgress = false;
+				joinedAudioSession(session);
+			break;
+			
+			case streamStateRinging:
+				if(incoming)
+				{
+					// Send the voice chat invite to the GUI layer
+					// TODO: Question: Should we correlate with the mute list here?
+					session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
+					session->mVoiceInvitePending = true;
+					if(session->mName.empty())
+					{
+						lookupName(session->mCallerID);
+					}
+					else
+					{
+						// Act like we just finished resolving the name
+						avatarNameResolved(session->mCallerID, session->mName);
+					}
+				}
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
+			break;
+			
+		}
+		
+	}
+	else
+	{
+		LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::textStreamUpdatedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle, 
+	bool enabled,
+	int state, 
+	bool incoming)
+{
+	sessionState *session = findSession(sessionHandle);
+	
+	if(session)
+	{
+		// Save the state for later use
+		session->mTextStreamState = state;
+		
+		// We know about this session
+		switch(state)
+		{
+			case 0:	// We see this when the text stream closes
+				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
+			break;
+			
+			case 1:	// We see this on an incoming call from the Connector
+				// Try to send any text messages queued for this session.
+				sendQueuedTextMessages(session);
+
+				// Send the text chat invite to the GUI layer
+				// TODO: Question: Should we correlate with the mute list here?
+				session->mTextInvitePending = true;
+				if(session->mName.empty())
+				{
+					lookupName(session->mCallerID);
+				}
+				else
+				{
+					// Act like we just finished resolving the name
+					avatarNameResolved(session->mCallerID, session->mName);
+				}
+			break;
+
+			default:
+				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
+			break;
+			
+		}
+	}
+}
+
+void LLVivoxVoiceClient::participantAddedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &nameString, 
+		std::string &displayNameString, 
+		int participantType)
+{
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		participantState *participant = session->addParticipant(uriString);
+		if(participant)
+		{
+			participant->mAccountName = nameString;
+
+			LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName 
+					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+
+			if(participant->mAvatarIDValid)
+			{
+				// Initiate a lookup
+				lookupName(participant->mAvatarID);
+			}
+			else
+			{
+				// If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
+				std::string namePortion = nameFromsipURI(uriString);
+				if(namePortion.empty())
+				{
+					// Problem with the SIP URI, fall back to the display name
+					namePortion = displayNameString;
+				}
+				if(namePortion.empty())
+				{
+					// Problems with both of the above, fall back to the account name
+					namePortion = nameString;
+				}
+				
+				// Set the display name (which is a hint to the active speakers window not to do its own lookup)
+				participant->mDisplayName = namePortion;
+				avatarNameResolved(participant->mAvatarID, namePortion);
+			}
+		}
+	}
+}
+
+void LLVivoxVoiceClient::participantRemovedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &nameString)
+{
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		participantState *participant = session->findParticipant(uriString);
+		if(participant)
+		{
+			session->removeParticipant(participant);
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+	}
+}
+
+
+void LLVivoxVoiceClient::participantUpdatedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
+		std::string &uriString, 
+		std::string &alias, 
+		bool isModeratorMuted, 
+		bool isSpeaking, 
+		int volume, 
+		F32 energy)
+{
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		participantState *participant = session->findParticipant(uriString);
+		
+		if(participant)
+		{
+			participant->mIsSpeaking = isSpeaking;
+			participant->mIsModeratorMuted = isModeratorMuted;
+
+			// SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
+			if (isSpeaking)
+			{
+				participant->mSpeakingTimeout.reset();
+				participant->mPower = energy;
+			}
+			else
+			{
+				participant->mPower = 0.0f;
+			}
+			participant->mVolume = volume;
+			
+			// *HACK: mantipov: added while working on EXT-3544                                                                                   
+			/*                                                                                                                                    
+			 Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE                                                            
+			 LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.                                                                    
+			 
+			 participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted                             
+			 Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.                 
+			 Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.                                         
+			 
+			 But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()                               
+			 voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager                                          
+			 and event is not fired.                                                                                                               
+			 
+			 So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it                                                
+			 in LLCallFloater::draw()                                                                                                              
+			 */
+			LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
+			
+			// ignore session ID of local chat                                                                                                    
+			if (voice_cnl && voice_cnl->getSessionID().notNull())
+			{
+				LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID());
+				if (speaker_manager)
+				{
+					speaker_manager->update(true);
+				}
+			}
+			
+		}
+		else
+		{
+			LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
+		}
+	}
+	else
+	{
+		LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::buddyPresenceEvent(
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &statusString,
+		std::string &applicationString)
+{
+	buddyListEntry *buddy = findBuddy(uriString);
+	
+	if(buddy)
+	{
+		LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL;
+		LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
+
+		if(applicationString.empty())
+		{
+			// This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way.
+			// NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
+
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// User went offline with a non-SLim-enabled viewer.
+				buddy->mOnlineSL = false;
+			}
+			else if ( stricmp("Online", statusString.c_str())== 0) 
+			{
+				// User came online with a non-SLim-enabled viewer.
+				buddy->mOnlineSL = true;
+			}
+			else
+			{
+				// If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
+				// 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.
+				buddy->mOnlineSLim = true;
+			} 
+		}
+		else if(applicationString.find("SecondLifeViewer") != std::string::npos)
+		{
+			// This presence event is from a viewer that sets the application string
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// Viewer says they're offline
+				buddy->mOnlineSL = false;
+			}
+			else
+			{
+				// Viewer says they're online
+				buddy->mOnlineSL = true;
+			}
+		}
+		else
+		{
+			// This presence event is from something which is NOT the SL viewer (assume it's SLim).
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// SLim says they're offline
+				buddy->mOnlineSLim = false;
+			}
+			else
+			{
+				// SLim says they're online
+				buddy->mOnlineSLim = true;
+			}
+		} 
+
+		LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
+		
+		// HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
+		LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
+
+		notifyFriendObservers();
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
+	}	
+}
+
+void LLVivoxVoiceClient::messageEvent(
+		std::string &sessionHandle, 
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &messageHeader, 
+		std::string &messageBody,
+		std::string &applicationString)
+{
+	LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
+//	LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
+	
+	if(messageHeader.find("text/html") != std::string::npos)
+	{
+		std::string message;
+
+		{
+			const std::string startMarker = "<body";
+			const std::string startMarker2 = ">";
+			const std::string endMarker = "</body>";
+			const std::string startSpan = "<span";
+			const std::string endSpan = "</span>";
+			std::string::size_type start;
+			std::string::size_type end;
+			
+			// Default to displaying the raw string, so the message gets through.
+			message = messageBody;
+
+			// Find the actual message text within the XML fragment
+			start = messageBody.find(startMarker);
+			start = messageBody.find(startMarker2, start);
+			end = messageBody.find(endMarker);
+
+			if(start != std::string::npos)
+			{
+				start += startMarker2.size();
+				
+				if(end != std::string::npos)
+					end -= start;
+					
+				message.assign(messageBody, start, end);
+			}
+			else 
+			{
+				// Didn't find a <body>, try looking for a <span> instead.
+				start = messageBody.find(startSpan);
+				start = messageBody.find(startMarker2, start);
+				end = messageBody.find(endSpan);
+				
+				if(start != std::string::npos)
+				{
+					start += startMarker2.size();
+					
+					if(end != std::string::npos)
+						end -= start;
+					
+					message.assign(messageBody, start, end);
+				}			
+			}
+		}	
+		
+//		LL_DEBUGS("Voice") << "    raw message = \n" << message << LL_ENDL;
+
+		// strip formatting tags
+		{
+			std::string::size_type start;
+			std::string::size_type end;
+			
+			while((start = message.find('<')) != std::string::npos)
+			{
+				if((end = message.find('>', start + 1)) != std::string::npos)
+				{
+					// Strip out the tag
+					message.erase(start, (end + 1) - start);
+				}
+				else
+				{
+					// Avoid an infinite loop
+					break;
+				}
+			}
+		}
+		
+		// Decode ampersand-escaped chars
+		{
+			std::string::size_type mark = 0;
+
+			// The text may contain text encoded with &lt;, &gt;, and &amp;
+			mark = 0;
+			while((mark = message.find("&lt;", mark)) != std::string::npos)
+			{
+				message.replace(mark, 4, "<");
+				mark += 1;
+			}
+			
+			mark = 0;
+			while((mark = message.find("&gt;", mark)) != std::string::npos)
+			{
+				message.replace(mark, 4, ">");
+				mark += 1;
+			}
+			
+			mark = 0;
+			while((mark = message.find("&amp;", mark)) != std::string::npos)
+			{
+				message.replace(mark, 5, "&");
+				mark += 1;
+			}
+		}
+		
+		// strip leading/trailing whitespace (since we always seem to get a couple newlines)
+		LLStringUtil::trim(message);
+		
+//		LL_DEBUGS("Voice") << "    stripped message = \n" << message << LL_ENDL;
+		
+		sessionState *session = findSession(sessionHandle);
+		if(session)
+		{
+			bool is_busy = gAgent.getBusy();
+			bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
+			bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
+			bool quiet_chat = false;
+			LLChat chat;
+
+			chat.mMuted = is_muted && !is_linden;
+			
+			if(!chat.mMuted)
+			{
+				chat.mFromID = session->mCallerID;
+				chat.mFromName = session->mName;
+				chat.mSourceType = CHAT_SOURCE_AGENT;
+
+				if(is_busy && !is_linden)
+				{
+					quiet_chat = true;
+					// TODO: Question: Return busy mode response here?  Or maybe when session is started instead?
+				}
+				
+				LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
+				gIMMgr->addMessage(session->mIMSessionID,
+						session->mCallerID,
+						session->mName.c_str(),
+						message.c_str(),
+						LLStringUtil::null,		// default arg
+						IM_NOTHING_SPECIAL,		// default arg
+						0,						// default arg
+						LLUUID::null,			// default arg
+						LLVector3::zero,		// default arg
+						true);					// prepend name and make it a link to the user's profile
+
+				chat.mText = std::string("IM: ") + session->mName + std::string(": ") + message;
+				// If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
+				LLFloaterChat::addChat( chat, TRUE, quiet_chat );
+			}
+		}		
+	}
+}
+
+void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
+{
+	sessionState *session = findSession(sessionHandle);
+	
+	if(session)
+	{
+		participantState *participant = session->findParticipant(uriString);
+		if(participant)
+		{
+			if (!stricmp(notificationType.c_str(), "Typing"))
+			{
+				// Other end started typing
+				// TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
+				// It requires an LLIMInfo for the message, which we don't have here.
+			}
+			else if (!stricmp(notificationType.c_str(), "NotTyping"))
+			{
+				// Other end stopped typing
+				// TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
+				// It requires an LLIMInfo for the message, which we don't have here.
+			}
+			else
+			{
+				LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
+			}
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
+{
+	buddyListEntry *buddy = findBuddy(buddyURI);
+	
+	if(!buddy)
+	{
+		// Couldn't find buddy by URI, try converting the alias...
+		if(!alias.empty())
+		{
+			LLUUID id;
+			if(IDFromName(alias, id))
+			{
+				buddy = findBuddy(id);
+			}
+		}
+	}
+	
+	if(buddy)
+	{
+		std::ostringstream stream;
+		
+		if(buddy->mCanSeeMeOnline)
+		{
+			// Sending the response will create an auto-accept rule
+			buddy->mHasAutoAcceptListEntry = true;
+		}
+		else
+		{
+			// Sending the response will create a block rule
+			buddy->mHasBlockListEntry = true;
+		}
+		
+		if(buddy->mInSLFriends)
+		{
+			buddy->mInVivoxBuddies = true;
+		}
+		
+		stream
+			<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+				<< "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
+				<< "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
+				<< "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
+			<< "</Request>"
+			<< "\n\n\n";
+			
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::auxAudioPropertiesEvent(F32 energy)
+{
+	LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL;
+	mTuningEnergy = energy;
+}
+
+void LLVivoxVoiceClient::buddyListChanged()
+{
+	// This is called after we receive a BuddyAndGroupListChangedEvent.
+	mBuddyListMapPopulated = true;
+	mFriendsListDirty = true;
+}
+
+void LLVivoxVoiceClient::muteListChanged()
+{
+	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
+	if(mAudioSession)
+	{
+		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
+		
+		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
+		{
+			participantState *p = iter->second;
+			
+			// Check to see if this participant is on the mute list already
+			if(p->updateMuteState())
+				mAudioSession->mVolumeDirty = true;
+		}
+	}
+}
+
+void LLVivoxVoiceClient::updateFriends(U32 mask)
+{
+	if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
+	{
+		// Just resend the whole friend list to the daemon
+		mFriendsListDirty = true;
+	}
+}
+
+/////////////////////////////
+// Managing list of participants
+LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : 
+	 mURI(uri), 
+	 mPTT(false), 
+	 mIsSpeaking(false), 
+	 mIsModeratorMuted(false), 
+	 mLastSpokeTimestamp(0.f), 
+	 mPower(0.f), 
+	 mVolume(-1), 
+	 mOnMuteList(false), 
+	 mUserVolume(-1), 
+	 mVolumeDirty(false), 
+	 mAvatarIDValid(false),
+	 mIsSelf(false)
+{
+}
+
+LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri)
+{
+	participantState *result = NULL;
+	bool useAlternateURI = false;
+	
+	// Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it
+	// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
+	{
+		participantMap::iterator iter = mParticipantsByURI.find(uri);
+
+		if(iter == mParticipantsByURI.end())
+		{
+			if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
+			{
+				// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
+				// Use mSIPURI instead, since it will be properly encoded.
+				iter = mParticipantsByURI.find(mSIPURI);
+				useAlternateURI = true;
+			}
+		}
+
+		if(iter != mParticipantsByURI.end())
+		{
+			result = iter->second;
+		}
+	}
+		
+	if(!result)
+	{
+		// participant isn't already in one list or the other.
+		result = new participantState(useAlternateURI?mSIPURI:uri);
+		mParticipantsByURI.insert(participantMap::value_type(result->mURI, result));
+		mParticipantsChanged = true;
+		
+		// Try to do a reverse transform on the URI to get the GUID back.
+		{
+			LLUUID id;
+			if(LLVivoxVoiceClient::getInstance()->IDFromName(result->mURI, id))
+			{
+				result->mAvatarIDValid = true;
+				result->mAvatarID = id;
+
+				if(result->updateMuteState())
+					mVolumeDirty = true;
+			}
+			else
+			{
+				// Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
+				// This tells both code in LLVivoxVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
+				setUUIDFromStringHash(result->mAvatarID, uri);
+			}
+		}
+		
+		mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result));
+		
+		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
+	}
+	
+	return result;
+}
+
+bool LLVivoxVoiceClient::participantState::updateMuteState()
+{
+	bool result = false;
+	
+	if(mAvatarIDValid)
+	{
+		bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
+		if(mOnMuteList != isMuted)
+		{
+			mOnMuteList = isMuted;
+			mVolumeDirty = true;
+			result = true;
+		}
+	}
+	return result;
+}
+
+bool LLVivoxVoiceClient::participantState::isAvatar()
+{
+	return mAvatarIDValid;
+}
+
+void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant)
+{
+	if(participant)
+	{
+		participantMap::iterator iter = mParticipantsByURI.find(participant->mURI);
+		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID);
+		
+		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
+		
+		if(iter == mParticipantsByURI.end())
+		{
+			LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
+		}
+		else if(iter2 == mParticipantsByUUID.end())
+		{
+			LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
+		}
+		else if(iter->second != iter2->second)
+		{
+			LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
+		}
+		else
+		{
+			mParticipantsByURI.erase(iter);
+			mParticipantsByUUID.erase(iter2);
+			
+			delete participant;
+			mParticipantsChanged = true;
+		}
+	}
+}
+
+void LLVivoxVoiceClient::sessionState::removeAllParticipants()
+{
+	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+
+	while(!mParticipantsByURI.empty())
+	{
+		removeParticipant(mParticipantsByURI.begin()->second);
+	}
+	
+	if(!mParticipantsByUUID.empty())
+	{
+		LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL;
+	}
+}
+
+std::vector<LLUUID> LLVivoxVoiceClient::getParticipantList(void)
+{
+	std::vector<LLUUID> result;
+	if(mAudioSession)
+	{
+		for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin();
+			iter != mAudioSession->mParticipantsByUUID.end(); 
+			iter++)
+		{
+			result.push_back(iter->first);
+		}
+	}
+	return result;
+}
+
+
+LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri)
+{
+	participantState *result = NULL;
+	
+	participantMap::iterator iter = mParticipantsByURI.find(uri);
+
+	if(iter == mParticipantsByURI.end())
+	{
+		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
+		{
+			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
+			// Look up the other URI
+			iter = mParticipantsByURI.find(mSIPURI);
+		}
+	}
+
+	if(iter != mParticipantsByURI.end())
+	{
+		result = iter->second;
+	}
+		
+	return result;
+}
+
+LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
+{
+	participantState * result = NULL;
+	participantUUIDMap::iterator iter = mParticipantsByUUID.find(id);
+
+	if(iter != mParticipantsByUUID.end())
+	{
+		result = iter->second;
+	}
+
+	return result;
+}
+
+LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id)
+{
+	participantState * result = NULL;
+	
+	if(mAudioSession)
+	{
+		result = mAudioSession->findParticipantByID(id);
+	}
+	
+	return result;
+}
+
+
+void LLVivoxVoiceClient::parcelChanged()
+{
+	if(getState() >= stateNoChannel)
+	{
+		// If the user is logged in, start a channel lookup.
+		LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
+
+		std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
+		LLSD data;
+		LLHTTPClient::post(
+			url,
+			data,
+			new LLVivoxVoiceClientCapResponder);
+	}
+	else
+	{
+		// The transition to stateNoChannel needs to kick this off again.
+		LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::switchChannel(
+	std::string uri,
+	bool spatial,
+	bool no_reconnect,
+	bool is_p2p,
+	std::string hash)
+{
+	bool needsSwitch = false;
+	
+	LL_DEBUGS("Voice") 
+		<< "called in state " << state2string(getState()) 
+		<< " with uri \"" << uri << "\"" 
+		<< (spatial?", spatial is true":", spatial is false")
+		<< LL_ENDL;
+	
+	switch(getState())
+	{
+		case stateJoinSessionFailed:
+		case stateJoinSessionFailedWaiting:
+		case stateNoChannel:
+			// Always switch to the new URI from these states.
+			needsSwitch = true;
+		break;
+
+		default:
+			if(mSessionTerminateRequested)
+			{
+				// If a terminate has been requested, we need to compare against where the URI we're already headed to.
+				if(mNextAudioSession)
+				{
+					if(mNextAudioSession->mSIPURI != uri)
+						needsSwitch = true;
+				}
+				else
+				{
+					// mNextAudioSession is null -- this probably means we're on our way back to spatial.
+					if(!uri.empty())
+					{
+						// We do want to process a switch in this case.
+						needsSwitch = true;
+					}
+				}
+			}
+			else
+			{
+				// Otherwise, compare against the URI we're in now.
+				if(mAudioSession)
+				{
+					if(mAudioSession->mSIPURI != uri)
+					{
+						needsSwitch = true;
+					}
+				}
+				else
+				{
+					if(!uri.empty())
+					{
+						// mAudioSession is null -- it's not clear what case would cause this.
+						// For now, log it as a warning and see if it ever crops up.
+						LL_WARNS("Voice") << "No current audio session." << LL_ENDL;
+					}
+				}
+			}
+		break;
+	}
+	
+	if(needsSwitch)
+	{
+		if(uri.empty())
+		{
+			// Leave any channel we may be in
+			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
+
+			sessionState *oldSession = mNextAudioSession;
+			mNextAudioSession = NULL;
+
+			// The old session may now need to be deleted.
+			reapSession(oldSession);
+
+			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
+
+			mNextAudioSession = addSession(uri);
+			mNextAudioSession->mHash = hash;
+			mNextAudioSession->mIsSpatial = spatial;
+			mNextAudioSession->mReconnect = !no_reconnect;
+			mNextAudioSession->mIsP2P = is_p2p;
+		}
+		
+		if(getState() <= stateNoChannel)
+		{
+			// We're already set up to join a channel, just needed to fill in the session URI
+		}
+		else
+		{
+			// State machine will come around and rejoin if uri/handle is not empty.
+			sessionTerminate();
+		}
+	}
+}
+
+void LLVivoxVoiceClient::joinSession(sessionState *session)
+{
+	mNextAudioSession = session;
+	
+	if(getState() <= stateNoChannel)
+	{
+		// We're already set up to join a channel, just needed to fill in the session handle
+	}
+	else
+	{
+		// State machine will come around and rejoin if uri/handle is not empty.
+		sessionTerminate();
+	}
+}
+
+void LLVivoxVoiceClient::setNonSpatialChannel(
+	const std::string &uri,
+	const std::string &credentials)
+{
+	switchChannel(uri, false, false, false, credentials);
+}
+
+void LLVivoxVoiceClient::setSpatialChannel(
+	const std::string &uri,
+	const std::string &credentials)
+{
+	mSpatialSessionURI = uri;
+	mSpatialSessionCredentials = credentials;
+	mAreaVoiceDisabled = mSpatialSessionURI.empty();
+
+	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
+	
+	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
+	{
+		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
+		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
+	}
+	else
+	{
+		switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
+	}
+}
+
+void LLVivoxVoiceClient::callUser(const LLUUID &uuid)
+{
+	std::string userURI = sipURIFromID(uuid);
+
+	switchChannel(userURI, false, true, true);
+}
+
+LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid)
+{
+	// Figure out if a session with the user already exists
+	sessionState *session = findSession(uuid);
+	if(!session)
+	{
+		// No session with user, need to start one.
+		std::string uri = sipURIFromID(uuid);
+		session = addSession(uri);
+		session->mIsSpatial = false;
+		session->mReconnect = false;	
+		session->mIsP2P = true;
+		session->mCallerID = uuid;
+	}
+	
+	if(session)
+	{
+		if(session->mHandle.empty())
+		{
+			// Session isn't active -- start it up.
+			sessionCreateSendMessage(session, false, true);
+		}
+		else
+		{	
+			// Session is already active -- start up text.
+			sessionTextConnectSendMessage(session);
+		}
+	}
+	
+	return session;
+}
+
+BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
+{
+	bool result = false;
+
+	// Attempt to locate the indicated session
+	sessionState *session = startUserIMSession(participant_id);
+	if(session)
+	{
+		// found the session, attempt to send the message
+		session->mTextMsgQueue.push(message);
+		
+		// Try to send queued messages (will do nothing if the session is not open yet)
+		sendQueuedTextMessages(session);
+
+		// The message is queued, so we succeed.
+		result = true;
+	}	
+	else
+	{
+		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
+	}
+	
+	return result;
+}
+
+void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session)
+{
+	if(session->mTextStreamState == 1)
+	{
+		if(!session->mTextMsgQueue.empty())
+		{
+			std::ostringstream stream;
+			
+			while(!session->mTextMsgQueue.empty())
+			{
+				std::string message = session->mTextMsgQueue.front();
+				session->mTextMsgQueue.pop();
+				stream
+				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
+					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+					<< "<MessageHeader>text/HTML</MessageHeader>"
+					<< "<MessageBody>" << message << "</MessageBody>"
+				<< "</Request>"
+				<< "\n\n\n";
+			}		
+			writeString(stream.str());
+		}
+	}
+	else
+	{
+		// Session isn't connected yet, defer until later.
+	}
+}
+
+void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
+{
+	// Figure out if a session with the user exists
+	sessionState *session = findSession(uuid);
+	if(session)
+	{
+		// found the session
+		if(!session->mHandle.empty())
+		{
+			sessionTextDisconnectSendMessage(session);
+		}
+	}	
+	else
+	{
+		LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
+	}
+}
+
+bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle)
+{
+	// this is only ever used to answer incoming p2p call invites.
+	
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		session->mIsSpatial = false;
+		session->mReconnect = false;	
+		session->mIsP2P = true;
+
+		joinSession(session);
+		return true;
+	}
+	
+	return false;
+}
+
+BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id)
+{
+	bool result = false;
+	buddyListEntry *buddy = findBuddy(id);
+	if(buddy)
+	{
+		result = buddy->mOnlineSLim;
+		LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
+	}
+
+	if(!result)
+	{
+		// 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.
+		sessionState *session = findSession(id);
+		if(session && !session->mHandle.empty())
+		{
+			if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
+			{
+				LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
+				// we have a p2p text session open with this user, so by definition they're online.
+				result = true;
+			}
+		}
+	}
+	
+	return result;
+}
+
+// Returns true if the indicated participant in the current audio session is really an SL avatar.
+// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls.
+BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id)
+{
+	BOOL result = TRUE; 
+	sessionState *session = findSession(id);
+	
+	if(session != NULL)
+	{
+		// this is a p2p session with the indicated caller, or the session with the specified UUID.
+		if(session->mSynthesizedCallerID)
+			result = FALSE;
+	}
+	else
+	{
+		// Didn't find a matching session -- check the current audio session for a matching participant
+		if(mAudioSession != NULL)
+		{
+			participantState *participant = findParticipantByID(id);
+			if(participant != NULL)
+			{
+				result = participant->isAvatar();
+			}
+		}
+	}
+	
+	return result;
+}
+
+// Returns true if calling back the session URI after the session has closed is possible.
+// Currently this will be false only for PSTN P2P calls.		
+BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id)
+{
+	BOOL result = TRUE; 
+	sessionState *session = findSession(session_id);
+	
+	if(session != NULL)
+	{
+		result = session->isCallBackPossible();
+	}
+	
+	return result;
+}
+
+// Returns true if the session can accepte text IM's.
+// Currently this will be false only for PSTN P2P calls.
+BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id)
+{
+	bool result = TRUE; 
+	sessionState *session = findSession(session_id);
+	
+	if(session != NULL)
+	{
+		result = session->isTextIMPossible();
+	}
+	
+	return result;
+}
+		
+
+void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle)
+{
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		sessionMediaDisconnectSendMessage(session);
+	}
+}
+
+void LLVivoxVoiceClient::leaveNonSpatialChannel()
+{
+	LL_DEBUGS("Voice") 
+		<< "called in state " << state2string(getState()) 
+		<< LL_ENDL;
+	
+	// Make sure we don't rejoin the current session.	
+	sessionState *oldNextSession = mNextAudioSession;
+	mNextAudioSession = NULL;
+	
+	// Most likely this will still be the current session at this point, but check it anyway.
+	reapSession(oldNextSession);
+	
+	verifySessionState();
+	
+	sessionTerminate();
+}
+
+std::string LLVivoxVoiceClient::getCurrentChannel()
+{
+	std::string result;
+	
+	if((getState() == stateRunning) && !mSessionTerminateRequested)
+	{
+		result = getAudioSessionURI();
+	}
+	
+	return result;
+}
+
+bool LLVivoxVoiceClient::inProximalChannel()
+{
+	bool result = false;
+	
+	if((getState() == stateRunning) && !mSessionTerminateRequested)
+	{
+		result = inSpatialChannel();
+	}
+	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id)
+{
+	std::string result;
+	result = "sip:";
+	result += nameFromID(id);
+	result += "@";
+	result += mVoiceSIPURIHostName;
+	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
+{
+	std::string result;
+	if(avatar)
+	{
+		result = "sip:";
+		result += nameFromID(avatar->getID());
+		result += "@";
+		result += mVoiceSIPURIHostName;
+	}
+	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
+{
+	std::string result;
+	if(avatar)
+	{
+		result = nameFromID(avatar->getID());
+	}	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid)
+{
+	std::string result;
+	
+	if (uuid.isNull()) {
+		//VIVOX, the uuid emtpy look for the mURIString and return that instead.
+		//result.assign(uuid.mURIStringName);
+		LLStringUtil::replaceChar(result, '_', ' ');
+		return result;
+	}
+	// Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
+	result = "x";
+	
+	// Base64 encode and replace the pieces of base64 that are less compatible 
+	// with e-mail local-parts.
+	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
+	result += LLBase64::encode(uuid.mData, UUID_BYTES);
+	LLStringUtil::replaceChar(result, '+', '-');
+	LLStringUtil::replaceChar(result, '/', '_');
+	
+	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
+	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
+	
+	// The reverse transform can be done with:
+	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
+	
+	return result;
+}
+
+bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
+{
+	bool result = false;
+	
+	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
+	// If it is, convert to a bare name before doing the transform.
+	std::string name = nameFromsipURI(inName);
+	
+	// Doesn't look like a SIP URI, assume it's an actual name.
+	if(name.empty())
+		name = inName;
+
+	// This will only work if the name is of the proper form.
+	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
+	// "xFnPP04IpREWNkuw1cOXlhw=="
+	
+	if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
+	{
+		// The name appears to have the right form.
+
+		// Reverse the transforms done by nameFromID
+		std::string temp = name;
+		LLStringUtil::replaceChar(temp, '-', '+');
+		LLStringUtil::replaceChar(temp, '_', '/');
+
+		U8 rawuuid[UUID_BYTES + 1]; 
+		int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
+		if(len == UUID_BYTES)
+		{
+			// The decode succeeded.  Stuff the bits into the result's UUID
+			memcpy(uuid.mData, rawuuid, UUID_BYTES);
+			result = true;
+		}
+	} 
+	
+	if(!result)
+	{
+		// VIVOX:  not a standard account name, just copy the URI name mURIString field
+		// and hope for the best.  bpj
+		uuid.setNull();  // VIVOX, set the uuid field to nulls
+	}
+	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
+{
+	return avatar->getFullname();
+}
+
+std::string LLVivoxVoiceClient::sipURIFromName(std::string &name)
+{
+	std::string result;
+	result = "sip:";
+	result += name;
+	result += "@";
+	result += mVoiceSIPURIHostName;
+
+//	LLStringUtil::toLower(result);
+
+	return result;
+}
+
+std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri)
+{
+	std::string result;
+
+	std::string::size_type sipOffset, atOffset;
+	sipOffset = uri.find("sip:");
+	atOffset = uri.find("@");
+	if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
+	{
+		result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
+	}
+	
+	return result;
+}
+
+bool LLVivoxVoiceClient::inSpatialChannel(void)
+{
+	bool result = false;
+	
+	if(mAudioSession)
+		result = mAudioSession->mIsSpatial;
+		
+	return result;
+}
+
+std::string LLVivoxVoiceClient::getAudioSessionURI()
+{
+	std::string result;
+	
+	if(mAudioSession)
+		result = mAudioSession->mSIPURI;
+		
+	return result;
+}
+
+std::string LLVivoxVoiceClient::getAudioSessionHandle()
+{
+	std::string result;
+	
+	if(mAudioSession)
+		result = mAudioSession->mHandle;
+		
+	return result;
+}
+
+
+/////////////////////////////
+// Sending updates of current state
+
+void LLVivoxVoiceClient::enforceTether(void)
+{
+	LLVector3d tethered	= mCameraRequestedPosition;
+
+	// constrain 'tethered' to within 50m of mAvatarPosition.
+	{
+		F32 max_dist = 50.0f;
+		LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
+		F32 camera_distance = (F32)camera_offset.magVec();
+		if(camera_distance > max_dist)
+		{
+			tethered = mAvatarPosition + 
+				(max_dist / camera_distance) * camera_offset;
+		}
+	}
+	
+	if(dist_vec(mCameraPosition, tethered) > 0.1)
+	{
+		mCameraPosition = tethered;
+		mSpatialCoordsDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::updatePosition(void)
+{
+	
+	LLVOAvatarSelf *agent = gAgent.getAvatarObject();
+	LLViewerRegion *region = gAgent.getRegion();
+	if(region && agent)
+	{
+		LLMatrix3 rot;
+		LLVector3d pos;
+		
+		// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
+		// They're currently always set to zero.
+		
+		// Send the current camera position to the voice code
+		rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());		
+		pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin());
+		
+		LLVivoxVoiceClient::getInstance()->setCameraPosition(
+															 pos,				// position
+															 LLVector3::zero, 	// velocity
+															 rot);				// rotation matrix
+		
+		// Send the current avatar position to the voice code
+		rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
+		
+		pos = agent->getPositionGlobal();
+		// TODO: Can we get the head offset from outside the LLVOAvatar?
+		//			pos += LLVector3d(mHeadOffset);
+		pos += LLVector3d(0.f, 0.f, 1.f);
+		
+		LLVivoxVoiceClient::getInstance()->setAvatarPosition(
+															 pos,				// position
+															 LLVector3::zero, 	// velocity
+															 rot);				// rotation matrix
+	}
+}
+
+void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+	mCameraRequestedPosition = position;
+	
+	if(mCameraVelocity != velocity)
+	{
+		mCameraVelocity = velocity;
+		mSpatialCoordsDirty = true;
+	}
+	
+	if(mCameraRot != rot)
+	{
+		mCameraRot = rot;
+		mSpatialCoordsDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+	if(dist_vec(mAvatarPosition, position) > 0.1)
+	{
+		mAvatarPosition = position;
+		mSpatialCoordsDirty = true;
+	}
+	
+	if(mAvatarVelocity != velocity)
+	{
+		mAvatarVelocity = velocity;
+		mSpatialCoordsDirty = true;
+	}
+	
+	if(mAvatarRot != rot)
+	{
+		mAvatarRot = rot;
+		mSpatialCoordsDirty = true;
+	}
+}
+
+bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
+{
+	bool result = false;
+	
+	if(region)
+	{
+		name = region->getName();
+	}
+	
+	if(!name.empty())
+		result = true;
+	
+	return result;
+}
+
+void LLVivoxVoiceClient::leaveChannel(void)
+{
+	if(getState() == stateRunning)
+	{
+		LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;
+		mChannelName.clear();
+		sessionTerminate();
+	}
+}
+
+void LLVivoxVoiceClient::setMuteMic(bool muted)
+{
+	mMuteMic = muted;
+}
+
+void LLVivoxVoiceClient::setUserPTTState(bool ptt)
+{
+	mUserPTTState = ptt;
+}
+
+bool LLVivoxVoiceClient::getUserPTTState()
+{
+	return mUserPTTState;
+}
+
+void LLVivoxVoiceClient::inputUserControlState(bool down)
+{
+	if(mPTTIsToggle)
+	{
+		if(down) // toggle open-mic state on 'down'                                                        
+		{
+			toggleUserPTTState();
+		}
+	}
+	else // set open-mic state as an absolute                                                                  
+	{
+		setUserPTTState(down);
+	}
+}
+
+
+void LLVivoxVoiceClient::toggleUserPTTState(void)
+{
+	mUserPTTState = !mUserPTTState;
+}
+
+void LLVivoxVoiceClient::setVoiceEnabled(bool enabled)
+{
+	if (enabled != mVoiceEnabled)
+	{
+		// TODO: Refactor this so we don't call into LLVoiceChannel, but simply
+		// use the status observer
+		mVoiceEnabled = enabled;
+		LLVoiceClientStatusObserver::EStatusType status;
+		
+		
+		if (enabled)
+		{
+			LLVoiceChannel::getCurrentVoiceChannel()->activate();
+			status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
+		}
+		else
+		{
+			// Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it.
+			LLVoiceChannel::getCurrentVoiceChannel()->deactivate();
+			status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED;
+		}
+	}
+}
+
+bool LLVivoxVoiceClient::voiceEnabled()
+{
+	return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
+}
+
+void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled)
+{
+	mLipSyncEnabled = enabled;
+}
+
+BOOL LLVivoxVoiceClient::lipSyncEnabled()
+{
+	   
+	if ( mVoiceEnabled && stateDisabled != getState() )
+	{
+		return mLipSyncEnabled;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+void LLVivoxVoiceClient::setUsePTT(bool usePTT)
+{
+	if(usePTT && !mUsePTT)
+	{
+		// When the user turns on PTT, reset the current state.
+		mUserPTTState = false;
+	}
+	mUsePTT = usePTT;
+}
+
+void LLVivoxVoiceClient::setPTTIsToggle(bool PTTIsToggle)
+{
+	if(!PTTIsToggle && mPTTIsToggle)
+	{
+		// When the user turns off toggle, reset the current state.
+		mUserPTTState = false;
+	}
+	
+	mPTTIsToggle = PTTIsToggle;
+}
+
+bool LLVivoxVoiceClient::getPTTIsToggle()
+{
+	return mPTTIsToggle;
+}
+
+void LLVivoxVoiceClient::setPTTKey(std::string &key)
+{
+	if(key == "MiddleMouse")
+	{
+		mPTTIsMiddleMouse = true;
+	}
+	else
+	{
+		mPTTIsMiddleMouse = false;
+		if(!LLKeyboard::keyFromString(key, &mPTTKey))
+		{
+			// If the call failed, don't match any key.
+			key = KEY_NONE;
+		}
+	}
+}
+
+void LLVivoxVoiceClient::setEarLocation(S32 loc)
+{
+	if(mEarLocation != loc)
+	{
+		LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
+		
+		mEarLocation = loc;
+		mSpatialCoordsDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::setVoiceVolume(F32 volume)
+{
+	int scaled_volume = scale_speaker_volume(volume);	
+
+	if(scaled_volume != mSpeakerVolume)
+	{
+		if((scaled_volume == 0) || (mSpeakerVolume == 0))
+		{
+			mSpeakerMuteDirty = true;
+		}
+
+		mSpeakerVolume = scaled_volume;
+		mSpeakerVolumeDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::setMicGain(F32 volume)
+{
+	int scaled_volume = scale_mic_volume(volume);
+	
+	if(scaled_volume != mMicVolume)
+	{
+		mMicVolume = scaled_volume;
+		mMicVolumeDirty = true;
+	}
+}
+
+void LLVivoxVoiceClient::keyDown(KEY key, MASK mask)
+{	
+	if (gKeyboard->getKeyRepeated(key))
+	{
+		// ignore auto-repeat keys                                                                         
+		return;
+	}
+	
+	if(!mPTTIsMiddleMouse)
+	{
+		bool down = (mPTTKey != KEY_NONE)
+		&& gKeyboard->getKeyDown(mPTTKey);
+		inputUserControlState(down);
+	}
+	
+	
+}
+void LLVivoxVoiceClient::keyUp(KEY key, MASK mask)
+{
+	if(!mPTTIsMiddleMouse)
+	{
+		bool down = (mPTTKey != KEY_NONE)
+		&& gKeyboard->getKeyDown(mPTTKey);
+		inputUserControlState(down);
+	}
+	
+}
+void LLVivoxVoiceClient::middleMouseState(bool down)
+{
+	if(mPTTIsMiddleMouse)
+	{
+        if(mPTTIsMiddleMouse)
+        {
+			inputUserControlState(down);
+        }		
+	}
+}
+
+/////////////////////////////
+// Accessors for data related to nearby speakers
+BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id)
+{
+	BOOL result = FALSE;
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		// I'm not sure what the semantics of this should be.
+		// For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
+		result = TRUE;
+	}
+	
+	return result;
+}
+
+std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id)
+{
+	std::string result;
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		result = participant->mDisplayName;
+	}
+	
+	return result;
+}
+
+
+
+BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id)
+{
+	BOOL result = FALSE;
+
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
+		{
+			participant->mIsSpeaking = FALSE;
+		}
+		result = participant->mIsSpeaking;
+	}
+	
+	return result;
+}
+
+BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id)
+{
+	BOOL result = FALSE;
+
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		result = participant->mIsModeratorMuted;
+	}
+	
+	return result;
+}
+
+F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id)
+{		
+	F32 result = 0;
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		result = participant->mPower;
+	}
+	
+	return result;
+}
+
+
+
+BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id)
+{
+	BOOL result = FALSE;
+
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		// I'm not sure what the semantics of this should be.
+		// Does "using PTT" mean they're configured with a push-to-talk button?
+		// For now, we know there's no PTT mechanism in place, so nobody is using it.
+	}
+	
+	return result;
+}
+
+BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id)
+{
+	BOOL result = FALSE;
+	
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		result = participant->mOnMuteList;
+	}
+
+	return result;
+}
+
+// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
+// internal = 400 * external^2
+F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id)
+{
+	F32 result = 0.0f;
+	
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		S32 ires = 100; // nominal default volume
+		
+		if(participant->mIsSelf)
+		{
+			// Always make it look like the user's own volume is set at the default.
+		}
+		else if(participant->mUserVolume != -1)
+		{
+			// Use the internal volume
+			ires = participant->mUserVolume;
+			
+			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
+//			LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL;
+		}
+		else if(participant->mVolume != -1)
+		{
+			// Map backwards from vivox volume 
+
+			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
+//			LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL;
+
+			if(participant->mVolume < 56)
+			{
+				ires = (participant->mVolume * 100) / 56;
+			}
+			else
+			{
+				ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100;
+			}
+		}
+		result = sqrtf(((F32)ires) / 400.f);
+	}
+
+	// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging.
+//	LL_DEBUGS("Voice") << "returning " << result << LL_ENDL;
+
+	return result;
+}
+
+void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
+{
+	if(mAudioSession)
+	{
+		participantState *participant = findParticipantByID(id);
+		if (participant)
+		{
+			// volume can amplify by as much as 4x!
+			S32 ivol = (S32)(400.f * volume * volume);
+			participant->mUserVolume = llclamp(ivol, 0, 400);
+			participant->mVolumeDirty = TRUE;
+			mAudioSession->mVolumeDirty = TRUE;
+		}
+	}
+}
+
+std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id)
+{
+	std::string result;
+
+	participantState *participant = findParticipantByID(id);
+	if(participant)
+	{
+		result = participant->mGroupID;
+	}
+	
+	return result;
+}
+
+BOOL LLVivoxVoiceClient::getAreaVoiceDisabled()
+{
+	return mAreaVoiceDisabled;
+}
+
+void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
+	
+	if(!mMainSessionGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Start</RecordingControlType>" 
+		<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
+		<< "<Filename>" << "" << "</Filename>"
+		<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
+		<< "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
+		<< "</Request>\n\n\n";
+
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename)
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Flush</RecordingControlType>" 
+		<< "<Filename>" << filename << "</Filename>"
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::recordingStop()
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Stop</RecordingControlType>" 
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename)
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Start</RecordingControlType>" 
+		<< "<Filename>" << filename << "</Filename>"
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::filePlaybackStop()
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Stop</RecordingControlType>" 
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVivoxVoiceClient::filePlaybackSetPaused(bool paused)
+{
+	// TODO: Implement once Vivox gives me a sample
+}
+
+void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed)
+{
+	// TODO: Implement once Vivox gives me a sample
+}
+
+LLVivoxVoiceClient::sessionState::sessionState() :
+	mMediaStreamState(streamStateUnknown),
+	mTextStreamState(streamStateUnknown),
+	mCreateInProgress(false),
+	mMediaConnectInProgress(false),
+	mVoiceInvitePending(false),
+	mTextInvitePending(false),
+	mSynthesizedCallerID(false),
+	mIsChannel(false),
+	mIsSpatial(false),
+	mIsP2P(false),
+	mIncoming(false),
+	mVoiceEnabled(false),
+	mReconnect(false),
+	mVolumeDirty(false),
+	mParticipantsChanged(false)
+{
+}
+
+LLVivoxVoiceClient::sessionState::~sessionState()
+{
+	removeAllParticipants();
+}
+
+bool LLVivoxVoiceClient::sessionState::isCallBackPossible()
+{
+	// This may change to be explicitly specified by vivox in the future...
+	// Currently, only PSTN P2P calls cannot be returned.
+	// Conveniently, this is also the only case where we synthesize a caller UUID.
+	return !mSynthesizedCallerID;
+}
+
+bool LLVivoxVoiceClient::sessionState::isTextIMPossible()
+{
+	// This may change to be explicitly specified by vivox in the future...
+	return !mSynthesizedCallerID;
+}
+
+
+LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void)
+{
+	return mSessions.begin();
+}
+
+LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void)
+{
+	return mSessions.end();
+}
+
+
+LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle)
+{
+	sessionState *result = NULL;
+	sessionMap::iterator iter = mSessionsByHandle.find(handle);
+	if(iter != mSessionsByHandle.end())
+	{
+		result = iter->second;
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
+{	
+	sessionState *result = NULL;
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+		if(session->mCreateInProgress && (session->mSIPURI == uri))
+		{
+			result = session;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id)
+{
+	sessionState *result = NULL;
+	
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+		if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id))
+		{
+			result = session;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle)
+{
+	sessionState *result = NULL;
+	
+	if(handle.empty())
+	{
+		// No handle supplied.
+		// Check whether there's already a session with this URI
+		for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+		{
+			sessionState *s = *iter;
+			if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
+			{
+				// TODO: I need to think about this logic... it's possible that this case should raise an internal error.
+				result = s;
+				break;
+			}
+		}
+	}
+	else // (!handle.empty())
+	{
+		// Check for an existing session with this handle
+		sessionMap::iterator iter = mSessionsByHandle.find(handle);
+		
+		if(iter != mSessionsByHandle.end())
+		{
+			result = iter->second;
+		}
+	}
+
+	if(!result)
+	{
+		// No existing session found.
+		
+		LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
+		result = new sessionState();
+		result->mSIPURI = uri;
+		result->mHandle = handle;
+		
+		mSessions.insert(result);
+
+		if(!result->mHandle.empty())
+		{
+			mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result));
+		}
+	}
+	else
+	{
+		// Found an existing session
+		
+		if(uri != result->mSIPURI)
+		{
+			// TODO: Should this be an internal error?
+			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
+			setSessionURI(result, uri);
+		}
+
+		if(handle != result->mHandle)
+		{
+			if(handle.empty())
+			{
+				// There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break.
+				LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL;
+			}
+			else
+			{
+				// TODO: Should this be an internal error?
+				LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
+				setSessionHandle(result, handle);
+			}
+		}
+		
+		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
+	}
+
+	verifySessionState();
+		
+	return result;
+}
+
+void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
+{
+	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
+	
+	if(!session->mHandle.empty())
+	{
+		// Remove session from the map if it should have been there.
+		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle);
+		if(iter != mSessionsByHandle.end())
+		{
+			if(iter->second != session)
+			{
+				LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
+			}
+
+			mSessionsByHandle.erase(iter);
+		}
+		else
+		{
+			LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
+		}
+	}
+			
+	session->mHandle = handle;
+
+	if(!handle.empty())
+	{
+		mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session));
+	}
+
+	verifySessionState();
+}
+
+void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
+{
+	// There used to be a map of session URIs to sessions, which made this complex....
+	session->mSIPURI = uri;
+
+	verifySessionState();
+}
+
+void LLVivoxVoiceClient::deleteSession(sessionState *session)
+{
+	// Remove the session from the handle map
+	if(!session->mHandle.empty())
+	{
+		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle);
+		if(iter != mSessionsByHandle.end())
+		{
+			if(iter->second != session)
+			{
+				LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL;
+			}
+			mSessionsByHandle.erase(iter);
+		}
+	}
+
+	// Remove the session from the URI map
+	mSessions.erase(session);
+	
+	// At this point, the session should be unhooked from all lists and all state should be consistent.
+	verifySessionState();
+
+	// If this is the current audio session, clean up the pointer which will soon be dangling.
+	if(mAudioSession == session)
+	{
+		mAudioSession = NULL;
+		mAudioSessionChanged = true;
+	}
+
+	// ditto for the next audio session
+	if(mNextAudioSession == session)
+	{
+		mNextAudioSession = NULL;
+	}
+
+	// delete the session
+	delete session;
+}
+
+void LLVivoxVoiceClient::deleteAllSessions()
+{
+	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+
+	while(!mSessions.empty())
+	{
+		deleteSession(*(sessionsBegin()));
+	}
+	
+	if(!mSessionsByHandle.empty())
+	{
+		LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL;
+	}
+}
+
+void LLVivoxVoiceClient::verifySessionState(void)
+{
+	// This is mostly intended for debugging problems with session state management.
+	LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
+
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+
+		LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
+		
+		if(!session->mHandle.empty())
+		{
+			// every session with a non-empty handle needs to be in the handle map
+			sessionMap::iterator i2 = mSessionsByHandle.find(session->mHandle);
+			if(i2 == mSessionsByHandle.end())
+			{
+				LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
+			}
+			else
+			{
+				if(i2->second != session)
+				{
+					LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
+				}
+			}
+		}
+	}
+		
+	// check that every entry in the handle map points to a valid session in the session set
+	for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
+	{
+		sessionState *session = iter->second;
+		sessionIterator i2 = mSessions.find(session);
+		if(i2 == mSessions.end())
+		{
+			LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
+		}
+		else
+		{
+			if(session->mHandle != (*i2)->mHandle)
+			{
+				LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
+			}
+		}
+	}
+}
+
+LLVivoxVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
+	mURI(uri)
+{
+	mOnlineSL = false;
+	mOnlineSLim = false;
+	mCanSeeMeOnline = true;
+	mHasBlockListEntry = false;
+	mHasAutoAcceptListEntry = false;
+	mNameResolved = false;
+	mInVivoxBuddies = false;
+	mInSLFriends = false;
+	mNeedsNameUpdate = false;
+}
+
+void LLVivoxVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
+{
+	buddyListEntry *buddy = addBuddy(uri, displayName);
+	buddy->mInVivoxBuddies = true;	
+}
+
+LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri)
+{
+	std::string empty;
+	buddyListEntry *buddy = addBuddy(uri, empty);
+	if(buddy->mDisplayName.empty())
+	{
+		buddy->mNameResolved = false;
+	}
+	return buddy;
+}
+
+LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter = mBuddyListMap.find(uri);
+	
+	if(iter != mBuddyListMap.end())
+	{
+		// Found a matching buddy already in the map.
+		LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
+		result = iter->second;
+	}
+
+	if(!result)
+	{
+		// participant isn't already in one list or the other.
+		LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
+		result = new buddyListEntry(uri);
+		result->mDisplayName = displayName;
+
+		if(IDFromName(uri, result->mUUID)) 
+		{
+			// Extracted UUID from name successfully.
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL;
+		}
+
+		mBuddyListMap.insert(buddyListMap::value_type(result->mURI, result));
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const std::string &uri)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter = mBuddyListMap.find(uri);
+	if(iter != mBuddyListMap.end())
+	{
+		result = iter->second;
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const LLUUID &id)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter;
+
+	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
+	{
+		if(iter->second->mUUID == id)
+		{
+			result = iter->second;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddyByDisplayName(const std::string &name)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter;
+
+	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
+	{
+		if(iter->second->mDisplayName == name)
+		{
+			result = iter->second;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+void LLVivoxVoiceClient::deleteBuddy(const std::string &uri)
+{
+	buddyListMap::iterator iter = mBuddyListMap.find(uri);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
+		buddyListEntry *buddy = iter->second;
+		mBuddyListMap.erase(iter);
+		delete buddy;
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
+	}
+	
+}
+
+void LLVivoxVoiceClient::deleteAllBuddies(void)
+{
+	while(!mBuddyListMap.empty())
+	{
+		deleteBuddy(mBuddyListMap.begin()->first);
+	}
+	
+	// Don't want to correlate with friends list when we've emptied the buddy list.
+	mBuddyListMapPopulated = false;
+	
+	// Don't want to correlate with friends list when we've reset the block rules.
+	mBlockRulesListReceived = false;
+	mAutoAcceptRulesListReceived = false;
+}
+
+void LLVivoxVoiceClient::deleteAllBlockRules(void)
+{
+	// Clear the block list entry flags from all local buddy list entries
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	{
+		buddy_it->second->mHasBlockListEntry = false;
+	}
+}
+
+void LLVivoxVoiceClient::deleteAllAutoAcceptRules(void)
+{
+	// Clear the auto-accept list entry flags from all local buddy list entries
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	{
+		buddy_it->second->mHasAutoAcceptListEntry = false;
+	}
+}
+
+void LLVivoxVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
+{
+	buddyListEntry *buddy = NULL;
+
+	// blockMask is the SIP URI of a friends list entry
+	buddyListMap::iterator iter = mBuddyListMap.find(blockMask);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
+		buddy = iter->second;
+	}
+
+	if(buddy == NULL)
+	{
+		LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
+		buddy = addBuddy(blockMask);
+	}
+	
+	if(buddy != NULL)
+	{
+		buddy->mHasBlockListEntry = true;
+	}
+}
+
+void LLVivoxVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
+{
+	buddyListEntry *buddy = NULL;
+
+	// blockMask is the SIP URI of a friends list entry
+	buddyListMap::iterator iter = mBuddyListMap.find(autoAcceptMask);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
+		buddy = iter->second;
+	}
+
+	if(buddy == NULL)
+	{
+		LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
+		buddy = addBuddy(autoAcceptMask);
+	}
+
+	if(buddy != NULL)
+	{
+		buddy->mHasAutoAcceptListEntry = true;
+	}
+}
+
+void LLVivoxVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
+{
+	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
+	mBlockRulesListReceived = true;
+}
+
+void LLVivoxVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
+{
+	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
+	mAutoAcceptRulesListReceived = true;
+}
+
+void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
+{
+	mParticipantObservers.insert(observer);
+}
+
+void LLVivoxVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
+{
+	mParticipantObservers.erase(observer);
+}
+
+void LLVivoxVoiceClient::notifyParticipantObservers()
+{
+	for (observer_set_t::iterator it = mParticipantObservers.begin();
+		it != mParticipantObservers.end();
+		)
+	{
+		LLVoiceClientParticipantObserver* observer = *it;
+		observer->onChange();
+		// In case onChange() deleted an entry.
+		it = mParticipantObservers.upper_bound(observer);
+	}
+}
+
+void LLVivoxVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
+{
+	mStatusObservers.insert(observer);
+}
+
+void LLVivoxVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
+{
+	mStatusObservers.erase(observer);
+}
+
+void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
+{
+	if(mAudioSession)
+	{
+		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
+		{
+			switch(mAudioSession->mErrorStatusCode)
+			{
+				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break;
+				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break;
+				case 20715:
+					//invalid channel, we may be using a set of poorly cached
+					//info
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+					break;
+				case 1009:
+					//invalid username and password
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+					break;
+			}
+
+			// Reset the error code to make sure it won't be reused later by accident.
+			mAudioSession->mErrorStatusCode = 0;
+		}
+		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
+		{
+			switch(mAudioSession->mErrorStatusCode)
+			{
+				case 404:	// NOT_FOUND
+				case 480:	// TEMPORARILY_UNAVAILABLE
+				case 408:	// REQUEST_TIMEOUT
+					// call failed because other user was not available
+					// treat this as an error case
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+
+					// Reset the error code to make sure it won't be reused later by accident.
+					mAudioSession->mErrorStatusCode = 0;
+				break;
+			}
+		}
+	}
+		
+	LL_DEBUGS("Voice") 
+		<< " " << LLVoiceClientStatusObserver::status2string(status)  
+		<< ", session URI " << getAudioSessionURI() 
+		<< (inSpatialChannel()?", proximal is true":", proximal is false")
+	<< LL_ENDL;
+
+	for (status_observer_set_t::iterator it = mStatusObservers.begin();
+		it != mStatusObservers.end();
+		)
+	{
+		LLVoiceClientStatusObserver* observer = *it;
+		observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
+		// In case onError() deleted an entry.
+		it = mStatusObservers.upper_bound(observer);
+	}
+
+}
+
+void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer)
+{
+	mFriendObservers.insert(observer);
+}
+
+void LLVivoxVoiceClient::removeObserver(LLFriendObserver* observer)
+{
+	mFriendObservers.erase(observer);
+}
+
+void LLVivoxVoiceClient::notifyFriendObservers()
+{
+	for (friend_observer_set_t::iterator it = mFriendObservers.begin();
+		it != mFriendObservers.end();
+		)
+	{
+		LLFriendObserver* observer = *it;
+		it++;
+		// The only friend-related thing we notify on is online/offline transitions.
+		observer->changed(LLFriendObserver::ONLINE);
+	}
+}
+
+void LLVivoxVoiceClient::lookupName(const LLUUID &id)
+{
+	BOOL is_group = FALSE;
+	gCacheName->get(id, is_group, &LLVivoxVoiceClient::onAvatarNameLookup);
+}
+
+//static
+void LLVivoxVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
+{
+		std::string name = llformat("%s %s", first.c_str(), last.c_str());
+		LLVivoxVoiceClient::getInstance()->avatarNameResolved(id, name);
+	
+}
+
+void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
+{
+	// If the avatar whose name just resolved is on our friends list, resync the friends list.
+	if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
+	{
+		mFriendsListDirty = true;
+	}
+	
+	// Iterate over all sessions.
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+
+		// Check for this user as a participant in this session
+		participantState *participant = session->findParticipantByID(id);
+		if(participant)
+		{
+			// Found -- fill in the name
+			participant->mAccountName = name;
+			// and post a "participants updated" message to listeners later.
+			session->mParticipantsChanged = true;
+		}
+		
+		// Check whether this is a p2p session whose caller name just resolved
+		if(session->mCallerID == id)
+		{
+			// this session's "caller ID" just resolved.  Fill in the name.
+			session->mName = name;
+			if(session->mTextInvitePending)
+			{
+				session->mTextInvitePending = false;
+
+				// We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.				
+			}
+			if(session->mVoiceInvitePending)
+			{
+				session->mVoiceInvitePending = false;
+
+				gIMMgr->inviteToSession(
+										LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID),
+										session->mName,
+										session->mCallerID, 
+										session->mName, 
+										IM_SESSION_P2P_INVITE, 
+										LLIMMgr::INVITATION_TYPE_VOICE,
+										session->mHandle);
+			}
+			
+		}
+	}
+}
+
+
+LLVivoxProtocolParser::LLVivoxProtocolParser()
+{
+	parser = NULL;
+	parser = XML_ParserCreate(NULL);
+	
+	reset();
+}
+
+void LLVivoxProtocolParser::reset()
+{
+	responseDepth = 0;
+	ignoringTags = false;
+	accumulateText = false;
+	energy = 0.f;
+	ignoreDepth = 0;
+	isChannel = false;
+	isEvent = false;
+	isLocallyMuted = false;
+	isModeratorMuted = false;
+	isSpeaking = false;
+	participantType = 0;
+	squelchDebugOutput = false;
+	returnCode = -1;
+	state = 0;
+	statusCode = 0;
+	volume = 0;
+	textBuffer.clear();
+	alias.clear();
+	numberOfAliases = 0;
+	applicationString.clear();
+}
+
+//virtual 
+LLVivoxProtocolParser::~LLVivoxProtocolParser()
+{
+	if (parser)
+		XML_ParserFree(parser);
+}
+
+// virtual
+LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
+													  const LLChannelDescriptors& channels,
+													  buffer_ptr_t& buffer,
+													  bool& eos,
+													  LLSD& context,
+													  LLPumpIO* pump)
+{
+	LLBufferStream istr(channels, buffer.get());
+	std::ostringstream ostr;
+	while (istr.good())
+	{
+		char buf[1024];
+		istr.read(buf, sizeof(buf));
+		mInput.append(buf, istr.gcount());
+	}
+	
+	// Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
+	int start = 0;
+	int delim;
+	while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
+	{	
+		
+		// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
+		reset();
+		
+		XML_ParserReset(parser, NULL);
+		XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
+		XML_SetCharacterDataHandler(parser, ExpatCharHandler);
+		XML_SetUserData(parser, this);	
+		XML_Parse(parser, mInput.data() + start, delim - start, false);
+		
+		// If this message isn't set to be squelched, output the raw XML received.
+		if(!squelchDebugOutput)
+		{
+			LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
+		}
+		
+		start = delim + 3;
+	}
+	
+	if(start != 0)
+		mInput = mInput.substr(start);
+	
+	LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
+	
+	if(!LLVivoxVoiceClient::getInstance()->mConnected)
+	{
+		// If voice has been disabled, we just want to close the socket.  This does so.
+		LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL;
+		return STATUS_STOP;
+	}
+	
+	return STATUS_OK;
+}
+
+void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
+{
+	if (data)
+	{
+		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
+		object->StartTag(el, attr);
+	}
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
+{
+	if (data)
+	{
+		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
+		object->EndTag(el);
+	}
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
+{
+	if (data)
+	{
+		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data;
+		object->CharData(s, len);
+	}
+}
+
+// --------------------------------------------------------------------------------
+
+
+void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
+{
+	// Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
+	textBuffer.clear();
+	// only accumulate text if we're not ignoring tags.
+	accumulateText = !ignoringTags;
+	
+	if (responseDepth == 0)
+	{	
+		isEvent = !stricmp("Event", tag);
+		
+		if (!stricmp("Response", tag) || isEvent)
+		{
+			// Grab the attributes
+			while (*attr)
+			{
+				const char	*key = *attr++;
+				const char	*value = *attr++;
+				
+				if (!stricmp("requestId", key))
+				{
+					requestId = value;
+				}
+				else if (!stricmp("action", key))
+				{
+					actionString = value;
+				}
+				else if (!stricmp("type", key))
+				{
+					eventTypeString = value;
+				}
+			}
+		}
+		LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
+	}
+	else
+	{
+		if (ignoringTags)
+		{
+			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+		}
+		else
+		{
+			LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
+			
+			// Ignore the InputXml stuff so we don't get confused
+			if (!stricmp("InputXml", tag))
+			{
+				ignoringTags = true;
+				ignoreDepth = responseDepth;
+				accumulateText = false;
+				
+				LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
+			}
+			else if (!stricmp("CaptureDevices", tag))
+			{
+				LLVivoxVoiceClient::getInstance()->clearCaptureDevices();
+			}			
+			else if (!stricmp("RenderDevices", tag))
+			{
+				LLVivoxVoiceClient::getInstance()->clearRenderDevices();
+			}
+			else if (!stricmp("CaptureDevice", tag))
+			{
+				deviceString.clear();
+			}
+			else if (!stricmp("RenderDevice", tag))
+			{
+				deviceString.clear();
+			}			
+			else if (!stricmp("Buddies", tag))
+			{
+				LLVivoxVoiceClient::getInstance()->deleteAllBuddies();
+			}
+			else if (!stricmp("BlockRules", tag))
+			{
+				LLVivoxVoiceClient::getInstance()->deleteAllBlockRules();
+			}
+			else if (!stricmp("AutoAcceptRules", tag))
+			{
+				LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules();
+			}
+			
+		}
+	}
+	responseDepth++;
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::EndTag(const char *tag)
+{
+	const std::string& string = textBuffer;
+	
+	responseDepth--;
+	
+	if (ignoringTags)
+	{
+		if (ignoreDepth == responseDepth)
+		{
+			LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
+			ignoringTags = false;
+		}
+		else
+		{
+			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+		}
+	}
+	
+	if (!ignoringTags)
+	{
+		LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+		
+		// Closing a tag. Finalize the text we've accumulated and reset
+		if (!stricmp("ReturnCode", tag))
+			returnCode = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("SessionHandle", tag))
+			sessionHandle = string;
+		else if (!stricmp("SessionGroupHandle", tag))
+			sessionGroupHandle = string;
+		else if (!stricmp("StatusCode", tag))
+			statusCode = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("StatusString", tag))
+			statusString = string;
+		else if (!stricmp("ParticipantURI", tag))
+			uriString = string;
+		else if (!stricmp("Volume", tag))
+			volume = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("Energy", tag))
+			energy = (F32)strtod(string.c_str(), NULL);
+		else if (!stricmp("IsModeratorMuted", tag))
+			isModeratorMuted = !stricmp(string.c_str(), "true");
+		else if (!stricmp("IsSpeaking", tag))
+			isSpeaking = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Alias", tag))
+			alias = string;
+		else if (!stricmp("NumberOfAliases", tag))
+			numberOfAliases = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("Application", tag))
+			applicationString = string;
+		else if (!stricmp("ConnectorHandle", tag))
+			connectorHandle = string;
+		else if (!stricmp("VersionID", tag))
+			versionID = string;
+		else if (!stricmp("AccountHandle", tag))
+			accountHandle = string;
+		else if (!stricmp("State", tag))
+			state = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("URI", tag))
+			uriString = string;
+		else if (!stricmp("IsChannel", tag))
+			isChannel = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Incoming", tag))
+			incoming = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Enabled", tag))
+			enabled = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Name", tag))
+			nameString = string;
+		else if (!stricmp("AudioMedia", tag))
+			audioMediaString = string;
+		else if (!stricmp("ChannelName", tag))
+			nameString = string;
+		else if (!stricmp("DisplayName", tag))
+			displayNameString = string;
+		else if (!stricmp("Device", tag))
+			deviceString = string;		
+		else if (!stricmp("AccountName", tag))
+			nameString = string;
+		else if (!stricmp("ParticipantType", tag))
+			participantType = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("IsLocallyMuted", tag))
+			isLocallyMuted = !stricmp(string.c_str(), "true");
+		else if (!stricmp("MicEnergy", tag))
+			energy = (F32)strtod(string.c_str(), NULL);
+		else if (!stricmp("ChannelName", tag))
+			nameString = string;
+		else if (!stricmp("ChannelURI", tag))
+			uriString = string;
+		else if (!stricmp("BuddyURI", tag))
+			uriString = string;
+		else if (!stricmp("Presence", tag))
+			statusString = string;
+		else if (!stricmp("CaptureDevice", tag))
+		{
+			LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString);
+		}
+		else if (!stricmp("RenderDevice", tag))
+		{
+			LLVivoxVoiceClient::getInstance()->addRenderDevice(deviceString);
+		}
+		else if (!stricmp("Buddy", tag))
+		{
+			LLVivoxVoiceClient::getInstance()->processBuddyListEntry(uriString, displayNameString);
+		}
+		else if (!stricmp("BlockRule", tag))
+		{
+			LLVivoxVoiceClient::getInstance()->addBlockRule(blockMask, presenceOnly);
+		}
+		else if (!stricmp("BlockMask", tag))
+			blockMask = string;
+		else if (!stricmp("PresenceOnly", tag))
+			presenceOnly = string;
+		else if (!stricmp("AutoAcceptRule", tag))
+		{
+			LLVivoxVoiceClient::getInstance()->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
+		}
+		else if (!stricmp("AutoAcceptMask", tag))
+			autoAcceptMask = string;
+		else if (!stricmp("AutoAddAsBuddy", tag))
+			autoAddAsBuddy = string;
+		else if (!stricmp("MessageHeader", tag))
+			messageHeader = string;
+		else if (!stricmp("MessageBody", tag))
+			messageBody = string;
+		else if (!stricmp("NotificationType", tag))
+			notificationType = string;
+		else if (!stricmp("HasText", tag))
+			hasText = !stricmp(string.c_str(), "true");
+		else if (!stricmp("HasAudio", tag))
+			hasAudio = !stricmp(string.c_str(), "true");
+		else if (!stricmp("HasVideo", tag))
+			hasVideo = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Terminated", tag))
+			terminated = !stricmp(string.c_str(), "true");
+		else if (!stricmp("SubscriptionHandle", tag))
+			subscriptionHandle = string;
+		else if (!stricmp("SubscriptionType", tag))
+			subscriptionType = string;
+		
+	
+		textBuffer.clear();
+		accumulateText= false;
+		
+		if (responseDepth == 0)
+		{
+			// We finished all of the XML, process the data
+			processResponse(tag);
+		}
+	}
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::CharData(const char *buffer, int length)
+{
+	/*
+	 This method is called for anything that isn't a tag, which can be text you
+	 want that lies between tags, and a lot of stuff you don't want like file formatting
+	 (tabs, spaces, CR/LF, etc).
+	 
+	 Only copy text if we are in accumulate mode...
+	 */
+	if (accumulateText)
+		textBuffer.append(buffer, length);
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::processResponse(std::string tag)
+{
+	LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
+	
+	// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs.
+	// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
+	// so I believe this will give correct behavior.
+	
+	if(returnCode == 0)
+		statusCode = 0;
+	
+	if (isEvent)
+	{
+		const char *eventTypeCstr = eventTypeString.c_str();
+		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
+		{
+			LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
+		{
+			/*
+			 <Event type="SessionAddedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+			 <Uri>sip:confctl-1408789@bhr.vivox.com</Uri>
+			 <IsChannel>true</IsChannel>
+			 <Incoming>false</Incoming>
+			 <ChannelName />
+			 </Event>
+			 */
+			LLVivoxVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
+		{
+			LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
+		{
+			LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle);
+		}
+		else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
+		{
+			/*
+			 <Event type="MediaStreamUpdatedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+			 <StatusCode>200</StatusCode>
+			 <StatusString>OK</StatusString>
+			 <State>2</State>
+			 <Incoming>false</Incoming>
+			 </Event>
+			 */
+			LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
+		}		
+		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
+		{
+			/*
+			 <Event type="TextStreamUpdatedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
+			 <Enabled>true</Enabled>
+			 <State>1</State>
+			 <Incoming>true</Incoming>
+			 </Event>
+			 */
+			LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
+		}
+		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
+		{
+			/* 
+			 <Event type="ParticipantAddedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
+			 <ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
+			 <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
+			 <DisplayName />
+			 <ParticipantType>0</ParticipantType>
+			 </Event>
+			 */
+			LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
+		}
+		else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
+		{
+			/*
+			 <Event type="ParticipantRemovedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
+			 <ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
+			 <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
+			 </Event>
+			 */
+			LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
+		}
+		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
+		{
+			/*
+			 <Event type="ParticipantUpdatedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+			 <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
+			 <IsModeratorMuted>false</IsModeratorMuted>
+			 <IsSpeaking>true</IsSpeaking>
+			 <Volume>44</Volume>
+			 <Energy>0.0879437</Energy>
+			 </Event>
+			 */
+			
+			// These happen so often that logging them is pretty useless.
+			squelchDebugOutput = true;
+			
+			LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
+		}
+		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
+		{
+			LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy);
+		}
+		else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
+		{
+			LLVivoxVoiceClient::getInstance()->buddyPresenceEvent(uriString, alias, statusString, applicationString);
+		}
+		else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
+		{
+			// The buddy list was updated during parsing.
+			// Need to recheck against the friends list.
+			LLVivoxVoiceClient::getInstance()->buddyListChanged();
+		}
+		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
+		{
+			/*
+			 <Event type="BuddyChangedEvent">
+			 <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
+			 <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
+			 <DisplayName>Monroe Tester</DisplayName>
+			 <BuddyData />
+			 <GroupID>0</GroupID>
+			 <ChangeType>Set</ChangeType>
+			 </Event>
+			 */		
+			// TODO: Question: Do we need to process this at all?
+		}
+		else if (!stricmp(eventTypeCstr, "MessageEvent"))  
+		{
+			LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))  
+		{
+			LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType);
+		}
+		else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))  
+		{
+			LLVivoxVoiceClient::getInstance()->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))  
+		{
+			/*
+			 <Event type="SessionUpdatedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+			 <Uri>sip:confctl-9@bhd.vivox.com</Uri>
+			 <IsMuted>0</IsMuted>
+			 <Volume>50</Volume>
+			 <TransmitEnabled>1</TransmitEnabled>
+			 <IsFocused>0</IsFocused>
+			 <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
+			 <SessionFontID>0</SessionFontID>
+			 </Event>
+			 */
+			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
+		}
+		
+		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))  
+		{
+			/*
+			 <Event type="SessionGroupRemovedEvent">
+			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+			 </Event>
+			 */
+			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
+		}
+		else
+		{
+			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
+		}
+	}
+	else
+	{
+		const char *actionCstr = actionString.c_str();
+		if (!stricmp(actionCstr, "Connector.Create.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
+		}
+		else if (!stricmp(actionCstr, "Account.Login.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
+		}
+		else if (!stricmp(actionCstr, "Session.Create.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);			
+		}
+		else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);			
+		}
+		else if (!stricmp(actionCstr, "Session.Connect.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString);			
+		}
+		else if (!stricmp(actionCstr, "Account.Logout.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString);			
+		}
+		else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);			
+		}
+		else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->accountListBlockRulesResponse(statusCode, statusString);						
+		}
+		else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
+		{
+			LLVivoxVoiceClient::getInstance()->accountListAutoAcceptRulesResponse(statusCode, statusString);						
+		}
+		else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
+		{
+			// We don't need to process these, but they're so spammy we don't want to log them.
+			squelchDebugOutput = true;
+		}
+		/*
+		 else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
+		 {
+		 LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString);
+		 }
+		 else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
+		 {
+		 
+		 }
+		 else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
+		 {
+		 
+		 }
+		 */
+	}
+}
+
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bf13e679318cd44af9a34dc7e51a5bc7bccebca
--- /dev/null
+++ b/indra/newview/llvoicevivox.h
@@ -0,0 +1,905 @@
+/** 
+ * @file llvoicevivox.h
+ * @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef LL_VOICE_VIVOX_H
+#define LL_VOICE_VIVOX_H
+
+class LLVOAvatar;
+class LLVivoxProtocolParser;
+
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llchainio.h"
+#include "lliosocket.h"
+#include "v3math.h"
+#include "llframetimer.h"
+#include "llviewerregion.h"
+#include "llcallingcard.h"   // for LLFriendObserver
+
+#ifdef LL_STANDALONE
+# include "expat.h"
+#else
+# include "expat/expat.h"
+#endif
+#include "llvoiceclient.h"
+
+
+class LLVivoxVoiceAccountProvisionResponder;
+class LLVivoxVoiceClientMuteListObserver;
+class LLVivoxVoiceClientFriendsObserver;	
+
+
+class LLVivoxVoiceClientParticipantObserver
+{
+public:
+	virtual ~LLVivoxVoiceClientParticipantObserver() { }
+	virtual void onChange() = 0;
+};
+
+
+class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public LLVoiceModuleInterface
+{
+	LOG_CLASS(LLVivoxVoiceClient);
+public:
+	LLVivoxVoiceClient();	
+	virtual ~LLVivoxVoiceClient();
+	
+	
+	/// @name LLVoiceModuleInterface virtual implementations
+	///  @see LLVoiceModuleInterface
+	//@{
+	virtual void init(LLPumpIO *pump);	// Call this once at application startup (creates connector)
+	virtual void terminate();	// Call this to clean up during shutdown
+	
+	virtual const LLVoiceVersionInfo& getVersion();
+	
+	virtual void updateSettings(); // call after loading settings and whenever they change
+	
+	/////////////////////
+	/// @name Tuning
+	//@{
+	virtual void tuningStart();
+	virtual void tuningStop();
+	virtual bool inTuningMode();
+	
+	virtual void tuningSetMicVolume(float volume);
+	virtual void tuningSetSpeakerVolume(float volume);
+	virtual float tuningGetEnergy(void);
+	//@}
+	
+	/////////////////////
+	/// @name Devices
+	//@{
+	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+	// i.e. when the daemon is running and connected, and the device lists are populated.
+	virtual bool deviceSettingsAvailable();
+	
+	// Requery the vivox daemon for the current list of input/output devices.
+	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+	// (use this if you want to know when it's done).
+	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+	virtual void refreshDeviceLists(bool clearCurrentList = true);
+	
+	virtual void setCaptureDevice(const std::string& name);
+	virtual void setRenderDevice(const std::string& name);
+	
+	virtual LLVoiceDeviceList& getCaptureDevices();
+	virtual LLVoiceDeviceList& getRenderDevices();
+	//@}	
+	
+	virtual std::vector<LLUUID> getParticipantList(void);
+	// Send a text message to the specified user, initiating the session if necessary.
+	virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message);
+	
+	// close any existing text IM session with the specified user
+	virtual void endUserIMSession(const LLUUID &uuid);
+	
+	// Returns true if calling back the session URI after the session has closed is possible.
+	// Currently this will be false only for PSTN P2P calls.		
+	// NOTE: this will return true if the session can't be found. 
+	virtual BOOL isSessionCallBackPossible(const LLUUID &session_id);
+	
+	// Returns true if the session can accepte text IM's.
+	// Currently this will be false only for PSTN P2P calls.
+	// NOTE: this will return true if the session can't be found. 
+	virtual BOOL isSessionTextIMPossible(const LLUUID &session_id);
+	
+	
+	////////////////////////////
+	/// @name Channel stuff
+	//@{
+	// returns true iff the user is currently in a proximal (local spatial) channel.
+	// Note that gestures should only fire if this returns true.
+	virtual bool inProximalChannel();
+	
+	virtual void setNonSpatialChannel(const std::string &uri,
+									  const std::string &credentials);
+	
+	virtual void setSpatialChannel(const std::string &uri,
+								   const std::string &credentials);
+	
+	virtual void leaveNonSpatialChannel();
+	
+	virtual void leaveChannel(void);	
+	
+	// Returns the URI of the current channel, or an empty string if not currently in a channel.
+	// NOTE that it will return an empty string if it's in the process of joining a channel.
+	virtual std::string getCurrentChannel();
+	//@}
+	
+	
+	//////////////////////////
+	/// @name invitations
+	//@{
+	// start a voice channel with the specified user
+	virtual void callUser(const LLUUID &uuid);	
+	virtual bool answerInvite(std::string &channelHandle);
+	virtual void declineInvite(std::string &channelHandle);
+	//@}
+	
+	/////////////////////////
+	/// @name Volume/gain
+	//@{
+	virtual void setVoiceVolume(F32 volume);
+	virtual void setMicGain(F32 volume);
+	//@}
+	
+	/////////////////////////
+	/// @name enable disable voice and features
+	//@{
+	virtual bool voiceEnabled();
+	virtual void setVoiceEnabled(bool enabled);
+	virtual BOOL lipSyncEnabled();	
+	virtual void setLipSyncEnabled(BOOL enabled);
+	virtual void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
+	//@}
+	
+	////////////////////////
+	/// @name PTT
+	//@{
+	virtual void setUserPTTState(bool ptt);
+	virtual bool getUserPTTState();
+	virtual void setUsePTT(bool usePTT);
+	virtual void setPTTIsToggle(bool PTTIsToggle);
+	virtual bool getPTTIsToggle();
+	virtual void inputUserControlState(bool down);  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs	
+	virtual void toggleUserPTTState(void);
+	
+	virtual void keyDown(KEY key, MASK mask);
+	virtual void keyUp(KEY key, MASK mask);
+	virtual void middleMouseState(bool down);
+	//@}
+	
+	//////////////////////////
+	/// @name nearby speaker accessors
+	//@{
+	virtual BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar
+	virtual std::string getDisplayName(const LLUUID& id);
+	virtual BOOL isOnlineSIP(const LLUUID &id);
+	virtual BOOL isParticipantAvatar(const LLUUID &id);
+	virtual BOOL getIsSpeaking(const LLUUID& id);
+	virtual BOOL getIsModeratorMuted(const LLUUID& id);
+	virtual F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
+	virtual BOOL getOnMuteList(const LLUUID& id);
+	virtual F32 getUserVolume(const LLUUID& id);
+	virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)	
+	//@}
+	
+	// authorize the user
+	virtual void userAuthorized(const std::string& user_id,
+								const LLUUID &agentID);
+	
+	//////////////////////////////
+	/// @name Status notification
+	//@{
+	virtual void addObserver(LLVoiceClientStatusObserver* observer);
+	virtual void removeObserver(LLVoiceClientStatusObserver* observer);
+	virtual void addObserver(LLFriendObserver* observer);
+	virtual void removeObserver(LLFriendObserver* observer);		
+	virtual void addObserver(LLVoiceClientParticipantObserver* observer);
+	virtual void removeObserver(LLVoiceClientParticipantObserver* observer);
+	
+	
+	
+	//@}
+	
+	virtual std::string sipURIFromID(const LLUUID &id);
+	//@}
+
+				
+protected:
+	//////////////////////
+	// Vivox Specific definitions	
+	
+	friend class LLVivoxVoiceAccountProvisionResponder;
+	friend class LLVivoxVoiceClientMuteListObserver;
+	friend class LLVivoxVoiceClientFriendsObserver;	
+	
+	enum streamState
+	{
+		streamStateUnknown = 0,
+		streamStateIdle = 1,
+		streamStateConnected = 2,
+		streamStateRinging = 3,
+	};	
+	struct participantState
+	{
+	public:
+		participantState(const std::string &uri);
+		
+		bool updateMuteState();
+		bool isAvatar();
+		
+		std::string mURI;
+		LLUUID mAvatarID;
+		std::string mAccountName;
+		std::string mDisplayName;
+		LLFrameTimer mSpeakingTimeout;
+		F32	mLastSpokeTimestamp;
+		F32 mPower;
+		int mVolume;
+		std::string mGroupID;
+		int mUserVolume;
+		bool mPTT;
+		bool mIsSpeaking;
+		bool mIsModeratorMuted;
+		bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted)
+		bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
+		bool mAvatarIDValid;
+		bool mIsSelf;
+	};
+	
+	typedef std::map<const std::string, participantState*> participantMap;
+	
+	typedef std::map<const LLUUID, participantState*> participantUUIDMap;
+	
+	struct sessionState
+	{
+	public:
+		sessionState();
+		~sessionState();
+		
+		participantState *addParticipant(const std::string &uri);
+		// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
+		// Take care not to use the pointer again after that.
+		void removeParticipant(participantState *participant);
+		void removeAllParticipants();
+		
+		participantState *findParticipant(const std::string &uri);
+		participantState *findParticipantByID(const LLUUID& id);
+		
+		bool isCallBackPossible();
+		bool isTextIMPossible();
+		
+		std::string mHandle;
+		std::string mGroupHandle;
+		std::string mSIPURI;
+		std::string mAlias;
+		std::string mName;
+		std::string mAlternateSIPURI;
+		std::string mHash;			// Channel password
+		std::string mErrorStatusString;
+		std::queue<std::string> mTextMsgQueue;
+		
+		LLUUID		mIMSessionID;
+		LLUUID		mCallerID;
+		int			mErrorStatusCode;
+		int			mMediaStreamState;
+		int			mTextStreamState;
+		bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet.
+		bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet.
+		bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup)
+		bool		mTextInvitePending;		// True if a text invite is pending for this session (usually waiting on a name lookup)
+		bool		mSynthesizedCallerID;	// True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup.
+		bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN)
+		bool		mIsSpatial;	// True for spatial channels
+		bool		mIsP2P;
+		bool		mIncoming;
+		bool		mVoiceEnabled;
+		bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped
+		// Set to true when the mute state of someone in the participant list changes.
+		// The code will have to walk the list to find the changed participant(s).
+		bool		mVolumeDirty;
+		
+		bool		mParticipantsChanged;
+		participantMap mParticipantsByURI;
+		participantUUIDMap mParticipantsByUUID;
+	};
+
+	// internal state for a simple state machine.  This is used to deal with the asynchronous nature of some of the messages.
+	// Note: if you change this list, please make corresponding changes to LLVivoxVoiceClient::state2string().
+	enum state
+	{
+		stateDisableCleanup,
+		stateDisabled,				// Voice is turned off.
+		stateStart,					// Class is initialized, socket is created
+		stateDaemonLaunched,		// Daemon has been launched
+		stateConnecting,			// connect() call has been issued
+		stateConnected,				// connection to the daemon has been made, send some initial setup commands.
+		stateIdle,					// socket is connected, ready for messaging
+		stateMicTuningStart,
+		stateMicTuningRunning,		
+		stateMicTuningStop,
+		stateConnectorStart,		// connector needs to be started
+		stateConnectorStarting,		// waiting for connector handle
+		stateConnectorStarted,		// connector handle received
+		stateLoginRetry,			// need to retry login (failed due to changing password)
+		stateLoginRetryWait,		// waiting for retry timer
+		stateNeedsLogin,			// send login request
+		stateLoggingIn,				// waiting for account handle
+		stateLoggedIn,				// account handle received
+		stateCreatingSessionGroup,	// Creating the main session group
+		stateNoChannel,				// 
+		stateJoiningSession,		// waiting for session handle
+		stateSessionJoined,			// session handle received
+		stateRunning,				// in session, steady state
+		stateLeavingSession,		// waiting for terminate session response
+		stateSessionTerminated,		// waiting for terminate session response
+		
+		stateLoggingOut,			// waiting for logout response
+		stateLoggedOut,				// logout response received
+		stateConnectorStopping,		// waiting for connector stop
+		stateConnectorStopped,		// connector stop received
+		
+		// We go to this state if the login fails because the account needs to be provisioned.
+		
+		// error states.  No way to recover from these yet.
+		stateConnectorFailed,
+		stateConnectorFailedWaiting,
+		stateLoginFailed,
+		stateLoginFailedWaiting,
+		stateJoinSessionFailed,
+		stateJoinSessionFailedWaiting,
+		
+		stateJail					// Go here when all else has failed.  Nothing will be retried, we're done.
+	};
+	
+	typedef std::map<std::string, sessionState*> sessionMap;
+	
+	
+	
+	///////////////////////////////////////////////////////
+	// Private Member Functions
+	//////////////////////////////////////////////////////
+	
+	//////////////////////////////
+	/// @name TVC/Server management and communication
+	//@{
+	// Call this if the connection to the daemon terminates unexpectedly.  It will attempt to reset everything and relaunch.
+	void daemonDied();
+	
+	// Call this if we're just giving up on voice (can't provision an account, etc.).  It will clean up and go away.
+	void giveUp();	
+	
+	// write to the tvc
+	bool writeString(const std::string &str);
+	
+	void connectorCreate();
+	void connectorShutdown();	
+	void closeSocket(void);	
+	
+	void requestVoiceAccountProvision(S32 retries = 3);
+	void login(
+			   const std::string& account_name,
+			   const std::string& password,
+			   const std::string& voice_sip_uri_hostname,
+			   const std::string& voice_account_server_uri);
+	void loginSendMessage();
+	void logout();
+	void logoutSendMessage();	
+	
+	
+	//@}
+	
+	//------------------------------------
+	// tuning
+	
+	void tuningRenderStartSendMessage(const std::string& name, bool loop);
+	void tuningRenderStopSendMessage();
+
+	void tuningCaptureStartSendMessage(int duration);
+	void tuningCaptureStopSendMessage();
+
+	bool inTuningStates();
+
+	//----------------------------------
+	// devices
+	void clearCaptureDevices();
+	void addCaptureDevice(const std::string& name);
+	void clearRenderDevices();
+	void addRenderDevice(const std::string& name);	
+	void buildSetAudioDevices(std::ostringstream &stream);
+	
+	void getCaptureDevicesSendMessage();
+	void getRenderDevicesSendMessage();
+	
+	// local audio updates
+	void buildLocalAudioUpdates(std::ostringstream &stream);		
+
+
+	/////////////////////////////
+	// Response/Event handlers
+	void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID);
+	void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases);
+	void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
+	void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
+	void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString);
+	void logoutResponse(int statusCode, std::string &statusString);
+	void connectorShutdownResponse(int statusCode, std::string &statusString);
+
+	void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
+	void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming);
+	void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming);
+	void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString);
+	void sessionGroupAddedEvent(std::string &sessionGroupHandle);
+	void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle);
+	void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType);
+	void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString);
+	void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
+	void auxAudioPropertiesEvent(F32 energy);
+	void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString);
+	void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString);
+	void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType);
+	void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType);
+	
+	void buddyListChanged();
+	void muteListChanged();
+	void updateFriends(U32 mask);
+		
+	/////////////////////////////
+	// Sending updates of current state
+	void updatePosition(void);
+	void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+	void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+	bool channelFromRegion(LLViewerRegion *region, std::string &name);
+
+	void setEarLocation(S32 loc);
+
+	
+	/////////////////////////////
+	// Accessors for data related to nearby speakers
+
+	// MBW -- XXX -- Not sure how to get this data out of the TVC
+	BOOL getUsingPTT(const LLUUID& id);
+	std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable)
+
+	/////////////////////////////
+	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled.
+										// Use this to determine whether to show a "no speech" icon in the menu bar.
+		
+	
+	// PTT
+	void setPTTKey(std::string &key);
+	
+	/////////////////////////////
+	// Recording controls
+	void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200);
+	void recordingLoopSave(const std::string& filename);
+	void recordingStop();
+	
+	// Playback controls
+	void filePlaybackStart(const std::string& filename);
+	void filePlaybackStop();
+	void filePlaybackSetPaused(bool paused);
+	void filePlaybackSetMode(bool vox = false, float speed = 1.0f);
+	
+	participantState *findParticipantByID(const LLUUID& id);
+	
+
+	////////////////////////////////////////
+	// voice sessions.
+	typedef std::set<sessionState*> sessionSet;
+			
+	typedef sessionSet::iterator sessionIterator;
+	sessionIterator sessionsBegin(void);
+	sessionIterator sessionsEnd(void);
+
+	sessionState *findSession(const std::string &handle);
+	sessionState *findSessionBeingCreatedByURI(const std::string &uri);
+	sessionState *findSession(const LLUUID &participant_id);
+	sessionState *findSessionByCreateID(const std::string &create_id);
+	
+	sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null);
+	void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null);
+	void setSessionURI(sessionState *session, const std::string &uri);
+	void deleteSession(sessionState *session);
+	void deleteAllSessions(void);
+
+	void verifySessionState(void);
+
+	void joinedAudioSession(sessionState *session);
+	void leftAudioSession(sessionState *session);
+
+	// This is called in several places where the session _may_ need to be deleted.
+	// It contains logic for whether to delete the session or keep it around.
+	void reapSession(sessionState *session);
+	
+	// Returns true if the session seems to indicate we've moved to a region on a different voice server
+	bool sessionNeedsRelog(sessionState *session);
+	
+	
+	//////////////////////////////////////
+	// buddy list stuff, needed for SLIM later
+	struct buddyListEntry
+	{
+		buddyListEntry(const std::string &uri);
+		std::string mURI;
+		std::string mDisplayName;
+		LLUUID	mUUID;
+		bool mOnlineSL;
+		bool mOnlineSLim;
+		bool mCanSeeMeOnline;
+		bool mHasBlockListEntry;
+		bool mHasAutoAcceptListEntry;
+		bool mNameResolved;
+		bool mInSLFriends;
+		bool mInVivoxBuddies;
+		bool mNeedsNameUpdate;
+	};
+
+	typedef std::map<std::string, buddyListEntry*> buddyListMap;
+	
+	// This should be called when parsing a buddy list entry sent by SLVoice.		
+	void processBuddyListEntry(const std::string &uri, const std::string &displayName);
+
+	buddyListEntry *addBuddy(const std::string &uri);
+	buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName);
+	buddyListEntry *findBuddy(const std::string &uri);
+	buddyListEntry *findBuddy(const LLUUID &id);
+	buddyListEntry *findBuddyByDisplayName(const std::string &name);
+	void deleteBuddy(const std::string &uri);
+	void deleteAllBuddies(void);
+
+	void deleteAllBlockRules(void);
+	void addBlockRule(const std::string &blockMask, const std::string &presenceOnly);
+	void deleteAllAutoAcceptRules(void);
+	void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy);
+	void accountListBlockRulesResponse(int statusCode, const std::string &statusString);						
+	void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);						
+	
+	/////////////////////////////
+	// session control messages
+
+	void accountListBlockRulesSendMessage();
+	void accountListAutoAcceptRulesSendMessage();
+	
+	void sessionGroupCreateSendMessage();
+	void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
+	void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
+	void sessionMediaConnectSendMessage(sessionState *session);		// just joins the audio session
+	void sessionTextConnectSendMessage(sessionState *session);		// just joins the text session
+	void sessionTerminateSendMessage(sessionState *session);
+	void sessionGroupTerminateSendMessage(sessionState *session);
+	void sessionMediaDisconnectSendMessage(sessionState *session);
+	void sessionTextDisconnectSendMessage(sessionState *session);
+
+	// Pokes the state machine to leave the audio session next time around.
+	void sessionTerminate();	
+	
+	// Pokes the state machine to shut down the connector and restart it.
+	void requestRelog();
+	
+	// Does the actual work to get out of the audio session
+	void leaveAudioSession();
+	
+	void lookupName(const LLUUID &id);
+	static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group);
+	void avatarNameResolved(const LLUUID &id, const std::string &name);
+		
+private:
+	LLVoiceVersionInfo mVoiceVersion;
+		
+	state mState;
+	bool mSessionTerminateRequested;
+	bool mRelogRequested;
+	
+	void setState(state inState);
+	state getState(void)  { return mState; };
+	std::string state2string(state inState);
+	
+	void stateMachine();
+	static void idle(void *user_data);
+	
+	LLHost mDaemonHost;
+	LLSocket::ptr_t mSocket;
+	bool mConnected;
+	
+	
+	LLPumpIO *mPump;
+	friend class LLVivoxProtocolParser;
+	
+	std::string mAccountName;
+	std::string mAccountPassword;
+	std::string mAccountDisplayName;
+			
+	bool mTuningMode;
+	float mTuningEnergy;
+	std::string mTuningAudioFile;
+	int mTuningMicVolume;
+	bool mTuningMicVolumeDirty;
+	int mTuningSpeakerVolume;
+	bool mTuningSpeakerVolumeDirty;
+	state mTuningExitState;					// state to return to when we leave tuning mode.
+	
+	std::string mSpatialSessionURI;
+	std::string mSpatialSessionCredentials;
+
+	std::string mMainSessionGroupHandle; // handle of the "main" session group.
+	
+	std::string mChannelName;			// Name of the channel to be looked up 
+	bool mAreaVoiceDisabled;
+	sessionState *mAudioSession;		// Session state for the current audio session
+	bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified.
+
+	sessionState *mNextAudioSession;	// Session state for the audio session we're trying to join
+
+//		std::string mSessionURI;			// URI of the session we're in.
+//		std::string mSessionHandle;		// returned by ?
+	
+	S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings
+	std::string mCurrentRegionName;		// Used to detect parcel boundary crossings
+	
+	std::string mConnectorHandle;	// returned by "Create Connector" message
+	std::string mAccountHandle;		// returned by login message		
+	int 		mNumberOfAliases;
+	U32 mCommandCookie;
+
+	std::string mVoiceAccountServerURI;
+	std::string mVoiceSIPURIHostName;
+	
+	int mLoginRetryCount;
+	
+	sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map.
+	sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list.
+	
+	bool mBuddyListMapPopulated;
+	bool mBlockRulesListReceived;
+	bool mAutoAcceptRulesListReceived;
+	buddyListMap mBuddyListMap;
+	
+	LLVoiceDeviceList mCaptureDevices;
+	LLVoiceDeviceList mRenderDevices;
+
+	std::string mCaptureDevice;
+	std::string mRenderDevice;
+	bool mCaptureDeviceDirty;
+	bool mRenderDeviceDirty;
+	
+	// This should be called when the code detects we have changed parcels.
+	// It initiates the call to the server that gets the parcel channel.
+	void parcelChanged();
+	
+	void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
+	void joinSession(sessionState *session);
+	
+	std::string nameFromAvatar(LLVOAvatar *avatar);
+	std::string nameFromID(const LLUUID &id);
+	bool IDFromName(const std::string name, LLUUID &uuid);
+	std::string displayNameFromAvatar(LLVOAvatar *avatar);
+	std::string sipURIFromAvatar(LLVOAvatar *avatar);
+	std::string sipURIFromName(std::string &name);
+	
+	// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not.
+	std::string nameFromsipURI(const std::string &uri);		
+
+	bool inSpatialChannel(void);
+	std::string getAudioSessionURI();
+	std::string getAudioSessionHandle();
+			
+	void sendPositionalUpdate(void);
+	
+	void buildSetCaptureDevice(std::ostringstream &stream);
+	void buildSetRenderDevice(std::ostringstream &stream);
+	
+	void clearAllLists();
+	void checkFriend(const LLUUID& id);
+	void sendFriendsListUpdates();
+
+	// start a text IM session with the specified user
+	// This will be asynchronous, the session may be established at a future time.
+	sessionState* startUserIMSession(const LLUUID& uuid);
+	void sendQueuedTextMessages(sessionState *session);
+	
+	void enforceTether(void);
+	
+	bool		mSpatialCoordsDirty;
+	
+	LLVector3d	mCameraPosition;
+	LLVector3d	mCameraRequestedPosition;
+	LLVector3	mCameraVelocity;
+	LLMatrix3	mCameraRot;
+
+	LLVector3d	mAvatarPosition;
+	LLVector3	mAvatarVelocity;
+	LLMatrix3	mAvatarRot;
+	
+	bool		mPTTDirty;
+	bool		mPTT;
+	
+	bool		mUsePTT;
+	bool		mPTTIsMiddleMouse;
+	KEY			mPTTKey;
+	bool		mPTTIsToggle;
+	bool		mUserPTTState;
+	bool		mMuteMic;
+			
+	// Set to true when the friends list is known to have changed.
+	bool		mFriendsListDirty;
+	
+	enum
+	{
+		earLocCamera = 0,		// ear at camera
+		earLocAvatar,			// ear at avatar
+		earLocMixed				// ear at avatar location/camera direction
+	};
+	
+	S32			mEarLocation;  
+	
+	bool		mSpeakerVolumeDirty;
+	bool		mSpeakerMuteDirty;
+	int			mSpeakerVolume;
+
+	int			mMicVolume;
+	bool		mMicVolumeDirty;
+	
+	bool		mVoiceEnabled;
+	bool		mWriteInProgress;
+	std::string mWriteString;
+	size_t		mWriteOffset;
+	
+	LLTimer		mUpdateTimer;
+	
+	BOOL		mLipSyncEnabled;
+
+	typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
+	observer_set_t mParticipantObservers;
+
+	void notifyParticipantObservers();
+
+	typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
+	status_observer_set_t mStatusObservers;
+	
+	void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
+
+	typedef std::set<LLFriendObserver*> friend_observer_set_t;
+	friend_observer_set_t mFriendObservers;
+	void notifyFriendObservers();
+};
+
+/** 
+ * @class LLVivoxProtocolParser
+ * @brief This class helps construct new LLIOPipe specializations
+ * @see LLIOPipe
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLVivoxProtocolParser : public LLIOPipe
+{
+	LOG_CLASS(LLVivoxProtocolParser);
+public:
+	LLVivoxProtocolParser();
+	virtual ~LLVivoxProtocolParser();
+	
+protected:
+	/* @name LLIOPipe virtual implementations
+	 */
+	//@{
+	/** 
+	 * @brief Process the data in buffer
+	 */
+	virtual EStatus process_impl(
+								 const LLChannelDescriptors& channels,
+								 buffer_ptr_t& buffer,
+								 bool& eos,
+								 LLSD& context,
+								 LLPumpIO* pump);
+	//@}
+	
+	std::string 	mInput;
+	
+	// Expat control members
+	XML_Parser		parser;
+	int				responseDepth;
+	bool			ignoringTags;
+	bool			isEvent;
+	int				ignoreDepth;
+	
+	// Members for processing responses. The values are transient and only valid within a call to processResponse().
+	bool			squelchDebugOutput;
+	int				returnCode;
+	int				statusCode;
+	std::string		statusString;
+	std::string		requestId;
+	std::string		actionString;
+	std::string		connectorHandle;
+	std::string		versionID;
+	std::string		accountHandle;
+	std::string		sessionHandle;
+	std::string		sessionGroupHandle;
+	std::string		alias;
+	std::string		applicationString;
+	
+	// Members for processing events. The values are transient and only valid within a call to processResponse().
+	std::string		eventTypeString;
+	int				state;
+	std::string		uriString;
+	bool			isChannel;
+	bool			incoming;
+	bool			enabled;
+	std::string		nameString;
+	std::string		audioMediaString;
+	std::string     deviceString;
+	std::string		displayNameString;
+	int				participantType;
+	bool			isLocallyMuted;
+	bool			isModeratorMuted;
+	bool			isSpeaking;
+	int				volume;
+	F32				energy;
+	std::string		messageHeader;
+	std::string		messageBody;
+	std::string		notificationType;
+	bool			hasText;
+	bool			hasAudio;
+	bool			hasVideo;
+	bool			terminated;
+	std::string		blockMask;
+	std::string		presenceOnly;
+	std::string		autoAcceptMask;
+	std::string		autoAddAsBuddy;
+	int				numberOfAliases;
+	std::string		subscriptionHandle;
+	std::string		subscriptionType;
+	
+	
+	// Members for processing text between tags
+	std::string		textBuffer;
+	bool			accumulateText;
+	
+	void			reset();
+	
+	void			processResponse(std::string tag);
+	
+	static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
+	static void XMLCALL ExpatEndTag(void *data, const char *el);
+	static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
+	
+	void			StartTag(const char *tag, const char **attr);
+	void			EndTag(const char *tag);
+	void			CharData(const char *buffer, int length);
+	
+};
+
+
+#endif //LL_VIVOX_VOICE_CLIENT_H
+
+
+
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index fac7aef690dbb5b037cca946b90b44f0994e4a3d..cb5cec7bdbae06bba52eca76ec7fc0f6e83a164c 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -49,7 +49,7 @@ libcurl Version: [LIBCURL_VERSION]
 J2C Decoder Version: [J2C_VERSION]
 Audio Driver Version: [AUDIO_DRIVER_VERSION]
 Qt Webkit Version: [QT_WEBKIT_VERSION]
-Vivox Version: [VIVOX_VERSION]
+Voice Server Version: [VOICE_VERSION]
 </floater.string>
   <floater.string
      name="none">
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 0db18525d766b2c0a850793c51a6f3468ef21bc0..1917fd9b5c7bfc4954ddb3180660d90a621c208e 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -397,6 +397,15 @@ def construct(self):
 
         self.disable_manifest_check()
 
+        # Diamondware Runtimes
+        if self.prefix(src="diamondware-runtime/i686-win32", dst=""):
+            self.path("SLVoice_dwTVC.exe")
+            self.path("libcurl.dll")
+            self.path("libeay32.dll")
+            self.path("ssleay32.dll")
+            self.path("zlib1.dll")
+            self.end_prefix()
+
         # pull in the crash logger and updater from other projects
         # tag:"crash-logger" here as a cue to the exporter
         self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
@@ -606,6 +615,9 @@ def construct(self):
                 self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
                 self.path("vivox-runtime/universal-darwin/libvivoxplatform.dylib", "libvivoxplatform.dylib")
                 self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
+                # DiamondWare runtime                                           
+                self.path("diamondware-runtime/universal-darwin/SLVoice_dwTVC","SLVoice_dwTVC")
+                self.path("diamondware-runtime/universal-darwin/libfmodex.dylib", "libfmodex.dylib")
 
                 libdir = "../../libraries/universal-darwin/lib_release"
                 dylibs = {}
@@ -898,6 +910,11 @@ def construct(self):
                     pass
             self.end_prefix("lib")
 
+            # Diamondware runtimes
+            if self.prefix(src="diamondware-runtime/i686-linux", dst="bin"):
+                    self.path("SLVoice_dwTVC")
+                    self.end_prefix()
+
             # Vivox runtimes
             if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):
                     self.path("SLVoice")
diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp
index 452930a3b3626e651a6db2bed1083d602519847b..8af7453bd60a1a3227eeaf5bc5994a375a2bc16a 100644
--- a/indra/viewer_components/login/lllogin.cpp
+++ b/indra/viewer_components/login/lllogin.cpp
@@ -131,6 +131,7 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)
     std::string coroname = 
         LLCoros::instance().launch("LLLogin::Impl::login_",
                                    boost::bind(&Impl::login_, this, _1, uri, login_params));
+    LL_DEBUGS("LLLogin") << " connected with  uri '" << uri << "', login_params " << login_params << LL_ENDL;	
 }
 
 void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_params)