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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file LLIMMgr.cpp
  3.  * @brief Container for Instant Messaging
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llimview.h"
  34. #include "llfloaterreg.h"
  35. #include "llfontgl.h"
  36. #include "llrect.h"
  37. #include "llerror.h"
  38. #include "llbutton.h"
  39. #include "llhttpclient.h"
  40. #include "llsdutil_math.h"
  41. #include "llstring.h"
  42. #include "lltrans.h"
  43. #include "lluictrlfactory.h"
  44. #include "llagent.h"
  45. #include "llagentui.h"
  46. #include "llappviewer.h"
  47. #include "llavatariconctrl.h"
  48. #include "llbottomtray.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 "lltextutil.h"
  64. #include "llviewercontrol.h"
  65. #include "llviewerparcelmgr.h"
  66. const static std::string IM_TIME("time");
  67. const static std::string IM_TEXT("message");
  68. const static std::string IM_FROM("from");
  69. const static std::string IM_FROM_ID("from_id");
  70. const static std::string NO_SESSION("(IM Session Doesn't Exist)");
  71. const static std::string ADHOC_NAME_SUFFIX(" Conference");
  72. const static std::string NEARBY_P2P_BY_OTHER("nearby_P2P_by_other");
  73. const static std::string NEARBY_P2P_BY_AGENT("nearby_P2P_by_agent");
  74. std::string LLCallDialogManager::sPreviousSessionlName = "";
  75. LLIMModel::LLIMSession::SType LLCallDialogManager::sPreviousSessionType = LLIMModel::LLIMSession::P2P_SESSION;
  76. std::string LLCallDialogManager::sCurrentSessionlName = "";
  77. LLIMModel::LLIMSession* LLCallDialogManager::sSession = NULL;
  78. LLVoiceChannel::EState LLCallDialogManager::sOldState = LLVoiceChannel::STATE_READY;
  79. const LLUUID LLOutgoingCallDialog::OCD_KEY = LLUUID("7CF78E11-0CFE-498D-ADB9-1417BF03DDB4");
  80. //
  81. // Globals
  82. //
  83. LLIMMgr* gIMMgr = NULL;
  84. void toast_callback(const LLSD& msg){
  85. // do not show toast in busy mode or it goes from agent
  86. if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
  87. {
  88. return;
  89. }
  90. // check whether incoming IM belongs to an active session or not
  91. if (LLIMModel::getInstance()->getActiveSessionID().notNull()
  92. && LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"])
  93. {
  94. return;
  95. }
  96. // Skip toasting for system messages
  97. if (msg["from_id"].asUUID() == LLUUID::null)
  98. {
  99. return;
  100. }
  101. // Skip toasting if we have open window of IM with this session id
  102. LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]);
  103. if (open_im_floater && open_im_floater->getVisible())
  104. {
  105. return;
  106. }
  107. LLSD args;
  108. args["MESSAGE"] = msg["message"];
  109. args["TIME"] = msg["time"];
  110. args["FROM"] = msg["from"];
  111. args["FROM_ID"] = msg["from_id"];
  112. args["SESSION_ID"] = msg["session_id"];
  113. LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID()));
  114. }
  115. void LLIMModel::setActiveSessionID(const LLUUID& session_id)
  116. {
  117. // check if such an ID really exists
  118. if (!findIMSession(session_id))
  119. {
  120. llwarns << "Trying to set as active a non-existent session!" << llendl;
  121. return;
  122. }
  123. mActiveSessionID = session_id;
  124. }
  125. LLIMModel::LLIMModel() 
  126. {
  127. addNewMsgCallback(LLIMFloater::newIMCallback);
  128. addNewMsgCallback(toast_callback);
  129. }
  130. LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids, bool voice)
  131. : mSessionID(session_id),
  132. mName(name),
  133. mType(type),
  134. mParticipantUnreadMessageCount(0),
  135. mNumUnread(0),
  136. mOtherParticipantID(other_participant_id),
  137. mInitialTargetIDs(ids),
  138. mVoiceChannel(NULL),
  139. mSpeakers(NULL),
  140. mSessionInitialized(false),
  141. mCallBackEnabled(true),
  142. mTextIMPossible(true),
  143. mOtherParticipantIsAvatar(true),
  144. mStartCallOnInitialize(false),
  145. mStartedAsIMCall(voice)
  146. {
  147. // set P2P type by default
  148. mSessionType = P2P_SESSION;
  149. if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type)
  150. {
  151. mVoiceChannel  = new LLVoiceChannelP2P(session_id, name, other_participant_id);
  152. mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID);
  153. // check if it was AVALINE call
  154. if (!mOtherParticipantIsAvatar)
  155. {
  156. mSessionType = AVALINE_SESSION;
  157. }
  158. else
  159. {
  160. mVoiceChannel = new LLVoiceChannelGroup(session_id, name);
  161. // determine whether it is group or conference session
  162. if (gAgent.isInGroup(mSessionID))
  163. {
  164. mSessionType = GROUP_SESSION;
  165. }
  166. else
  167. {
  168. mSessionType = ADHOC_SESSION;
  169. }
  170. if(mVoiceChannel)
  171. {
  172. mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3));
  173. }
  174. mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
  175. // All participants will be added to the list of people we've recently interacted with.
  176. // we need to add only _active_ speakers...so comment this. 
  177. // may delete this later on cleanup
  178. //mSpeakers->addListener(&LLRecentPeople::instance(), "add");
  179. //we need to wait for session initialization for outgoing ad-hoc and group chat session
  180. //correct session id for initiated ad-hoc chat will be received from the server
  181. if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, 
  182. mInitialTargetIDs, mType))
  183. {
  184. //we don't need to wait for any responses
  185. //so we're already initialized
  186. mSessionInitialized = true;
  187. }
  188. if (IM_NOTHING_SPECIAL == type)
  189. {
  190. mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID);
  191. mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID);
  192. }
  193. buildHistoryFileName();
  194. if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
  195. {
  196. std::list<LLSD> chat_history;
  197. //involves parsing of a chat history
  198. LLLogChat::loadAllHistory(mHistoryFileName, chat_history);
  199. addMessagesFromHistory(chat_history);
  200. }
  201. }
  202. void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction)
  203. {
  204. std::string you = LLTrans::getString("You");
  205. std::string started_call = LLTrans::getString("started_call");
  206. std::string joined_call = LLTrans::getString("joined_call");
  207. std::string other_avatar_name = "";
  208. std::string message;
  209. switch(mSessionType)
  210. {
  211. case AVALINE_SESSION:
  212. // no text notifications
  213. break;
  214. case P2P_SESSION:
  215. gCacheName->getFullName(mOtherParticipantID, other_avatar_name);
  216. if(direction == LLVoiceChannel::INCOMING_CALL)
  217. {
  218. switch(new_state)
  219. {
  220. case LLVoiceChannel::STATE_CALL_STARTED :
  221. message = other_avatar_name + " " + started_call;
  222. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  223. break;
  224. case LLVoiceChannel::STATE_CONNECTED :
  225. message = you + " " + joined_call;
  226. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  227. default:
  228. break;
  229. }
  230. }
  231. else // outgoing call
  232. {
  233. switch(new_state)
  234. {
  235. case LLVoiceChannel::STATE_CALL_STARTED :
  236. message = you + " " + started_call;
  237. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  238. break;
  239. case LLVoiceChannel::STATE_CONNECTED :
  240. message = other_avatar_name + " " + joined_call;
  241. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  242. default:
  243. break;
  244. }
  245. }
  246. break;
  247. case GROUP_SESSION:
  248. case ADHOC_SESSION:
  249. if(direction == LLVoiceChannel::INCOMING_CALL)
  250. {
  251. switch(new_state)
  252. {
  253. case LLVoiceChannel::STATE_CONNECTED :
  254. message = you + " " + joined_call;
  255. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  256. default:
  257. break;
  258. }
  259. }
  260. else // outgoing call
  261. {
  262. switch(new_state)
  263. {
  264. case LLVoiceChannel::STATE_CALL_STARTED :
  265. message = you + " " + started_call;
  266. LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
  267. break;
  268. default:
  269. break;
  270. }
  271. }
  272. }
  273. // Update speakers list when connected
  274. if (LLVoiceChannel::STATE_CONNECTED == new_state)
  275. {
  276. mSpeakers->update(true);
  277. }
  278. }
  279. LLIMModel::LLIMSession::~LLIMSession()
  280. {
  281. delete mSpeakers;
  282. mSpeakers = NULL;
  283. // End the text IM session if necessary
  284. if(gVoiceClient && mOtherParticipantID.notNull())
  285. {
  286. switch(mType)
  287. {
  288. case IM_NOTHING_SPECIAL:
  289. case IM_SESSION_P2P_INVITE:
  290. gVoiceClient->endUserIMSession(mOtherParticipantID);
  291. break;
  292. default:
  293. // Appease the linux compiler
  294. break;
  295. }
  296. }
  297. mVoiceChannelStateChangeConnection.disconnect();
  298. // 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).
  299. mVoiceChannel->deactivate();
  300. delete mVoiceChannel;
  301. mVoiceChannel = NULL;
  302. }
  303. void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id)
  304. {
  305. mSessionInitialized = true;
  306. if (new_session_id != mSessionID)
  307. {
  308. mSessionID = new_session_id;
  309. mVoiceChannel->updateSessionID(new_session_id);
  310. }
  311. }
  312. void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time)
  313. {
  314. LLSD message;
  315. message["from"] = from;
  316. message["from_id"] = from_id;
  317. message["message"] = utf8_text;
  318. message["time"] = time; 
  319. message["index"] = (LLSD::Integer)mMsgs.size(); 
  320. mMsgs.push_front(message); 
  321. if (mSpeakers && from_id.notNull())
  322. {
  323. mSpeakers->speakerChatted(from_id);
  324. mSpeakers->setSpeakerTyping(from_id, FALSE);
  325. }
  326. }
  327. void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history)
  328. {
  329. std::list<LLSD>::const_iterator it = history.begin();
  330. while (it != history.end())
  331. {
  332. const LLSD& msg = *it;
  333. std::string from = msg[IM_FROM];
  334. LLUUID from_id = LLUUID::null;
  335. if (msg[IM_FROM_ID].isUndefined())
  336. {
  337. gCacheName->getUUID(from, from_id);
  338. }
  339. std::string timestamp = msg[IM_TIME];
  340. std::string text = msg[IM_TEXT];
  341. addMessage(from, from_id, text, timestamp);
  342. it++;
  343. }
  344. }
  345. void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)
  346. {
  347. if (!userdata) return;
  348. LLIMSession* self = (LLIMSession*) userdata;
  349. if (type == LLLogChat::LOG_LINE)
  350. {
  351. self->addMessage("", LLSD(), msg["message"].asString(), "");
  352. }
  353. else if (type == LLLogChat::LOG_LLSD)
  354. {
  355. self->addMessage(msg["from"].asString(), msg["from_id"].asUUID(), msg["message"].asString(), msg["time"].asString());
  356. }
  357. }
  358. LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const
  359. {
  360. return get_if_there(mId2SessionMap, session_id,
  361. (LLIMModel::LLIMSession*) NULL);
  362. }
  363. //*TODO consider switching to using std::set instead of std::list for holding LLUUIDs across the whole code
  364. LLIMModel::LLIMSession* LLIMModel::findAdHocIMSession(const std::vector<LLUUID>& ids)
  365. {
  366. S32 num = ids.size();
  367. if (!num) return NULL;
  368. if (mId2SessionMap.empty()) return NULL;
  369. std::map<LLUUID, LLIMSession*>::const_iterator it = mId2SessionMap.begin();
  370. for (; it != mId2SessionMap.end(); ++it)
  371. {
  372. LLIMSession* session = (*it).second;
  373. if (!session->isAdHoc()) continue;
  374. if (session->mInitialTargetIDs.size() != num) continue;
  375. std::list<LLUUID> tmp_list(session->mInitialTargetIDs.begin(), session->mInitialTargetIDs.end());
  376. std::vector<LLUUID>::const_iterator iter = ids.begin();
  377. while (iter != ids.end())
  378. {
  379. tmp_list.remove(*iter);
  380. ++iter;
  381. if (tmp_list.empty()) 
  382. {
  383. break;
  384. }
  385. }
  386. if (tmp_list.empty() && iter == ids.end())
  387. {
  388. return session;
  389. }
  390. }
  391. return NULL;
  392. }
  393. bool LLIMModel::LLIMSession::isAdHoc()
  394. {
  395. return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID));
  396. }
  397. bool LLIMModel::LLIMSession::isP2P()
  398. {
  399. return IM_NOTHING_SPECIAL == mType;
  400. }
  401. bool LLIMModel::LLIMSession::isOtherParticipantAvaline()
  402. {
  403. return !mOtherParticipantIsAvatar;
  404. }
  405. void LLIMModel::LLIMSession::buildHistoryFileName()
  406. {
  407. mHistoryFileName = mName;
  408. //ad-hoc requires sophisticated chat history saving schemes
  409. if (isAdHoc())
  410. {
  411. //in case of outgoing ad-hoc sessions
  412. if (mInitialTargetIDs.size())
  413. {
  414. std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end());
  415. mHistoryFileName = mName + " hash" + generateHash(sorted_uuids);
  416. return;
  417. }
  418. //in case of incoming ad-hoc sessions
  419. mHistoryFileName = mName + " " + LLLogChat::timestamp(true) + " " + mSessionID.asString().substr(0, 4);
  420. }
  421. }
  422. //static
  423. std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids)
  424. {
  425. LLMD5 md5_uuid;
  426. std::set<LLUUID>::const_iterator it = sorted_uuids.begin();
  427. while (it != sorted_uuids.end())
  428. {
  429. md5_uuid.update((unsigned char*)(*it).mData, 16);
  430. it++;
  431. }
  432. md5_uuid.finalize();
  433. LLUUID participants_md5_hash;
  434. md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData);
  435. return participants_md5_hash.asString();
  436. }
  437. void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id)
  438. {
  439. LLIMSession* session = findIMSession(old_session_id);
  440. if (session)
  441. {
  442. session->sessionInitReplyReceived(new_session_id);
  443. if (old_session_id != new_session_id)
  444. {
  445. mId2SessionMap.erase(old_session_id);
  446. mId2SessionMap[new_session_id] = session;
  447. gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id);
  448. }
  449. LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id);
  450. if (im_floater)
  451. {
  452. im_floater->sessionInitReplyReceived(new_session_id);
  453. }
  454. // auto-start the call on session initialization?
  455. if (session->mStartCallOnInitialize)
  456. {
  457. gIMMgr->startCall(new_session_id);
  458. }
  459. }
  460. }
  461. void LLIMModel::testMessages()
  462. {
  463. LLUUID bot1_id("d0426ec6-6535-4c11-a5d9-526bb0c654d9");
  464. LLUUID bot1_session_id;
  465. std::string from = "IM Tester";
  466. bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id);
  467. newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id);
  468. addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!");
  469. LLUUID bot2_id;
  470. std::string firstname[] = {"Roflcopter", "Joe"};
  471. std::string lastname[] = {"Linden", "Tester", "Resident", "Schmoe"};
  472. S32 rand1 = ll_rand(sizeof firstname)/(sizeof firstname[0]);
  473. S32 rand2 = ll_rand(sizeof lastname)/(sizeof lastname[0]);
  474. from = firstname[rand1] + " " + lastname[rand2];
  475. bot2_id.generate(from);
  476. LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id);
  477. newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id);
  478. addMessage(bot2_session_id, from, bot2_id, "Test Message: Hello there, I have a question. Can I bother you for a second? ");
  479. addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ.");
  480. }
  481. //session name should not be empty
  482. bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, 
  483.    const LLUUID& other_participant_id, const std::vector<LLUUID>& ids, bool voice)
  484. {
  485. if (name.empty())
  486. {
  487. llwarns << "Attempt to create a new session with empty name; id = " << session_id << llendl;
  488. return false;
  489. }
  490. if (findIMSession(session_id))
  491. {
  492. llwarns << "IM Session " << session_id << " already exists" << llendl;
  493. return false;
  494. }
  495. LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice);
  496. mId2SessionMap[session_id] = session;
  497. LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, name, other_participant_id);
  498. return true;
  499. }
  500. bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice)
  501. {
  502. std::vector<LLUUID> no_ids;
  503. return newSession(session_id, name, type, other_participant_id, no_ids, voice);
  504. }
  505. bool LLIMModel::clearSession(const LLUUID& session_id)
  506. {
  507. if (mId2SessionMap.find(session_id) == mId2SessionMap.end()) return false;
  508. delete (mId2SessionMap[session_id]);
  509. mId2SessionMap.erase(session_id);
  510. return true;
  511. }
  512. void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index)
  513. {
  514. LLIMSession* session = findIMSession(session_id);
  515. if (!session) 
  516. {
  517. llwarns << "session " << session_id << "does not exist " << llendl;
  518. return;
  519. }
  520. int i = session->mMsgs.size() - start_index;
  521. for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); 
  522. iter != session->mMsgs.end() && i > 0;
  523. iter++)
  524. {
  525. LLSD msg;
  526. msg = *iter;
  527. messages.push_back(*iter);
  528. i--;
  529. }
  530. session->mNumUnread = 0;
  531. session->mParticipantUnreadMessageCount = 0;
  532. LLSD arg;
  533. arg["session_id"] = session_id;
  534. arg["num_unread"] = 0;
  535. arg["participant_unread"] = session->mParticipantUnreadMessageCount;
  536. mNoUnreadMsgsSignal(arg);
  537. }
  538. bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) {
  539. LLIMSession* session = findIMSession(session_id);
  540. if (!session) 
  541. {
  542. llwarns << "session " << session_id << "does not exist " << llendl;
  543. return false;
  544. }
  545. session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately
  546. return true;
  547. }
  548. bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
  549. {
  550. if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
  551. {
  552. LLLogChat::saveHistory(file_name, from, from_id, utf8_text);
  553. return true;
  554. }
  555. else
  556. {
  557. return false;
  558. }
  559. }
  560. bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
  561. {
  562. return logToFile(LLIMModel::getInstance()->getHistoryFileName(session_id), from, from_id, utf8_text);
  563. }
  564. bool LLIMModel::proccessOnlineOfflineNotification(
  565. const LLUUID& session_id, 
  566. const std::string& utf8_text)
  567. {
  568. // Add system message to history
  569. return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text);
  570. }
  571. bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, 
  572.    const std::string& utf8_text, bool log2file /* = true */) { 
  573. LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file);
  574. if (!session) return false;
  575. //good place to add some1 to recent list
  576. //other places may be called from message history.
  577. if( !from_id.isNull() &&
  578. ( session->isP2PSessionType() || session->isAdHocSessionType() ) )
  579. LLRecentPeople::instance().add(from_id);
  580. // notify listeners
  581. LLSD arg;
  582. arg["session_id"] = session_id;
  583. arg["num_unread"] = session->mNumUnread;
  584. arg["participant_unread"] = session->mParticipantUnreadMessageCount;
  585. arg["message"] = utf8_text;
  586. arg["from"] = from;
  587. arg["from_id"] = from_id;
  588. arg["time"] = LLLogChat::timestamp(false);
  589. mNewMsgSignal(arg);
  590. return true;
  591. }
  592. LLIMModel::LLIMSession* LLIMModel::addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, 
  593.  const std::string& utf8_text, bool log2file /* = true */)
  594. {
  595. LLIMSession* session = findIMSession(session_id);
  596. if (!session)
  597. {
  598. llwarns << "session " << session_id << "does not exist " << llendl;
  599. return NULL;
  600. }
  601. addToHistory(session_id, from, from_id, utf8_text);
  602. if (log2file) logToFile(session_id, from, from_id, utf8_text);
  603. session->mNumUnread++;
  604. //update count of unread messages from real participant
  605. if (!(from_id.isNull() || from_id == gAgentID || SYSTEM_FROM == from))
  606. {
  607. ++(session->mParticipantUnreadMessageCount);
  608. }
  609. return session;
  610. }
  611. const std::string& LLIMModel::getName(const LLUUID& session_id) const
  612. {
  613. LLIMSession* session = findIMSession(session_id);
  614. if (!session) 
  615. {
  616. llwarns << "session " << session_id << "does not exist " << llendl;
  617. return NO_SESSION;
  618. }
  619. return session->mName;
  620. }
  621. const S32 LLIMModel::getNumUnread(const LLUUID& session_id) const
  622. {
  623. LLIMSession* session = findIMSession(session_id);
  624. if (!session)
  625. {
  626. llwarns << "session " << session_id << "does not exist " << llendl;
  627. return -1;
  628. }
  629. return session->mNumUnread;
  630. }
  631. const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const
  632. {
  633. LLIMSession* session = findIMSession(session_id);
  634. if (!session)
  635. {
  636. llwarns << "session " << session_id << "does not exist " << llendl;
  637. return LLUUID::null;
  638. }
  639. return session->mOtherParticipantID;
  640. }
  641. EInstantMessage LLIMModel::getType(const LLUUID& session_id) const
  642. {
  643. LLIMSession* session = findIMSession(session_id);
  644. if (!session)
  645. {
  646. llwarns << "session " << session_id << "does not exist " << llendl;
  647. return IM_COUNT;
  648. }
  649. return session->mType;
  650. }
  651. LLVoiceChannel* LLIMModel::getVoiceChannel( const LLUUID& session_id ) const
  652. {
  653. LLIMSession* session = findIMSession(session_id);
  654. if (!session)
  655. {
  656. llwarns << "session " << session_id << "does not exist " << llendl;
  657. return NULL;
  658. }
  659. return session->mVoiceChannel;
  660. }
  661. LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const
  662. {
  663. LLIMSession* session = findIMSession(session_id);
  664. if (!session)
  665. {
  666. llwarns << "session " << session_id << " does not exist " << llendl;
  667. return NULL;
  668. }
  669. return session->mSpeakers;
  670. }
  671. const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const
  672. {
  673. LLIMSession* session = findIMSession(session_id);
  674. if (!session)
  675. {
  676. llwarns << "session " << session_id << " does not exist " << llendl;
  677. return LLStringUtil::null;
  678. }
  679. return session->mHistoryFileName;
  680. }
  681. // TODO get rid of other participant ID
  682. void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) 
  683. {
  684. std::string name;
  685. LLAgentUI::buildFullname(name);
  686. pack_instant_message(
  687. gMessageSystem,
  688. gAgent.getID(),
  689. FALSE,
  690. gAgent.getSessionID(),
  691. other_participant_id,
  692. name,
  693. std::string("typing"),
  694. IM_ONLINE,
  695. (typing ? IM_TYPING_START : IM_TYPING_STOP),
  696. session_id);
  697. gAgent.sendReliableMessage();
  698. }
  699. void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id)
  700. {
  701. if(session_id.notNull())
  702. {
  703. std::string name;
  704. LLAgentUI::buildFullname(name);
  705. pack_instant_message(
  706. gMessageSystem,
  707. gAgent.getID(),
  708. FALSE,
  709. gAgent.getSessionID(),
  710. other_participant_id,
  711. name, 
  712. LLStringUtil::null,
  713. IM_ONLINE,
  714. IM_SESSION_LEAVE,
  715. session_id);
  716. gAgent.sendReliableMessage();
  717. }
  718. }
  719. //*TODO this method is better be moved to the LLIMMgr
  720. void LLIMModel::sendMessage(const std::string& utf8_text,
  721.  const LLUUID& im_session_id,
  722.  const LLUUID& other_participant_id,
  723.  EInstantMessage dialog)
  724. {
  725. std::string name;
  726. bool sent = false;
  727. LLAgentUI::buildFullname(name);
  728. const LLRelationship* info = NULL;
  729. info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
  730. U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
  731. if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
  732. {
  733. // User is online through the OOW connector, but not with a regular viewer.  Try to send the message via SLVoice.
  734. sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text);
  735. }
  736. if(!sent)
  737. {
  738. // Send message normally.
  739. // default to IM_SESSION_SEND unless it's nothing special - in
  740. // which case it's probably an IM to everyone.
  741. U8 new_dialog = dialog;
  742. if ( dialog != IM_NOTHING_SPECIAL )
  743. {
  744. new_dialog = IM_SESSION_SEND;
  745. }
  746. pack_instant_message(
  747. gMessageSystem,
  748. gAgent.getID(),
  749. FALSE,
  750. gAgent.getSessionID(),
  751. other_participant_id,
  752. name.c_str(),
  753. utf8_text.c_str(),
  754. offline,
  755. (EInstantMessage)new_dialog,
  756. im_session_id);
  757. gAgent.sendReliableMessage();
  758. }
  759. // If there is a mute list and this is not a group chat...
  760. if ( LLMuteList::getInstance() )
  761. {
  762. // ... the target should not be in our mute list for some message types.
  763. // Auto-remove them if present.
  764. switch( dialog )
  765. {
  766. case IM_NOTHING_SPECIAL:
  767. case IM_GROUP_INVITATION:
  768. case IM_INVENTORY_OFFERED:
  769. case IM_SESSION_INVITE:
  770. case IM_SESSION_P2P_INVITE:
  771. case IM_SESSION_CONFERENCE_START:
  772. case IM_SESSION_SEND: // This one is marginal - erring on the side of hearing.
  773. case IM_LURE_USER:
  774. case IM_GODLIKE_LURE_USER:
  775. case IM_FRIENDSHIP_OFFERED:
  776. LLMuteList::getInstance()->autoRemove(other_participant_id, LLMuteList::AR_IM);
  777. break;
  778. default: ; // do nothing
  779. }
  780. }
  781. if((dialog == IM_NOTHING_SPECIAL) && 
  782.    (other_participant_id.notNull()))
  783. {
  784. // Do we have to replace the /me's here?
  785. std::string from;
  786. LLAgentUI::buildFullname(from);
  787. LLIMModel::getInstance()->addMessage(im_session_id, from, gAgentID, utf8_text);
  788. //local echo for the legacy communicate panel
  789. std::string history_echo;
  790. LLAgentUI::buildFullname(history_echo);
  791. history_echo += ": " + utf8_text;
  792. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
  793. if (speaker_mgr)
  794. {
  795. speaker_mgr->speakerChatted(gAgentID);
  796. speaker_mgr->setSpeakerTyping(gAgentID, FALSE);
  797. }
  798. }
  799. // Add the recipient to the recent people list.
  800. LLRecentPeople::instance().add(other_participant_id);
  801. }
  802. void session_starter_helper(
  803. const LLUUID& temp_session_id,
  804. const LLUUID& other_participant_id,
  805. EInstantMessage im_type)
  806. {
  807. LLMessageSystem *msg = gMessageSystem;
  808. msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
  809. msg->nextBlockFast(_PREHASH_AgentData);
  810. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  811. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  812. msg->nextBlockFast(_PREHASH_MessageBlock);
  813. msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
  814. msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id);
  815. msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
  816. msg->addU8Fast(_PREHASH_Dialog, im_type);
  817. msg->addUUIDFast(_PREHASH_ID, temp_session_id);
  818. msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
  819. std::string name;
  820. LLAgentUI::buildFullname(name);
  821. msg->addStringFast(_PREHASH_FromAgentName, name);
  822. msg->addStringFast(_PREHASH_Message, LLStringUtil::null);
  823. msg->addU32Fast(_PREHASH_ParentEstateID, 0);
  824. msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
  825. msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
  826. }
  827. void start_deprecated_conference_chat(
  828. const LLUUID& temp_session_id,
  829. const LLUUID& creator_id,
  830. const LLUUID& other_participant_id,
  831. const LLSD& agents_to_invite)
  832. {
  833. U8* bucket;
  834. U8* pos;
  835. S32 count;
  836. S32 bucket_size;
  837. // *FIX: this could suffer from endian issues
  838. count = agents_to_invite.size();
  839. bucket_size = UUID_BYTES * count;
  840. bucket = new U8[bucket_size];
  841. pos = bucket;
  842. for(S32 i = 0; i < count; ++i)
  843. {
  844. LLUUID agent_id = agents_to_invite[i].asUUID();
  845. memcpy(pos, &agent_id, UUID_BYTES);
  846. pos += UUID_BYTES;
  847. }
  848. session_starter_helper(
  849. temp_session_id,
  850. other_participant_id,
  851. IM_SESSION_CONFERENCE_START);
  852. gMessageSystem->addBinaryDataFast(
  853. _PREHASH_BinaryBucket,
  854. bucket,
  855. bucket_size);
  856. gAgent.sendReliableMessage();
  857.  
  858. delete[] bucket;
  859. }
  860. class LLStartConferenceChatResponder : public LLHTTPClient::Responder
  861. {
  862. public:
  863. LLStartConferenceChatResponder(
  864. const LLUUID& temp_session_id,
  865. const LLUUID& creator_id,
  866. const LLUUID& other_participant_id,
  867. const LLSD& agents_to_invite)
  868. {
  869. mTempSessionID = temp_session_id;
  870. mCreatorID = creator_id;
  871. mOtherParticipantID = other_participant_id;
  872. mAgents = agents_to_invite;
  873. }
  874. virtual void error(U32 statusNum, const std::string& reason)
  875. {
  876. //try an "old school" way.
  877. if ( statusNum == 400 )
  878. {
  879. start_deprecated_conference_chat(
  880. mTempSessionID,
  881. mCreatorID,
  882. mOtherParticipantID,
  883. mAgents);
  884. }
  885. //else throw an error back to the client?
  886. //in theory we should have just have these error strings
  887. //etc. set up in this file as opposed to the IMMgr,
  888. //but the error string were unneeded here previously
  889. //and it is not worth the effort switching over all
  890. //the possible different language translations
  891. }
  892. private:
  893. LLUUID mTempSessionID;
  894. LLUUID mCreatorID;
  895. LLUUID mOtherParticipantID;
  896. LLSD mAgents;
  897. };
  898. // Returns true if any messages were sent, false otherwise.
  899. // Is sort of equivalent to "does the server need to do anything?"
  900. bool LLIMModel::sendStartSession(
  901. const LLUUID& temp_session_id,
  902. const LLUUID& other_participant_id,
  903. const std::vector<LLUUID>& ids,
  904. EInstantMessage dialog)
  905. {
  906. if ( dialog == IM_SESSION_GROUP_START )
  907. {
  908. session_starter_helper(
  909. temp_session_id,
  910. other_participant_id,
  911. dialog);
  912. gMessageSystem->addBinaryDataFast(
  913. _PREHASH_BinaryBucket,
  914. EMPTY_BINARY_BUCKET,
  915. EMPTY_BINARY_BUCKET_SIZE);
  916. gAgent.sendReliableMessage();
  917. return true;
  918. }
  919. else if ( dialog == IM_SESSION_CONFERENCE_START )
  920. {
  921. LLSD agents;
  922. for (int i = 0; i < (S32) ids.size(); i++)
  923. {
  924. agents.append(ids[i]);
  925. }
  926. //we have a new way of starting conference calls now
  927. LLViewerRegion* region = gAgent.getRegion();
  928. if (region)
  929. {
  930. std::string url = region->getCapability(
  931. "ChatSessionRequest");
  932. LLSD data;
  933. data["method"] = "start conference";
  934. data["session-id"] = temp_session_id;
  935. data["params"] = agents;
  936. LLHTTPClient::post(
  937. url,
  938. data,
  939. new LLStartConferenceChatResponder(
  940. temp_session_id,
  941. gAgent.getID(),
  942. other_participant_id,
  943. data["params"]));
  944. }
  945. else
  946. {
  947. start_deprecated_conference_chat(
  948. temp_session_id,
  949. gAgent.getID(),
  950. other_participant_id,
  951. agents);
  952. }
  953. //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id)
  954. return true;
  955. }
  956. return false;
  957. }
  958. //
  959. // Helper Functions
  960. //
  961. class LLViewerChatterBoxInvitationAcceptResponder :
  962. public LLHTTPClient::Responder
  963. {
  964. public:
  965. LLViewerChatterBoxInvitationAcceptResponder(
  966. const LLUUID& session_id,
  967. LLIMMgr::EInvitationType invitation_type)
  968. {
  969. mSessionID = session_id;
  970. mInvitiationType = invitation_type;
  971. }
  972. void result(const LLSD& content)
  973. {
  974. if ( gIMMgr)
  975. {
  976. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
  977. if (speaker_mgr)
  978. {
  979. //we've accepted our invitation
  980. //and received a list of agents that were
  981. //currently in the session when the reply was sent
  982. //to us.  Now, it is possible that there were some agents
  983. //to slip in/out between when that message was sent to us
  984. //and now.
  985. //the agent list updates we've received have been
  986. //accurate from the time we were added to the session
  987. //but unfortunately, our base that we are receiving here
  988. //may not be the most up to date.  It was accurate at
  989. //some point in time though.
  990. speaker_mgr->setSpeakers(content);
  991. //we now have our base of users in the session
  992. //that was accurate at some point, but maybe not now
  993. //so now we apply all of the udpates we've received
  994. //in case of race conditions
  995. speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID));
  996. }
  997. if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType)
  998. {
  999. gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL);
  1000. }
  1001. if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE 
  1002. || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE)
  1003. && LLIMModel::getInstance()->findIMSession(mSessionID))
  1004. {
  1005. // TODO remove in 2010, for voice calls we do not open an IM window
  1006. //LLIMFloater::show(mSessionID);
  1007. }
  1008. gIMMgr->clearPendingAgentListUpdates(mSessionID);
  1009. gIMMgr->clearPendingInvitation(mSessionID);
  1010. }
  1011. }
  1012. void error(U32 statusNum, const std::string& reason)
  1013. {
  1014. //throw something back to the viewer here?
  1015. if ( gIMMgr )
  1016. {
  1017. gIMMgr->clearPendingAgentListUpdates(mSessionID);
  1018. gIMMgr->clearPendingInvitation(mSessionID);
  1019. if ( 404 == statusNum )
  1020. {
  1021. std::string error_string;
  1022. error_string = "session_does_not_exist_error";
  1023. gIMMgr->showSessionStartError(error_string, mSessionID);
  1024. }
  1025. }
  1026. }
  1027. private:
  1028. LLUUID mSessionID;
  1029. LLIMMgr::EInvitationType mInvitiationType;
  1030. };
  1031. // the other_participant_id is either an agent_id, a group_id, or an inventory
  1032. // folder item_id (collection of calling cards)
  1033. // static
  1034. LLUUID LLIMMgr::computeSessionID(
  1035. EInstantMessage dialog,
  1036. const LLUUID& other_participant_id)
  1037. {
  1038. LLUUID session_id;
  1039. if (IM_SESSION_GROUP_START == dialog)
  1040. {
  1041. // slam group session_id to the group_id (other_participant_id)
  1042. session_id = other_participant_id;
  1043. }
  1044. else if (IM_SESSION_CONFERENCE_START == dialog)
  1045. {
  1046. session_id.generate();
  1047. }
  1048. else if (IM_SESSION_INVITE == dialog)
  1049. {
  1050. // use provided session id for invites
  1051. session_id = other_participant_id;
  1052. }
  1053. else
  1054. {
  1055. LLUUID agent_id = gAgent.getID();
  1056. if (other_participant_id == agent_id)
  1057. {
  1058. // if we try to send an IM to ourselves then the XOR would be null
  1059. // so we just make the session_id the same as the agent_id
  1060. session_id = agent_id;
  1061. }
  1062. else
  1063. {
  1064. // peer-to-peer or peer-to-asset session_id is the XOR
  1065. session_id = other_participant_id ^ agent_id;
  1066. }
  1067. }
  1068. return session_id;
  1069. }
  1070. void
  1071. LLIMMgr::showSessionStartError(
  1072. const std::string& error_string,
  1073. const LLUUID session_id)
  1074. {
  1075. if (!hasSession(session_id)) return;
  1076. LLSD args;
  1077. args["REASON"] = LLTrans::getString(error_string);
  1078. args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id);
  1079. LLSD payload;
  1080. payload["session_id"] = session_id;
  1081. LLNotificationsUtil::add(
  1082. "ChatterBoxSessionStartError",
  1083. args,
  1084. payload,
  1085. LLIMMgr::onConfirmForceCloseError);
  1086. }
  1087. void
  1088. LLIMMgr::showSessionEventError(
  1089. const std::string& event_string,
  1090. const std::string& error_string,
  1091. const LLUUID session_id)
  1092. {
  1093. LLSD args;
  1094. LLStringUtil::format_map_t event_args;
  1095. event_args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id);
  1096. args["REASON"] =
  1097. LLTrans::getString(error_string);
  1098. args["EVENT"] =
  1099. LLTrans::getString(event_string, event_args);
  1100. LLNotificationsUtil::add(
  1101. "ChatterBoxSessionEventError",
  1102. args);
  1103. }
  1104. void
  1105. LLIMMgr::showSessionForceClose(
  1106. const std::string& reason_string,
  1107. const LLUUID session_id)
  1108. {
  1109. if (!hasSession(session_id)) return;
  1110. LLSD args;
  1111. args["NAME"] = LLIMModel::getInstance()->getName(session_id);
  1112. args["REASON"] = LLTrans::getString(reason_string);
  1113. LLSD payload;
  1114. payload["session_id"] = session_id;
  1115. LLNotificationsUtil::add(
  1116. "ForceCloseChatterBoxSession",
  1117. args,
  1118. payload,
  1119. LLIMMgr::onConfirmForceCloseError);
  1120. }
  1121. //static
  1122. bool
  1123. LLIMMgr::onConfirmForceCloseError(
  1124. const LLSD& notification,
  1125. const LLSD& response)
  1126. {
  1127. //only 1 option really
  1128. LLUUID session_id = notification["payload"]["session_id"];
  1129. LLFloater* floater = LLIMFloater::findInstance(session_id);
  1130. if ( floater )
  1131. {
  1132. floater->closeFloater(FALSE);
  1133. }
  1134. return false;
  1135. }
  1136. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1137. // Class LLCallDialogManager
  1138. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1139. LLCallDialogManager::LLCallDialogManager()
  1140. {
  1141. }
  1142. LLCallDialogManager::~LLCallDialogManager()
  1143. {
  1144. }
  1145. void LLCallDialogManager::initClass()
  1146. {
  1147. LLVoiceChannel::setCurrentVoiceChannelChangedCallback(LLCallDialogManager::onVoiceChannelChanged);
  1148. }
  1149. void LLCallDialogManager::onVoiceChannelChanged(const LLUUID &session_id)
  1150. {
  1151. LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
  1152. if(!session)
  1153. {
  1154. sPreviousSessionlName = sCurrentSessionlName;
  1155. sCurrentSessionlName = ""; // Empty string results in "Nearby Voice Chat" after substitution
  1156. return;
  1157. }
  1158. if (sSession)
  1159. {
  1160. // store previous session type to process Avaline calls in dialogs
  1161. sPreviousSessionType = sSession->mSessionType;
  1162. }
  1163. sSession = session;
  1164. sSession->mVoiceChannel->setStateChangedCallback(boost::bind(LLCallDialogManager::onVoiceChannelStateChanged, _1, _2, _3, _4));
  1165. if(sCurrentSessionlName != session->mName)
  1166. {
  1167. sPreviousSessionlName = sCurrentSessionlName;
  1168. sCurrentSessionlName = session->mName;
  1169. }
  1170. if (LLVoiceChannel::getCurrentVoiceChannel()->getState() == LLVoiceChannel::STATE_CALL_STARTED &&
  1171. LLVoiceChannel::getCurrentVoiceChannel()->getCallDirection() == LLVoiceChannel::OUTGOING_CALL)
  1172. {
  1173. //*TODO get rid of duplicated code
  1174. LLSD mCallDialogPayload;
  1175. mCallDialogPayload["session_id"] = sSession->mSessionID;
  1176. mCallDialogPayload["session_name"] = sSession->mName;
  1177. mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID;
  1178. mCallDialogPayload["old_channel_name"] = sPreviousSessionlName;
  1179. mCallDialogPayload["old_session_type"] = sPreviousSessionType;
  1180. mCallDialogPayload["state"] = LLVoiceChannel::STATE_CALL_STARTED;
  1181. mCallDialogPayload["disconnected_channel_name"] = sSession->mName;
  1182. mCallDialogPayload["session_type"] = sSession->mSessionType;
  1183. LLOutgoingCallDialog* ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
  1184. if(ocd)
  1185. {
  1186. ocd->show(mCallDialogPayload);
  1187. }
  1188. }
  1189. }
  1190. void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent)
  1191. {
  1192. LLSD mCallDialogPayload;
  1193. LLOutgoingCallDialog* ocd = NULL;
  1194. if(sOldState == new_state)
  1195. {
  1196. return;
  1197. }
  1198. sOldState = new_state;
  1199. mCallDialogPayload["session_id"] = sSession->mSessionID;
  1200. mCallDialogPayload["session_name"] = sSession->mName;
  1201. mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID;
  1202. mCallDialogPayload["old_channel_name"] = sPreviousSessionlName;
  1203. mCallDialogPayload["old_session_type"] = sPreviousSessionType;
  1204. mCallDialogPayload["state"] = new_state;
  1205. mCallDialogPayload["disconnected_channel_name"] = sSession->mName;
  1206. mCallDialogPayload["session_type"] = sSession->mSessionType;
  1207. mCallDialogPayload["ended_by_agent"] = ended_by_agent;
  1208. switch(new_state)
  1209. {
  1210. case LLVoiceChannel::STATE_CALL_STARTED :
  1211. // do not show "Calling to..." if it is incoming call
  1212. if(direction == LLVoiceChannel::INCOMING_CALL)
  1213. {
  1214. return;
  1215. }
  1216. break;
  1217. case LLVoiceChannel::STATE_HUNG_UP:
  1218. // this state is coming before session is changed, so, put it into payload map
  1219. mCallDialogPayload["old_session_type"] = sSession->mSessionType;
  1220. break;
  1221. case LLVoiceChannel::STATE_CONNECTED :
  1222. ocd = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
  1223. if (ocd)
  1224. {
  1225. ocd->closeFloater();
  1226. }
  1227. return;
  1228. default:
  1229. break;
  1230. }
  1231. ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
  1232. if(ocd)
  1233. {
  1234. ocd->show(mCallDialogPayload);
  1235. }
  1236. }
  1237. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1238. // Class LLCallDialog
  1239. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1240. LLCallDialog::LLCallDialog(const LLSD& payload)
  1241. : LLDockableFloater(NULL, false, payload),
  1242.   mPayload(payload),
  1243.   mLifetime(DEFAULT_LIFETIME)
  1244. {
  1245. setAutoFocus(FALSE);
  1246. }
  1247. void LLCallDialog::getAllowedRect(LLRect& rect)
  1248. {
  1249. rect = gViewerWindow->getWorldViewRectScaled();
  1250. }
  1251. BOOL LLCallDialog::postBuild()
  1252. {
  1253. if (!LLDockableFloater::postBuild())
  1254. return FALSE;
  1255. // dock the dialog to the Speak Button, where other sys messages appear
  1256. LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_panel");
  1257. setDockControl(new LLDockControl(
  1258. anchor_panel, this,
  1259. getDockTongue(), LLDockControl::TOP,
  1260. boost::bind(&LLCallDialog::getAllowedRect, this, _1)));
  1261. return TRUE;
  1262. }
  1263. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1264. // Class LLOutgoingCallDialog
  1265. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1266. LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) :
  1267. LLCallDialog(payload)
  1268. {
  1269. LLOutgoingCallDialog* instance = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
  1270. if(instance && instance->getVisible())
  1271. {
  1272. instance->onCancel(instance);
  1273. }
  1274. }
  1275. void LLCallDialog::draw()
  1276. {
  1277. if (lifetimeHasExpired())
  1278. {
  1279. onLifetimeExpired();
  1280. }
  1281. if (getDockControl() != NULL)
  1282. {
  1283. LLDockableFloater::draw();
  1284. }
  1285. }
  1286. // virtual
  1287. void LLCallDialog::onOpen(const LLSD& key)
  1288. {
  1289. LLDockableFloater::onOpen(key);
  1290. // it should be over the all floaters. EXT-5116
  1291. gFloaterView->bringToFront(this);
  1292. }
  1293. void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id)
  1294. {
  1295. // *NOTE: 12/28/2009: check avaline calls: LLVoiceClient::isParticipantAvatar returns false for them
  1296. bool participant_is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
  1297. bool is_group = participant_is_avatar && gAgent.isInGroup(session_id);
  1298. LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon");
  1299. LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon");
  1300. avatar_icon->setVisible(!is_group);
  1301. group_icon->setVisible(is_group);
  1302. if (is_group)
  1303. {
  1304. group_icon->setValue(session_id);
  1305. }
  1306. else if (participant_is_avatar)
  1307. {
  1308. avatar_icon->setValue(participant_id);
  1309. }
  1310. else
  1311. {
  1312. avatar_icon->setValue("Avaline_Icon");
  1313. avatar_icon->setToolTip(std::string(""));
  1314. }
  1315. }
  1316. bool LLCallDialog::lifetimeHasExpired()
  1317. {
  1318. if (mLifetimeTimer.getStarted())
  1319. {
  1320. F32 elapsed_time = mLifetimeTimer.getElapsedTimeF32();
  1321. if (elapsed_time > mLifetime) 
  1322. {
  1323. return true;
  1324. }
  1325. }
  1326. return false;
  1327. }
  1328. void LLCallDialog::onLifetimeExpired()
  1329. {
  1330. mLifetimeTimer.stop();
  1331. closeFloater();
  1332. }
  1333. void LLOutgoingCallDialog::show(const LLSD& key)
  1334. {
  1335. mPayload = key;
  1336. //will be false only if voice in parcel is disabled and channel we leave is nearby(checked further)
  1337. bool show_oldchannel = LLViewerParcelMgr::getInstance()->allowAgentVoice();
  1338. // hide all text at first
  1339. hideAllText();
  1340. // init notification's lifetime
  1341. std::istringstream ss( getString("lifetime") );
  1342. if (!(ss >> mLifetime))
  1343. {
  1344. mLifetime = DEFAULT_LIFETIME;
  1345. }
  1346. // customize text strings
  1347. // tell the user which voice channel they are leaving
  1348. if (!mPayload["old_channel_name"].asString().empty())
  1349. {
  1350. bool was_avaline_call = LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["old_session_type"].asInteger();
  1351. std::string old_caller_name = mPayload["old_channel_name"].asString();
  1352. if (was_avaline_call)
  1353. {
  1354. old_caller_name = LLTextUtil::formatPhoneNumber(old_caller_name);
  1355. }
  1356. childSetTextArg("leaving", "[CURRENT_CHAT]", old_caller_name);
  1357. show_oldchannel = true;
  1358. }
  1359. else
  1360. {
  1361. childSetTextArg("leaving", "[CURRENT_CHAT]", getString("localchat"));
  1362. }
  1363. if (!mPayload["disconnected_channel_name"].asString().empty())
  1364. {
  1365. std::string channel_name = mPayload["disconnected_channel_name"].asString();
  1366. if (LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["session_type"].asInteger())
  1367. {
  1368. channel_name = LLTextUtil::formatPhoneNumber(channel_name);
  1369. }
  1370. childSetTextArg("nearby", "[VOICE_CHANNEL_NAME]", channel_name);
  1371. childSetTextArg("nearby_P2P_by_other", "[VOICE_CHANNEL_NAME]", mPayload["disconnected_channel_name"].asString());
  1372. // skipping "You will now be reconnected to nearby" in notification when call is ended by disabling voice,
  1373. // so no reconnection to nearby chat happens (EXT-4397)
  1374. bool voice_works = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking();
  1375. std::string reconnect_nearby = voice_works ? LLTrans::getString("reconnect_nearby") : std::string();
  1376. childSetTextArg("nearby", "[RECONNECT_NEARBY]", reconnect_nearby);
  1377. const std::string& nearby_str = mPayload["ended_by_agent"] ? NEARBY_P2P_BY_AGENT : NEARBY_P2P_BY_OTHER;
  1378. childSetTextArg(nearby_str, "[RECONNECT_NEARBY]", reconnect_nearby);
  1379. }
  1380. std::string callee_name = mPayload["session_name"].asString();
  1381. LLUUID session_id = mPayload["session_id"].asUUID();
  1382. bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
  1383. if (callee_name == "anonymous")
  1384. {
  1385. callee_name = getString("anonymous");
  1386. }
  1387. else if (!is_avatar)
  1388. {
  1389. callee_name = LLTextUtil::formatPhoneNumber(callee_name);
  1390. }
  1391. setTitle(callee_name);
  1392. LLSD callee_id = mPayload["other_user_id"];
  1393. childSetTextArg("calling", "[CALLEE_NAME]", callee_name);
  1394. childSetTextArg("connecting", "[CALLEE_NAME]", callee_name);
  1395. // for outgoing group calls callee_id == group id == session id
  1396. setIcon(callee_id, callee_id);
  1397. // stop timer by default
  1398. mLifetimeTimer.stop();
  1399. // show only necessary strings and controls
  1400. switch(mPayload["state"].asInteger())
  1401. {
  1402. case LLVoiceChannel::STATE_CALL_STARTED :
  1403. getChild<LLTextBox>("calling")->setVisible(true);
  1404. getChild<LLButton>("Cancel")->setVisible(true);
  1405. if(show_oldchannel)
  1406. {
  1407. getChild<LLTextBox>("leaving")->setVisible(true);
  1408. }
  1409. break;
  1410. case LLVoiceChannel::STATE_RINGING :
  1411. if(show_oldchannel)
  1412. {
  1413. getChild<LLTextBox>("leaving")->setVisible(true);
  1414. }
  1415. getChild<LLTextBox>("connecting")->setVisible(true);
  1416. break;
  1417. case LLVoiceChannel::STATE_ERROR :
  1418. getChild<LLTextBox>("noanswer")->setVisible(true);
  1419. getChild<LLButton>("Cancel")->setVisible(false);
  1420. setCanClose(true);
  1421. mLifetimeTimer.start();
  1422. break;
  1423. case LLVoiceChannel::STATE_HUNG_UP :
  1424. if (mPayload["session_type"].asInteger() == LLIMModel::LLIMSession::P2P_SESSION)
  1425. {
  1426. const std::string& nearby_str = mPayload["ended_by_agent"] ? NEARBY_P2P_BY_AGENT : NEARBY_P2P_BY_OTHER;
  1427. getChild<LLTextBox>(nearby_str)->setVisible(true);
  1428. else
  1429. {
  1430. getChild<LLTextBox>("nearby")->setVisible(true);
  1431. }
  1432. getChild<LLButton>("Cancel")->setVisible(false);
  1433. setCanClose(true);
  1434. mLifetimeTimer.start();
  1435. }
  1436. openFloater(LLOutgoingCallDialog::OCD_KEY);
  1437. }
  1438. void LLOutgoingCallDialog::hideAllText()
  1439. {
  1440. getChild<LLTextBox>("calling")->setVisible(false);
  1441. getChild<LLTextBox>("leaving")->setVisible(false);
  1442. getChild<LLTextBox>("connecting")->setVisible(false);
  1443. getChild<LLTextBox>("nearby_P2P_by_other")->setVisible(false);
  1444. getChild<LLTextBox>("nearby_P2P_by_agent")->setVisible(false);
  1445. getChild<LLTextBox>("nearby")->setVisible(false);
  1446. getChild<LLTextBox>("noanswer")->setVisible(false);
  1447. }
  1448. //static
  1449. void LLOutgoingCallDialog::onCancel(void* user_data)
  1450. {
  1451. LLOutgoingCallDialog* self = (LLOutgoingCallDialog*)user_data;
  1452. if (!gIMMgr)
  1453. return;
  1454. LLUUID session_id = self->mPayload["session_id"].asUUID();
  1455. gIMMgr->endCall(session_id);
  1456. self->closeFloater();
  1457. }
  1458. BOOL LLOutgoingCallDialog::postBuild()
  1459. {
  1460. BOOL success = LLCallDialog::postBuild();
  1461. childSetAction("Cancel", onCancel, this);
  1462. setCanDrag(FALSE);
  1463. return success;
  1464. }
  1465. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1466. // Class LLIncomingCallDialog
  1467. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1468. LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) :
  1469. LLCallDialog(payload)
  1470. {
  1471. }
  1472. void LLIncomingCallDialog::onLifetimeExpired()
  1473. {
  1474. // check whether a call is valid or not
  1475. if (LLVoiceClient::getInstance()->findSession(mPayload["caller_id"].asUUID()))
  1476. {
  1477. // restart notification's timer if call is still valid
  1478. mLifetimeTimer.start();
  1479. }
  1480. else
  1481. {
  1482. // close invitation if call is already not valid
  1483. mLifetimeTimer.stop();
  1484. LLUUID session_id = mPayload["session_id"].asUUID();
  1485. gIMMgr->clearPendingAgentListUpdates(session_id);
  1486. gIMMgr->clearPendingInvitation(session_id);
  1487. closeFloater();
  1488. }
  1489. }
  1490. BOOL LLIncomingCallDialog::postBuild()
  1491. {
  1492. LLCallDialog::postBuild();
  1493. LLUUID session_id = mPayload["session_id"].asUUID();
  1494. LLSD caller_id = mPayload["caller_id"];
  1495. std::string caller_name = mPayload["caller_name"].asString();
  1496. // init notification's lifetime
  1497. std::istringstream ss( getString("lifetime") );
  1498. if (!(ss >> mLifetime))
  1499. {
  1500. mLifetime = DEFAULT_LIFETIME;
  1501. }
  1502. std::string call_type;
  1503. if (gAgent.isInGroup(session_id))
  1504. {
  1505. LLStringUtil::format_map_t args;
  1506. LLGroupData data;
  1507. if (gAgent.getGroupData(session_id, data))
  1508. {
  1509. args["[GROUP]"] = data.mName;
  1510. call_type = getString(mPayload["notify_box_type"], args);
  1511. }
  1512. }
  1513. else
  1514. {
  1515. call_type = getString(mPayload["notify_box_type"]);
  1516. }
  1517. // check to see if this is an Avaline call
  1518. bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
  1519. childSetVisible("Start IM", is_avatar); // no IM for avaline
  1520. if (caller_name == "anonymous")
  1521. {
  1522. caller_name = getString("anonymous");
  1523. }
  1524. else if (!is_avatar)
  1525. {
  1526. caller_name = LLTextUtil::formatPhoneNumber(caller_name);
  1527. }
  1528. setTitle(caller_name + " " + call_type);
  1529. LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name");
  1530. caller_name_widget->setValue(caller_name + " " + call_type);
  1531. setIcon(session_id, caller_id);
  1532. childSetAction("Accept", onAccept, this);
  1533. childSetAction("Reject", onReject, this);
  1534. childSetAction("Start IM", onStartIM, this);
  1535. setDefaultBtn("Accept");
  1536. std::string notify_box_type = mPayload["notify_box_type"].asString();
  1537. if(notify_box_type != "VoiceInviteGroup" && notify_box_type != "VoiceInviteAdHoc")
  1538. {
  1539. // starting notification's timer for P2P and AVALINE invitations
  1540. mLifetimeTimer.start();
  1541. }
  1542. else
  1543. {
  1544. mLifetimeTimer.stop();
  1545. }
  1546. setCanDrag(FALSE);
  1547. return TRUE;
  1548. }
  1549. void LLIncomingCallDialog::onOpen(const LLSD& key)
  1550. {
  1551. LLCallDialog::onOpen(key);
  1552. // tell the user which voice channel they would be leaving
  1553. LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel();
  1554. if (voice && !voice->getSessionName().empty())
  1555. {
  1556. childSetTextArg("question", "[CURRENT_CHAT]", voice->getSessionName());
  1557. }
  1558. else
  1559. {
  1560. childSetTextArg("question", "[CURRENT_CHAT]", getString("localchat"));
  1561. }
  1562. }
  1563. //static
  1564. void LLIncomingCallDialog::onAccept(void* user_data)
  1565. {
  1566. LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
  1567. self->processCallResponse(0);
  1568. self->closeFloater();
  1569. }
  1570. //static
  1571. void LLIncomingCallDialog::onReject(void* user_data)
  1572. {
  1573. LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
  1574. self->processCallResponse(1);
  1575. self->closeFloater();
  1576. }
  1577. //static
  1578. void LLIncomingCallDialog::onStartIM(void* user_data)
  1579. {
  1580. LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
  1581. self->processCallResponse(2);
  1582. self->closeFloater();
  1583. }
  1584. void LLIncomingCallDialog::processCallResponse(S32 response)
  1585. {
  1586. if (!gIMMgr || gDisconnected)
  1587. return;
  1588. LLUUID session_id = mPayload["session_id"].asUUID();
  1589. LLUUID caller_id = mPayload["caller_id"].asUUID();
  1590. std::string session_name = mPayload["session_name"].asString();
  1591. EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger();
  1592. LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger();
  1593. bool voice = true;
  1594. switch(response)
  1595. {
  1596. case 2: // start IM: just don't start the voice chat
  1597. {
  1598. voice = false;
  1599. /* FALLTHROUGH */
  1600. }
  1601. case 0: // accept
  1602. {
  1603. if (type == IM_SESSION_P2P_INVITE)
  1604. {
  1605. // create a normal IM session
  1606. session_id = gIMMgr->addP2PSession(
  1607. session_name,
  1608. caller_id,
  1609. mPayload["session_handle"].asString(),
  1610. mPayload["session_uri"].asString());
  1611. if (voice)
  1612. {
  1613. gIMMgr->startCall(session_id, LLVoiceChannel::INCOMING_CALL);
  1614. }
  1615. gIMMgr->clearPendingAgentListUpdates(session_id);
  1616. gIMMgr->clearPendingInvitation(session_id);
  1617. }
  1618. else
  1619. {
  1620. //session name should not be empty, but it can contain spaces so we don't trim
  1621. std::string correct_session_name = session_name;
  1622. if (session_name.empty())
  1623. {
  1624. llwarns << "Received an empty session name from a server" << llendl;
  1625. switch(type){
  1626. case IM_SESSION_CONFERENCE_START:
  1627. case IM_SESSION_GROUP_START:
  1628. case IM_SESSION_INVITE:
  1629. if (gAgent.isInGroup(session_id))
  1630. {
  1631. LLGroupData data;
  1632. if (!gAgent.getGroupData(session_id, data)) break;
  1633. correct_session_name = data.mName;
  1634. }
  1635. else
  1636. {
  1637. if (gCacheName->getFullName(caller_id, correct_session_name))
  1638. {
  1639. correct_session_name.append(ADHOC_NAME_SUFFIX); 
  1640. }
  1641. }
  1642. llinfos << "Corrected session name is " << correct_session_name << llendl; 
  1643. break;
  1644. default: 
  1645. llwarning("Received an empty session name from a server and failed to generate a new proper session name", 0);
  1646. break;
  1647. }
  1648. }
  1649. LLUUID new_session_id = gIMMgr->addSession(correct_session_name, type, session_id, true);
  1650. std::string url = gAgent.getRegion()->getCapability(
  1651. "ChatSessionRequest");
  1652. if (voice)
  1653. {
  1654. LLSD data;
  1655. data["method"] = "accept invitation";
  1656. data["session-id"] = session_id;
  1657. LLHTTPClient::post(
  1658. url,
  1659. data,
  1660. new LLViewerChatterBoxInvitationAcceptResponder(
  1661. session_id,
  1662. inv_type));
  1663. // send notification message to the corresponding chat 
  1664. if (mPayload["notify_box_type"].asString() == "VoiceInviteGroup" || mPayload["notify_box_type"].asString() == "VoiceInviteAdHoc")
  1665. {
  1666. std::string started_call = LLTrans::getString("started_call");
  1667. std::string message = mPayload["caller_name"].asString() + " " + started_call;
  1668. LLIMModel::getInstance()->addMessageSilently(session_id, SYSTEM_FROM, LLUUID::null, message);
  1669. }
  1670. }
  1671. }
  1672. if (voice)
  1673. {
  1674. break;
  1675. }
  1676. }
  1677. case 1: // decline
  1678. {
  1679. if (type == IM_SESSION_P2P_INVITE)
  1680. {
  1681. if(gVoiceClient)
  1682. {
  1683. std::string s = mPayload["session_handle"].asString();
  1684. gVoiceClient->declineInvite(s);
  1685. }
  1686. }
  1687. else
  1688. {
  1689. std::string url = gAgent.getRegion()->getCapability(
  1690. "ChatSessionRequest");
  1691. LLSD data;
  1692. data["method"] = "decline invitation";
  1693. data["session-id"] = session_id;
  1694. LLHTTPClient::post(
  1695. url,
  1696. data,
  1697. NULL);
  1698. }
  1699. }
  1700. gIMMgr->clearPendingAgentListUpdates(session_id);
  1701. gIMMgr->clearPendingInvitation(session_id);
  1702. }
  1703. }
  1704. bool inviteUserResponse(const LLSD& notification, const LLSD& response)
  1705. {
  1706. if (!gIMMgr)
  1707. return false;
  1708. const LLSD& payload = notification["payload"];
  1709. LLUUID session_id = payload["session_id"].asUUID();
  1710. EInstantMessage type = (EInstantMessage)payload["type"].asInteger();
  1711. LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger();
  1712. S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
  1713. switch(option) 
  1714. {
  1715. case 0: // accept
  1716. {
  1717. if (type == IM_SESSION_P2P_INVITE)
  1718. {
  1719. // create a normal IM session
  1720. session_id = gIMMgr->addP2PSession(
  1721. payload["session_name"].asString(),
  1722. payload["caller_id"].asUUID(),
  1723. payload["session_handle"].asString(),
  1724. payload["session_uri"].asString());
  1725. gIMMgr->startCall(session_id);
  1726. gIMMgr->clearPendingAgentListUpdates(session_id);
  1727. gIMMgr->clearPendingInvitation(session_id);
  1728. }
  1729. else
  1730. {
  1731. LLUUID new_session_id = gIMMgr->addSession(
  1732. payload["session_name"].asString(),
  1733. type,
  1734. session_id, true);
  1735. std::string url = gAgent.getRegion()->getCapability(
  1736. "ChatSessionRequest");
  1737. LLSD data;
  1738. data["method"] = "accept invitation";
  1739. data["session-id"] = session_id;
  1740. LLHTTPClient::post(
  1741. url,
  1742. data,
  1743. new LLViewerChatterBoxInvitationAcceptResponder(
  1744. session_id,
  1745. inv_type));
  1746. }
  1747. }
  1748. break;
  1749. case 2: // mute (also implies ignore, so this falls through to the "ignore" case below)
  1750. {
  1751. // mute the sender of this invite
  1752. if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID()))
  1753. {
  1754. LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT);
  1755. LLMuteList::getInstance()->add(mute);
  1756. }
  1757. }
  1758. /* FALLTHROUGH */
  1759. case 1: // decline
  1760. {
  1761. if (type == IM_SESSION_P2P_INVITE)
  1762. {
  1763. if(gVoiceClient)
  1764. {
  1765. std::string s = payload["session_handle"].asString();
  1766. gVoiceClient->declineInvite(s);
  1767. }
  1768. }
  1769. else
  1770. {
  1771. std::string url = gAgent.getRegion()->getCapability(
  1772. "ChatSessionRequest");
  1773. LLSD data;
  1774. data["method"] = "decline invitation";
  1775. data["session-id"] = session_id;
  1776. LLHTTPClient::post(
  1777. url,
  1778. data,
  1779. NULL);
  1780. }
  1781. }
  1782. gIMMgr->clearPendingAgentListUpdates(session_id);
  1783. gIMMgr->clearPendingInvitation(session_id);
  1784. break;
  1785. }
  1786. return false;
  1787. }
  1788. //
  1789. // Member Functions
  1790. //
  1791. LLIMMgr::LLIMMgr()
  1792. {
  1793. mPendingInvitations = LLSD::emptyMap();
  1794. mPendingAgentListUpdates = LLSD::emptyMap();
  1795. LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1));
  1796. }
  1797. // Add a message to a session. 
  1798. void LLIMMgr::addMessage(
  1799. const LLUUID& session_id,
  1800. const LLUUID& target_id,
  1801. const std::string& from,
  1802. const std::string& msg,
  1803. const std::string& session_name,
  1804. EInstantMessage dialog,
  1805. U32 parent_estate_id,
  1806. const LLUUID& region_id,
  1807. const LLVector3& position,
  1808. bool link_name) // If this is true, then we insert the name and link it to a profile
  1809. {
  1810. LLUUID other_participant_id = target_id;
  1811. // don't process muted IMs
  1812. if (LLMuteList::getInstance()->isMuted(
  1813. other_participant_id,
  1814. LLMute::flagTextChat) && !LLMuteList::getInstance()->isLinden(from))
  1815. {
  1816. return;
  1817. }
  1818. LLUUID new_session_id = session_id;
  1819. if (new_session_id.isNull())
  1820. {
  1821. //no session ID...compute new one
  1822. new_session_id = computeSessionID(dialog, other_participant_id);
  1823. }
  1824. //*NOTE session_name is empty in case of incoming P2P sessions
  1825. std::string fixed_session_name = from;
  1826. if(!session_name.empty() && session_name.size()>1)
  1827. {
  1828. fixed_session_name = session_name;
  1829. }
  1830. bool new_session = !hasSession(new_session_id);
  1831. if (new_session)
  1832. {
  1833. LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id);
  1834. // When we get a new IM, and if you are a god, display a bit
  1835. // of information about the source. This is to help liaisons
  1836. // when answering questions.
  1837. if(gAgent.isGodlike())
  1838. {
  1839. // *TODO:translate (low priority, god ability)
  1840. std::ostringstream bonus_info;
  1841. bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " "
  1842. << parent_estate_id
  1843. << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "")
  1844. << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : "");
  1845. // once we have web-services (or something) which returns
  1846. // information about a region id, we can print this out
  1847. // and even have it link to map-teleport or something.
  1848. //<< "*** region_id: " << region_id << std::endl
  1849. //<< "*** position: " << position << std::endl;
  1850. LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str());
  1851. }
  1852. make_ui_sound("UISndNewIncomingIMSession");
  1853. }
  1854. LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg);
  1855. }
  1856. void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args)
  1857. {
  1858. LLUIString message;
  1859. // null session id means near me (chat history)
  1860. if (session_id.isNull())
  1861. {
  1862. message = LLTrans::getString(message_name);
  1863. message.setArgs(args);
  1864. LLChat chat(message);
  1865. chat.mSourceType = CHAT_SOURCE_SYSTEM;
  1866. LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD());
  1867. if(nearby_chat)
  1868. {
  1869. nearby_chat->addMessage(chat);
  1870. }
  1871. }
  1872. else // going to IM session
  1873. {
  1874. if (hasSession(session_id))
  1875. {
  1876. message = LLTrans::getString(message_name + "-im");
  1877. message.setArgs(args);
  1878. gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString());
  1879. }
  1880. }
  1881. }
  1882. S32 LLIMMgr::getNumberOfUnreadIM()
  1883. {
  1884. std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it;
  1885. S32 num = 0;
  1886. for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it)
  1887. {
  1888. num += (*it).second->mNumUnread;
  1889. }
  1890. return num;
  1891. }
  1892. S32 LLIMMgr::getNumberOfUnreadParticipantMessages()
  1893. {
  1894. std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it;
  1895. S32 num = 0;
  1896. for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it)
  1897. {
  1898. num += (*it).second->mParticipantUnreadMessageCount;
  1899. }
  1900. return num;
  1901. }
  1902. void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id)
  1903. {
  1904. LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(session_id);
  1905. if (!session) return;
  1906. if (session->mSessionInitialized)
  1907. {
  1908. startCall(session_id);
  1909. }
  1910. else
  1911. {
  1912. session->mStartCallOnInitialize = true;
  1913. }
  1914. }
  1915. LLUUID LLIMMgr::addP2PSession(const std::string& name,
  1916. const LLUUID& other_participant_id,
  1917. const std::string& voice_session_handle,
  1918. const std::string& caller_uri)
  1919. {
  1920. LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id, true);
  1921. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
  1922. if (speaker_mgr)
  1923. {
  1924. LLVoiceChannelP2P* voice_channel = dynamic_cast<LLVoiceChannelP2P*>(speaker_mgr->getVoiceChannel());
  1925. if (voice_channel)
  1926. {
  1927. voice_channel->setSessionHandle(voice_session_handle, caller_uri);
  1928. }
  1929. }
  1930. return session_id;
  1931. }
  1932. // This adds a session to the talk view. The name is the local name of
  1933. // the session, dialog specifies the type of session. If the session
  1934. // exists, it is brought forward.  Specifying id = NULL results in an
  1935. // im session to everyone. Returns the uuid of the session.
  1936. LLUUID LLIMMgr::addSession(
  1937. const std::string& name,
  1938. EInstantMessage dialog,
  1939. const LLUUID& other_participant_id, bool voice)
  1940. {
  1941. LLDynamicArray<LLUUID> ids;
  1942. ids.put(other_participant_id);
  1943. return addSession(name, dialog, other_participant_id, ids, voice);
  1944. }
  1945. // Adds a session using the given session_id.  If the session already exists 
  1946. // the dialog type is assumed correct. Returns the uuid of the session.
  1947. LLUUID LLIMMgr::addSession(
  1948. const std::string& name,
  1949. EInstantMessage dialog,
  1950. const LLUUID& other_participant_id,
  1951. const LLDynamicArray<LLUUID>& ids, bool voice)
  1952. {
  1953. if (0 == ids.getLength())
  1954. {
  1955. return LLUUID::null;
  1956. }
  1957. if (name.empty())
  1958. {
  1959. llwarning("Session name cannot be null!", 0);
  1960. return LLUUID::null;
  1961. }
  1962. LLUUID session_id = computeSessionID(dialog,other_participant_id);
  1963. bool new_session = !LLIMModel::getInstance()->findIMSession(session_id);
  1964. //works only for outgoing ad-hoc sessions
  1965. if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size())
  1966. {
  1967. LLIMModel::LLIMSession* ad_hoc_found = LLIMModel::getInstance()->findAdHocIMSession(ids);
  1968. if (ad_hoc_found)
  1969. {
  1970. new_session = false;
  1971. session_id = ad_hoc_found->mSessionID;
  1972. }
  1973. }
  1974. if (new_session)
  1975. {
  1976. LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice);
  1977. }
  1978. //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions
  1979. if (!new_session) return session_id;
  1980. //Per Plan's suggestion commented "explicit offline status warning" out to make Dessie happier (see EXT-3609)
  1981. //*TODO After February 2010 remove this commented out line if no one will be missing that warning
  1982. //noteOfflineUsers(session_id, floater, ids);
  1983. // Only warn for regular IMs - not group IMs
  1984. if( dialog == IM_NOTHING_SPECIAL )
  1985. {
  1986. noteMutedUsers(session_id, ids);
  1987. }
  1988. return session_id;
  1989. }
  1990. bool LLIMMgr::leaveSession(const LLUUID& session_id)
  1991. {
  1992. LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
  1993. if (!im_session) return false;
  1994. LLIMModel::getInstance()->sendLeaveSession(session_id, im_session->mOtherParticipantID);
  1995. gIMMgr->removeSession(session_id);
  1996. return true;
  1997. }
  1998. // Removes data associated with a particular session specified by session_id
  1999. void LLIMMgr::removeSession(const LLUUID& session_id)
  2000. {
  2001. llassert_always(hasSession(session_id));
  2002. clearPendingInvitation(session_id);
  2003. clearPendingAgentListUpdates(session_id);
  2004. LLIMModel::getInstance()->clearSession(session_id);
  2005. notifyObserverSessionRemoved(session_id);
  2006. }
  2007. void LLIMMgr::inviteToSession(
  2008. const LLUUID& session_id, 
  2009. const std::string& session_name, 
  2010. const LLUUID& caller_id, 
  2011. const std::string& caller_name,
  2012. EInstantMessage type,
  2013. EInvitationType inv_type,
  2014. const std::string& session_handle,
  2015. const std::string& session_uri)
  2016. {
  2017. //ignore invites from muted residents
  2018. if (LLMuteList::getInstance()->isMuted(caller_id))
  2019. {
  2020. return;
  2021. }
  2022. std::string notify_box_type;
  2023. BOOL ad_hoc_invite = FALSE;
  2024. if(type == IM_SESSION_P2P_INVITE)
  2025. {
  2026. //P2P is different...they only have voice invitations
  2027. notify_box_type = "VoiceInviteP2P";
  2028. }
  2029. else if ( gAgent.isInGroup(session_id) )
  2030. {
  2031. //only really old school groups have voice invitations
  2032. notify_box_type = "VoiceInviteGroup";
  2033. }
  2034. else if ( inv_type == INVITATION_TYPE_VOICE )
  2035. {
  2036. //else it's an ad-hoc
  2037. //and a voice ad-hoc
  2038. notify_box_type = "VoiceInviteAdHoc";
  2039. ad_hoc_invite = TRUE;
  2040. }
  2041. else if ( inv_type == INVITATION_TYPE_IMMEDIATE )
  2042. {
  2043. notify_box_type = "InviteAdHoc";
  2044. ad_hoc_invite = TRUE;
  2045. }
  2046. LLSD payload;
  2047. payload["session_id"] = session_id;
  2048. payload["session_name"] = session_name;
  2049. payload["caller_id"] = caller_id;
  2050. payload["caller_name"] = caller_name;
  2051. payload["type"] = type;
  2052. payload["inv_type"] = inv_type;
  2053. payload["session_handle"] = session_handle;
  2054. payload["session_uri"] = session_uri;
  2055. payload["notify_box_type"] = notify_box_type;
  2056. LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
  2057. if (channelp && channelp->callStarted())
  2058. {
  2059. // you have already started a call to the other user, so just accept the invite
  2060. LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 0);
  2061. return;
  2062. }
  2063. if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
  2064. {
  2065. // is the inviter a friend?
  2066. if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
  2067. {
  2068. // if not, and we are ignoring voice invites from non-friends
  2069. // then silently decline
  2070. if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
  2071. {
  2072. // invite not from a friend, so decline
  2073. LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1);
  2074. return;
  2075. }
  2076. }
  2077. }
  2078. if ( !mPendingInvitations.has(session_id.asString()) )
  2079. {
  2080. if (caller_name.empty())
  2081. {
  2082. gCacheName->get(caller_id, FALSE, boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3, _4));
  2083. }
  2084. else
  2085. {
  2086. LLFloaterReg::showInstance("incoming_call", payload, FALSE);
  2087. }
  2088. mPendingInvitations[session_id.asString()] = LLSD();
  2089. }
  2090. }
  2091. void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
  2092. {
  2093. payload["caller_name"] = first + " " + last;
  2094. payload["session_name"] = payload["caller_name"].asString();
  2095. std::string notify_box_type = payload["notify_box_type"].asString();
  2096. LLFloaterReg::showInstance("incoming_call", payload, FALSE);
  2097. }
  2098. //*TODO disconnects all sessions
  2099. void LLIMMgr::disconnectAllSessions()
  2100. {
  2101. //*TODO disconnects all IM sessions
  2102. }
  2103. BOOL LLIMMgr::hasSession(const LLUUID& session_id)
  2104. {
  2105. return LLIMModel::getInstance()->findIMSession(session_id) != NULL;
  2106. }
  2107. void LLIMMgr::clearPendingInvitation(const LLUUID& session_id)
  2108. {
  2109. if ( mPendingInvitations.has(session_id.asString()) )
  2110. {
  2111. mPendingInvitations.erase(session_id.asString());
  2112. }
  2113. }
  2114. void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body)
  2115. {
  2116. LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
  2117. if ( im_floater )
  2118. {
  2119. im_floater->processAgentListUpdates(body);
  2120. }
  2121. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
  2122. if (speaker_mgr)
  2123. {
  2124. speaker_mgr->updateSpeakers(body);
  2125. // also the same call is added into LLVoiceClient::participantUpdatedEvent because
  2126. // sometimes it is called AFTER LLViewerChatterBoxSessionAgentListUpdates::post()
  2127. // when moderation state changed too late. See EXT-3544.
  2128. speaker_mgr->update(true);
  2129. }
  2130. else
  2131. {
  2132. //we don't have a speaker manager yet..something went wrong
  2133. //we are probably receiving an update here before
  2134. //a start or an acceptance of an invitation.  Race condition.
  2135. gIMMgr->addPendingAgentListUpdates(
  2136. session_id,
  2137. body);
  2138. }
  2139. }
  2140. LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id)
  2141. {
  2142. if ( mPendingAgentListUpdates.has(session_id.asString()) )
  2143. {
  2144. return mPendingAgentListUpdates[session_id.asString()];
  2145. }
  2146. else
  2147. {
  2148. return LLSD();
  2149. }
  2150. }
  2151. void LLIMMgr::addPendingAgentListUpdates(
  2152. const LLUUID& session_id,
  2153. const LLSD& updates)
  2154. {
  2155. LLSD::map_const_iterator iter;
  2156. if ( !mPendingAgentListUpdates.has(session_id.asString()) )
  2157. {
  2158. //this is a new agent list update for this session
  2159. mPendingAgentListUpdates[session_id.asString()] = LLSD::emptyMap();
  2160. }
  2161. if (
  2162. updates.has("agent_updates") &&
  2163. updates["agent_updates"].isMap() &&
  2164. updates.has("updates") &&
  2165. updates["updates"].isMap() )
  2166. {
  2167. //new school update
  2168. LLSD update_types = LLSD::emptyArray();
  2169. LLSD::array_iterator array_iter;
  2170. update_types.append("agent_updates");
  2171. update_types.append("updates");
  2172. for (
  2173. array_iter = update_types.beginArray();
  2174. array_iter != update_types.endArray();
  2175. ++array_iter)
  2176. {
  2177. //we only want to include the last update for a given agent
  2178. for (
  2179. iter = updates[array_iter->asString()].beginMap();
  2180. iter != updates[array_iter->asString()].endMap();
  2181. ++iter)
  2182. {
  2183. mPendingAgentListUpdates[session_id.asString()][array_iter->asString()][iter->first] =
  2184. iter->second;
  2185. }
  2186. }
  2187. }
  2188. else if (
  2189. updates.has("updates") &&
  2190. updates["updates"].isMap() )
  2191. {
  2192. //old school update where the SD contained just mappings
  2193. //of agent_id -> "LEAVE"/"ENTER"
  2194. //only want to keep last update for each agent
  2195. for (
  2196. iter = updates["updates"].beginMap();
  2197. iter != updates["updates"].endMap();
  2198. ++iter)
  2199. {
  2200. mPendingAgentListUpdates[session_id.asString()]["updates"][iter->first] =
  2201. iter->second;
  2202. }
  2203. }
  2204. }
  2205. void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id)
  2206. {
  2207. if ( mPendingAgentListUpdates.has(session_id.asString()) )
  2208. {
  2209. mPendingAgentListUpdates.erase(session_id.asString());
  2210. }
  2211. }
  2212. void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
  2213. {
  2214. for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
  2215. {
  2216. (*it)->sessionAdded(session_id, name, other_participant_id);
  2217. }
  2218. }
  2219. void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id)
  2220. {
  2221. for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
  2222. {
  2223. (*it)->sessionRemoved(session_id);
  2224. }
  2225. }
  2226. void LLIMMgr::notifyObserverSessionIDUpdated( const LLUUID& old_session_id, const LLUUID& new_session_id )
  2227. {
  2228. for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
  2229. {
  2230. (*it)->sessionIDUpdated(old_session_id, new_session_id);
  2231. }
  2232. }
  2233. void LLIMMgr::addSessionObserver(LLIMSessionObserver *observer)
  2234. {
  2235. mSessionObservers.push_back(observer);
  2236. }
  2237. void LLIMMgr::removeSessionObserver(LLIMSessionObserver *observer)
  2238. {
  2239. mSessionObservers.remove(observer);
  2240. }
  2241. bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction)
  2242. {
  2243. LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
  2244. if (!voice_channel) return false;
  2245. voice_channel->setCallDirection(direction);
  2246. voice_channel->activate();
  2247. return true;
  2248. }
  2249. bool LLIMMgr::endCall(const LLUUID& session_id)
  2250. {
  2251. LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
  2252. if (!voice_channel) return false;
  2253. voice_channel->deactivate();
  2254. return true;
  2255. }
  2256. bool LLIMMgr::isVoiceCall(const LLUUID& session_id)
  2257. {
  2258. LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
  2259. if (!im_session) return false;
  2260. return im_session->mStartedAsIMCall;
  2261. }
  2262. void LLIMMgr::noteOfflineUsers(
  2263. const LLUUID& session_id,
  2264. const LLDynamicArray<LLUUID>& ids)
  2265. {
  2266. S32 count = ids.count();
  2267. if(count == 0)
  2268. {
  2269. const std::string& only_user = LLTrans::getString("only_user_message");
  2270. LLIMModel::getInstance()->addMessage(session_id, SYSTEM_FROM, LLUUID::null, only_user);
  2271. }
  2272. else
  2273. {
  2274. const LLRelationship* info = NULL;
  2275. LLAvatarTracker& at = LLAvatarTracker::instance();
  2276. LLIMModel& im_model = LLIMModel::instance();
  2277. for(S32 i = 0; i < count; ++i)
  2278. {
  2279. info = at.getBuddyInfo(ids.get(i));
  2280. std::string first, last;
  2281. if(info && !info->isOnline()
  2282.    && gCacheName->getName(ids.get(i), first, last))
  2283. {
  2284. LLUIString offline = LLTrans::getString("offline_message");
  2285. offline.setArg("[FIRST]", first);
  2286. offline.setArg("[LAST]", last);
  2287. im_model.proccessOnlineOfflineNotification(session_id, offline);
  2288. }
  2289. }
  2290. }
  2291. }
  2292. void LLIMMgr::noteMutedUsers(const LLUUID& session_id,
  2293.   const LLDynamicArray<LLUUID>& ids)
  2294. {
  2295. // Don't do this if we don't have a mute list.
  2296. LLMuteList *ml = LLMuteList::getInstance();
  2297. if( !ml )
  2298. {
  2299. return;
  2300. }
  2301. S32 count = ids.count();
  2302. if(count > 0)
  2303. {
  2304. LLIMModel* im_model = LLIMModel::getInstance();
  2305. for(S32 i = 0; i < count; ++i)
  2306. {
  2307. if( ml->isMuted(ids.get(i)) )
  2308. {
  2309. LLUIString muted = LLTrans::getString("muted_message");
  2310. im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, muted);
  2311. break;
  2312. }
  2313. }
  2314. }
  2315. }
  2316. void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info)
  2317. {
  2318. processIMTypingCore(im_info, TRUE);
  2319. }
  2320. void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
  2321. {
  2322. processIMTypingCore(im_info, FALSE);
  2323. }
  2324. void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
  2325. {
  2326. LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
  2327. LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
  2328. if ( im_floater )
  2329. {
  2330. im_floater->processIMTyping(im_info, typing);
  2331. }
  2332. }
  2333. class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
  2334. {
  2335. public:
  2336. virtual void describe(Description& desc) const
  2337. {
  2338. desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session");
  2339. desc.postAPI();
  2340. desc.input(
  2341. "{"client_session_id": UUID, "session_id": UUID, "success" boolean, "reason": string");
  2342. desc.source(__FILE__, __LINE__);
  2343. }
  2344. virtual void post(ResponsePtr response,
  2345.   const LLSD& context,
  2346.   const LLSD& input) const
  2347. {
  2348. LLSD body;
  2349. LLUUID temp_session_id;
  2350. LLUUID session_id;
  2351. bool success;
  2352. body = input["body"];
  2353. success = body["success"].asBoolean();
  2354. temp_session_id = body["temp_session_id"].asUUID();
  2355. if ( success )
  2356. {
  2357. session_id = body["session_id"].asUUID();
  2358. LLIMModel::getInstance()->processSessionInitializedReply(temp_session_id, session_id);
  2359. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
  2360. if (speaker_mgr)
  2361. {
  2362. speaker_mgr->setSpeakers(body);
  2363. speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id));
  2364. }
  2365. LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
  2366. if ( im_floater )
  2367. {
  2368. if ( body.has("session_info") )
  2369. {
  2370. im_floater->processSessionUpdate(body["session_info"]);
  2371. }
  2372. }
  2373. gIMMgr->clearPendingAgentListUpdates(session_id);
  2374. }
  2375. else
  2376. {
  2377. //throw an error dialog and close the temp session's floater
  2378. gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id);
  2379. }
  2380. gIMMgr->clearPendingAgentListUpdates(session_id);
  2381. }
  2382. };
  2383. class LLViewerChatterBoxSessionEventReply : public LLHTTPNode
  2384. {
  2385. public:
  2386. virtual void describe(Description& desc) const
  2387. {
  2388. desc.shortInfo("Used for receiving a reply to a ChatterBox session event");
  2389. desc.postAPI();
  2390. desc.input(
  2391. "{"event": string, "reason": string, "success": boolean, "session_id": UUID");
  2392. desc.source(__FILE__, __LINE__);
  2393. }
  2394. virtual void post(ResponsePtr response,
  2395.   const LLSD& context,
  2396.   const LLSD& input) const
  2397. {
  2398. LLUUID session_id;
  2399. bool success;
  2400. LLSD body = input["body"];
  2401. success = body["success"].asBoolean();
  2402. session_id = body["session_id"].asUUID();
  2403. if ( !success )
  2404. {
  2405. //throw an error dialog
  2406. gIMMgr->showSessionEventError(
  2407. body["event"].asString(),
  2408. body["error"].asString(),
  2409. session_id);
  2410. }
  2411. }
  2412. };
  2413. class LLViewerForceCloseChatterBoxSession: public LLHTTPNode
  2414. {
  2415. public:
  2416. virtual void post(ResponsePtr response,
  2417.   const LLSD& context,
  2418.   const LLSD& input) const
  2419. {
  2420. LLUUID session_id;
  2421. std::string reason;
  2422. session_id = input["body"]["session_id"].asUUID();
  2423. reason = input["body"]["reason"].asString();
  2424. gIMMgr->showSessionForceClose(reason, session_id);
  2425. }
  2426. };
  2427. class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
  2428. {
  2429. public:
  2430. virtual void post(
  2431. ResponsePtr responder,
  2432. const LLSD& context,
  2433. const LLSD& input) const
  2434. {
  2435. const LLUUID& session_id = input["body"]["session_id"].asUUID();
  2436. gIMMgr->processAgentListUpdates(session_id, input["body"]);
  2437. }
  2438. };
  2439. class LLViewerChatterBoxSessionUpdate : public LLHTTPNode
  2440. {
  2441. public:
  2442. virtual void post(
  2443. ResponsePtr responder,
  2444. const LLSD& context,
  2445. const LLSD& input) const
  2446. {
  2447. LLUUID session_id = input["body"]["session_id"].asUUID();
  2448. LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
  2449. if ( im_floater )
  2450. {
  2451. im_floater->processSessionUpdate(input["body"]["info"]);
  2452. }
  2453. LLIMSpeakerMgr* im_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
  2454. if (im_mgr)
  2455. {
  2456. im_mgr->processSessionUpdate(input["body"]["info"]);
  2457. }
  2458. }
  2459. };
  2460. class LLViewerChatterBoxInvitation : public LLHTTPNode
  2461. {
  2462. public:
  2463. virtual void post(
  2464. ResponsePtr response,
  2465. const LLSD& context,
  2466. const LLSD& input) const
  2467. {
  2468. //for backwards compatiblity reasons...we need to still
  2469. //check for 'text' or 'voice' invitations...bleh
  2470. if ( input["body"].has("instantmessage") )
  2471. {
  2472. LLSD message_params =
  2473. input["body"]["instantmessage"]["message_params"];
  2474. //do something here to have the IM invite behave
  2475. //just like a normal IM
  2476. //this is just replicated code from process_improved_im
  2477. //and should really go in it's own function -jwolk
  2478. if (gNoRender)
  2479. {
  2480. return;
  2481. }
  2482. LLChat chat;
  2483. std::string message = message_params["message"].asString();
  2484. std::string name = message_params["from_name"].asString();
  2485. LLUUID from_id = message_params["from_id"].asUUID();
  2486. LLUUID session_id = message_params["id"].asUUID();
  2487. std::vector<U8> bin_bucket = message_params["data"]["binary_bucket"].asBinary();
  2488. U8 offline = (U8)message_params["offline"].asInteger();
  2489. time_t timestamp =
  2490. (time_t) message_params["timestamp"].asInteger();
  2491. BOOL is_busy = gAgent.getBusy();
  2492. BOOL is_muted = LLMuteList::getInstance()->isMuted(
  2493. from_id,
  2494. name,
  2495. LLMute::flagTextChat);
  2496. BOOL is_linden = LLMuteList::getInstance()->isLinden(name);
  2497. std::string separator_string(": ");
  2498. chat.mMuted = is_muted && !is_linden;
  2499. chat.mFromID = from_id;
  2500. chat.mFromName = name;
  2501. if (!is_linden && (is_busy || is_muted))
  2502. {
  2503. return;
  2504. }
  2505. // standard message, not from system
  2506. std::string saved;
  2507. if(offline == IM_OFFLINE)
  2508. {
  2509. saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str());
  2510. }
  2511. std::string buffer = saved + message;
  2512. BOOL is_this_agent = FALSE;
  2513. if(from_id == gAgentID)
  2514. {
  2515. is_this_agent = TRUE;
  2516. }
  2517. gIMMgr->addMessage(
  2518. session_id,
  2519. from_id,
  2520. name,
  2521. buffer,
  2522. std::string((char*)&bin_bucket[0]),
  2523. IM_SESSION_INVITE,
  2524. message_params["parent_estate_id"].asInteger(),
  2525. message_params["region_id"].asUUID(),
  2526. ll_vector3_from_sd(message_params["position"]),
  2527. true);
  2528. //K now we want to accept the invitation
  2529. std::string url = gAgent.getRegion()->getCapability(
  2530. "ChatSessionRequest");
  2531. if ( url != "" )
  2532. {
  2533. LLSD data;
  2534. data["method"] = "accept invitation";
  2535. data["session-id"] = session_id;
  2536. LLHTTPClient::post(
  2537. url,
  2538. data,
  2539. new LLViewerChatterBoxInvitationAcceptResponder(
  2540. session_id,
  2541. LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE));
  2542. }
  2543. } //end if invitation has instant message
  2544. else if ( input["body"].has("voice") )
  2545. {
  2546. if (gNoRender)
  2547. {
  2548. return;
  2549. }
  2550. if(!LLVoiceClient::voiceEnabled())
  2551. {
  2552. // Don't display voice invites unless the user has voice enabled.
  2553. return;
  2554. }
  2555. gIMMgr->inviteToSession(
  2556. input["body"]["session_id"].asUUID(), 
  2557. input["body"]["session_name"].asString(), 
  2558. input["body"]["from_id"].asUUID(),
  2559. input["body"]["from_name"].asString(),
  2560. IM_SESSION_INVITE,
  2561. LLIMMgr::INVITATION_TYPE_VOICE);
  2562. }
  2563. else if ( input["body"].has("immediate") )
  2564. {
  2565. gIMMgr->inviteToSession(
  2566. input["body"]["session_id"].asUUID(), 
  2567. input["body"]["session_name"].asString(), 
  2568. input["body"]["from_id"].asUUID(),
  2569. input["body"]["from_name"].asString(),
  2570. IM_SESSION_INVITE,
  2571. LLIMMgr::INVITATION_TYPE_IMMEDIATE);
  2572. }
  2573. }
  2574. };
  2575. LLHTTPRegistration<LLViewerChatterBoxSessionStartReply>
  2576.    gHTTPRegistrationMessageChatterboxsessionstartreply(
  2577.    "/message/ChatterBoxSessionStartReply");
  2578. LLHTTPRegistration<LLViewerChatterBoxSessionEventReply>
  2579.    gHTTPRegistrationMessageChatterboxsessioneventreply(
  2580.    "/message/ChatterBoxSessionEventReply");
  2581. LLHTTPRegistration<LLViewerForceCloseChatterBoxSession>
  2582.     gHTTPRegistrationMessageForceclosechatterboxsession(
  2583. "/message/ForceCloseChatterBoxSession");
  2584. LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates>
  2585.     gHTTPRegistrationMessageChatterboxsessionagentlistupdates(
  2586.     "/message/ChatterBoxSessionAgentListUpdates");
  2587. LLHTTPRegistration<LLViewerChatterBoxSessionUpdate>
  2588.     gHTTPRegistrationMessageChatterBoxSessionUpdate(
  2589.     "/message/ChatterBoxSessionUpdate");
  2590. LLHTTPRegistration<LLViewerChatterBoxInvitation>
  2591.     gHTTPRegistrationMessageChatterBoxInvitation(
  2592. "/message/ChatterBoxInvitation");