PageRenderTime 125ms CodeModel.GetById 12ms app.highlight 99ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llimview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2377 lines | 1833 code | 341 blank | 203 comment | 277 complexity | 54c08c246ae1231623a78865f9d7ce0a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file LLIMMgr.cpp
   3 * @brief Container for Instant Messaging
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llimview.h"
  30
  31#include "llavatarnamecache.h"	// IDEVO
  32#include "llfloaterreg.h"
  33#include "llfontgl.h"
  34#include "llgl.h"
  35#include "llrect.h"
  36#include "llerror.h"
  37#include "llbutton.h"
  38#include "llhttpclient.h"
  39#include "llsdutil_math.h"
  40#include "llstring.h"
  41#include "lltextutil.h"
  42#include "lltrans.h"
  43#include "lluictrlfactory.h"
  44
  45#include "llagent.h"
  46#include "llagentui.h"
  47#include "llappviewer.h"
  48#include "llavatariconctrl.h"
  49#include "llcallingcard.h"
  50#include "llchat.h"
  51#include "llimfloater.h"
  52#include "llgroupiconctrl.h"
  53#include "llmd5.h"
  54#include "llmutelist.h"
  55#include "llrecentpeople.h"
  56#include "llviewermessage.h"
  57#include "llviewerwindow.h"
  58#include "llnotifications.h"
  59#include "llnotificationsutil.h"
  60#include "llnearbychat.h"
  61#include "llspeakers.h" //for LLIMSpeakerMgr
  62#include "lltextbox.h"
  63#include "lltoolbarview.h"
  64#include "llviewercontrol.h"
  65#include "llviewerparcelmgr.h"
  66
  67
  68const static std::string ADHOC_NAME_SUFFIX(" Conference");
  69
  70const static std::string NEARBY_P2P_BY_OTHER("nearby_P2P_by_other");
  71const static std::string NEARBY_P2P_BY_AGENT("nearby_P2P_by_agent");
  72
  73/** Timeout of outgoing session initialization (in seconds) */
  74const static U32 SESSION_INITIALIZATION_TIMEOUT = 30;
  75
  76std::string LLCallDialogManager::sPreviousSessionlName = "";
  77LLIMModel::LLIMSession::SType LLCallDialogManager::sPreviousSessionType = LLIMModel::LLIMSession::P2P_SESSION;
  78std::string LLCallDialogManager::sCurrentSessionlName = "";
  79LLIMModel::LLIMSession* LLCallDialogManager::sSession = NULL;
  80LLVoiceChannel::EState LLCallDialogManager::sOldState = LLVoiceChannel::STATE_READY;
  81const LLUUID LLOutgoingCallDialog::OCD_KEY = LLUUID("7CF78E11-0CFE-498D-ADB9-1417BF03DDB4");
  82//
  83// Globals
  84//
  85LLIMMgr* gIMMgr = NULL;
  86
  87
  88BOOL LLSessionTimeoutTimer::tick()
  89{
  90	if (mSessionId.isNull()) return TRUE;
  91
  92	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId);
  93	if (session && !session->mSessionInitialized)
  94	{
  95		gIMMgr->showSessionStartError("session_initialization_timed_out_error", mSessionId);
  96	}
  97	return TRUE;
  98}
  99
 100static void on_avatar_name_cache_toast(const LLUUID& agent_id,
 101									   const LLAvatarName& av_name,
 102									   LLSD msg)
 103{
 104	LLSD args;
 105	args["MESSAGE"] = msg["message"];
 106	args["TIME"] = msg["time"];
 107	// *TODO: Can this ever be an object name or group name?
 108	args["FROM"] = av_name.getCompleteName();
 109	args["FROM_ID"] = msg["from_id"];
 110	args["SESSION_ID"] = msg["session_id"];
 111	LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID()));
 112}
 113
 114void toast_callback(const LLSD& msg){
 115	// do not show toast in busy mode or it goes from agent
 116	if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
 117	{
 118		return;
 119	}
 120
 121	// check whether incoming IM belongs to an active session or not
 122	if (LLIMModel::getInstance()->getActiveSessionID().notNull()
 123			&& LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"])
 124	{
 125		return;
 126	}
 127
 128	// Skip toasting for system messages
 129	if (msg["from_id"].asUUID() == LLUUID::null)
 130	{
 131		return;
 132	}
 133
 134	// *NOTE Skip toasting if the user disable it in preferences/debug settings ~Alexandrea
 135	LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(
 136				msg["session_id"]);
 137	if (!gSavedSettings.getBOOL("EnableGroupChatPopups")
 138			&& session->isGroupSessionType())
 139	{
 140		return;
 141	}
 142	if (!gSavedSettings.getBOOL("EnableIMChatPopups")
 143			&& !session->isGroupSessionType())
 144	{
 145		return;
 146	}
 147
 148	// Skip toasting if we have open window of IM with this session id
 149	LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]);
 150	if (open_im_floater && open_im_floater->getVisible())
 151	{
 152		return;
 153	}
 154
 155	LLAvatarNameCache::get(msg["from_id"].asUUID(),
 156		boost::bind(&on_avatar_name_cache_toast,
 157			_1, _2, msg));
 158}
 159
 160void LLIMModel::setActiveSessionID(const LLUUID& session_id)
 161{
 162	// check if such an ID really exists
 163	if (!findIMSession(session_id))
 164	{
 165		llwarns << "Trying to set as active a non-existent session!" << llendl;
 166		return;
 167	}
 168
 169	mActiveSessionID = session_id;
 170}
 171
 172LLIMModel::LLIMModel() 
 173{
 174	addNewMsgCallback(LLIMFloater::newIMCallback);
 175	addNewMsgCallback(toast_callback);
 176}
 177
 178LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
 179:	mSessionID(session_id),
 180	mName(name),
 181	mType(type),
 182	mParticipantUnreadMessageCount(0),
 183	mNumUnread(0),
 184	mOtherParticipantID(other_participant_id),
 185	mInitialTargetIDs(ids),
 186	mVoiceChannel(NULL),
 187	mSpeakers(NULL),
 188	mSessionInitialized(false),
 189	mCallBackEnabled(true),
 190	mTextIMPossible(true),
 191	mOtherParticipantIsAvatar(true),
 192	mStartCallOnInitialize(false),
 193	mStartedAsIMCall(voice)
 194{
 195	// set P2P type by default
 196	mSessionType = P2P_SESSION;
 197
 198	if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType)
 199	{
 200		mVoiceChannel  = new LLVoiceChannelP2P(session_id, name, other_participant_id);
 201		mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID);
 202
 203		// check if it was AVALINE call
 204		if (!mOtherParticipantIsAvatar)
 205		{
 206			mSessionType = AVALINE_SESSION;
 207		} 
 208	}
 209	else
 210	{
 211		mVoiceChannel = new LLVoiceChannelGroup(session_id, name);
 212
 213		// determine whether it is group or conference session
 214		if (gAgent.isInGroup(mSessionID))
 215		{
 216			mSessionType = GROUP_SESSION;
 217		}
 218		else
 219		{
 220			mSessionType = ADHOC_SESSION;
 221		} 
 222	}
 223
 224	if(mVoiceChannel)
 225	{
 226		mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3));
 227	}
 228		
 229	mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
 230
 231	// All participants will be added to the list of people we've recently interacted with.
 232
 233	// we need to add only _active_ speakers...so comment this. 
 234	// may delete this later on cleanup
 235	//mSpeakers->addListener(&LLRecentPeople::instance(), "add");
 236
 237	//we need to wait for session initialization for outgoing ad-hoc and group chat session
 238	//correct session id for initiated ad-hoc chat will be received from the server
 239	if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, 
 240		mInitialTargetIDs, mType))
 241	{
 242		//we don't need to wait for any responses
 243		//so we're already initialized
 244		mSessionInitialized = true;
 245	}
 246	else
 247	{
 248		//tick returns TRUE - timer will be deleted after the tick
 249		new LLSessionTimeoutTimer(mSessionID, SESSION_INITIALIZATION_TIMEOUT);
 250	}
 251
 252	if (IM_NOTHING_SPECIAL == mType)
 253	{
 254		mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID);
 255		mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID);
 256	}
 257
 258	buildHistoryFileName();
 259
 260	if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
 261	{
 262		std::list<LLSD> chat_history;
 263
 264		//involves parsing of a chat history
 265		LLLogChat::loadAllHistory(mHistoryFileName, chat_history);
 266		addMessagesFromHistory(chat_history);
 267	}
 268
 269	// Localizing name of ad-hoc session. STORM-153
 270	// Changing name should happen here- after the history file was created, so that
 271	// history files have consistent (English) names in different locales.
 272	if (isAdHocSessionType() && IM_SESSION_INVITE == mType)
 273	{
 274		LLAvatarNameCache::get(mOtherParticipantID,
 275							   boost::bind(&LLIMModel::LLIMSession::onAdHocNameCache,
 276							   this, _2));
 277	}
 278}
 279
 280void LLIMModel::LLIMSession::onAdHocNameCache(const LLAvatarName& av_name)
 281{
 282	if (av_name.mIsTemporaryName)
 283	{
 284		S32 separator_index = mName.rfind(" ");
 285		std::string name = mName.substr(0, separator_index);
 286		++separator_index;
 287		std::string conference_word = mName.substr(separator_index, mName.length());
 288
 289		// additional check that session name is what we expected
 290		if ("Conference" == conference_word)
 291		{
 292			LLStringUtil::format_map_t args;
 293			args["[AGENT_NAME]"] = name;
 294			LLTrans::findString(mName, "conference-title-incoming", args);
 295		}
 296	}
 297	else
 298	{
 299		LLStringUtil::format_map_t args;
 300		args["[AGENT_NAME]"] = av_name.getCompleteName();
 301		LLTrans::findString(mName, "conference-title-incoming", args);
 302	}
 303}
 304
 305void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction)
 306{
 307	std::string you_joined_call = LLTrans::getString("you_joined_call");
 308	std::string you_started_call = LLTrans::getString("you_started_call");
 309	std::string other_avatar_name = "";
 310
 311	std::string message;
 312
 313	switch(mSessionType)
 314	{
 315	case AVALINE_SESSION:
 316		// no text notifications
 317		break;
 318	case P2P_SESSION:
 319		gCacheName->getFullName(mOtherParticipantID, other_avatar_name); // voice
 320
 321		if(direction == LLVoiceChannel::INCOMING_CALL)
 322		{
 323			switch(new_state)
 324			{
 325			case LLVoiceChannel::STATE_CALL_STARTED :
 326				{
 327					LLStringUtil::format_map_t string_args;
 328					string_args["[NAME]"] = other_avatar_name;
 329					message = LLTrans::getString("name_started_call", string_args);
 330					LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);				
 331					break;
 332				}
 333			case LLVoiceChannel::STATE_CONNECTED :
 334				LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_joined_call);
 335			default:
 336				break;
 337			}
 338		}
 339		else // outgoing call
 340		{
 341			switch(new_state)
 342			{
 343			case LLVoiceChannel::STATE_CALL_STARTED :
 344				LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_started_call);
 345				break;
 346			case LLVoiceChannel::STATE_CONNECTED :
 347				message = LLTrans::getString("answered_call");
 348				LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
 349			default:
 350				break;
 351			}
 352		}
 353		break;
 354
 355	case GROUP_SESSION:
 356	case ADHOC_SESSION:
 357		if(direction == LLVoiceChannel::INCOMING_CALL)
 358		{
 359			switch(new_state)
 360			{
 361			case LLVoiceChannel::STATE_CONNECTED :
 362				LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_joined_call);
 363			default:
 364				break;
 365			}
 366		}
 367		else // outgoing call
 368		{
 369			switch(new_state)
 370			{
 371			case LLVoiceChannel::STATE_CALL_STARTED :
 372				LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_started_call);
 373				break;
 374			default:
 375				break;
 376			}
 377		}
 378	}
 379	// Update speakers list when connected
 380	if (LLVoiceChannel::STATE_CONNECTED == new_state)
 381	{
 382		mSpeakers->update(true);
 383	}
 384}
 385
 386LLIMModel::LLIMSession::~LLIMSession()
 387{
 388	delete mSpeakers;
 389	mSpeakers = NULL;
 390
 391	// End the text IM session if necessary
 392	if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull())
 393	{
 394		switch(mType)
 395		{
 396		case IM_NOTHING_SPECIAL:
 397		case IM_SESSION_P2P_INVITE:
 398			LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID);
 399			break;
 400
 401		default:
 402			// Appease the linux compiler
 403			break;
 404		}
 405	}
 406
 407	mVoiceChannelStateChangeConnection.disconnect();
 408
 409	// HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
 410	mVoiceChannel->deactivate();
 411
 412	delete mVoiceChannel;
 413	mVoiceChannel = NULL;
 414}
 415
 416void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id)
 417{
 418	mSessionInitialized = true;
 419
 420	if (new_session_id != mSessionID)
 421	{
 422		mSessionID = new_session_id;
 423		mVoiceChannel->updateSessionID(new_session_id);
 424	}
 425}
 426
 427void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history)
 428{
 429	LLSD message;
 430	message["from"] = from;
 431	message["from_id"] = from_id;
 432	message["message"] = utf8_text;
 433	message["time"] = time; 
 434	message["index"] = (LLSD::Integer)mMsgs.size(); 
 435	message["is_history"] = is_history;
 436
 437	mMsgs.push_front(message); 
 438
 439	if (mSpeakers && from_id.notNull())
 440	{
 441		mSpeakers->speakerChatted(from_id);
 442		mSpeakers->setSpeakerTyping(from_id, FALSE);
 443	}
 444}
 445
 446void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history)
 447{
 448	std::list<LLSD>::const_iterator it = history.begin();
 449	while (it != history.end())
 450	{
 451		const LLSD& msg = *it;
 452
 453		std::string from = msg[IM_FROM];
 454		LLUUID from_id;
 455		if (msg[IM_FROM_ID].isDefined())
 456		{
 457			from_id = msg[IM_FROM_ID].asUUID();
 458		}
 459		else
 460		{
 461			// convert it to a legacy name if we have a complete name
 462			std::string legacy_name = gCacheName->buildLegacyName(from);
 463 			gCacheName->getUUID(legacy_name, from_id);
 464		}
 465
 466		std::string timestamp = msg[IM_TIME];
 467		std::string text = msg[IM_TEXT];
 468
 469		addMessage(from, from_id, text, timestamp, true);
 470
 471		it++;
 472	}
 473}
 474
 475void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)
 476{
 477	if (!userdata) return;
 478
 479	LLIMSession* self = (LLIMSession*) userdata;
 480
 481	if (type == LLLogChat::LOG_LINE)
 482	{
 483		self->addMessage("", LLSD(), msg["message"].asString(), "", true);
 484	}
 485	else if (type == LLLogChat::LOG_LLSD)
 486	{
 487		self->addMessage(msg["from"].asString(), msg["from_id"].asUUID(), msg["message"].asString(), msg["time"].asString(), true);
 488	}
 489}
 490
 491LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const
 492{
 493	return get_if_there(mId2SessionMap, session_id,
 494		(LLIMModel::LLIMSession*) NULL);
 495}
 496
 497//*TODO consider switching to using std::set instead of std::list for holding LLUUIDs across the whole code
 498LLIMModel::LLIMSession* LLIMModel::findAdHocIMSession(const uuid_vec_t& ids)
 499{
 500	S32 num = ids.size();
 501	if (!num) return NULL;
 502
 503	if (mId2SessionMap.empty()) return NULL;
 504
 505	std::map<LLUUID, LLIMSession*>::const_iterator it = mId2SessionMap.begin();
 506	for (; it != mId2SessionMap.end(); ++it)
 507	{
 508		LLIMSession* session = (*it).second;
 509	
 510		if (!session->isAdHoc()) continue;
 511		if (session->mInitialTargetIDs.size() != num) continue;
 512
 513		std::list<LLUUID> tmp_list(session->mInitialTargetIDs.begin(), session->mInitialTargetIDs.end());
 514
 515		uuid_vec_t::const_iterator iter = ids.begin();
 516		while (iter != ids.end())
 517		{
 518			tmp_list.remove(*iter);
 519			++iter;
 520			
 521			if (tmp_list.empty()) 
 522			{
 523				break;
 524			}
 525		}
 526
 527		if (tmp_list.empty() && iter == ids.end())
 528		{
 529			return session;
 530		}
 531	}
 532
 533	return NULL;
 534}
 535
 536bool LLIMModel::LLIMSession::isOutgoingAdHoc()
 537{
 538	return IM_SESSION_CONFERENCE_START == mType;
 539}
 540
 541bool LLIMModel::LLIMSession::isAdHoc()
 542{
 543	return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID));
 544}
 545
 546bool LLIMModel::LLIMSession::isP2P()
 547{
 548	return IM_NOTHING_SPECIAL == mType;
 549}
 550
 551bool LLIMModel::LLIMSession::isOtherParticipantAvaline()
 552{
 553	return !mOtherParticipantIsAvatar;
 554}
 555
 556void LLIMModel::LLIMSession::buildHistoryFileName()
 557{
 558	mHistoryFileName = mName;
 559
 560	//ad-hoc requires sophisticated chat history saving schemes
 561	if (isAdHoc())
 562	{
 563		/* in case of outgoing ad-hoc sessions we need to make specilized names
 564		* if this naming system is ever changed then the filtering definitions in 
 565		* lllogchat.cpp need to be change acordingly so that the filtering for the
 566		* date stamp code introduced in STORM-102 will work properly and not add
 567		* a date stamp to the Ad-hoc conferences.
 568		*/
 569		if (mInitialTargetIDs.size())
 570		{
 571			std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end());
 572			mHistoryFileName = mName + " hash" + generateHash(sorted_uuids);
 573		}
 574		else
 575		{
 576			//in case of incoming ad-hoc sessions
 577			mHistoryFileName = mName + " " + LLLogChat::timestamp(true) + " " + mSessionID.asString().substr(0, 4);
 578		}
 579	}
 580	else if (isP2P()) // look up username to use as the log name
 581	{
 582		LLAvatarName av_name;
 583		// For outgoing sessions we already have a cached name
 584		// so no need for a callback in LLAvatarNameCache::get()
 585		if (LLAvatarNameCache::get(mOtherParticipantID, &av_name))
 586		{
 587			if (av_name.mUsername.empty())
 588			{
 589				// Display names are off, use mDisplayName which will be the legacy name
 590				mHistoryFileName = LLCacheName::buildUsername(av_name.mDisplayName);
 591			}
 592			else
 593			{
 594				mHistoryFileName =  av_name.mUsername;
 595			}
 596		}
 597		else
 598		{
 599			// Incoming P2P sessions include a name that we can use to build a history file name
 600			mHistoryFileName = LLCacheName::buildUsername(mName);
 601		}
 602	}
 603}
 604
 605//static
 606std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids)
 607{
 608	LLMD5 md5_uuid;
 609	
 610	std::set<LLUUID>::const_iterator it = sorted_uuids.begin();
 611	while (it != sorted_uuids.end())
 612	{
 613		md5_uuid.update((unsigned char*)(*it).mData, 16);
 614		it++;
 615	}
 616	md5_uuid.finalize();
 617
 618	LLUUID participants_md5_hash;
 619	md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData);
 620	return participants_md5_hash.asString();
 621}
 622
 623void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id)
 624{
 625	LLIMSession* session = findIMSession(old_session_id);
 626	if (session)
 627	{
 628		session->sessionInitReplyReceived(new_session_id);
 629
 630		if (old_session_id != new_session_id)
 631		{
 632			mId2SessionMap.erase(old_session_id);
 633			mId2SessionMap[new_session_id] = session;
 634
 635			gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id);
 636		}
 637
 638		LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id);
 639		if (im_floater)
 640		{
 641			im_floater->sessionInitReplyReceived(new_session_id);
 642		}
 643
 644		// auto-start the call on session initialization?
 645		if (session->mStartCallOnInitialize)
 646		{
 647			gIMMgr->startCall(new_session_id);
 648		}
 649	}
 650}
 651
 652void LLIMModel::testMessages()
 653{
 654	LLUUID bot1_id("d0426ec6-6535-4c11-a5d9-526bb0c654d9");
 655	LLUUID bot1_session_id;
 656	std::string from = "IM Tester";
 657
 658	bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id);
 659	newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id);
 660	addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!");
 661
 662	LLUUID bot2_id;
 663	std::string firstname[] = {"Roflcopter", "Joe"};
 664	std::string lastname[] = {"Linden", "Tester", "Resident", "Schmoe"};
 665
 666	S32 rand1 = ll_rand(sizeof firstname)/(sizeof firstname[0]);
 667	S32 rand2 = ll_rand(sizeof lastname)/(sizeof lastname[0]);
 668	
 669	from = firstname[rand1] + " " + lastname[rand2];
 670	bot2_id.generate(from);
 671	LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id);
 672	newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id);
 673	addMessage(bot2_session_id, from, bot2_id, "Test Message: Hello there, I have a question. Can I bother you for a second? ");
 674	addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ.");
 675}
 676
 677//session name should not be empty
 678bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, 
 679						   const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
 680{
 681	if (name.empty())
 682	{
 683		llwarns << "Attempt to create a new session with empty name; id = " << session_id << llendl;
 684		return false;
 685	}
 686
 687	if (findIMSession(session_id))
 688	{
 689		llwarns << "IM Session " << session_id << " already exists" << llendl;
 690		return false;
 691	}
 692
 693	LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice);
 694	mId2SessionMap[session_id] = session;
 695
 696	// When notifying observer, name of session is used instead of "name", because they may not be the
 697	// same if it is an adhoc session (in this case name is localized in LLIMSession constructor).
 698	std::string session_name = LLIMModel::getInstance()->getName(session_id);
 699	LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, session_name, other_participant_id);
 700
 701	return true;
 702
 703}
 704
 705bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice)
 706{
 707	uuid_vec_t no_ids;
 708	return newSession(session_id, name, type, other_participant_id, no_ids, voice);
 709}
 710
 711bool LLIMModel::clearSession(const LLUUID& session_id)
 712{
 713	if (mId2SessionMap.find(session_id) == mId2SessionMap.end()) return false;
 714	delete (mId2SessionMap[session_id]);
 715	mId2SessionMap.erase(session_id);
 716	return true;
 717}
 718
 719void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index)
 720{
 721	LLIMSession* session = findIMSession(session_id);
 722	if (!session)
 723	{
 724		llwarns << "session " << session_id << "does not exist " << llendl;
 725		return;
 726	}
 727
 728	int i = session->mMsgs.size() - start_index;
 729
 730	for (std::list<LLSD>::iterator iter = session->mMsgs.begin();
 731		iter != session->mMsgs.end() && i > 0;
 732		iter++)
 733	{
 734		LLSD msg;
 735		msg = *iter;
 736		messages.push_back(*iter);
 737		i--;
 738	}
 739}
 740
 741void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id)
 742{
 743	LLIMSession* session = findIMSession(session_id);
 744	if (!session)
 745	{
 746		llwarns << "session " << session_id << "does not exist " << llendl;
 747		return;
 748	}
 749
 750	session->mNumUnread = 0;
 751	session->mParticipantUnreadMessageCount = 0;
 752	
 753	LLSD arg;
 754	arg["session_id"] = session_id;
 755	arg["num_unread"] = 0;
 756	arg["participant_unread"] = session->mParticipantUnreadMessageCount;
 757	mNoUnreadMsgsSignal(arg);
 758}
 759
 760void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index)
 761{
 762	getMessagesSilently(session_id, messages, start_index);
 763
 764	sendNoUnreadMessages(session_id);
 765}
 766
 767bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) {
 768	
 769	LLIMSession* session = findIMSession(session_id);
 770
 771	if (!session) 
 772	{
 773		llwarns << "session " << session_id << "does not exist " << llendl;
 774		return false;
 775	}
 776
 777	session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately
 778
 779	return true;
 780}
 781
 782bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
 783{
 784	if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
 785	{	
 786		std::string from_name = from;
 787
 788		LLAvatarName av_name;
 789		if (!from_id.isNull() && 
 790			LLAvatarNameCache::get(from_id, &av_name) &&
 791			!av_name.mIsDisplayNameDefault)
 792		{	
 793			from_name = av_name.getCompleteName();
 794		}
 795
 796		LLLogChat::saveHistory(file_name, from_name, from_id, utf8_text);
 797		return true;
 798	}
 799	else
 800	{
 801		return false;
 802	}
 803}
 804
 805bool LLIMModel::proccessOnlineOfflineNotification(
 806	const LLUUID& session_id, 
 807	const std::string& utf8_text)
 808{
 809	// Add system message to history
 810	return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text);
 811}
 812
 813bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, 
 814						   const std::string& utf8_text, bool log2file /* = true */) { 
 815
 816	LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file);
 817	if (!session) return false;
 818
 819	//good place to add some1 to recent list
 820	//other places may be called from message history.
 821	if( !from_id.isNull() &&
 822		( session->isP2PSessionType() || session->isAdHocSessionType() ) )
 823		LLRecentPeople::instance().add(from_id);
 824
 825	// notify listeners
 826	LLSD arg;
 827	arg["session_id"] = session_id;
 828	arg["num_unread"] = session->mNumUnread;
 829	arg["participant_unread"] = session->mParticipantUnreadMessageCount;
 830	arg["message"] = utf8_text;
 831	arg["from"] = from;
 832	arg["from_id"] = from_id;
 833	arg["time"] = LLLogChat::timestamp(false);
 834	mNewMsgSignal(arg);
 835
 836	return true;
 837}
 838
 839LLIMModel::LLIMSession* LLIMModel::addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, 
 840													 const std::string& utf8_text, bool log2file /* = true */)
 841{
 842	LLIMSession* session = findIMSession(session_id);
 843
 844	if (!session)
 845	{
 846		llwarns << "session " << session_id << "does not exist " << llendl;
 847		return NULL;
 848	}
 849
 850	// replace interactive system message marker with correct from string value
 851	std::string from_name = from;
 852	if (INTERACTIVE_SYSTEM_FROM == from)
 853	{
 854		from_name = SYSTEM_FROM;
 855	}
 856
 857	addToHistory(session_id, from_name, from_id, utf8_text);
 858	if (log2file)
 859	{
 860		logToFile(getHistoryFileName(session_id), from_name, from_id, utf8_text);
 861	}
 862	
 863	session->mNumUnread++;
 864
 865	//update count of unread messages from real participant
 866	if (!(from_id.isNull() || from_id == gAgentID || SYSTEM_FROM == from)
 867			// we should increment counter for interactive system messages()
 868			|| INTERACTIVE_SYSTEM_FROM == from)
 869	{
 870		++(session->mParticipantUnreadMessageCount);
 871	}
 872
 873	return session;
 874}
 875
 876
 877const std::string LLIMModel::getName(const LLUUID& session_id) const
 878{
 879	LLIMSession* session = findIMSession(session_id);
 880
 881	if (!session) 
 882	{
 883		llwarns << "session " << session_id << "does not exist " << llendl;
 884		return LLTrans::getString("no_session_message");
 885	}
 886
 887	return session->mName;
 888}
 889
 890const S32 LLIMModel::getNumUnread(const LLUUID& session_id) const
 891{
 892	LLIMSession* session = findIMSession(session_id);
 893	if (!session)
 894	{
 895		llwarns << "session " << session_id << "does not exist " << llendl;
 896		return -1;
 897	}
 898
 899	return session->mNumUnread;
 900}
 901
 902const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const
 903{
 904	LLIMSession* session = findIMSession(session_id);
 905	if (!session)
 906	{
 907		llwarns << "session " << session_id << "does not exist " << llendl;
 908		return LLUUID::null;
 909	}
 910
 911	return session->mOtherParticipantID;
 912}
 913
 914EInstantMessage LLIMModel::getType(const LLUUID& session_id) const
 915{
 916	LLIMSession* session = findIMSession(session_id);
 917	if (!session)
 918	{
 919		llwarns << "session " << session_id << "does not exist " << llendl;
 920		return IM_COUNT;
 921	}
 922
 923	return session->mType;
 924}
 925
 926LLVoiceChannel* LLIMModel::getVoiceChannel( const LLUUID& session_id ) const
 927{
 928	LLIMSession* session = findIMSession(session_id);
 929	if (!session)
 930	{
 931		llwarns << "session " << session_id << "does not exist " << llendl;
 932		return NULL;
 933	}
 934
 935	return session->mVoiceChannel;
 936}
 937
 938LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const
 939{
 940	LLIMSession* session = findIMSession(session_id);
 941	if (!session)
 942	{
 943		llwarns << "session " << session_id << " does not exist " << llendl;
 944		return NULL;
 945	}
 946
 947	return session->mSpeakers;
 948}
 949
 950const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const
 951{
 952	LLIMSession* session = findIMSession(session_id);
 953	if (!session)
 954	{
 955		llwarns << "session " << session_id << " does not exist " << llendl;
 956		return LLStringUtil::null;
 957	}
 958
 959	return session->mHistoryFileName;
 960}
 961
 962
 963// TODO get rid of other participant ID
 964void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) 
 965{
 966	std::string name;
 967	LLAgentUI::buildFullname(name);
 968
 969	pack_instant_message(
 970		gMessageSystem,
 971		gAgent.getID(),
 972		FALSE,
 973		gAgent.getSessionID(),
 974		other_participant_id,
 975		name,
 976		std::string("typing"),
 977		IM_ONLINE,
 978		(typing ? IM_TYPING_START : IM_TYPING_STOP),
 979		session_id);
 980	gAgent.sendReliableMessage();
 981}
 982
 983void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id)
 984{
 985	if(session_id.notNull())
 986	{
 987		std::string name;
 988		LLAgentUI::buildFullname(name);
 989		pack_instant_message(
 990			gMessageSystem,
 991			gAgent.getID(),
 992			FALSE,
 993			gAgent.getSessionID(),
 994			other_participant_id,
 995			name, 
 996			LLStringUtil::null,
 997			IM_ONLINE,
 998			IM_SESSION_LEAVE,
 999			session_id);
1000		gAgent.sendReliableMessage();
1001	}
1002}
1003
1004//*TODO this method is better be moved to the LLIMMgr
1005void LLIMModel::sendMessage(const std::string& utf8_text,
1006					 const LLUUID& im_session_id,
1007					 const LLUUID& other_participant_id,
1008					 EInstantMessage dialog)
1009{
1010	std::string name;
1011	bool sent = false;
1012	LLAgentUI::buildFullname(name);
1013
1014	const LLRelationship* info = NULL;
1015	info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
1016	
1017	U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
1018	
1019	if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
1020	{
1021		// User is online through the OOW connector, but not with a regular viewer.  Try to send the message via SLVoice.
1022		sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);
1023	}
1024	
1025	if(!sent)
1026	{
1027		// Send message normally.
1028
1029		// default to IM_SESSION_SEND unless it's nothing special - in
1030		// which case it's probably an IM to everyone.
1031		U8 new_dialog = dialog;
1032
1033		if ( dialog != IM_NOTHING_SPECIAL )
1034		{
1035			new_dialog = IM_SESSION_SEND;
1036		}
1037		pack_instant_message(
1038			gMessageSystem,
1039			gAgent.getID(),
1040			FALSE,
1041			gAgent.getSessionID(),
1042			other_participant_id,
1043			name.c_str(),
1044			utf8_text.c_str(),
1045			offline,
1046			(EInstantMessage)new_dialog,
1047			im_session_id);
1048		gAgent.sendReliableMessage();
1049	}
1050
1051	// If there is a mute list and this is not a group chat...
1052	if ( LLMuteList::getInstance() )
1053	{
1054		// ... the target should not be in our mute list for some message types.
1055		// Auto-remove them if present.
1056		switch( dialog )
1057		{
1058		case IM_NOTHING_SPECIAL:
1059		case IM_GROUP_INVITATION:
1060		case IM_INVENTORY_OFFERED:
1061		case IM_SESSION_INVITE:
1062		case IM_SESSION_P2P_INVITE:
1063		case IM_SESSION_CONFERENCE_START:
1064		case IM_SESSION_SEND: // This one is marginal - erring on the side of hearing.
1065		case IM_LURE_USER:
1066		case IM_GODLIKE_LURE_USER:
1067		case IM_FRIENDSHIP_OFFERED:
1068			LLMuteList::getInstance()->autoRemove(other_participant_id, LLMuteList::AR_IM);
1069			break;
1070		default: ; // do nothing
1071		}
1072	}
1073
1074	if((dialog == IM_NOTHING_SPECIAL) && 
1075	   (other_participant_id.notNull()))
1076	{
1077		// Do we have to replace the /me's here?
1078		std::string from;
1079		LLAgentUI::buildFullname(from);
1080		LLIMModel::getInstance()->addMessage(im_session_id, from, gAgentID, utf8_text);
1081
1082		//local echo for the legacy communicate panel
1083		std::string history_echo;
1084		LLAgentUI::buildFullname(history_echo);
1085
1086		history_echo += ": " + utf8_text;
1087
1088		LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
1089		if (speaker_mgr)
1090		{
1091			speaker_mgr->speakerChatted(gAgentID);
1092			speaker_mgr->setSpeakerTyping(gAgentID, FALSE);
1093		}
1094	}
1095
1096	// Add the recipient to the recent people list.
1097	bool is_not_group_id = LLGroupMgr::getInstance()->getGroupData(other_participant_id) == NULL;
1098
1099	if (is_not_group_id)
1100	{
1101		LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(im_session_id);
1102		if( session == 0)//??? shouldn't really happen
1103		{
1104			LLRecentPeople::instance().add(other_participant_id);
1105			return;
1106		}
1107		// IM_SESSION_INVITE means that this is an Ad-hoc incoming chat
1108		//		(it can be also Group chat but it is checked above)
1109		// In this case mInitialTargetIDs contains Ad-hoc session ID and it should not be added
1110		// to Recent People to prevent showing of an item with (???)(???). See EXT-8246.
1111		// Concrete participants will be added into this list once they sent message in chat.
1112		if (IM_SESSION_INVITE == dialog) return;
1113			
1114		if (IM_SESSION_CONFERENCE_START == dialog) // outgoing ad-hoc session
1115		{
1116			// Add only online members of conference to recent list (EXT-8658)
1117			addSpeakersToRecent(im_session_id);
1118		}
1119		else // outgoing P2P session
1120		{
1121			// Add the recepient of the session.
1122			if (!session->mInitialTargetIDs.empty())
1123			{
1124				LLRecentPeople::instance().add(*(session->mInitialTargetIDs.begin()));
1125			}
1126		}
1127	}
1128}
1129
1130void LLIMModel::addSpeakersToRecent(const LLUUID& im_session_id)
1131{
1132	LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
1133	LLSpeakerMgr::speaker_list_t speaker_list;
1134	if(speaker_mgr != NULL)
1135	{
1136		speaker_mgr->getSpeakerList(&speaker_list, true);
1137	}
1138	for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++)
1139	{
1140		const LLPointer<LLSpeaker>& speakerp = *it;
1141		LLRecentPeople::instance().add(speakerp->mID);
1142	}
1143}
1144
1145void session_starter_helper(
1146	const LLUUID& temp_session_id,
1147	const LLUUID& other_participant_id,
1148	EInstantMessage im_type)
1149{
1150	LLMessageSystem *msg = gMessageSystem;
1151
1152	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
1153	msg->nextBlockFast(_PREHASH_AgentData);
1154	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1155	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1156
1157	msg->nextBlockFast(_PREHASH_MessageBlock);
1158	msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
1159	msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id);
1160	msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
1161	msg->addU8Fast(_PREHASH_Dialog, im_type);
1162	msg->addUUIDFast(_PREHASH_ID, temp_session_id);
1163	msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
1164
1165	std::string name;
1166	LLAgentUI::buildFullname(name);
1167
1168	msg->addStringFast(_PREHASH_FromAgentName, name);
1169	msg->addStringFast(_PREHASH_Message, LLStringUtil::null);
1170	msg->addU32Fast(_PREHASH_ParentEstateID, 0);
1171	msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
1172	msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
1173}
1174
1175void start_deprecated_conference_chat(
1176	const LLUUID& temp_session_id,
1177	const LLUUID& creator_id,
1178	const LLUUID& other_participant_id,
1179	const LLSD& agents_to_invite)
1180{
1181	U8* bucket;
1182	U8* pos;
1183	S32 count;
1184	S32 bucket_size;
1185
1186	// *FIX: this could suffer from endian issues
1187	count = agents_to_invite.size();
1188	bucket_size = UUID_BYTES * count;
1189	bucket = new U8[bucket_size];
1190	pos = bucket;
1191
1192	for(S32 i = 0; i < count; ++i)
1193	{
1194		LLUUID agent_id = agents_to_invite[i].asUUID();
1195		
1196		memcpy(pos, &agent_id, UUID_BYTES);
1197		pos += UUID_BYTES;
1198	}
1199
1200	session_starter_helper(
1201		temp_session_id,
1202		other_participant_id,
1203		IM_SESSION_CONFERENCE_START);
1204
1205	gMessageSystem->addBinaryDataFast(
1206		_PREHASH_BinaryBucket,
1207		bucket,
1208		bucket_size);
1209
1210	gAgent.sendReliableMessage();
1211 
1212	delete[] bucket;
1213}
1214
1215class LLStartConferenceChatResponder : public LLHTTPClient::Responder
1216{
1217public:
1218	LLStartConferenceChatResponder(
1219		const LLUUID& temp_session_id,
1220		const LLUUID& creator_id,
1221		const LLUUID& other_participant_id,
1222		const LLSD& agents_to_invite)
1223	{
1224		mTempSessionID = temp_session_id;
1225		mCreatorID = creator_id;
1226		mOtherParticipantID = other_participant_id;
1227		mAgents = agents_to_invite;
1228	}
1229
1230	virtual void error(U32 statusNum, const std::string& reason)
1231	{
1232		//try an "old school" way.
1233		if ( statusNum == 400 )
1234		{
1235			start_deprecated_conference_chat(
1236				mTempSessionID,
1237				mCreatorID,
1238				mOtherParticipantID,
1239				mAgents);
1240		}
1241
1242		//else throw an error back to the client?
1243		//in theory we should have just have these error strings
1244		//etc. set up in this file as opposed to the IMMgr,
1245		//but the error string were unneeded here previously
1246		//and it is not worth the effort switching over all
1247		//the possible different language translations
1248	}
1249
1250private:
1251	LLUUID mTempSessionID;
1252	LLUUID mCreatorID;
1253	LLUUID mOtherParticipantID;
1254
1255	LLSD mAgents;
1256};
1257
1258// Returns true if any messages were sent, false otherwise.
1259// Is sort of equivalent to "does the server need to do anything?"
1260bool LLIMModel::sendStartSession(
1261	const LLUUID& temp_session_id,
1262	const LLUUID& other_participant_id,
1263	const uuid_vec_t& ids,
1264	EInstantMessage dialog)
1265{
1266	if ( dialog == IM_SESSION_GROUP_START )
1267	{
1268		session_starter_helper(
1269			temp_session_id,
1270			other_participant_id,
1271			dialog);
1272		gMessageSystem->addBinaryDataFast(
1273				_PREHASH_BinaryBucket,
1274				EMPTY_BINARY_BUCKET,
1275				EMPTY_BINARY_BUCKET_SIZE);
1276		gAgent.sendReliableMessage();
1277
1278		return true;
1279	}
1280	else if ( dialog == IM_SESSION_CONFERENCE_START )
1281	{
1282		LLSD agents;
1283		for (int i = 0; i < (S32) ids.size(); i++)
1284		{
1285			agents.append(ids[i]);
1286		}
1287
1288		//we have a new way of starting conference calls now
1289		LLViewerRegion* region = gAgent.getRegion();
1290		if (region)
1291		{
1292			std::string url = region->getCapability(
1293				"ChatSessionRequest");
1294			LLSD data;
1295			data["method"] = "start conference";
1296			data["session-id"] = temp_session_id;
1297
1298			data["params"] = agents;
1299
1300			LLHTTPClient::post(
1301				url,
1302				data,
1303				new LLStartConferenceChatResponder(
1304					temp_session_id,
1305					gAgent.getID(),
1306					other_participant_id,
1307					data["params"]));
1308		}
1309		else
1310		{
1311			start_deprecated_conference_chat(
1312				temp_session_id,
1313				gAgent.getID(),
1314				other_participant_id,
1315				agents);
1316		}
1317
1318		//we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id)
1319		return true;
1320	}
1321
1322	return false;
1323}
1324
1325//
1326// Helper Functions
1327//
1328
1329class LLViewerChatterBoxInvitationAcceptResponder :
1330	public LLHTTPClient::Responder
1331{
1332public:
1333	LLViewerChatterBoxInvitationAcceptResponder(
1334		const LLUUID& session_id,
1335		LLIMMgr::EInvitationType invitation_type)
1336	{
1337		mSessionID = session_id;
1338		mInvitiationType = invitation_type;
1339	}
1340
1341	void result(const LLSD& content)
1342	{
1343		if ( gIMMgr)
1344		{
1345			LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
1346			if (speaker_mgr)
1347			{
1348				//we've accepted our invitation
1349				//and received a list of agents that were
1350				//currently in the session when the reply was sent
1351				//to us.  Now, it is possible that there were some agents
1352				//to slip in/out between when that message was sent to us
1353				//and now.
1354
1355				//the agent list updates we've received have been
1356				//accurate from the time we were added to the session
1357				//but unfortunately, our base that we are receiving here
1358				//may not be the most up to date.  It was accurate at
1359				//some point in time though.
1360				speaker_mgr->setSpeakers(content);
1361
1362				//we now have our base of users in the session
1363				//that was accurate at some point, but maybe not now
1364				//so now we apply all of the udpates we've received
1365				//in case of race conditions
1366				speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID));
1367			}
1368
1369			if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType)
1370			{
1371				gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL);
1372			}
1373
1374			if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE 
1375				|| mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE)
1376				&& LLIMModel::getInstance()->findIMSession(mSessionID))
1377			{
1378				// TODO remove in 2010, for voice calls we do not open an IM window
1379				//LLIMFloater::show(mSessionID);
1380			}
1381
1382			gIMMgr->clearPendingAgentListUpdates(mSessionID);
1383			gIMMgr->clearPendingInvitation(mSessionID);
1384		}
1385	}
1386
1387	void error(U32 statusNum, const std::string& reason)
1388	{		
1389		//throw something back to the viewer here?
1390		if ( gIMMgr )
1391		{
1392			gIMMgr->clearPendingAgentListUpdates(mSessionID);
1393			gIMMgr->clearPendingInvitation(mSessionID);
1394			if ( 404 == statusNum )
1395			{
1396				std::string error_string;
1397				error_string = "session_does_not_exist_error";
1398				gIMMgr->showSessionStartError(error_string, mSessionID);
1399			}
1400		}
1401	}
1402
1403private:
1404	LLUUID mSessionID;
1405	LLIMMgr::EInvitationType mInvitiationType;
1406};
1407
1408
1409// the other_participant_id is either an agent_id, a group_id, or an inventory
1410// folder item_id (collection of calling cards)
1411
1412// static
1413LLUUID LLIMMgr::computeSessionID(
1414	EInstantMessage dialog,
1415	const LLUUID& other_participant_id)
1416{
1417	LLUUID session_id;
1418	if (IM_SESSION_GROUP_START == dialog)
1419	{
1420		// slam group session_id to the group_id (other_participant_id)
1421		session_id = other_participant_id;
1422	}
1423	else if (IM_SESSION_CONFERENCE_START == dialog)
1424	{
1425		session_id.generate();
1426	}
1427	else if (IM_SESSION_INVITE == dialog)
1428	{
1429		// use provided session id for invites
1430		session_id = other_participant_id;
1431	}
1432	else
1433	{
1434		LLUUID agent_id = gAgent.getID();
1435		if (other_participant_id == agent_id)
1436		{
1437			// if we try to send an IM to ourselves then the XOR would be null
1438			// so we just make the session_id the same as the agent_id
1439			session_id = agent_id;
1440		}
1441		else
1442		{
1443			// peer-to-peer or peer-to-asset session_id is the XOR
1444			session_id = other_participant_id ^ agent_id;
1445		}
1446	}
1447	return session_id;
1448}
1449
1450void
1451LLIMMgr::showSessionStartError(
1452	const std::string& error_string,
1453	const LLUUID session_id)
1454{
1455	if (!hasSession(session_id)) return;
1456
1457	LLSD args;
1458	args["REASON"] = LLTrans::getString(error_string);
1459	args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id);
1460
1461	LLSD payload;
1462	payload["session_id"] = session_id;
1463
1464	LLNotificationsUtil::add(
1465		"ChatterBoxSessionStartError",
1466		args,
1467		payload,
1468		LLIMMgr::onConfirmForceCloseError);
1469}
1470
1471void
1472LLIMMgr::showSessionEventError(
1473	const std::string& event_string,
1474	const std::string& error_string,
1475	const LLUUID session_id)
1476{
1477	LLSD args;
1478	LLStringUtil::format_map_t event_args;
1479
1480	event_args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id);
1481
1482	args["REASON"] =
1483		LLTrans::getString(error_string);
1484	args["EVENT"] =
1485		LLTrans::getString(event_string, event_args);
1486
1487	LLNotificationsUtil::add(
1488		"ChatterBoxSessionEventError",
1489		args);
1490}
1491
1492void
1493LLIMMgr::showSessionForceClose(
1494	const std::string& reason_string,
1495	const LLUUID session_id)
1496{
1497	if (!hasSession(session_id)) return;
1498
1499	LLSD args;
1500
1501	args["NAME"] = LLIMModel::getInstance()->getName(session_id);
1502	args["REASON"] = LLTrans::getString(reason_string);
1503
1504	LLSD payload;
1505	payload["session_id"] = session_id;
1506
1507	LLNotificationsUtil::add(
1508		"ForceCloseChatterBoxSession",
1509		args,
1510		payload,
1511		LLIMMgr::onConfirmForceCloseError);
1512}
1513
1514//static
1515bool
1516LLIMMgr::onConfirmForceCloseError(
1517	const LLSD& notification,
1518	const LLSD& response)
1519{
1520	//only 1 option really
1521	LLUUID session_id = notification["payload"]["session_id"];
1522
1523	LLFloater* floater = LLIMFloater::findInstance(session_id);
1524	if ( floater )
1525	{
1526		floater->closeFloater(FALSE);
1527	}
1528	return false;
1529}
1530
1531
1532//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1533// Class LLCallDialogManager
1534//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1535
1536LLCallDialogManager::LLCallDialogManager()
1537{
1538}
1539
1540LLCallDialogManager::~LLCallDialogManager()
1541{
1542}
1543
1544void LLCallDialogManager::initClass()
1545{
1546	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(LLCallDialogManager::onVoiceChannelChanged);
1547}
1548
1549void LLCallDialogManager::onVoiceChannelChanged(const LLUUID &session_id)
1550{
1551	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
1552	if(!session)
1553	{		
1554		sPreviousSessionlName = sCurrentSessionlName;
1555		sCurrentSessionlName = ""; // Empty string results in "Nearby Voice Chat" after substitution
1556		return;
1557	}
1558	
1559	if (sSession)
1560	{
1561		// store previous session type to process Avaline calls in dialogs
1562		sPreviousSessionType = sSession->mSessionType;
1563	}
1564
1565	sSession = session;
1566
1567	static boost::signals2::connection prev_channel_state_changed_connection;
1568	// disconnect previously connected callback to avoid have invalid sSession in onVoiceChannelStateChanged()
1569	prev_channel_state_changed_connection.disconnect();
1570	prev_channel_state_changed_connection =
1571		sSession->mVoiceChannel->setStateChangedCallback(boost::bind(LLCallDialogManager::onVoiceChannelStateChanged, _1, _2, _3, _4));
1572
1573	if(sCurrentSessionlName != session->mName)
1574	{
1575		sPreviousSessionlName = sCurrentSessionlName;
1576		sCurrentSessionlName = session->mName;
1577	}
1578
1579	if (LLVoiceChannel::getCurrentVoiceChannel()->getState() == LLVoiceChannel::STATE_CALL_STARTED &&
1580		LLVoiceChannel::getCurrentVoiceChannel()->getCallDirection() == LLVoiceChannel::OUTGOING_CALL)
1581	{
1582		
1583		//*TODO get rid of duplicated code
1584		LLSD mCallDialogPayload;
1585		mCallDialogPayload["session_id"] = sSession->mSessionID;
1586		mCallDialogPayload["session_name"] = sSession->mName;
1587		mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID;
1588		mCallDialogPayload["old_channel_name"] = sPreviousSessionlName;
1589		mCallDialogPayload["old_session_type"] = sPreviousSessionType;
1590		mCallDialogPayload["state"] = LLVoiceChannel::STATE_CALL_STARTED;
1591		mCallDialogPayload["disconnected_channel_name"] = sSession->mName;
1592		mCallDialogPayload["session_type"] = sSession->mSessionType;
1593
1594		LLOutgoingCallDialog* ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
1595		if(ocd)
1596		{
1597			ocd->show(mCallDialogPayload);
1598		}	
1599	}
1600
1601}
1602
1603void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent)
1604{
1605	LLSD mCallDialogPayload;
1606	LLOutgoingCallDialog* ocd = NULL;
1607
1608	if(sOldState == new_state)
1609	{
1610		return;
1611	}
1612
1613	sOldState = new_state;
1614
1615	mCallDialogPayload["session_id"] = sSession->mSessionID;
1616	mCallDialogPayload["session_name"] = sSession->mName;
1617	mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID;
1618	mCallDialogPayload["old_channel_name"] = sPreviousSessionlName;
1619	mCallDialogPayload["old_session_type"] = sPreviousSessionType;
1620	mCallDialogPayload["state"] = new_state;
1621	mCallDialogPayload["disconnected_channel_name"] = sSession->mName;
1622	mCallDialogPayload["session_type"] = sSession->mSessionType;
1623	mCallDialogPayload["ended_by_agent"] = ended_by_agent;
1624
1625	switch(new_state)
1626	{			
1627	case LLVoiceChannel::STATE_CALL_STARTED :
1628		// do not show "Calling to..." if it is incoming call
1629		if(direction == LLVoiceChannel::INCOMING_CALL)
1630		{
1631			return;
1632		}
1633		break;
1634
1635	case LLVoiceChannel::STATE_HUNG_UP:
1636		// this state is coming before session is changed, so, put it into payload map
1637		mCallDialogPayload["old_session_type"] = sSession->mSessionType;
1638		break;
1639
1640	case LLVoiceChannel::STATE_CONNECTED :
1641		ocd = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
1642		if (ocd)
1643		{
1644			ocd->closeFloater();
1645		}
1646		return;
1647
1648	default:
1649		break;
1650	}
1651
1652	ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
1653	if(ocd)
1654	{
1655		ocd->show(mCallDialogPayload);
1656	}	
1657}
1658
1659//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1660// Class LLCallDialog
1661//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1662LLCallDialog::LLCallDialog(const LLSD& payload)
1663	: LLDockableFloater(NULL, false, payload),
1664
1665	  mPayload(payload),
1666	  mLifetime(DEFAULT_LIFETIME)
1667{
1668	setAutoFocus(FALSE);
1669	// force docked state since this floater doesn't save it between recreations
1670	setDocked(true);
1671}
1672
1673LLCallDialog::~LLCallDialog()
1674{
1675	LLUI::removePopup(this);
1676}
1677
1678BOOL LLCallDialog::postBuild()
1679{
1680	if (!LLDockableFloater::postBuild() || !gToolBarView)
1681		return FALSE;
1682	
1683	dockToToolbarButton("speak");
1684	
1685	return TRUE;
1686}
1687
1688void LLCallDialog::dockToToolbarButton(const std::string& toolbarButtonName)
1689{
1690	LLDockControl::DocAt dock_pos = getDockControlPos(toolbarButtonName);
1691	LLView *anchor_panel = gToolBarView->findChildView(toolbarButtonName);
1692
1693	setUseTongue(anchor_panel);
1694
1695	setDockControl(new LLDockControl(anchor_panel, this, getDockTongue(dock_pos), dock_pos));
1696}
1697
1698LLDockControl::DocAt LLCallDialog::getDockControlPos(const std::string& toolbarButtonName)
1699{
1700	LLCommandId command_id(toolbarButtonName);
1701	S32 toolbar_loc = gToolBarView->hasCommand(command_id);
1702	
1703	LLDockControl::DocAt doc_at = LLDockControl::TOP;
1704	
1705	switch (toolbar_loc)
1706	{
1707		case LLToolBarView::TOOLBAR_LEFT:
1708			doc_at = LLDockControl::RIGHT;
1709			break;
1710			
1711		case LLToolBarView::TOOLBAR_RIGHT:
1712			doc_at = LLDockControl::LEFT;
1713			break;
1714	}
1715	
1716	return doc_at;
1717}
1718
1719
1720//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1721// Class LLOutgoingCallDialog
1722//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1723LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) :
1724LLCallDialog(payload)
1725{
1726	LLOutgoingCallDialog* instance = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
1727	if(instance && instance->getVisible())
1728	{
1729		instance->onCancel(instance);
1730	}	
1731}
1732
1733void LLCallDialog::draw()
1734{
1735	if (lifetimeHasExpired())
1736	{
1737		onLifetimeExpired();
1738	}
1739
1740	if (getDockControl() != NULL)
1741	{
1742		LLDockableFloater::draw();
1743	}
1744}
1745
1746// virtual
1747void LLCallDialog::onOpen(const LLSD& key)
1748{
1749	LLDockableFloater::onOpen(key);
1750
1751	// it should be over the all floaters. EXT-5116
1752	LLUI::addPopup(this);
1753}
1754
1755void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id)
1756{
1757	// *NOTE: 12/28/2009: check avaline calls: LLVoiceClient::isParticipantAvatar returns false for them
1758	bool participant_is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
1759
1760	bool is_group = participant_is_avatar && gAgent.isInGroup(session_id);
1761
1762	LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon");
1763	LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon");
1764
1765	avatar_icon->setVisible(!is_group);
1766	group_icon->setVisible(is_group);
1767
1768	if (is_group)
1769	{
1770		group_icon->setValue(session_id);
1771	}
1772	else if (participant_is_avatar)
1773	{
1774		avatar_icon->setValue(participant_id);
1775	}
1776	else
1777	{
1778		avatar_icon->setValue("Avaline_Icon");
1779		avatar_icon->setToolTip(std::string(""));
1780	}
1781}
1782
1783bool LLCallDialog::lifetimeHasExpired()
1784{
1785	if (mLifetimeTimer.getStarted())
1786	{
1787		F32 elapsed_time = mLifetimeTimer.getElapsedTimeF32();
1788		if (elapsed_time > mLifetime) 
1789		{
1790			return true;
1791		}
1792	}
1793	return false;
1794}
1795
1796void LLCallDialog::onLifetimeExpired()
1797{
1798	mLifetimeTimer.stop();
1799	closeFloater();
1800}
1801
1802void LLOutgoingCallDialog::show(const LLSD& key)
1803{
1804	mPayload = key;
1805
1806	//will be false only if voice in parcel is disabled and channel we leave is nearby(checked further)
1807	bool show_oldchannel = LLViewerParcelMgr::getInstance()->allowAgentVoice();
1808
1809	// hide all text at first
1810	hideAllText();
1811
1812	// init notification's lifetime
1813	std::istringstream ss( getString("lifetime") );
1814	if (!(ss >> mLifetime))
1815	{
1816		mLifetime = DEFAULT_LIFETIME;
1817	}
1818
1819	// customize text strings
1820	// tell the user which voice channel they are leaving
1821	if (!mPayload["old_channel_name"].asString().empty())
1822	{
1823		bool was_avaline_call = LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["old_session_type"].asInteger();
1824
1825		std::string old_caller_name = mPayload["old_channel_name"].asString();
1826		if (was_avaline_call)
1827		{
1828			old_caller_name = LLTextUtil::formatPhoneNumber(

Large files files are truncated, but you can click here to view the full file