00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llimview.h"
00035
00036 #include "llfontgl.h"
00037 #include "llrect.h"
00038 #include "llerror.h"
00039 #include "llbutton.h"
00040 #include "llhttpclient.h"
00041 #include "llsdutil.h"
00042 #include "llstring.h"
00043 #include "linked_lists.h"
00044 #include "llvieweruictrlfactory.h"
00045
00046 #include "llagent.h"
00047 #include "llcallingcard.h"
00048 #include "llchat.h"
00049 #include "llviewerwindow.h"
00050 #include "llresmgr.h"
00051 #include "llfloaterchat.h"
00052 #include "llfloaterchatterbox.h"
00053 #include "llfloaternewim.h"
00054 #include "llhttpnode.h"
00055 #include "llimpanel.h"
00056 #include "llresizebar.h"
00057 #include "lltabcontainer.h"
00058 #include "viewer.h"
00059 #include "llfloater.h"
00060 #include "llmutelist.h"
00061 #include "llresizehandle.h"
00062 #include "llkeyboard.h"
00063 #include "llui.h"
00064 #include "llviewermenu.h"
00065 #include "llcallingcard.h"
00066 #include "lltoolbar.h"
00067 #include "llviewermessage.h"
00068 #include "llnotify.h"
00069 #include "llviewerregion.h"
00070
00071 #include "llfirstuse.h"
00072
00073 const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
00074 const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
00075
00076
00077
00078
00079 LLIMMgr* gIMMgr = NULL;
00080
00081
00082
00083
00084
00085 static LLString sOnlyUserMessage;
00086 static LLUIString sOfflineMessage;
00087
00088 static std::map<std::string,LLString> sEventStringsMap;
00089 static std::map<std::string,LLString> sErrorStringsMap;
00090 static std::map<std::string,LLString> sForceCloseSessionMap;
00091 static LLUIString sInviteMessage;
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 LLUUID LLIMMgr::computeSessionID(
00108 EInstantMessage dialog,
00109 const LLUUID& other_participant_id)
00110 {
00111 LLUUID session_id;
00112 if (IM_SESSION_GROUP_START == dialog)
00113 {
00114
00115 session_id = other_participant_id;
00116 }
00117 else if (IM_SESSION_CONFERENCE_START == dialog)
00118 {
00119 session_id.generate();
00120 }
00121 else if (IM_SESSION_INVITE == dialog)
00122 {
00123
00124 session_id = other_participant_id;
00125 }
00126 else
00127 {
00128 LLUUID agent_id = gAgent.getID();
00129 if (other_participant_id == agent_id)
00130 {
00131
00132
00133 session_id = agent_id;
00134 }
00135 else
00136 {
00137
00138 session_id = other_participant_id ^ agent_id;
00139 }
00140 }
00141 return session_id;
00142 }
00143
00144
00145
00146
00147
00148 LLFloaterIM::LLFloaterIM()
00149 {
00150
00151
00152
00153
00154 this->mAutoResize = FALSE;
00155 gUICtrlFactory->buildFloater(this, "floater_im.xml");
00156 }
00157
00158 BOOL LLFloaterIM::postBuild()
00159 {
00160 sOnlyUserMessage = getFormattedUIString("only_user_message");
00161 sOfflineMessage = getUIString("offline_message");
00162
00163 sErrorStringsMap["generic"] =
00164 getFormattedUIString("generic_request_error");
00165 sErrorStringsMap["unverified"] =
00166 getFormattedUIString("insufficient_perms_error");
00167 sErrorStringsMap["no_user_911"] =
00168 getFormattedUIString("user_no_help");
00169
00170 sEventStringsMap["add"] =
00171 getFormattedUIString("add_session_event");
00172 sEventStringsMap["message"] =
00173 getFormattedUIString("message_session_event");
00174
00175 sForceCloseSessionMap["removed"] =
00176 getFormattedUIString("removed_from_group");
00177
00178 sInviteMessage = getUIString("invite_message");
00179 return TRUE;
00180 }
00181
00182
00183
00184
00185
00186
00187
00188 class LLIMViewFriendObserver : public LLFriendObserver
00189 {
00190 public:
00191 LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {}
00192 virtual ~LLIMViewFriendObserver() {}
00193 virtual void changed(U32 mask)
00194 {
00195 if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
00196 {
00197 mTV->refresh();
00198 }
00199 }
00200 protected:
00201 LLIMMgr* mTV;
00202 };
00203
00204
00205 class LLIMMgr::LLIMSessionInvite
00206 {
00207 public:
00208 LLIMSessionInvite(const LLUUID& session_id, const LLString& session_name, const LLUUID& caller_id,const LLString& caller_name, EInstantMessage type, const LLString& session_handle, const LLString& notify_box) :
00209 mSessionID(session_id),
00210 mSessionName(session_name),
00211 mCallerID(caller_id),
00212 mCallerName(caller_name),
00213 mType(type),
00214 mSessionHandle(session_handle),
00215 mNotifyBox(notify_box)
00216 {};
00217
00218 LLUUID mSessionID;
00219 LLString mSessionName;
00220 LLUUID mCallerID;
00221 LLString mCallerName;
00222 EInstantMessage mType;
00223 LLString mSessionHandle;
00224 LLString mNotifyBox;
00225 };
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235 EInstantMessage LLIMMgr::defaultIMTypeForAgent(const LLUUID& agent_id)
00236 {
00237 EInstantMessage type = IM_NOTHING_SPECIAL;
00238 if(is_agent_friend(agent_id))
00239 {
00240 if(LLAvatarTracker::instance().isBuddyOnline(agent_id))
00241 {
00242 type = IM_SESSION_CONFERENCE_START;
00243 }
00244 }
00245 return type;
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256 void LLIMMgr::toggle(void*)
00257 {
00258 static BOOL return_to_mouselook = FALSE;
00259
00260
00261 llassert( gIMMgr );
00262 BOOL old_state = gIMMgr->getFloaterOpen();
00263
00264
00265 if( gAgent.cameraMouselook() && old_state )
00266 {
00267 return_to_mouselook = TRUE;
00268 gAgent.changeCameraToDefault();
00269 return;
00270 }
00271
00272 BOOL new_state = !old_state;
00273
00274 if (new_state)
00275 {
00276
00277 if ( gAgent.cameraMouselook() )
00278 {
00279 return_to_mouselook = TRUE;
00280 gAgent.changeCameraToDefault();
00281 }
00282 }
00283 else
00284 {
00285
00286 if ( gAgent.cameraThirdPerson() && return_to_mouselook )
00287 {
00288 gAgent.changeCameraToMouselook();
00289 }
00290 return_to_mouselook = FALSE;
00291 }
00292
00293 gIMMgr->setFloaterOpen( new_state );
00294 }
00295
00296
00297
00298
00299
00300 LLIMMgr::LLIMMgr() :
00301 mFriendObserver(NULL),
00302 mIMReceived(FALSE)
00303 {
00304 mFriendObserver = new LLIMViewFriendObserver(this);
00305 LLAvatarTracker::instance().addObserver(mFriendObserver);
00306
00307
00308
00309 LLFloaterIM* dummy_floater = new LLFloaterIM();
00310 delete dummy_floater;
00311
00312 mPendingVoiceInvitations = LLSD::emptyMap();
00313 mPendingAgentListUpdates = LLSD::emptyMap();
00314 }
00315
00316 LLIMMgr::~LLIMMgr()
00317 {
00318 LLAvatarTracker::instance().removeObserver(mFriendObserver);
00319 delete mFriendObserver;
00320
00321 }
00322
00323
00324 void LLIMMgr::addMessage(
00325 const LLUUID& session_id,
00326 const LLUUID& target_id,
00327 const char* from,
00328 const char* msg,
00329 const char* session_name,
00330 EInstantMessage dialog,
00331 U32 parent_estate_id,
00332 const LLUUID& region_id,
00333 const LLVector3& position)
00334 {
00335 LLUUID other_participant_id = target_id;
00336 bool is_from_system = target_id.isNull();
00337
00338
00339 if (gMuteListp->isMuted(
00340 other_participant_id,
00341 LLMute::flagTextChat) && !gMuteListp->isLinden(from))
00342 {
00343 return;
00344 }
00345
00346
00347
00348 if( other_participant_id == gAgent.getID() )
00349 {
00350 other_participant_id = LLUUID::null;
00351 }
00352
00353 LLFloaterIMPanel* floater;
00354 LLUUID new_session_id = session_id;
00355 if (new_session_id.isNull())
00356 {
00357
00358 new_session_id = computeSessionID(dialog, other_participant_id);
00359 }
00360 floater = findFloaterBySession(new_session_id);
00361 if (!floater)
00362 {
00363 floater = findFloaterBySession(other_participant_id);
00364 if (floater)
00365 {
00366 llinfos << "found the IM session " << session_id
00367 << " by participant " << other_participant_id << llendl;
00368 }
00369 }
00370
00371
00372 if(!floater)
00373 {
00374 const char* name = from;
00375 if(session_name && (strlen(session_name)>1))
00376 {
00377 name = session_name;
00378 }
00379
00380
00381 floater = createFloater(
00382 new_session_id,
00383 other_participant_id,
00384 name,
00385 dialog,
00386 FALSE);
00387
00388
00389
00390
00391 if(gAgent.isGodlike())
00392 {
00393
00394 std::ostringstream bonus_info;
00395 bonus_info << "*** parent estate: "
00396 << parent_estate_id
00397 << ((parent_estate_id == 1) ? ", mainland" : "")
00398 << ((parent_estate_id == 5) ? ", teen" : "");
00399
00400
00401
00402
00403
00404
00405
00406 floater->addHistoryLine(bonus_info.str(), gSavedSettings.getColor4("SystemChatColor"));
00407 }
00408
00409 make_ui_sound("UISndNewIncomingIMSession");
00410 }
00411
00412
00413 if ( is_from_system )
00414 {
00415 floater->addHistoryLine(
00416 other_participant_id,
00417 msg,
00418 gSavedSettings.getColor4("SystemChatColor"));
00419 }
00420 else
00421 {
00422 floater->addHistoryLine(other_participant_id, msg);
00423 }
00424
00425 LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(LLSD());
00426
00427 if( !chat_floater->getVisible() && !floater->getVisible())
00428 {
00429
00430 LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater();
00431
00432
00433
00434 chat_floater->selectFloater(floater);
00435
00436
00437
00438 if ( previouslyActiveFloater && getIMReceived() )
00439 {
00440 chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE);
00441 }
00442
00443
00444 notifyNewIM();
00445 }
00446 }
00447
00448 void LLIMMgr::addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args)
00449 {
00450 LLUIString message;
00451
00452
00453 if (session_id.isNull())
00454 {
00455 LLFloaterChat* floaterp = LLFloaterChat::getInstance();
00456
00457 message = floaterp->getUIString(message_name);
00458 message.setArgList(args);
00459
00460 LLChat chat(message);
00461 chat.mSourceType = CHAT_SOURCE_SYSTEM;
00462 LLFloaterChat::getInstance()->addChatHistory(chat);
00463 }
00464 else
00465 {
00466 LLFloaterIMPanel* floaterp = findFloaterBySession(session_id);
00467 if (floaterp)
00468 {
00469 message = floaterp->getUIString(message_name);
00470 message.setArgList(args);
00471
00472 gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString().c_str());
00473 }
00474 }
00475 }
00476
00477 void LLIMMgr::notifyNewIM()
00478 {
00479 if(!gIMMgr->getFloaterOpen())
00480 {
00481 mIMReceived = TRUE;
00482 }
00483 }
00484
00485 void LLIMMgr::clearNewIMNotification()
00486 {
00487 mIMReceived = FALSE;
00488 }
00489
00490 BOOL LLIMMgr::getIMReceived() const
00491 {
00492 return mIMReceived;
00493 }
00494
00495
00496
00497 BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid)
00498 {
00499 LLFloaterIMPanel* floater = findFloaterBySession(uuid);
00500 if(floater) return TRUE;
00501 return FALSE;
00502 }
00503
00504 LLUUID LLIMMgr::addP2PSession(const std::string& name,
00505 const LLUUID& other_participant_id,
00506 const LLString& voice_session_handle)
00507 {
00508 LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
00509
00510 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
00511 if(floater)
00512 {
00513 LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel();
00514 voice_channelp->setSessionHandle(voice_session_handle);
00515 }
00516
00517 return session_id;
00518 }
00519
00520
00521
00522
00523
00524 LLUUID LLIMMgr::addSession(const std::string& name,
00525 EInstantMessage dialog,
00526 const LLUUID& other_participant_id)
00527 {
00528 LLUUID session_id = computeSessionID(dialog, other_participant_id);
00529
00530 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
00531 if(!floater)
00532 {
00533 LLDynamicArray<LLUUID> ids;
00534 ids.put(other_participant_id);
00535
00536 floater = createFloater(session_id,
00537 other_participant_id,
00538 name,
00539 ids,
00540 dialog,
00541 TRUE);
00542
00543 noteOfflineUsers(floater, ids);
00544 LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
00545 }
00546 else
00547 {
00548 floater->open();
00549 }
00550
00551 floater->setInputFocus(TRUE);
00552 return floater->getSessionID();
00553 }
00554
00555
00556
00557 LLUUID LLIMMgr::addSession(const std::string& name,
00558 EInstantMessage dialog,
00559 const LLUUID& other_participant_id,
00560 const LLDynamicArray<LLUUID>& ids)
00561 {
00562 if (0 == ids.getLength())
00563 {
00564 return LLUUID::null;
00565 }
00566
00567 LLUUID session_id = computeSessionID(dialog,
00568 other_participant_id);
00569
00570 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
00571 if(!floater)
00572 {
00573
00574
00575 floater = createFloater(session_id,
00576 other_participant_id,
00577 name,
00578 ids,
00579 dialog,
00580 TRUE);
00581
00582 if ( !floater ) return LLUUID::null;
00583
00584 noteOfflineUsers(floater, ids);
00585 }
00586 LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
00587
00588 floater->setInputFocus(TRUE);
00589 return floater->getSessionID();
00590 }
00591
00592
00593
00594 void LLIMMgr::removeSession(const LLUUID& session_id)
00595 {
00596 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
00597 if(floater)
00598 {
00599 mFloaters.erase(floater->getHandle());
00600 LLFloaterChatterBox::getInstance(LLSD())->removeFloater(floater);
00601
00602 }
00603 }
00604
00605 void LLIMMgr::inviteToSession(
00606 const LLUUID& session_id,
00607 const LLString& session_name,
00608 const LLUUID& caller_id,
00609 const LLString& caller_name,
00610 EInstantMessage type,
00611 const LLString& session_handle)
00612 {
00613
00614 if (gMuteListp->isMuted(caller_id))
00615 {
00616 return;
00617 }
00618
00619 LLString notify_box_type;
00620
00621 BOOL ad_hoc_invite = FALSE;
00622 if(type == IM_SESSION_P2P_INVITE)
00623 {
00624 notify_box_type = "VoiceInviteP2P";
00625 }
00626 else if (gAgent.isInGroup(session_id))
00627 {
00628 notify_box_type = "VoiceInviteGroup";
00629 }
00630 else
00631 {
00632 notify_box_type = "VoiceInviteAdHoc";
00633 ad_hoc_invite = TRUE;
00634 }
00635
00636 LLIMSessionInvite* invite = new LLIMSessionInvite(
00637 session_id,
00638 session_name,
00639 caller_id,
00640 caller_name,
00641 type,
00642 session_handle,
00643 notify_box_type);
00644
00645 LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
00646 if (channelp && channelp->callStarted())
00647 {
00648
00649 inviteUserResponse(0, invite);
00650 return;
00651 }
00652
00653 if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
00654 {
00655
00656 if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
00657 {
00658
00659
00660 if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
00661 {
00662
00663 inviteUserResponse(1, invite);
00664 return;
00665 }
00666 }
00667 }
00668
00669 if ( !mPendingVoiceInvitations.has(session_id.asString()) )
00670 {
00671 if (caller_name.empty())
00672 {
00673 gCacheName->getName(caller_id, onInviteNameLookup, invite);
00674 }
00675 else
00676 {
00677 LLString::format_map_t args;
00678 args["[NAME]"] = caller_name;
00679 args["[GROUP]"] = session_name;
00680
00681 LLNotifyBox::showXml(notify_box_type,
00682 args,
00683 inviteUserResponse,
00684 (void*)invite);
00685
00686 }
00687 mPendingVoiceInvitations[session_id.asString()] = LLSD();
00688 }
00689 }
00690
00691
00692 void LLIMMgr::onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata)
00693 {
00694 LLIMSessionInvite* invite = (LLIMSessionInvite*)userdata;
00695
00696 invite->mCallerName = llformat("%s %s", first, last);
00697 invite->mSessionName = invite->mCallerName;
00698
00699 LLString::format_map_t args;
00700 args["[NAME]"] = invite->mCallerName;
00701
00702 LLNotifyBox::showXml(invite->mNotifyBox,
00703 args,
00704 inviteUserResponse,
00705 (void*)invite);
00706 }
00707
00708 class LLViewerChatterBoxInvitationAcceptResponder :
00709 public LLHTTPClient::Responder
00710 {
00711 public:
00712 LLViewerChatterBoxInvitationAcceptResponder(
00713 const LLUUID& session_id,
00714 bool is_voice_invitation)
00715 {
00716 mSessionID = session_id;
00717 mIsVoiceInvitiation = is_voice_invitation;
00718 }
00719
00720 void result(const LLSD& content)
00721 {
00722 if ( gIMMgr)
00723 {
00724 LLFloaterIMPanel* floaterp =
00725 gIMMgr->findFloaterBySession(mSessionID);
00726
00727 if (floaterp)
00728 {
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 floaterp->setSpeakersList(content["agents"]);
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751 floaterp->updateSpeakersList(
00752 gIMMgr->getPendingAgentListUpdates(mSessionID));
00753
00754 if ( mIsVoiceInvitiation )
00755 {
00756 floaterp->requestAutoConnect();
00757 LLFloaterIMPanel::onClickStartCall(floaterp);
00758
00759 LLFloaterChatterBox::showInstance(TRUE);
00760 }
00761 }
00762
00763 gIMMgr->clearPendingAgentListUpdates(mSessionID);
00764 if ( mIsVoiceInvitiation )
00765 {
00766 gIMMgr->clearPendingVoiceInviation(mSessionID);
00767 }
00768 }
00769 }
00770
00771 void error(U32 statusNum, const std::string& reason)
00772 {
00773
00774 if ( gIMMgr && mIsVoiceInvitiation )
00775 {
00776 gIMMgr->clearPendingVoiceInviation(mSessionID);
00777 }
00778 }
00779
00780 private:
00781 LLUUID mSessionID;
00782 bool mIsVoiceInvitiation;
00783 };
00784
00785
00786 void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
00787 {
00788 LLIMSessionInvite* invitep = (LLIMSessionInvite*)user_data;
00789
00790 switch(option)
00791 {
00792 case 0:
00793 {
00794 if (invitep->mType == IM_SESSION_P2P_INVITE)
00795 {
00796
00797 invitep->mSessionID = gIMMgr->addP2PSession(
00798 invitep->mSessionName,
00799 invitep->mCallerID,
00800 invitep->mSessionHandle);
00801
00802 LLFloaterIMPanel* im_floater =
00803 gIMMgr->findFloaterBySession(
00804 invitep->mSessionID);
00805 if (im_floater)
00806 {
00807 im_floater->requestAutoConnect();
00808 LLFloaterIMPanel::onClickStartCall(im_floater);
00809
00810 LLFloaterChatterBox::showInstance(TRUE);
00811 }
00812
00813 gIMMgr->clearPendingVoiceInviation(invitep->mSessionID);
00814 }
00815 else
00816 {
00817 gIMMgr->addSession(
00818 invitep->mSessionName,
00819 invitep->mType,
00820 invitep->mSessionID);
00821
00822 std::string url = gAgent.getRegion()->getCapability(
00823 "ChatSessionRequest");
00824
00825 LLSD data;
00826 data["method"] = "accept invitation";
00827 data["session-id"] = invitep->mSessionID;
00828 LLHTTPClient::post(
00829 url,
00830 data,
00831 new LLViewerChatterBoxInvitationAcceptResponder(
00832 invitep->mSessionID,
00833 true));
00834 }
00835 }
00836 break;
00837 case 2:
00838 {
00839
00840 if (!gMuteListp->isMuted(invitep->mCallerID))
00841 {
00842 LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT);
00843 gMuteListp->add(mute);
00844 }
00845 }
00846
00847
00848 case 1:
00849 {
00850 if (invitep->mType == IM_SESSION_P2P_INVITE)
00851 {
00852 if(gVoiceClient)
00853 {
00854 gVoiceClient->declineInvite(invitep->mSessionHandle);
00855 }
00856 }
00857 }
00858 break;
00859 }
00860
00861 delete invitep;
00862 }
00863
00864 void LLIMMgr::refresh()
00865 {
00866 }
00867
00868 void LLIMMgr::setFloaterOpen(BOOL set_open)
00869 {
00870 if (set_open)
00871 {
00872 LLFloaterChatterBox::showInstance(LLSD());
00873 }
00874 else
00875 {
00876 LLFloaterChatterBox::hideInstance(LLSD());
00877 }
00878 }
00879
00880
00881 BOOL LLIMMgr::getFloaterOpen()
00882 {
00883 return LLFloaterChatterBox::instanceVisible(LLSD());
00884 }
00885
00886 void LLIMMgr::disconnectAllSessions()
00887 {
00888 LLFloaterIMPanel* floater = NULL;
00889 std::set<LLViewHandle>::iterator handle_it;
00890 for(handle_it = mFloaters.begin();
00891 handle_it != mFloaters.end();
00892 )
00893 {
00894 floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
00895
00896
00897 ++handle_it;
00898
00899 if (floater)
00900 {
00901 floater->setEnabled(FALSE);
00902 floater->close(TRUE);
00903 }
00904 }
00905 }
00906
00907
00908
00909
00910
00911 LLFloaterIMPanel* LLIMMgr::findFloaterBySession(const LLUUID& session_id)
00912 {
00913 LLFloaterIMPanel* rv = NULL;
00914 std::set<LLViewHandle>::iterator handle_it;
00915 for(handle_it = mFloaters.begin();
00916 handle_it != mFloaters.end();
00917 ++handle_it)
00918 {
00919 rv = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
00920 if(rv && session_id == rv->getSessionID())
00921 {
00922 break;
00923 }
00924 rv = NULL;
00925 }
00926 return rv;
00927 }
00928
00929
00930 BOOL LLIMMgr::hasSession(const LLUUID& session_id)
00931 {
00932 return (findFloaterBySession(session_id) != NULL);
00933 }
00934
00935 void LLIMMgr::clearPendingVoiceInviation(const LLUUID& session_id)
00936 {
00937 if ( mPendingVoiceInvitations.has(session_id.asString()) )
00938 {
00939 mPendingVoiceInvitations.erase(session_id.asString());
00940 }
00941 }
00942
00943 LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id)
00944 {
00945 if ( mPendingAgentListUpdates.has(session_id.asString()) )
00946 {
00947 return mPendingAgentListUpdates[session_id.asString()];
00948 }
00949 else
00950 {
00951 return LLSD();
00952 }
00953 }
00954
00955 void LLIMMgr::addPendingAgentListUpdates(
00956 const LLUUID& session_id,
00957 const LLSD& updates)
00958 {
00959 LLSD::map_const_iterator iter;
00960
00961 for ( iter = updates.beginMap();
00962 iter != updates.endMap();
00963 iter++)
00964 {
00965
00966 mPendingAgentListUpdates[session_id.asString()][iter->first] =
00967 iter->second;
00968 }
00969 }
00970
00971 void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id)
00972 {
00973 if ( mPendingAgentListUpdates.has(session_id.asString()) )
00974 {
00975 mPendingAgentListUpdates.erase(session_id.asString());
00976 }
00977 }
00978
00979
00980
00981
00982
00983 LLFloaterIMPanel* LLIMMgr::createFloater(
00984 const LLUUID& session_id,
00985 const LLUUID& other_participant_id,
00986 const std::string& session_label,
00987 EInstantMessage dialog,
00988 BOOL user_initiated)
00989 {
00990 if (session_id.isNull())
00991 {
00992 llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
00993 }
00994
00995 llinfos << "LLIMMgr::createFloater: from " << other_participant_id
00996 << " in session " << session_id << llendl;
00997 LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
00998 LLRect(),
00999 session_label,
01000 session_id,
01001 other_participant_id,
01002 dialog);
01003 LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
01004 LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
01005 mFloaters.insert(floater->getHandle());
01006 return floater;
01007 }
01008
01009 LLFloaterIMPanel* LLIMMgr::createFloater(
01010 const LLUUID& session_id,
01011 const LLUUID& other_participant_id,
01012 const std::string& session_label,
01013 const LLDynamicArray<LLUUID>& ids,
01014 EInstantMessage dialog,
01015 BOOL user_initiated)
01016 {
01017 if (session_id.isNull())
01018 {
01019 llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
01020 }
01021
01022 llinfos << "LLIMMgr::createFloater: from " << other_participant_id
01023 << " in session " << session_id << llendl;
01024 LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
01025 LLRect(),
01026 session_label,
01027 session_id,
01028 other_participant_id,
01029 ids,
01030 dialog);
01031 LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
01032 LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
01033 mFloaters.insert(floater->getHandle());
01034 return floater;
01035 }
01036
01037 void LLIMMgr::noteOfflineUsers(LLFloaterIMPanel* floater,
01038 const LLDynamicArray<LLUUID>& ids)
01039 {
01040 S32 count = ids.count();
01041 if(count == 0)
01042 {
01043 floater->addHistoryLine(sOnlyUserMessage, gSavedSettings.getColor4("SystemChatColor"));
01044 }
01045 else
01046 {
01047 const LLRelationship* info = NULL;
01048 LLAvatarTracker& at = LLAvatarTracker::instance();
01049 for(S32 i = 0; i < count; ++i)
01050 {
01051 info = at.getBuddyInfo(ids.get(i));
01052 char first[DB_FIRST_NAME_BUF_SIZE];
01053 char last[DB_LAST_NAME_BUF_SIZE];
01054 if(info && !info->isOnline()
01055 && gCacheName->getName(ids.get(i), first, last))
01056 {
01057 LLUIString offline = sOfflineMessage;
01058 offline.setArg("[FIRST]", first);
01059 offline.setArg("[LAST]", last);
01060 floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor"));
01061 }
01062 }
01063 }
01064 }
01065
01066 void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info)
01067 {
01068 processIMTypingCore(im_info, TRUE);
01069 }
01070
01071 void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
01072 {
01073 processIMTypingCore(im_info, FALSE);
01074 }
01075
01076 void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
01077 {
01078 LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
01079 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
01080 if (floater)
01081 {
01082 floater->processIMTyping(im_info, typing);
01083 }
01084 }
01085
01086 void LLIMMgr::updateFloaterSessionID(
01087 const LLUUID& old_session_id,
01088 const LLUUID& new_session_id)
01089 {
01090 LLFloaterIMPanel* floater = findFloaterBySession(old_session_id);
01091 if (floater)
01092 {
01093 floater->sessionInitReplyReceived(new_session_id);
01094 }
01095 }
01096
01097 LLFloaterChatterBox* LLIMMgr::getFloater()
01098 {
01099 return LLFloaterChatterBox::getInstance(LLSD());
01100 }
01101
01102 void onConfirmForceCloseError(S32 option, void* data)
01103 {
01104
01105 LLFloaterIMPanel* floater = ((LLFloaterIMPanel*) data);
01106
01107 if ( floater ) floater->close(FALSE);
01108 }
01109
01110 class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
01111 {
01112 public:
01113 virtual void describe(Description& desc) const
01114 {
01115 desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session");
01116 desc.postAPI();
01117 desc.input(
01118 "{\"client_session_id\": UUID, \"session_id\": UUID, \"success\" boolean, \"reason\": string");
01119 desc.source(__FILE__, __LINE__);
01120 }
01121
01122 virtual void post(ResponsePtr response,
01123 const LLSD& context,
01124 const LLSD& input) const
01125 {
01126 LLSD body;
01127 LLUUID temp_session_id;
01128 LLUUID session_id;
01129 bool success;
01130
01131 body = input["body"];
01132 success = body["success"].asBoolean();
01133 temp_session_id = body["temp_session_id"].asUUID();
01134
01135 if ( success )
01136 {
01137 session_id = body["session_id"].asUUID();
01138 gIMMgr->updateFloaterSessionID(
01139 temp_session_id,
01140 session_id);
01141 LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
01142 if (floaterp)
01143 {
01144 floaterp->setSpeakersList(body["agents"]);
01145
01146
01147 floaterp->updateSpeakersList(
01148 gIMMgr->getPendingAgentListUpdates(session_id));
01149 }
01150 gIMMgr->clearPendingAgentListUpdates(session_id);
01151 }
01152 else
01153 {
01154
01155
01156 LLFloaterIMPanel* floater =
01157 gIMMgr->findFloaterBySession(temp_session_id);
01158 if (floater)
01159 {
01160 LLString::format_map_t args;
01161 args["[REASON]"] =
01162 sErrorStringsMap[body["error"].asString()];
01163 args["[RECIPIENT]"] = floater->getTitle();
01164
01165 gViewerWindow->alertXml("ChatterBoxSessionStartError",
01166 args,
01167 onConfirmForceCloseError,
01168 floater);
01169
01170 }
01171 }
01172 }
01173 };
01174
01175 class LLViewerChatterBoxSessionEventReply : public LLHTTPNode
01176 {
01177 public:
01178 virtual void describe(Description& desc) const
01179 {
01180 desc.shortInfo("Used for receiving a reply to a ChatterBox session event");
01181 desc.postAPI();
01182 desc.input(
01183 "{\"event\": string, \"reason\": string, \"success\": boolean, \"session_id\": UUID");
01184 desc.source(__FILE__, __LINE__);
01185 }
01186
01187 virtual void post(ResponsePtr response,
01188 const LLSD& context,
01189 const LLSD& input) const
01190 {
01191 LLUUID session_id;
01192 bool success;
01193
01194 LLSD body = input["body"];
01195 success = body["success"].asBoolean();
01196 session_id = body["session_id"].asUUID();
01197
01198 if ( !success )
01199 {
01200
01201 LLFloaterIMPanel* floater =
01202 gIMMgr->findFloaterBySession(session_id);
01203 if (floater)
01204 {
01205 LLString::format_map_t args;
01206 args["[REASON]"] =
01207 sErrorStringsMap[body["error"].asString()];
01208 args["[EVENT]"] =
01209 sEventStringsMap[body["event"].asString()];
01210 args["[RECIPIENT]"] = floater->getTitle();
01211
01212 gViewerWindow->alertXml("ChatterBoxSessionEventError",
01213 args);
01214 }
01215 }
01216 }
01217 };
01218
01219 class LLViewerForceCloseChatterBoxSession: public LLHTTPNode
01220 {
01221 public:
01222 virtual void post(ResponsePtr response,
01223 const LLSD& context,
01224 const LLSD& input) const
01225 {
01226 LLUUID session_id;
01227 LLString reason;
01228
01229 session_id = input["body"]["session_id"].asUUID();
01230 reason = input["body"]["reason"].asString();
01231
01232 LLFloaterIMPanel* floater =
01233 gIMMgr ->findFloaterBySession(session_id);
01234
01235 if ( floater )
01236 {
01237 LLString::format_map_t args;
01238
01239 args["[NAME]"] = floater->getTitle();
01240 args["[REASON]"] = sForceCloseSessionMap[reason];
01241
01242 gViewerWindow->alertXml("ForceCloseChatterBoxSession",
01243 args,
01244 onConfirmForceCloseError,
01245 floater);
01246 }
01247 }
01248 };
01249
01250 class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
01251 {
01252 public:
01253 virtual void post(
01254 ResponsePtr responder,
01255 const LLSD& context,
01256 const LLSD& input) const
01257 {
01258 LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID());
01259 if (floaterp)
01260 {
01261 floaterp->updateSpeakersList(input["body"]["updates"]);
01262 }
01263 else
01264 {
01265
01266
01267
01268 gIMMgr->addPendingAgentListUpdates(
01269 input["body"]["session_id"].asUUID(),
01270 input["body"]["updates"]);
01271 }
01272 }
01273 };
01274
01275 class LLViewerChatterBoxInvitation : public LLHTTPNode
01276 {
01277 public:
01278
01279 virtual void post(
01280 ResponsePtr response,
01281 const LLSD& context,
01282 const LLSD& input) const
01283 {
01284 if ( input["body"].has("instantmessage") )
01285 {
01286 LLString capability = input["body"]["capabilities"]["call"].asString();
01287
01288 LLSD message_params =
01289 input["body"]["instantmessage"]["message_params"];
01290
01291
01292
01293
01294
01295 if (gNoRender)
01296 {
01297 return;
01298 }
01299 char buffer[DB_IM_MSG_BUF_SIZE * 2];
01300 LLChat chat;
01301
01302 std::string message = message_params["message"].asString();
01303 std::string name = message_params["from_name"].asString();
01304 LLUUID from_id = message_params["from_id"].asUUID();
01305 LLUUID session_id = message_params["id"].asUUID();
01306 std::vector<U8> bin_bucket = message_params["data"]["binary_bucket"].asBinary();
01307 U8 offline = (U8)message_params["offline"].asInteger();
01308
01309 time_t timestamp =
01310 (time_t) message_params["timestamp"].asInteger();
01311
01312 BOOL is_busy = gAgent.getBusy();
01313 BOOL is_muted = gMuteListp->isMuted(
01314 from_id,
01315 name.c_str(),
01316 LLMute::flagTextChat);
01317
01318 BOOL is_linden = gMuteListp->isLinden(
01319 name.c_str());
01320 char separator_string[3]=": ";
01321 int message_offset=0;
01322
01323
01324 if (!strncmp(message.c_str(), "/me ", 4) ||
01325 !strncmp(message.c_str(), "/me'", 4))
01326 {
01327 strcpy(separator_string,"");
01328 message_offset = 3;
01329 }
01330
01331 chat.mMuted = is_muted && !is_linden;
01332 chat.mFromID = from_id;
01333 chat.mFromName = name;
01334
01335 if (!is_linden && (is_busy || is_muted))
01336 {
01337 return;
01338 }
01339
01340
01341 char saved[MAX_STRING];
01342 saved[0] = '\0';
01343 if(offline == IM_OFFLINE)
01344 {
01345 char time_buf[TIME_STR_LENGTH];
01346 snprintf(saved,
01347 MAX_STRING,
01348 "(Saved %s) ",
01349 formatted_time(timestamp, time_buf));
01350 }
01351 snprintf(
01352 buffer,
01353 sizeof(buffer),
01354 "%s%s%s%s",
01355 name.c_str(),
01356 separator_string,
01357 saved,
01358 (message.c_str() + message_offset));
01359
01360 BOOL is_this_agent = FALSE;
01361 if(from_id == gAgentID)
01362 {
01363 is_this_agent = TRUE;
01364 }
01365 gIMMgr->addMessage(
01366 session_id,
01367 from_id,
01368 name.c_str(),
01369 buffer,
01370 (char*)&bin_bucket[0],
01371 IM_SESSION_INVITE,
01372 message_params["parent_estate_id"].asInteger(),
01373 message_params["region_id"].asUUID(),
01374 ll_vector3_from_sd(message_params["position"]));
01375
01376 snprintf(
01377 buffer,
01378 sizeof(buffer),
01379 "%s%s%s%s",
01380 name.c_str(),
01381 separator_string,
01382 saved,
01383 (message.c_str()+message_offset));
01384 chat.mText = buffer;
01385 chat.mFromIM = true;
01386 LLFloaterChat::addChat(chat, is_this_agent);
01387
01388
01389 std::string url = gAgent.getRegion()->getCapability(
01390 "ChatSessionRequest");
01391
01392 if ( url != "" )
01393 {
01394 LLSD data;
01395 data["method"] = "accept invitation";
01396 data["session-id"] = input["body"]["session_id"];
01397 LLHTTPClient::post(
01398 url,
01399 data,
01400 new LLViewerChatterBoxInvitationAcceptResponder(
01401 input["body"]["session_id"],
01402 false));
01403 }
01404 }
01405 else if ( input["body"].has("voice") )
01406 {
01407 if (gNoRender)
01408 {
01409 return;
01410 }
01411
01412 if(!LLVoiceClient::voiceEnabled())
01413 {
01414
01415 return;
01416 }
01417
01418 gIMMgr->inviteToSession(
01419 input["body"]["session_id"].asUUID(),
01420 input["body"]["session_name"].asString(),
01421 input["body"]["from_id"].asUUID(),
01422 input["body"]["from_name"].asString(),
01423 IM_SESSION_INVITE);
01424 }
01425 }
01426 };
01427
01428 LLHTTPRegistration<LLViewerChatterBoxSessionStartReply>
01429 gHTTPRegistrationMessageChatterboxsessionstartreply(
01430 "/message/ChatterBoxSessionStartReply");
01431
01432 LLHTTPRegistration<LLViewerChatterBoxSessionEventReply>
01433 gHTTPRegistrationMessageChatterboxsessioneventreply(
01434 "/message/ChatterBoxSessionEventReply");
01435
01436 LLHTTPRegistration<LLViewerForceCloseChatterBoxSession>
01437 gHTTPRegistrationMessageForceclosechatterboxsession(
01438 "/message/ForceCloseChatterBoxSession");
01439
01440 LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates>
01441 gHTTPRegistrationMessageChatterboxsessionagentlistupdates(
01442 "/message/ChatterBoxSessionAgentListUpdates");
01443
01444 LLHTTPRegistration<LLViewerChatterBoxInvitation>
01445 gHTTPRegistrationMessageChatterBoxInvitation(
01446 "/message/ChatterBoxInvitation");