PageRenderTime 63ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

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