diff options
Diffstat (limited to 'indra/newview/llviewermessage.cpp')
| -rw-r--r-- | indra/newview/llviewermessage.cpp | 4899 |
1 files changed, 3207 insertions, 1692 deletions
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 6bd81e76e9..9b1f2e67c6 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2,143 +2,118 @@ * @file llviewermessage.cpp * @brief Dumping ground for viewer-side message system callbacks. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - #include "llviewermessage.h" +#include "boost/lexical_cast.hpp" -#include <deque> - -#include "audioengine.h" -#include "audiosettings.h" -#include "indra_constants.h" +// Linden libraries +#include "llanimationstates.h" +#include "llaudioengine.h" +#include "llavataractions.h" +#include "llavatarnamecache.h" // IDEVO HACK #include "lscript_byteformat.h" -#include "mean_collision_data.h" -#include "llfloaterbump.h" -#include "llassetstorage.h" -#include "llcachename.h" -#include "llchat.h" -#include "lldbstrings.h" #include "lleconomy.h" -#include "llfilepicker.h" -#include "llfocusmgr.h" +#include "lleventtimer.h" +#include "llfloaterreg.h" #include "llfollowcamparams.h" -#include "llfloaterreleasemsg.h" -#include "llinstantmessage.h" -#include "llquantize.h" -#include "llregionflags.h" +#include "llinventorydefines.h" #include "llregionhandle.h" #include "llsdserialize.h" -#include "llstring.h" #include "llteleportflags.h" -#include "lltracker.h" #include "lltransactionflags.h" +#include "llvfile.h" +#include "llvfs.h" #include "llxfermanager.h" -#include "message.h" -#include "sound_ids.h" -#include "lltimer.h" -#include "llmd5.h" +#include "mean_collision_data.h" #include "llagent.h" +#include "llagentcamera.h" #include "llcallingcard.h" -#include "llconsole.h" -#include "llviewercontrol.h" -#include "lldrawpool.h" +#include "llbuycurrencyhtml.h" #include "llfirstuse.h" -#include "llfloateractivespeakers.h" -#include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" -#include "llfloaterchat.h" -#include "llfloatergroupinfo.h" -#include "llfloaterimagepreview.h" #include "llfloaterland.h" #include "llfloaterregioninfo.h" #include "llfloaterlandholdings.h" -#include "llfloatermap.h" -#include "llurldispatcher.h" -#include "llfloatermute.h" #include "llfloaterpostcard.h" #include "llfloaterpreference.h" -#include "llfloaterreleasemsg.h" -#include "llfollowcam.h" -#include "llgroupnotify.h" -#include "llhudeffect.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" -#include "llimpanel.h" -#include "llinventorymodel.h" -#include "llinventoryview.h" -#include "llmenugl.h" -#include "llmutelist.h" -#include "llnetmap.h" -#include "llnotify.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llinventorypanel.h" +#include "llnearbychat.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" #include "llpanelgrouplandmoney.h" +#include "llrecentpeople.h" +#include "llscriptfloater.h" #include "llselectmgr.h" +#include "llsidetray.h" #include "llstartup.h" #include "llsky.h" +#include "llslurl.h" #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" -#include "lltool.h" -#include "lltoolbar.h" -#include "lltoolmgr.h" -#include "llui.h" // for make_ui_sound -#include "lluploaddialog.h" -#include "llviewercamera.h" +#include "llspeakers.h" +#include "lltrans.h" +#include "lltranslate.h" +#include "llviewerfoldertype.h" +#include "llvoavatar.h" // IDEVO HACK +#include "lluri.h" #include "llviewergenericmessage.h" -#include "llviewerinventory.h" #include "llviewermenu.h" -#include "llviewerobject.h" +#include "llviewerjoystick.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" -#include "llviewerpartsource.h" -#include "llviewerregion.h" #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerthrottle.h" #include "llviewerwindow.h" #include "llvlmanager.h" -#include "llvoavatar.h" +#include "llvoavatarself.h" #include "llvotextbubble.h" -#include "llweb.h" #include "llworld.h" #include "pipeline.h" -#include "llappviewer.h" #include "llfloaterworldmap.h" -#include "llkeythrottle.h" #include "llviewerdisplay.h" #include "llkeythrottle.h" +#include "llgroupactions.h" +#include "llagentui.h" +#include "llpanelblockedlist.h" +#include "llpanelplaceprofile.h" -#include <boost/tokenizer.hpp> +#include <boost/algorithm/string/split.hpp> // +#include <boost/regex.hpp> -#if LL_WINDOWS // For Windows specific error handler -#include "llwindebug.h" // For the invalid message handler +#include "llnotificationmanager.h" // + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) #endif // @@ -156,9 +131,8 @@ static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL = 10.0f; // seconds extern BOOL gDebugClicks; // function prototypes -void open_offer(const std::vector<LLUUID>& items, const std::string& from_name); -void friendship_offer_callback(S32 option, void* user_data); bool check_offer_throttle(const std::string& from_name, bool check_only); +static void process_money_balance_reply_extended(LLMessageSystem* msg); //inventory offer throttle globals LLFrameTimer gThrottleTimer; @@ -166,7 +140,7 @@ const U32 OFFER_THROTTLE_MAX_COUNT=5; //number of items per time period const F32 OFFER_THROTTLE_TIME=10.f; //time period in seconds //script permissions -const LLString SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = +const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = { "ScriptTakeMoney", "ActOnControlInputs", @@ -181,14 +155,93 @@ const LLString SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = "ControlYourCamera" }; -struct LLFriendshipOffer +const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = { - LLUUID mFromID; - LLUUID mTransactionID; - BOOL mOnline; - LLHost mHost; + TRUE, // ScriptTakeMoney, + FALSE, // ActOnControlInputs + FALSE, // RemapControlInputs + FALSE, // AnimateYourAvatar + FALSE, // AttachToYourAvatar + FALSE, // ReleaseOwnership, + FALSE, // LinkAndDelink, + FALSE, // AddAndRemoveJoints + FALSE, // ChangePermissions + FALSE, // TrackYourCamera, + FALSE // ControlYourCamera }; +bool friendship_offer_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLMessageSystem* msg = gMessageSystem; + const LLSD& payload = notification["payload"]; + + // add friend to recent people list + LLRecentPeople::instance().add(payload["from_id"]); + + switch(option) + { + case 0: + { + // accept + LLAvatarTracker::formFriendship(payload["from_id"]); + + const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + + // This will also trigger an onlinenotification if the user is online + msg->newMessageFast(_PREHASH_AcceptFriendship); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, fid); + msg->sendReliable(LLHost(payload["sender"].asString())); + + LLSD payload = notification["payload"]; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("FriendshipAcceptedByMe", + notification["substitutions"], payload); + break; + } + case 1: // Decline + { + LLSD payload = notification["payload"]; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("FriendshipDeclinedByMe", + notification["substitutions"], payload); + } + // fall-through + case 2: // Send IM - decline and start IM session + { + // decline + // We no longer notify other viewers, but we DO still send + // the rejection to the simulator to delete the pending userop. + msg->newMessageFast(_PREHASH_DeclineFriendship); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); + msg->sendReliable(LLHost(payload["sender"].asString())); + + // start IM session + if(2 == option) + { + LLAvatarActions::startIM(payload["from_id"].asUUID()); + } + } + default: + // close button probably, possibly timed out + break; + } + + return false; +} +static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); +static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback); + //const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have " // "requested not to be disturbed. Your message will still be shown in their IM " // "panel for later viewing."; @@ -198,11 +251,11 @@ struct LLFriendshipOffer // void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group, - S32 trx_type, const LLString& desc) + S32 trx_type, const std::string& desc) { - if(0 == amount) return; + if(0 == amount || !region) return; amount = abs(amount); - llinfos << "give_money(" << uuid << "," << amount << ")"<< llendl; + LL_INFOS("Messaging") << "give_money(" << uuid << "," << amount << ")"<< LL_ENDL; if(can_afford_transaction(amount)) { // gStatusBar->debitBalance(amount); @@ -219,12 +272,14 @@ void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY); msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY); msg->addS32Fast(_PREHASH_TransactionType, trx_type ); - msg->addStringFast(_PREHASH_Description, desc.c_str()); + msg->addStringFast(_PREHASH_Description, desc); msg->sendReliable(region->getHost()); } else { - LLFloaterBuyCurrency::buyCurrency("Giving", amount); + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", amount); + LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("giving", args), amount ); } } @@ -242,7 +297,7 @@ void send_complete_agent_movement(const LLHost& sim_host) void process_logout_reply(LLMessageSystem* msg, void**) { // The server has told us it's ok to quit. - llinfos << "process_logout_reply" << llendl; + LL_DEBUGS("Messaging") << "process_logout_reply" << LL_ENDL; LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); @@ -250,7 +305,7 @@ void process_logout_reply(LLMessageSystem* msg, void**) msg->getUUID("AgentData", "SessionID", session_id); if((agent_id != gAgent.getID()) || (session_id != gAgent.getSessionID())) { - llwarns << "Bogus Logout Reply" << llendl; + LL_WARNS("Messaging") << "Bogus Logout Reply" << LL_ENDL; } LLInventoryModel::update_map_t parents; @@ -268,7 +323,7 @@ void process_logout_reply(LLMessageSystem* msg, void**) // We do not need to track the asset ids, just account for an // updated inventory version. - llinfos << "process_logout_reply itemID=" << item_id << llendl; + LL_INFOS("Messaging") << "process_logout_reply itemID=" << item_id << LL_ENDL; LLInventoryItem* item = gInventory.getItem( item_id ); if( item ) { @@ -277,7 +332,7 @@ void process_logout_reply(LLMessageSystem* msg, void**) } else { - llinfos << "process_logout_reply item not found: " << item_id << llendl; + LL_INFOS("Messaging") << "process_logout_reply item not found: " << item_id << LL_ENDL; } } LLAppViewer::instance()->forceQuit(); @@ -285,8 +340,7 @@ void process_logout_reply(LLMessageSystem* msg, void**) void process_layer_data(LLMessageSystem *mesgsys, void **user_data) { - if(!gWorldp) return; - LLViewerRegion *regionp = gWorldp->getRegion(mesgsys->getSender()); + LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); if (!regionp || gNoRender) { @@ -301,15 +355,15 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data) size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data); if (0 == size) { - llwarns << "Layer data has zero size." << llendl; + LL_WARNS("Messaging") << "Layer data has zero size." << LL_ENDL; return; } if (size < 0) { // getSizeFast() is probably trying to tell us about an error - llwarns << "getSizeFast() returned negative result: " + LL_WARNS("Messaging") << "getSizeFast() returned negative result: " << size - << llendl; + << LL_ENDL; return; } U8 *datap = new U8[size]; @@ -325,194 +379,191 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data) } } -S32 exported_object_count = 0; -S32 exported_image_count = 0; -S32 current_object_count = 0; -S32 current_image_count = 0; - -extern LLNotifyBox *gExporterNotify; -extern LLUUID gExporterRequestID; -extern LLString gExportDirectory; - -extern LLUploadDialog *gExportDialog; - -LLString gExportedFile; - -std::map<LLUUID, LLString> gImageChecksums; - -void export_complete() -{ - LLUploadDialog::modalUploadFinished(); - gExporterRequestID.setNull(); - gExportDirectory = ""; - - FILE* fXML = LLFile::fopen(gExportedFile.c_str(), "rb"); /* Flawfinder: ignore */ - fseek(fXML, 0, SEEK_END); - long length = ftell(fXML); - fseek(fXML, 0, SEEK_SET); - U8 *buffer = new U8[length + 1]; - size_t nread = fread(buffer, 1, length, fXML); - if (nread < (size_t) length) - { - llwarns << "Short read" << llendl; - } - buffer[nread] = '\0'; - fclose(fXML); - - char *pos = (char *)buffer; - while ((pos = strstr(pos+1, "<sl:image ")) != 0) - { - char *pos_check = strstr(pos, "checksum=\""); - - if (pos_check) - { - char *pos_uuid = strstr(pos_check, "\">"); - - if (pos_uuid) - { - char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ - memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ - image_uuid_str[UUID_STR_SIZE-1] = 0; +// S32 exported_object_count = 0; +// S32 exported_image_count = 0; +// S32 current_object_count = 0; +// S32 current_image_count = 0; + +// extern LLNotifyBox *gExporterNotify; +// extern LLUUID gExporterRequestID; +// extern std::string gExportDirectory; + +// extern LLUploadDialog *gExportDialog; + +// std::string gExportedFile; + +// std::map<LLUUID, std::string> gImageChecksums; + +// void export_complete() +// { +// LLUploadDialog::modalUploadFinished(); +// gExporterRequestID.setNull(); +// gExportDirectory = ""; + +// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */ +// fseek(fXML, 0, SEEK_END); +// long length = ftell(fXML); +// fseek(fXML, 0, SEEK_SET); +// U8 *buffer = new U8[length + 1]; +// size_t nread = fread(buffer, 1, length, fXML); +// if (nread < (size_t) length) +// { +// LL_WARNS("Messaging") << "Short read" << LL_ENDL; +// } +// buffer[nread] = '\0'; +// fclose(fXML); + +// char *pos = (char *)buffer; +// while ((pos = strstr(pos+1, "<sl:image ")) != 0) +// { +// char *pos_check = strstr(pos, "checksum=\""); + +// if (pos_check) +// { +// char *pos_uuid = strstr(pos_check, "\">"); + +// if (pos_uuid) +// { +// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ +// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ +// image_uuid_str[UUID_STR_SIZE-1] = 0; - LLUUID image_uuid(image_uuid_str); - - llinfos << "Found UUID: " << image_uuid << llendl; - - std::map<LLUUID, LLString>::iterator itor = gImageChecksums.find(image_uuid); - if (itor != gImageChecksums.end()) - { - llinfos << "Replacing with checksum: " << itor->second << llendl; - if (itor->second.c_str() != NULL) - { - memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ - } - } - } - } - } - - FILE* fXMLOut = LLFile::fopen(gExportedFile.c_str(), "wb"); /* Flawfinder: ignore */ - if (fwrite(buffer, 1, length, fXMLOut) != length) - { - llwarns << "Short write" << llendl; - } - fclose(fXMLOut); - - delete [] buffer; -} - - -void exported_item_complete(const LLTSCode status, void *user_data) -{ - //LLString *filename = (LLString *)user_data; - - if (status < LLTS_OK) - { - llinfos << "Export failed!" << llendl; - } - else - { - ++current_object_count; - if (current_image_count == exported_image_count && current_object_count == exported_object_count) - { - llinfos << "*** Export complete ***" << llendl; - - export_complete(); - } - else - { - gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); - } - } -} - -struct exported_image_info -{ - LLUUID image_id; - LLString filename; - U32 image_num; -}; - -void exported_j2c_complete(const LLTSCode status, void *user_data) -{ - exported_image_info *info = (exported_image_info *)user_data; - LLUUID image_id = info->image_id; - U32 image_num = info->image_num; - LLString filename = info->filename; - delete info; - - if (status < LLTS_OK) - { - llinfos << "Image download failed!" << llendl; - } - else - { - FILE* fIn = LLFile::fopen(filename.c_str(), "rb"); /* Flawfinder: ignore */ - if (fIn) - { - LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C; - LLPointer<LLImageTGA> TargaUtility = new LLImageTGA; - - fseek(fIn, 0, SEEK_END); - S32 length = ftell(fIn); - fseek(fIn, 0, SEEK_SET); - U8 *buffer = ImageUtility->allocateData(length); - if (fread(buffer, 1, length, fIn) != length) - { - llwarns << "Short read" << llendl; - } - fclose(fIn); - LLFile::remove(filename.c_str()); - - // Convert to TGA - LLPointer<LLImageRaw> image = new LLImageRaw(); - - ImageUtility->updateData(); - ImageUtility->decode(image, 100000.0f); +// LLUUID image_uuid(image_uuid_str); + +// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL; + +// std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid); +// if (itor != gImageChecksums.end()) +// { +// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL; +// if (!itor->second.empty()) +// { +// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ +// } +// } +// } +// } +// } + +// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */ +// if (fwrite(buffer, 1, length, fXMLOut) != length) +// { +// LL_WARNS("Messaging") << "Short write" << LL_ENDL; +// } +// fclose(fXMLOut); + +// delete [] buffer; +// } + + +// void exported_item_complete(const LLTSCode status, void *user_data) +// { +// //std::string *filename = (std::string *)user_data; + +// if (status < LLTS_OK) +// { +// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL; +// } +// else +// { +// ++current_object_count; +// if (current_image_count == exported_image_count && current_object_count == exported_object_count) +// { +// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL; + +// export_complete(); +// } +// else +// { +// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); +// } +// } +// } + +// struct exported_image_info +// { +// LLUUID image_id; +// std::string filename; +// U32 image_num; +// }; + +// void exported_j2c_complete(const LLTSCode status, void *user_data) +// { +// exported_image_info *info = (exported_image_info *)user_data; +// LLUUID image_id = info->image_id; +// U32 image_num = info->image_num; +// std::string filename = info->filename; +// delete info; + +// if (status < LLTS_OK) +// { +// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL; +// } +// else +// { +// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ +// if (fIn) +// { +// LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C; +// LLPointer<LLImageTGA> TargaUtility = new LLImageTGA; + +// fseek(fIn, 0, SEEK_END); +// S32 length = ftell(fIn); +// fseek(fIn, 0, SEEK_SET); +// U8 *buffer = ImageUtility->allocateData(length); +// if (fread(buffer, 1, length, fIn) != length) +// { +// LL_WARNS("Messaging") << "Short read" << LL_ENDL; +// } +// fclose(fIn); +// LLFile::remove(filename); + +// // Convert to TGA +// LLPointer<LLImageRaw> image = new LLImageRaw(); + +// ImageUtility->updateData(); +// ImageUtility->decode(image, 100000.0f); - TargaUtility->encode(image); - U8 *data = TargaUtility->getData(); - S32 data_size = TargaUtility->getDataSize(); - - char *file_path = new char[filename.size()+1]; - strcpy(file_path, filename.c_str()); /* Flawfinder: ignore */ - char *end = strrchr(file_path, gDirUtilp->getDirDelimiter()[0]); - end[0] = 0; - LLString output_file = llformat("%s/image-%03d.tga", file_path, image_num);//filename; - delete [] file_path; - //S32 name_len = output_file.length(); - //strcpy(&output_file[name_len-3], "tga"); - FILE* fOut = LLFile::fopen(output_file.c_str(), "wb"); /* Flawfinder: ignore */ - char md5_hash_string[33]; /* Flawfinder: ignore */ - strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ - if (fOut) - { - if (fwrite(data, 1, data_size, fOut) != data_size) - { - llwarns << "Short write" << llendl; - } - fseek(fOut, 0, SEEK_SET); - fclose(fOut); - fOut = LLFile::fopen(output_file.c_str(), "rb"); /* Flawfinder: ignore */ - LLMD5 my_md5_hash(fOut); - my_md5_hash.hex_digest(md5_hash_string); - } - - gImageChecksums.insert(std::pair<LLUUID, LLString>(image_id, md5_hash_string)); - } - } +// TargaUtility->encode(image); +// U8 *data = TargaUtility->getData(); +// S32 data_size = TargaUtility->getDataSize(); - ++current_image_count; - if (current_image_count == exported_image_count && current_object_count == exported_object_count) - { - llinfos << "*** Export textures complete ***" << llendl; - export_complete(); - } - else - { - gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); - } -} +// std::string file_path = gDirUtilp->getDirName(filename); + +// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename; +// //S32 name_len = output_file.length(); +// //strcpy(&output_file[name_len-3], "tga"); +// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */ +// char md5_hash_string[33]; /* Flawfinder: ignore */ +// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ +// if (fOut) +// { +// if (fwrite(data, 1, data_size, fOut) != data_size) +// { +// LL_WARNS("Messaging") << "Short write" << LL_ENDL; +// } +// fseek(fOut, 0, SEEK_SET); +// fclose(fOut); +// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */ +// LLMD5 my_md5_hash(fOut); +// my_md5_hash.hex_digest(md5_hash_string); +// } + +// gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string)); +// } +// } + +// ++current_image_count; +// if (current_image_count == exported_image_count && current_object_count == exported_object_count) +// { +// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL; +// export_complete(); +// } +// else +// { +// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); +// } +//} void process_derez_ack(LLMessageSystem*, void**) { @@ -534,15 +585,15 @@ void process_places_reply(LLMessageSystem* msg, void** data) } else { - llwarns << "Got invalid PlacesReply message" << llendl; + LL_WARNS("Messaging") << "Got invalid PlacesReply message" << LL_ENDL; } } void send_sound_trigger(const LLUUID& sound_id, F32 gain) { - if (sound_id.isNull()) + if (sound_id.isNull() || gAgent.getRegion() == NULL) { - // zero guids don't get sent (no sound) + // disconnected agent or zero guids don't get sent (no sound) return; } @@ -564,34 +615,31 @@ void send_sound_trigger(const LLUUID& sound_id, F32 gain) gAgent.sendMessage(); } -struct LLJoinGroupData -{ - LLUUID mGroupID; - LLUUID mTransactionID; - std::string mName; - std::string mMessage; - S32 mFee; -}; - -void join_group_callback(S32 option, void* user_data) +bool join_group_response(const LLSD& notification, const LLSD& response) { - LLJoinGroupData* data = (LLJoinGroupData*)user_data; + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); BOOL delete_context_data = TRUE; bool accept_invite = false; - if (option == 2 && data && !data->mGroupID.isNull()) + LLUUID group_id = notification["payload"]["group_id"].asUUID(); + LLUUID transaction_id = notification["payload"]["transaction_id"].asUUID(); + std::string name = notification["payload"]["name"].asString(); + std::string message = notification["payload"]["message"].asString(); + S32 fee = notification["payload"]["fee"].asInteger(); + + if (option == 2 && !group_id.isNull()) { - LLFloaterGroupInfo::showFromUUID(data->mGroupID); - LLString::format_map_t args; - args["[MESSAGE]"] = data->mMessage; - LLNotifyBox::showXml("JoinGroup", args, &join_group_callback, data); - return; + LLGroupActions::show(group_id); + LLSD args; + args["MESSAGE"] = message; + LLNotificationsUtil::add("JoinGroup", args, notification["payload"]); + return false; } - if(option == 0 && data && !data->mGroupID.isNull()) + if(option == 0 && !group_id.isNull()) { // check for promotion or demotion. S32 max_groups = MAX_AGENT_GROUPS; - if(gAgent.isInGroup(data->mGroupID)) ++max_groups; + if(gAgent.isInGroup(group_id)) ++max_groups; if(gAgent.mGroups.count() < max_groups) { @@ -600,10 +648,9 @@ void join_group_callback(S32 option, void* user_data) else { delete_context_data = FALSE; - LLString::format_map_t args; - args["[NAME]"] = data->mName; - args["[INVITE]"] = data->mMessage; - LLAlertDialog::showXml("JoinedTooManyGroupsMember", args, join_group_callback, (void*)data); + LLSD args; + args["NAME"] = name; + LLNotificationsUtil::add("JoinedTooManyGroupsMember", args, notification["payload"]); } } @@ -611,57 +658,117 @@ void join_group_callback(S32 option, void* user_data) { // If there is a fee to join this group, make // sure the user is sure they want to join. - if (data->mFee > 0) + if (fee > 0) { delete_context_data = FALSE; - LLString::format_map_t args; - args["[COST]"] = llformat("%d", data->mFee); - // Set the fee to 0, so that we don't keep + LLSD args; + args["COST"] = llformat("%d", fee); + // Set the fee for next time to 0, so that we don't keep // asking about a fee. - data->mFee = 0; - gViewerWindow->alertXml("JoinGroupCanAfford", + LLSD next_payload = notification["payload"]; + next_payload["fee"] = 0; + LLNotificationsUtil::add("JoinGroupCanAfford", args, - join_group_callback, - (void*)data); + next_payload); } else { - send_improved_im(data->mGroupID, - "name", - "message", + send_improved_im(group_id, + std::string("name"), + std::string("message"), IM_ONLINE, IM_GROUP_INVITATION_ACCEPT, - data->mTransactionID); + transaction_id); } } - else if (data) + else { - send_improved_im(data->mGroupID, - "name", - "message", + send_improved_im(group_id, + std::string("name"), + std::string("message"), IM_ONLINE, IM_GROUP_INVITATION_DECLINE, - data->mTransactionID); + transaction_id); } - if(delete_context_data) + return false; +} + +static void highlight_inventory_items_in_panel(const std::vector<LLUUID>& items, LLInventoryPanel *inventory_panel) +{ + if (NULL == inventory_panel) return; + + for (std::vector<LLUUID>::const_iterator item_iter = items.begin(); + item_iter != items.end(); + ++item_iter) { - delete data; - data = NULL; + const LLUUID& item_id = (*item_iter); + if(!highlight_offered_object(item_id)) + { + continue; + } + + LLInventoryItem* item = gInventory.getItem(item_id); + llassert(item); + if (!item) { + continue; + } + + LL_DEBUGS("Inventory_Move") << "Highlighting inventory item: " << item->getName() << ", " << item_id << LL_ENDL; + LLFolderView* fv = inventory_panel->getRootFolder(); + if (fv) + { + LLFolderViewItem* fv_item = fv->getItemByID(item_id); + if (fv_item) + { + LLFolderViewItem* fv_folder = fv_item->getParentFolder(); + if (fv_folder) + { + // Parent folders can be different in case of 2 consecutive drag and drop + // operations when the second one is started before the first one completes. + LL_DEBUGS("Inventory_Move") << "Open folder: " << fv_folder->getName() << LL_ENDL; + fv_folder->setOpen(TRUE); + if (fv_folder->isSelected()) + { + fv->changeSelection(fv_folder, FALSE); + } + } + fv->changeSelection(fv_item, TRUE); + } + } } } +static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response); +static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response); +static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response); + //----------------------------------------------------------------------------- // Instant Message //----------------------------------------------------------------------------- -class LLOpenAgentOffer : public LLInventoryFetchObserver +class LLOpenAgentOffer : public LLInventoryFetchItemsObserver { public: - LLOpenAgentOffer(const std::string& from_name) : mFromName(from_name) {} + LLOpenAgentOffer(const LLUUID& object_id, + const std::string& from_name) : + LLInventoryFetchItemsObserver(object_id), + mFromName(from_name) {} + /*virtual*/ void startFetch() + { + for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if (cat) + { + mComplete.push_back((*it)); + } + } + LLInventoryFetchItemsObserver::startFetch(); + } /*virtual*/ void done() { - open_offer(mComplete, mFromName); + open_inventory_offer(mComplete, mFromName); gInventory.removeObserver(this); delete this; } @@ -669,24 +776,176 @@ private: std::string mFromName; }; +/** + * Class to observe adding of new items moved from the world to user's inventory to select them in inventory. + * + * We can't create it each time items are moved because "drop" event is sent separately for each + * element even while multi-dragging. We have to have the only instance of the observer. See EXT-4347. + */ +class LLViewerInventoryMoveFromWorldObserver : public LLInventoryAddItemByAssetObserver +{ +public: + LLViewerInventoryMoveFromWorldObserver() + : LLInventoryAddItemByAssetObserver() + , mActivePanel(NULL) + { + + } + + void setMoveIntoFolderID(const LLUUID& into_folder_uuid) {mMoveIntoFolderID = into_folder_uuid; } + +private: + /*virtual */void onAssetAdded(const LLUUID& asset_id) + { + // Store active Inventory panel. + mActivePanel = LLInventoryPanel::getActiveInventoryPanel(); + + // Store selected items (without destination folder) + mSelectedItems.clear(); + if (mActivePanel) + { + mSelectedItems = mActivePanel->getRootFolder()->getSelectionList(); + } + mSelectedItems.erase(mMoveIntoFolderID); + } + + /** + * Selects added inventory items watched by their Asset UUIDs if selection was not changed since + * all items were started to watch (dropped into a folder). + */ + void done() + { + // if selection is not changed since watch started lets hightlight new items. + if (mActivePanel && !isSelectionChanged()) + { + LL_DEBUGS("Inventory_Move") << "Selecting new items..." << LL_ENDL; + mActivePanel->clearSelection(); + highlight_inventory_items_in_panel(mAddedItems, mActivePanel); + } + } + + /** + * Returns true if selected inventory items were changed since moved inventory items were started to watch. + */ + bool isSelectionChanged() + { + const LLInventoryPanel * const current_active_panel = LLInventoryPanel::getActiveInventoryPanel(); + + if (NULL == mActivePanel || current_active_panel != mActivePanel) + { + return true; + } + + // get selected items (without destination folder) + selected_items_t selected_items = mActivePanel->getRootFolder()->getSelectionList(); + selected_items.erase(mMoveIntoFolderID); + + // compare stored & current sets of selected items + selected_items_t different_items; + std::set_symmetric_difference(mSelectedItems.begin(), mSelectedItems.end(), + selected_items.begin(), selected_items.end(), std::inserter(different_items, different_items.begin())); + + LL_DEBUGS("Inventory_Move") << "Selected firstly: " << mSelectedItems.size() + << ", now: " << selected_items.size() << ", difference: " << different_items.size() << LL_ENDL; + + return different_items.size() > 0; + } + + LLInventoryPanel *mActivePanel; + typedef std::set<LLUUID> selected_items_t; + selected_items_t mSelectedItems; + + /** + * UUID of FolderViewFolder into which watched items are moved. + * + * Destination FolderViewFolder becomes selected while mouse hovering (when dragged items are dropped). + * + * If mouse is moved out it set unselected and number of selected items is changed + * even if selected items in Inventory stay the same. + * So, it is used to update stored selection list. + * + * @see onAssetAdded() + * @see isSelectionChanged() + */ + LLUUID mMoveIntoFolderID; +}; + +LLViewerInventoryMoveFromWorldObserver* gInventoryMoveObserver = NULL; + +void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid) +{ + start_new_inventory_observer(); + + gInventoryMoveObserver->setMoveIntoFolderID(into_folder_uuid); + gInventoryMoveObserver->watchAsset(inv_item->getAssetUUID()); +} + //unlike the FetchObserver for AgentOffer, we only make one //instance of the AddedObserver for TaskOffers //and it never dies. We do this because we don't know the UUID of //task offers until they are accepted, so we don't wouldn't -//know what to watch for, so instead we just watch for all additions. -Gigs +//know what to watch for, so instead we just watch for all additions. class LLOpenTaskOffer : public LLInventoryAddedObserver { protected: /*virtual*/ void done() { - open_offer(mAdded, ""); + for (uuid_vec_t::iterator it = mAdded.begin(); it != mAdded.end();) + { + const LLUUID& item_uuid = *it; + bool was_moved = false; + LLInventoryObject* added_object = gInventory.getObject(item_uuid); + if (added_object) + { + // cast to item to get Asset UUID + LLInventoryItem* added_item = dynamic_cast<LLInventoryItem*>(added_object); + if (added_item) + { + const LLUUID& asset_uuid = added_item->getAssetUUID(); + if (gInventoryMoveObserver->isAssetWatched(asset_uuid)) + { + LL_DEBUGS("Inventory_Move") << "Found asset UUID: " << asset_uuid << LL_ENDL; + was_moved = true; + } + } + } + + if (was_moved) + { + it = mAdded.erase(it); + } + else ++it; + } + + open_inventory_offer(mAdded, ""); mAdded.clear(); } }; +class LLOpenTaskGroupOffer : public LLInventoryAddedObserver +{ +protected: + /*virtual*/ void done() + { + open_inventory_offer(mAdded, "group_offer"); + mAdded.clear(); + gInventory.removeObserver(this); + delete this; + } +}; + //one global instance to bind them LLOpenTaskOffer* gNewInventoryObserver=NULL; +class LLNewInventoryHintObserver : public LLInventoryAddedObserver +{ +protected: + /*virtual*/ void done() + { + LLFirstUse::newInventory(); + } +}; + void start_new_inventory_observer() { if (!gNewInventoryObserver) //task offer observer @@ -695,20 +954,30 @@ void start_new_inventory_observer() gNewInventoryObserver = new LLOpenTaskOffer; gInventory.addObserver(gNewInventoryObserver); } + + if (!gInventoryMoveObserver) //inventory move from the world observer + { + // Observer is deleted by gInventory + gInventoryMoveObserver = new LLViewerInventoryMoveFromWorldObserver; + gInventory.addObserver(gInventoryMoveObserver); + } + + gInventory.addObserver(new LLNewInventoryHintObserver()); } -class LLDiscardAgentOffer : public LLInventoryFetchComboObserver +class LLDiscardAgentOffer : public LLInventoryFetchItemsObserver { + LOG_CLASS(LLDiscardAgentOffer); public: LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) : + LLInventoryFetchItemsObserver(object_id), mFolderID(folder_id), mObjectID(object_id) {} virtual ~LLDiscardAgentOffer() {} virtual void done() { - lldebugs << "LLDiscardAgentOffer::done()" << llendl; - LLUUID trash_id; - trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); + LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL; + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); bool notify = false; if(trash_id.notNull() && mObjectID.notNull()) { @@ -730,9 +999,9 @@ public: } else { - llwarns << "DiscardAgentOffer unable to find: " + LL_WARNS("Messaging") << "DiscardAgentOffer unable to find: " << (trash_id.isNull() ? "trash " : "") - << (mObjectID.isNull() ? "object" : "") << llendl; + << (mObjectID.isNull() ? "object" : "") << LL_ENDL; } gInventory.removeObserver(this); if(notify) @@ -749,13 +1018,13 @@ protected: //Returns TRUE if we are OK, FALSE if we are throttled //Set check_only true if you want to know the throttle status -//without registering a hit -Gigs +//without registering a hit bool check_offer_throttle(const std::string& from_name, bool check_only) { static U32 throttle_count; static bool throttle_logged; LLChat chat; - LLString log_message; + std::string log_message; if (!gSavedSettings.getBOOL("ShowNewInventory")) return false; @@ -767,38 +1036,47 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) if(gThrottleTimer.checkExpirationAndReset(OFFER_THROTTLE_TIME)) { - //llinfos << "Throttle Expired" << llendl; + LL_DEBUGS("Messaging") << "Throttle Expired" << LL_ENDL; throttle_count=1; throttle_logged=false; return true; } else //has not expired { - //llinfos << "Throttle Not Expired, Count: " << throttle_count << llendl; + LL_DEBUGS("Messaging") << "Throttle Not Expired, Count: " << throttle_count << LL_ENDL; // When downloading the initial inventory we get a lot of new items - // coming in and can't tell that from spam. JC + // coming in and can't tell that from spam. if (LLStartUp::getStartupState() >= STATE_STARTED && throttle_count >= OFFER_THROTTLE_MAX_COUNT) { if (!throttle_logged) { // Use the name of the last item giver, who is probably the person - // spamming you. JC - std::ostringstream message; - message << LLAppViewer::instance()->getSecondLifeTitle(); + // spamming you. + + LLStringUtil::format_map_t arg; + std::string log_msg; + std::ostringstream time ; + time<<OFFER_THROTTLE_TIME; + + arg["APP_NAME"] = LLAppViewer::instance()->getSecondLifeTitle(); + arg["TIME"] = time.str(); + if (!from_name.empty()) { - message << ": Items coming in too fast from " << from_name; + arg["FROM_NAME"] = from_name; + log_msg = LLTrans::getString("ItemsComingInTooFastFrom", arg); } else { - message << ": Items coming in too fast"; + log_msg = LLTrans::getString("ItemsComingInTooFast", arg); } - message << ", automatic preview disabled for " - << OFFER_THROTTLE_TIME << " seconds."; - chat.mText = message.str(); + //this is kinda important, so actually put it on screen - LLFloaterChat::addChat(chat, FALSE, FALSE); + LLSD args; + args["MESSAGE"] = log_msg; + LLNotificationsUtil::add("SystemMessage", args); + throttle_logged=true; } return false; @@ -811,146 +1089,276 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) } } -void open_offer(const std::vector<LLUUID>& items, const std::string& from_name) +void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_name) { - std::vector<LLUUID>::const_iterator it = items.begin(); - std::vector<LLUUID>::const_iterator end = items.end(); - LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH)); - LLInventoryItem* item; - for(; it != end; ++it) + for (uuid_vec_t::const_iterator obj_iter = objects.begin(); + obj_iter != objects.end(); + ++obj_iter) { - item = gInventory.getItem(*it); - if(!item) + const LLUUID& obj_id = (*obj_iter); + if(!highlight_offered_object(obj_id)) { - llwarns << "Unable to show inventory item: " << *it << llendl; continue; } - if(gInventory.isObjectDescendentOf(*it, trash_id)) + + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (!obj) { + llwarns << "Cannot find object [ itemID:" << obj_id << " ] to open." << llendl; continue; } - //if we are throttled, don't display them - Gigs - if (check_offer_throttle(from_name, false)) + + const LLAssetType::EType asset_type = obj->getActualType(); + + // Either an inventory item or a category. + const LLInventoryItem* item = dynamic_cast<const LLInventoryItem*>(obj); + if (item) { - // I'm not sure this is a good idea. JC - bool show_keep_discard = item->getPermissions().getCreator() != gAgent.getID(); - //bool show_keep_discard = true; - switch(item->getType()) + //////////////////////////////////////////////////////////////////////////////// + // Special handling for various types. + if (check_offer_throttle(from_name, false)) // If we are throttled, don't display { - case LLAssetType::AT_NOTECARD: - open_notecard((LLViewerInventoryItem*)item, LLString("Note: ") + item->getName(), LLUUID::null, show_keep_discard, LLUUID::null, FALSE); - break; - case LLAssetType::AT_LANDMARK: - open_landmark((LLViewerInventoryItem*)item, LLString("Landmark: ") + item->getName(), show_keep_discard, LLUUID::null, FALSE); - break; - case LLAssetType::AT_TEXTURE: - open_texture(*it, LLString("Texture: ") + item->getName(), show_keep_discard, LLUUID::null, FALSE); - break; - default: - break; + LL_DEBUGS("Messaging") << "Highlighting inventory item: " << item->getUUID() << LL_ENDL; + // If we opened this ourselves, focus it + const BOOL take_focus = from_name.empty() ? TAKE_FOCUS_YES : TAKE_FOCUS_NO; + switch(asset_type) + { + case LLAssetType::AT_NOTECARD: + { + LLFloaterReg::showInstance("preview_notecard", LLSD(obj_id), take_focus); + break; + } + case LLAssetType::AT_LANDMARK: + { + LLInventoryCategory* parent_folder = gInventory.getCategory(item->getParentUUID()); + if ("inventory_handler" == from_name) + { + //we have to filter inventory_handler messages to avoid notification displaying + LLSideTray::getInstance()->showPanel("panel_places", + LLSD().with("type", "landmark").with("id", item->getUUID())); + } + else if("group_offer" == from_name) + { + // "group_offer" is passed by LLOpenTaskGroupOffer + // Notification about added landmark will be generated under the "from_name.empty()" called from LLOpenTaskOffer::done(). + LLSD args; + args["type"] = "landmark"; + args["id"] = obj_id; + LLSideTray::getInstance()->showPanel("panel_places", args); + + continue; + } + else if(from_name.empty()) + { + std::string folder_name; + if (parent_folder) + { + // Localize folder name. + // *TODO: share this code? + folder_name = parent_folder->getName(); + if (LLFolderType::lookupIsProtectedType(parent_folder->getPreferredType())) + { + LLTrans::findString(folder_name, "InvFolder " + folder_name); + } + } + else + { + folder_name = LLTrans::getString("Unknown"); + } + + // we receive a message from LLOpenTaskOffer, it mean that new landmark has been added. + LLSD args; + args["LANDMARK_NAME"] = item->getName(); + args["FOLDER_NAME"] = folder_name; + LLNotificationsUtil::add("LandmarkCreated", args); + } + } + break; + case LLAssetType::AT_TEXTURE: + { + LLFloaterReg::showInstance("preview_texture", LLSD(obj_id), take_focus); + break; + } + case LLAssetType::AT_ANIMATION: + LLFloaterReg::showInstance("preview_anim", LLSD(obj_id), take_focus); + break; + case LLAssetType::AT_SCRIPT: + LLFloaterReg::showInstance("preview_script", LLSD(obj_id), take_focus); + break; + case LLAssetType::AT_SOUND: + LLFloaterReg::showInstance("preview_sound", LLSD(obj_id), take_focus); + break; + default: + break; + } } } - //highlight item, if it's not in the trash or lost+found - - // Don't auto-open the inventory floater - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if(!view) - { - return; - } - //Trash Check - LLUUID trash_id; - trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); - if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id)) + //////////////////////////////////////////////////////////////////////////////// + // Highlight item + const BOOL auto_open = + gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false + !(asset_type == LLAssetType::AT_CALLINGCARD) && // don't open if it's a calling card + !(item && (item->getInventoryType() == LLInventoryType::IT_ATTACHMENT)) && // don't open if it's an item that's an attachment + !from_name.empty(); // don't open if it's not from anyone. + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); + if(active_panel) { - return; + LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL; + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); + active_panel->setSelection(obj_id, TAKE_FOCUS_NO); + gFocusMgr.setKeyboardFocus(focus_ctrl); } - LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); - //BOOL inventory_has_focus = gFocusMgr.childHasKeyboardFocus(view); - BOOL user_is_away = gAwayTimer.getStarted(); + } +} + +bool highlight_offered_object(const LLUUID& obj_id) +{ + const LLInventoryObject* obj = gInventory.getObject(obj_id); + if(!obj) + { + LL_WARNS("Messaging") << "Unable to show inventory item: " << obj_id << LL_ENDL; + return false; + } - // don't select lost and found items if the user is active - if (gInventory.isObjectDescendentOf(item->getUUID(), lost_and_found_id) - && !user_is_away) + //////////////////////////////////////////////////////////////////////////////// + // Don't highlight if it's in certain "quiet" folders which don't need UI + // notification (e.g. trash, cof, lost-and-found). + if(!gAgent.getAFK()) + { + const LLViewerInventoryCategory *parent = gInventory.getFirstNondefaultParent(obj_id); + if (parent) { - return; + const LLFolderType::EType parent_type = parent->getPreferredType(); + if (LLViewerFolderType::lookupIsQuietType(parent_type)) + { + return false; + } } - - //Not sure about this check. Could make it easy to miss incoming items. -Gigs - //don't dick with highlight while the user is working - //if(inventory_has_focus && !user_is_away) - // break; - //llinfos << "Highlighting" << item->getUUID() << llendl; - //highlight item - - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); - view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); - gFocusMgr.setKeyboardFocus(focus_ctrl); } + + return true; } void inventory_offer_mute_callback(const LLUUID& blocked_id, - const char* first_name, - const char* last_name, - BOOL is_group, - void* user_data) + const std::string& full_name, + bool is_group, + boost::shared_ptr<LLNotificationResponderInterface> offer_ptr) { - LLString from_name; + LLOfferInfo* offer = dynamic_cast<LLOfferInfo*>(offer_ptr.get()); + + std::string from_name = full_name; LLMute::EType type; - if (is_group) { type = LLMute::GROUP; - from_name = first_name; + } + else if(offer && offer->mFromObject) + { + //we have to block object by name because blocked_id is an id of owner + type = LLMute::BY_NAME; } else { type = LLMute::AGENT; - from_name += first_name; - from_name += " "; - from_name += last_name; } - LLMute mute(blocked_id, from_name, type); - if (gMuteListp->add(mute)) + // id should be null for BY_NAME mute, see LLMuteList::add for details + LLMute mute(type == LLMute::BY_NAME ? LLUUID::null : blocked_id, from_name, type); + if (LLMuteList::getInstance()->add(mute)) { - LLFloaterMute::showInstance(); - gFloaterMute->selectMute(blocked_id); + LLPanelBlockedList::showPanelAndSelect(blocked_id); } // purge the message queue of any previously queued inventory offers from the same source. - class OfferMatcher : public LLNotifyBoxView::Matcher + class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - BOOL matches(LLNotifyBox::notify_callback_t callback, void* cb_data) const + bool matches(const LLNotificationPtr notification) const { - return callback == inventory_offer_callback && ((LLOfferInfo*)cb_data)->mFromID == blocked_id; + if(notification->getName() == "ObjectGiveItem" + || notification->getName() == "UserGiveItem") + { + return (notification->getPayload()["from_id"].asUUID() == blocked_id); + } + return FALSE; } private: const LLUUID& blocked_id; }; - gNotifyBoxView->purgeMessagesMatching(OfferMatcher(blocked_id)); + + LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( + gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id)); } -void inventory_offer_callback(S32 button, void* user_data) - { - LLChat chat; - LLString log_message; - LLOfferInfo* info = (LLOfferInfo*)user_data; - if(!info) return; +LLOfferInfo::LLOfferInfo() + : LLNotificationResponderInterface() + , mFromGroup(FALSE) + , mFromObject(FALSE) + , mIM(IM_NOTHING_SPECIAL) + , mType(LLAssetType::AT_NONE) + , mPersist(false) +{ +} - // For muting, we need to add the mute, then decline the offer. - // This must be done here because: - // * callback may be called immediately, - // * adding the mute sends a message, - // * we can't build two messages at once. JC - if (2 == button) - { - gCacheName->get(info->mFromID, info->mFromGroup, inventory_offer_mute_callback, user_data); - } +LLOfferInfo::LLOfferInfo(const LLSD& sd) +{ + mIM = (EInstantMessage)sd["im_type"].asInteger(); + mFromID = sd["from_id"].asUUID(); + mFromGroup = sd["from_group"].asBoolean(); + mFromObject = sd["from_object"].asBoolean(); + mTransactionID = sd["transaction_id"].asUUID(); + mFolderID = sd["folder_id"].asUUID(); + mObjectID = sd["object_id"].asUUID(); + mType = LLAssetType::lookup(sd["type"].asString().c_str()); + mFromName = sd["from_name"].asString(); + mDesc = sd["description"].asString(); + mHost = LLHost(sd["sender"].asString()); + mPersist = sd["persist"].asBoolean(); +} + +LLOfferInfo::LLOfferInfo(const LLOfferInfo& info) +{ + mIM = info.mIM; + mFromID = info.mFromID; + mFromGroup = info.mFromGroup; + mFromObject = info.mFromObject; + mTransactionID = info.mTransactionID; + mFolderID = info.mFolderID; + mObjectID = info.mObjectID; + mType = info.mType; + mFromName = info.mFromName; + mDesc = info.mDesc; + mHost = info.mHost; + mPersist = info.mPersist; +} +LLSD LLOfferInfo::asLLSD() +{ + LLSD sd; + sd["im_type"] = mIM; + sd["from_id"] = mFromID; + sd["from_group"] = mFromGroup; + sd["from_object"] = mFromObject; + sd["transaction_id"] = mTransactionID; + sd["folder_id"] = mFolderID; + sd["object_id"] = mObjectID; + sd["type"] = LLAssetType::lookup(mType); + sd["from_name"] = mFromName; + sd["description"] = mDesc; + sd["sender"] = mHost.getIPandPort(); + sd["persist"] = mPersist; + return sd; +} + +void LLOfferInfo::fromLLSD(const LLSD& params) +{ + *this = params; +} + +void LLOfferInfo::send_auto_receive_response(void) +{ LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ImprovedInstantMessage); msg->nextBlockFast(_PREHASH_AgentData); @@ -958,127 +1366,142 @@ void inventory_offer_callback(S32 button, void* user_data) msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_MessageBlock); msg->addBOOLFast(_PREHASH_FromGroup, FALSE); - msg->addUUIDFast(_PREHASH_ToAgentID, info->mFromID); + msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); - msg->addUUIDFast(_PREHASH_ID, info->mTransactionID); + msg->addUUIDFast(_PREHASH_ID, mTransactionID); msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); msg->addStringFast(_PREHASH_FromAgentName, name); msg->addStringFast(_PREHASH_Message, ""); msg->addU32Fast(_PREHASH_ParentEstateID, 0); msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); + + // Auto Receive Message. The math for the dialog works, because the accept + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 1 greater than the offer integer value. + // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, + // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), + sizeof(mFolderID.mData)); + // send the message + msg->sendReliable(mHost); + + if(IM_INVENTORY_OFFERED == mIM) + { + // add buddy to recent people list + LLRecentPeople::instance().add(mFromID); + } +} + +void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response) +{ + initRespondFunctionMap(); + + const std::string name = notification["name"].asString(); + if(mRespondFunctions.find(name) == mRespondFunctions.end()) + { + llwarns << "Unexpected notification name : " << name << llendl; + llassert(!"Unexpected notification name"); + return; + } + + mRespondFunctions[name](notification, response); +} + +bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response) +{ + LLChat chat; + std::string log_message; + S32 button = LLNotificationsUtil::getSelectedOption(notification, response); + LLInventoryObserver* opener = NULL; LLViewerInventoryCategory* catp = NULL; - catp = (LLViewerInventoryCategory*)gInventory.getCategory(info->mObjectID); + catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID); LLViewerInventoryItem* itemp = NULL; if(!catp) { - itemp = (LLViewerInventoryItem*)gInventory.getItem(info->mObjectID); + itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); } - - // *TODO:translate - LLString from_string; // Used in the pop-up. - LLString chatHistory_string; // Used in chat history. - if (info->mFromObject == TRUE) + + // For muting, we need to add the mute, then decline the offer. + // This must be done here because: + // * callback may be called immediately, + // * adding the mute sends a message, + // * we can't build two messages at once. + if (2 == button) // Block { - if (info->mFromGroup) - { - std::string group_name; - if (gCacheName->getGroupName(info->mFromID, group_name)) - { - from_string = LLString("An object named '") + info->mFromName + "' owned by the group '" + group_name + "'"; - chatHistory_string = info->mFromName + " owned by the group '" + group_name + "'"; - } - else - { - from_string = LLString("An object named '") + info->mFromName + "' owned by an unknown group"; - chatHistory_string = info->mFromName + " owned by an unknown group"; - } - } - else + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + llassert(notification_ptr != NULL); + if (notification_ptr != NULL) { - std::string first_name, last_name; - if (gCacheName->getName(info->mFromID, first_name, last_name)) - { - from_string = LLString("An object named '") + info->mFromName + "' owned by " + first_name + " " + last_name; - chatHistory_string = info->mFromName + " owned by " + first_name + " " + last_name; - } - else - { - from_string = LLString("An object named '") + info->mFromName + "' owned by an unknown user"; - chatHistory_string = info->mFromName + " owned by an unknown user"; - } + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); } } - else - { - from_string = chatHistory_string = info->mFromName; - } + + std::string from_string; // Used in the pop-up. + std::string chatHistory_string; // Used in chat history. + + // TODO: when task inventory offers can also be handled the new way, migrate the code that sets these strings here: + from_string = chatHistory_string = mFromName; bool busy=FALSE; switch(button) { - case IOR_ACCEPT: - // ACCEPT. The math for the dialog works, because the accept - // for inventory_offered, task_inventory_offer or - // group_notice_inventory is 1 greater than the offer integer value. - // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, - // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED - msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 1)); - msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(info->mFolderID.mData), - sizeof(info->mFolderID.mData)); - // send the message - msg->sendReliable(info->mHost); - - //don't spam them if they are getting flooded - if (check_offer_throttle(info->mFromName, true)) - { - log_message = chatHistory_string + " gave you " + info->mDesc + "."; - chat.mText = log_message; - LLFloaterChat::addChatHistory(chat); - } - + case IOR_SHOW: // we will want to open this item when it comes back. - lldebugs << "Initializing an opener for tid: " << info->mTransactionID - << llendl; - switch (info->mIM) + LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID + << LL_ENDL; + switch (mIM) { case IM_INVENTORY_OFFERED: - { - // This is an offer from an agent. In this case, the back - // end has already copied the items into your inventory, - // so we can fetch it out of our inventory. - LLInventoryFetchObserver::item_ref_t items; - items.push_back(info->mObjectID); - LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(from_string); - open_agent_offer->fetchItems(items); - if(catp || (itemp && itemp->isComplete())) - { - open_agent_offer->done(); - } - else { - opener = open_agent_offer; + // This is an offer from an agent. In this case, the back + // end has already copied the items into your inventory, + // so we can fetch it out of our inventory. + LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(mObjectID, from_string); + open_agent_offer->startFetch(); + if(catp || (itemp && itemp->isFinished())) + { + open_agent_offer->done(); + } + else + { + opener = open_agent_offer; + } } - } break; - case IM_TASK_INVENTORY_OFFERED: case IM_GROUP_NOTICE: + opener = new LLOpenTaskGroupOffer; + send_auto_receive_response(); + break; + case IM_TASK_INVENTORY_OFFERED: case IM_GROUP_NOTICE_REQUESTED: - { // This is an offer from a task or group. // We don't use a new instance of an opener // We instead use the singular observer gOpenTaskOffer // Since it already exists, we don't need to actually do anything - } - break; + break; default: - llwarns << "inventory_offer_callback: unknown offer type" << llendl; + LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; break; - } // end switch (info->mIM) + } + break; + // end switch (mIM) + + case IOR_ACCEPT: + //don't spam them if they are getting flooded + if (check_offer_throttle(mFromName, true)) + { + log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + } break; case IOR_BUSY: @@ -1087,38 +1510,21 @@ void inventory_offer_callback(S32 button, void* user_data) case IOR_MUTE: // MUTE falls through to decline case IOR_DECLINE: - // DECLINE. The math for the dialog works, because the decline - // for inventory_offered, task_inventory_offer or - // group_notice_inventory is 2 greater than the offer integer value. - // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED, - // or IM_GROUP_NOTICE_INVENTORY_DECLINED - default: - // close button probably (or any of the fall-throughs from above) - msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 2)); - msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); - // send the message - msg->sendReliable(info->mHost); - - log_message = "You decline " + info->mDesc + " from " + info->mFromName + "."; - chat.mText = log_message; - if( gMuteListp->isMuted(info->mFromID ) && ! gMuteListp->isLinden(info->mFromName) ) // muting for SL-42269 - { - chat.mMuted = TRUE; - } - LLFloaterChat::addChatHistory(chat); - - // If it's from an agent, we have to fetch the item to throw - // it away. If it's from a task or group, just denying the - // request will suffice to discard the item. - if(IM_INVENTORY_OFFERED == info->mIM) - { - LLInventoryFetchComboObserver::folder_ref_t folders; - LLInventoryFetchComboObserver::item_ref_t items; - items.push_back(info->mObjectID); - LLDiscardAgentOffer* discard_agent_offer; - discard_agent_offer = new LLDiscardAgentOffer(info->mFolderID, info->mObjectID); - discard_agent_offer->fetch(folders, items); - if(catp || (itemp && itemp->isComplete())) + { + log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +"."; + chat.mText = log_message; + if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269 + { + chat.mMuted = TRUE; + } + + // *NOTE dzaporozhan + // Disabled logging to old chat floater to fix crash in group notices - EXT-4149 + // LLFloaterChat::addChatHistory(chat); + + LLDiscardAgentOffer* discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID); + discard_agent_offer->startFetch(); + if (catp || (itemp && itemp->isFinished())) { discard_agent_offer->done(); } @@ -1127,11 +1533,18 @@ void inventory_offer_callback(S32 button, void* user_data) opener = discard_agent_offer; } + + if (busy && (!mFromGroup && !mFromObject)) + { + busy_message(gMessageSystem, mFromID); + } + break; } - if (busy && (!info->mFromGroup && !info->mFromObject)) - { - busy_message(msg,info->mFromID); - } + default: + // close button probably + // The item has already been fetched and is in your inventory, we simply won't highlight it + // OR delete it if the notification gets killed, since we don't want that to be a vector for + // losing inventory offers. break; } @@ -1140,29 +1553,223 @@ void inventory_offer_callback(S32 button, void* user_data) gInventory.addObserver(opener); } - delete info; - info = NULL; + if(!mPersist) + { + delete this; + } + return false; +} + +bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const LLSD& response) +{ + LLChat chat; + std::string log_message; + S32 button = LLNotification::getSelectedOption(notification, response); + + // For muting, we need to add the mute, then decline the offer. + // This must be done here because: + // * callback may be called immediately, + // * adding the mute sends a message, + // * we can't build two messages at once. + if (2 == button) + { + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + llassert(notification_ptr != NULL); + if (notification_ptr != NULL) + { + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); + } + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ImprovedInstantMessage); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MessageBlock); + msg->addBOOLFast(_PREHASH_FromGroup, FALSE); + msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); + msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); + msg->addUUIDFast(_PREHASH_ID, mTransactionID); + msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary + std::string name; + LLAgentUI::buildFullname(name); + msg->addStringFast(_PREHASH_FromAgentName, name); + msg->addStringFast(_PREHASH_Message, ""); + msg->addU32Fast(_PREHASH_ParentEstateID, 0); + msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); + msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); + LLInventoryObserver* opener = NULL; + + std::string from_string; // Used in the pop-up. + std::string chatHistory_string; // Used in chat history. + if (mFromObject == TRUE) + { + if (mFromGroup) + { + std::string group_name; + if (gCacheName->getGroupName(mFromID, group_name)) + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" + + mFromName + LLTrans::getString("'") +" " + LLTrans::getString("InvOfferOwnedByGroup") + + " "+ "'" + group_name + "'"; + + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByGroup") + + " " + group_name + "'"; + } + else + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" + + mFromName +"'"+ " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); + } + } + else + { + std::string full_name; + if (gCacheName->getFullName(mFromID, full_name)) + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+ LLTrans::getString("'") + mFromName + + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedBy") + full_name; + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedBy") + " " + full_name; + } + else + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+LLTrans::getString("'") + + mFromName + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedByUnknownUser"); + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownUser"); + } + } + } + else + { + from_string = chatHistory_string = mFromName; + } + + bool busy=FALSE; + + switch(button) + { + case IOR_ACCEPT: + // ACCEPT. The math for the dialog works, because the accept + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 1 greater than the offer integer value. + // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, + // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), + sizeof(mFolderID.mData)); + // send the message + msg->sendReliable(mHost); + + //don't spam them if they are getting flooded + if (check_offer_throttle(mFromName, true)) + { + log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + } + + // we will want to open this item when it comes back. + LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID + << LL_ENDL; + switch (mIM) + { + case IM_TASK_INVENTORY_OFFERED: + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_REQUESTED: + { + // This is an offer from a task or group. + // We don't use a new instance of an opener + // We instead use the singular observer gOpenTaskOffer + // Since it already exists, we don't need to actually do anything + } + break; + default: + LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; + break; + } // end switch (mIM) + break; + + case IOR_BUSY: + //Busy falls through to decline. Says to make busy message. + busy=TRUE; + case IOR_MUTE: + // MUTE falls through to decline + case IOR_DECLINE: + // DECLINE. The math for the dialog works, because the decline + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 2 greater than the offer integer value. + // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED, + // or IM_GROUP_NOTICE_INVENTORY_DECLINED + default: + // close button probably (or any of the fall-throughs from above) + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 2)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); + // send the message + msg->sendReliable(mHost); + + log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +"."; + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + + if (busy && (!mFromGroup && !mFromObject)) + { + busy_message(msg,mFromID); + } + break; + } + + if(opener) + { + gInventory.addObserver(opener); + } - // Allow these to stack up, but once you deal with one, reset the - // position. - gFloaterView->resetStartingFloaterPosition(); + if(!mPersist) + { + delete this; + } + return false; } +class LLPostponedOfferNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() + { + LLSD substitutions = mParams.substitutions; + substitutions["NAME"] = mName; + mParams.substitutions = substitutions; + } +}; + +void LLOfferInfo::initRespondFunctionMap() +{ + if(mRespondFunctions.empty()) + { + mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2); + mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2); + } +} -void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) +void inventory_offer_handler(LLOfferInfo* info) { //Until throttling is implmented, busy mode should reject inventory instead of silently //accepting it. SEE SL-39554 if (gAgent.getBusy()) { - inventory_offer_callback(IOR_BUSY, info); + info->forceResponse(IOR_BUSY); return; } //If muted, don't even go through the messaging stuff. Just curtail the offer here. - if (gMuteListp->isMuted(info->mFromID, info->mFromName)) + if (LLMuteList::getInstance()->isMuted(info->mFromID, info->mFromName)) { - inventory_offer_callback(IOR_MUTE, info); + info->forceResponse(IOR_MUTE); return; } @@ -1174,139 +1781,375 @@ void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) { // For certain types, just accept the items into the inventory, // and possibly open them on receipt depending upon "ShowNewInventory". - inventory_offer_callback(IOR_ACCEPT, info); + info->forceResponse(IOR_ACCEPT); return; } - LLString::format_map_t args; - args["[OBJECTNAME]"] = info->mDesc; + // Strip any SLURL from the message display. (DEV-2754) + std::string msg = info->mDesc; + int indx = msg.find(" ( http://slurl.com/secondlife/"); + if(indx == std::string::npos) + { + // try to find new slurl host + indx = msg.find(" ( http://maps.secondlife.com/secondlife/"); + } + if(indx >= 0) + { + LLStringUtil::truncate(msg, indx); + } + + LLSD args; + args["[OBJECTNAME]"] = msg; + + LLSD payload; + // must protect against a NULL return from lookupHumanReadable() std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); if (!typestr.empty()) { - args["[OBJECTTYPE]"] = typestr; + // human readable matches string name from strings.xml + // lets get asset type localized name + args["OBJECTTYPE"] = LLTrans::getString(typestr); } else { - llwarns << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << llendl; - args["[OBJECTTYPE]"] = ""; + LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; + args["OBJECTTYPE"] = ""; // This seems safest, rather than propagating bogosity - llwarns << "Forcing an inventory-decline for probably-bad asset type." << llendl; - inventory_offer_callback(IOR_DECLINE, info); + LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; + info->forceResponse(IOR_DECLINE); return; } - // Name cache callbacks don't store userdata, so can't save - // off the LLOfferInfo. Argh. JC - BOOL name_found = FALSE; + // If mObjectID is null then generate the object_id based on msg to prevent + // multiple creation of chiclets for same object. + LLUUID object_id = info->mObjectID; + if (object_id.isNull()) + object_id.generate(msg); + + payload["from_id"] = info->mFromID; + // Needed by LLScriptFloaterManager to bind original notification with + // faked for toast one. + payload["object_id"] = object_id; + // Flag indicating that this notification is faked for toast. + payload["give_inventory_notification"] = FALSE; + args["OBJECTFROMNAME"] = info->mFromName; + args["NAME"] = info->mFromName; if (info->mFromGroup) { - std::string group_name; - if (gCacheName->getGroupName(info->mFromID, group_name)) - { - args["[FIRST]"] = group_name; - args["[LAST]"] = ""; - name_found = TRUE; - } + args["NAME_SLURL"] = LLSLURL("group", info->mFromID, "about").getSLURLString(); } else { - std::string first_name, last_name; - if (gCacheName->getName(info->mFromID, first_name, last_name)) - { - args["[FIRST]"] = first_name; - args["[LAST]"] = last_name; - name_found = TRUE; - } + args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); } - if (from_task) + std::string verb = "select?name=" + LLURI::escape(msg); + args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); + + LLNotification::Params p("ObjectGiveItem"); + + // Object -> Agent Inventory Offer + if (info->mFromObject) { - args["[OBJECTFROMNAME]"] = info->mFromName; - LLNotifyBox::showXml(name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser", - args, &inventory_offer_callback, (void*)info); + // Inventory Slurls don't currently work for non agent transfers, so only display the object name. + args["ITEM_SLURL"] = msg; + // Note: sets inventory_task_offer_callback as the callback + p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); + info->mPersist = true; + p.name = "ObjectGiveItem"; + // Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory. + LLPostponedNotification::add<LLPostponedOfferNotification>(p, info->mFromID, info->mFromGroup == TRUE); } - else + else // Agent -> Agent Inventory Offer { - // *TODO:translate -> [FIRST] [LAST] - args["[NAME]"] = info->mFromName; - LLNotifyBox::showXml("UserGiveItem", args, - &inventory_offer_callback, (void*)info); + p.responder = info; + // Note: sets inventory_offer_callback as the callback + // *TODO fix memory leak + // inventory_offer_callback() is not invoked if user received notification and + // closes viewer(without responding the notification) + p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); + info->mPersist = true; + p.name = "UserGiveItem"; + + // Prefetch the item into your local inventory. + LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); + fetch_item->startFetch(); + if(fetch_item->isFinished()) + { + fetch_item->done(); + } + else + { + gInventory.addObserver(fetch_item); + } + + // In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages). + info->send_auto_receive_response(); + + // Inform user that there is a script floater via toast system + { + payload["give_inventory_notification"] = TRUE; + p.payload = payload; + LLPostponedNotification::add<LLPostponedOfferNotification>(p, info->mFromID, false); + } } -} + LLFirstUse::newInventory(); +} -void group_vote_callback(S32 option, void *userdata) +bool lure_callback(const LLSD& notification, const LLSD& response) { - LLUUID *group_id = (LLUUID *)userdata; - if (!group_id) return; + S32 option = 0; + if (response.isInteger()) + { + option = response.asInteger(); + } + else + { + option = LLNotificationsUtil::getSelectedOption(notification, response); + } + + LLUUID from_id = notification["payload"]["from_id"].asUUID(); + LLUUID lure_id = notification["payload"]["lure_id"].asUUID(); + BOOL godlike = notification["payload"]["godlike"].asBoolean(); switch(option) { case 0: - // Vote Now - // Open up the voting tab - LLFloaterGroupInfo::showFromUUID(*group_id, "voting_tab"); + { + // accept + gAgent.teleportViaLure(lure_id, godlike); + } break; + case 1: default: - // Vote Later or - // close button + // decline + send_simple_im(from_id, + LLStringUtil::null, + IM_LURE_DECLINED, + lure_id); break; } - delete group_id; - group_id = NULL; + return false; } +static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); -struct LLLureInfo +bool goto_url_callback(const LLSD& notification, const LLSD& response) { - LLLureInfo(const LLUUID& from, const LLUUID& lure_id, BOOL godlike) : - mFromID(from), - mLureID(lure_id), - mGodlike(godlike) - {} + std::string url = notification["payload"]["url"].asString(); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if(1 == option) + { + LLWeb::loadURL(url); + } + return false; +} +static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); + +bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]); + } + return false; +} +static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback); - LLUUID mFromID; - LLUUID mLureID; - BOOL mGodlike; +class LLPostponedServerObjectNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() + { + LLSD payload = mParams.payload; + mParams.payload = payload; + } }; -void lure_callback(S32 option, void* user_data) +static bool parse_lure_bucket(const std::string& bucket, + U64& region_handle, + LLVector3& pos, + LLVector3& look_at, + U8& region_access) { - LLLureInfo* info = (LLLureInfo*)user_data; - if(!info) return; - switch(option) + // tokenize the bucket + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(bucket, sep); + tokenizer::iterator iter = tokens.begin(); + + S32 gx,gy,rx,ry,rz,lx,ly,lz; + try + { + gx = boost::lexical_cast<S32>((*(iter)).c_str()); + gy = boost::lexical_cast<S32>((*(++iter)).c_str()); + rx = boost::lexical_cast<S32>((*(++iter)).c_str()); + ry = boost::lexical_cast<S32>((*(++iter)).c_str()); + rz = boost::lexical_cast<S32>((*(++iter)).c_str()); + lx = boost::lexical_cast<S32>((*(++iter)).c_str()); + ly = boost::lexical_cast<S32>((*(++iter)).c_str()); + lz = boost::lexical_cast<S32>((*(++iter)).c_str()); + } + catch( boost::bad_lexical_cast& ) + { + LL_WARNS("parse_lure_bucket") + << "Couldn't parse lure bucket." + << LL_ENDL; + return false; + } + // Grab region access + region_access = SIM_ACCESS_MIN; + if (++iter != tokens.end()) { - case 0: + std::string access_str((*iter).c_str()); + LLStringUtil::trim(access_str); + if ( access_str == "A" ) { - // accept - send_simple_im(info->mFromID, - "", - IM_LURE_ACCEPTED, - info->mLureID); - gAgent.teleportViaLure(info->mLureID, info->mGodlike); + region_access = SIM_ACCESS_ADULT; } - break; - case 1: + else if ( access_str == "M" ) + { + region_access = SIM_ACCESS_MATURE; + } + else if ( access_str == "PG" ) + { + region_access = SIM_ACCESS_PG; + } + } + + pos.setVec((F32)rx, (F32)ry, (F32)rz); + look_at.setVec((F32)lx, (F32)ly, (F32)lz); + + region_handle = to_region_handle(gx, gy); + return true; +} + +// Strip out "Resident" for display, but only if the message came from a user +// (rather than a script) +static std::string clean_name_from_im(const std::string& name, EInstantMessage type) +{ + switch(type) + { + case IM_NOTHING_SPECIAL: + case IM_MESSAGEBOX: + case IM_GROUP_INVITATION: + case IM_INVENTORY_OFFERED: + case IM_INVENTORY_ACCEPTED: + case IM_INVENTORY_DECLINED: + case IM_GROUP_VOTE: + case IM_GROUP_MESSAGE_DEPRECATED: + //IM_TASK_INVENTORY_OFFERED + //IM_TASK_INVENTORY_ACCEPTED + //IM_TASK_INVENTORY_DECLINED + case IM_NEW_USER_DEFAULT: + case IM_SESSION_INVITE: + case IM_SESSION_P2P_INVITE: + case IM_SESSION_GROUP_START: + case IM_SESSION_CONFERENCE_START: + case IM_SESSION_SEND: + case IM_SESSION_LEAVE: + //IM_FROM_TASK + case IM_BUSY_AUTO_RESPONSE: + case IM_CONSOLE_AND_CHAT_HISTORY: + case IM_LURE_USER: + case IM_LURE_ACCEPTED: + case IM_LURE_DECLINED: + case IM_GODLIKE_LURE_USER: + case IM_YET_TO_BE_USED: + case IM_GROUP_ELECTION_DEPRECATED: + //IM_GOTO_URL + //IM_FROM_TASK_AS_ALERT + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_INVENTORY_ACCEPTED: + case IM_GROUP_NOTICE_INVENTORY_DECLINED: + case IM_GROUP_INVITATION_ACCEPT: + case IM_GROUP_INVITATION_DECLINE: + case IM_GROUP_NOTICE_REQUESTED: + case IM_FRIENDSHIP_OFFERED: + case IM_FRIENDSHIP_ACCEPTED: + case IM_FRIENDSHIP_DECLINED_DEPRECATED: + //IM_TYPING_START + //IM_TYPING_STOP + return LLCacheName::cleanFullName(name); default: - // decline - send_simple_im(info->mFromID, - "", - IM_LURE_DECLINED, - info->mLureID); - break; + return name; } - delete info; - info = NULL; } -void goto_url_callback(S32 option, void* user_data) +static std::string clean_name_from_task_im(const std::string& msg, + BOOL from_group) { - char* url = (char*)user_data; - if(1 == option) + boost::smatch match; + static const boost::regex returned_exp( + "(.*been returned to your inventory lost and found folder by )(.+)( (from|near).*)"); + if (boost::regex_match(msg, match, returned_exp)) + { + // match objects are 1-based for groups + std::string final = match[1].str(); + std::string name = match[2].str(); + // Don't try to clean up group names + if (!from_group) + { + if (LLAvatarNameCache::useDisplayNames()) + { + // ...just convert to username + final += LLCacheName::buildUsername(name); + } + else + { + // ...strip out legacy "Resident" name + final += LLCacheName::cleanFullName(name); + } + } + final += match[3].str(); + return final; + } + return msg; +} + +void notification_display_name_callback(const LLUUID& id, + const LLAvatarName& av_name, + const std::string& name, + LLSD& substitutions, + const LLSD& payload) +{ + substitutions["NAME"] = av_name.mDisplayName; + LLNotificationsUtil::add(name, substitutions, payload); +} + +class LLPostponedIMSystemTipNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() { - LLWeb::loadURL(url); + LLSD payload = mParams.payload; + payload["SESSION_NAME"] = mName; + mParams.payload = payload; } - delete[] url; + +}; + +// Callback for name resolution of a god/estate message +void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string message) +{ + LLSD args; + args["NAME"] = av_name.getCompleteName(); + args["MESSAGE"] = message; + LLNotificationsUtil::add("GodMessage", args); + + // Treat like a system message and put in chat history. + chat.mText = av_name.getCompleteName() + ": " + message; + + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(nearby_chat) + { + nearby_chat->addMessage(chat); + } + } void process_improved_im(LLMessageSystem *msg, void **user_data) @@ -1321,44 +2164,55 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) U8 offline; U8 d = 0; LLUUID session_id; - U32 t; - char name[DB_FULL_NAME_BUF_SIZE]; /* Flawfinder: ignore */ - char message[DB_IM_MSG_BUF_SIZE]; /* Flawfinder: ignore */ + U32 timestamp; + std::string name; + std::string message; U32 parent_estate_id = 0; LLUUID region_id; LLVector3 position; - char buffer[DB_IM_MSG_BUF_SIZE * 2]; /* Flawfinder: ignore */ U8 binary_bucket[MTUBYTES]; S32 binary_bucket_size; LLChat chat; - - // *TODO:translate - need to fix the full name to first/last (maybe) + std::string buffer; + + // *TODO: Translate - need to fix the full name to first/last (maybe) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id); msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id); msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline); msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id); - msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, t); + msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, timestamp); //msg->getData("MessageBlock", "Count", &count); - msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, DB_FULL_NAME_BUF_SIZE, name); - msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, DB_IM_MSG_BUF_SIZE, message); + msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, name); + msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, message); msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id); msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position); msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES); binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket); EInstantMessage dialog = (EInstantMessage)d; - time_t timestamp = (time_t)t; + + // make sure that we don't have an empty or all-whitespace name + LLStringUtil::trim(name); + if (name.empty()) + { + name = LLTrans::getString("Unnamed"); + } + // IDEVO convert new-style "Resident" names for display + name = clean_name_from_im(name, dialog); BOOL is_busy = gAgent.getBusy(); - BOOL is_muted = gMuteListp->isMuted(from_id, name, LLMute::flagTextChat); - BOOL is_linden = gMuteListp->isLinden(name); + BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); + BOOL is_linden = LLMuteList::getInstance()->isLinden(name); BOOL is_owned_by_me = FALSE; + BOOL is_friend = (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL) ? false : true; + BOOL accept_im_from_only_friend = gSavedSettings.getBOOL("VoiceCallsFriendsOnly"); chat.mMuted = is_muted && !is_linden; chat.mFromID = from_id; chat.mFromName = name; + chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT; LLViewerObject *source = gObjectList.findObject(session_id); //Session ID is probably the wrong thing. if (source) @@ -1366,28 +2220,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) is_owned_by_me = source->permYouOwner(); } - char separator_string[3]=": "; /* Flawfinder: ignore */ - int message_offset=0; + std::string separator_string(": "); - //Handle IRC styled /me messages. - if (!strncmp(message, "/me ", 4) || !strncmp(message, "/me'", 4)) - { - strcpy(separator_string,""); /* Flawfinder: ignore */ - message_offset=3; - } + LLSD args; + LLSD payload; + LLNotification::Params params; - LLString::format_map_t args; switch(dialog) { case IM_CONSOLE_AND_CHAT_HISTORY: - // These are used for system messages, hence don't need the name, - // as it is always "Second Life". - // *TODO:translate - args["[MESSAGE]"] = message; - - // Note: don't put the message in the IM history, even though was sent - // via the IM mechanism. - LLNotifyBox::showXml("SystemMessageTip",args); + args["MESSAGE"] = message; + payload["from_id"] = from_id; + + params.name = "IMSystemMessageTip"; + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add<LLPostponedIMSystemTipNotification>(params, from_id, false); break; case IM_NOTHING_SPECIAL: @@ -1399,7 +2247,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // do nothing -- don't distract newbies in // Prelude with global IMs } - else if (offline == IM_ONLINE && !is_linden && is_busy && strcmp(name, SYSTEM_FROM)) + else if (offline == IM_ONLINE && !is_linden && is_busy && name != SYSTEM_FROM) { // return a standard "busy" message, but only do it to online IM // (i.e. not other auto responses and not store-and-forward IM) @@ -1408,16 +2256,16 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // if there is not a panel for this conversation (i.e. it is a new IM conversation // initiated by the other party) then... std::string my_name; - gAgent.buildFullname(my_name); - LLString response = gSavedPerAccountSettings.getText("BusyModeResponse"); + LLAgentUI::buildFullname(my_name); + std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); pack_instant_message( gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), from_id, - my_name.c_str(), - response.c_str(), + my_name, + response, IM_ONLINE, IM_BUSY_AUTO_RESPONSE, session_id); @@ -1426,9 +2274,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // now store incoming IM in chat history - snprintf(buffer, sizeof(buffer), "%s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */ + buffer = message; - llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl; + LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; // add to IM panel, but do not bother the user gIMMgr->addMessage( @@ -1436,80 +2284,73 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, - NULL, + LLStringUtil::null, dialog, parent_estate_id, region_id, - position); - - // pretend this is chat generated by self, so it does not show up on screen - snprintf(buffer, sizeof(buffer), "IM: %s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */ - chat.mText = buffer; - LLFloaterChat::addChat( chat, TRUE, TRUE ); + position, + true); } else if (from_id.isNull()) { - // Messages from "Second Life" don't go to IM history - snprintf(buffer, sizeof(buffer), "%s: %s", name, message); /* Flawfinder: ignore */ - chat.mText = buffer; - LLFloaterChat::addChat(chat, FALSE, FALSE); + LLSD args; + args["MESSAGE"] = message; + LLNotificationsUtil::add("SystemMessage", args); } else if (to_id.isNull()) { - // Message to everyone from GOD - args["[NAME]"] = name; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("GodMessage", args); - - // Treat like a system message and put in chat history. - // Claim to be from a local agent so it doesn't go into - // console. - snprintf(buffer, sizeof(buffer), "%s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */ - chat.mText = buffer; - BOOL local_agent = TRUE; - LLFloaterChat::addChat(chat, FALSE, local_agent); + // Message to everyone from GOD, look up the fullname since + // server always slams name to legacy names + LLAvatarNameCache::get(from_id, boost::bind(god_message_name_cb, _2, chat, message)); } else { // standard message, not from system - char saved[MAX_STRING]; /* Flawfinder: ignore */ - saved[0] = '\0'; + std::string saved; if(offline == IM_OFFLINE) { - char time_buf[TIME_STR_LENGTH]; /* Flawfinder: ignore */ - snprintf(saved, MAX_STRING, "(Saved %s) ", /* Flawfinder: ignore */ - formatted_time(timestamp, time_buf)); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } - snprintf(buffer, sizeof(buffer), "%s%s%s%s", name, separator_string, saved,(message+message_offset)); /* Flawfinder: ignore */ + buffer = saved + message; - llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl; + LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; - if (!is_muted || is_linden) + bool mute_im = is_muted; + if(accept_im_from_only_friend&&!is_friend) + { + mute_im = true; + } + if (!mute_im || is_linden) { gIMMgr->addMessage( session_id, from_id, name, buffer, - NULL, + LLStringUtil::null, dialog, parent_estate_id, region_id, - position); - snprintf(buffer, sizeof(buffer), "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset)); /* Flawfinder: ignore */ - - chat.mText = buffer; - BOOL local_agent = FALSE; - LLFloaterChat::addChat( chat, TRUE, local_agent ); + position, + true); } else { + /* + EXT-5099 + currently there is no way to store in history only... + using LLNotificationsUtil::add will add message to Nearby Chat + // muted user, so don't start an IM session, just record line in chat // history. Pretend the chat is from a local agent, // so it will go into the history but not be shown on screen. - chat.mText = buffer; - BOOL local_agent = TRUE; - LLFloaterChat::addChat( chat, TRUE, local_agent ); + + LLSD args; + args["MESSAGE"] = buffer; + LLNotificationsUtil::add("SystemMessageTip", args); + */ } } break; @@ -1531,15 +2372,15 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_MESSAGEBOX: { // This is a block, modeless dialog. - //*TODO:translate - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("SystemMessage", args); + //*TODO: Translate + args["MESSAGE"] = message; + LLNotificationsUtil::add("SystemMessageTip", args); } break; case IM_GROUP_NOTICE: case IM_GROUP_NOTICE_REQUESTED: { - llinfos << "Received IM_GROUP_NOTICE message." << llendl; + LL_INFOS("Messaging") << "Received IM_GROUP_NOTICE message." << LL_ENDL; // Read the binary bucket for more information. struct notice_bucket_header_t { @@ -1558,7 +2399,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if ( (binary_bucket_size < (S32)((sizeof(notice_bucket_header_t) + sizeof(U8)))) || (binary_bucket[binary_bucket_size - 1] != '\0') ) { - llwarns << "Malformed group notice binary bucket" << llendl; + LL_WARNS("Messaging") << "Malformed group notice binary bucket" << LL_ENDL; break; } @@ -1566,19 +2407,21 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) U8 has_inventory = notice_bin_bucket->header.has_inventory; U8 asset_type = notice_bin_bucket->header.asset_type; LLUUID group_id = notice_bin_bucket->header.group_id; - const char* item_name = (const char*) notice_bin_bucket->item_name; + std::string item_name = ll_safe_string((const char*) notice_bin_bucket->item_name); // If there is inventory, give the user the inventory offer. LLOfferInfo* info = NULL; + if (has_inventory) { - info = new LLOfferInfo; + info = new LLOfferInfo(); + info->mIM = IM_GROUP_NOTICE; info->mFromID = from_id; info->mFromGroup = from_group; info->mTransactionID = session_id; info->mType = (LLAssetType::EType) asset_type; - info->mFolderID = gInventory.findCategoryUUIDForType(info->mType); + info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); std::string from_name; from_name += "A group member named "; @@ -1598,18 +2441,35 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); - LLString subj(*iter++); - LLString mes(*iter++); + std::string subj(*iter++); + std::string mes(*iter++); - if (IM_GROUP_NOTICE == dialog) + // Send the notification down the new path. + // For requested notices, we don't want to send the popups. + if (dialog != IM_GROUP_NOTICE_REQUESTED) { - subj += "\n"; - mes = "\n\n" + mes; - LLGroupNotifyBox::show(subj.c_str(),mes.c_str(),name,group_id,t,has_inventory,item_name,info); + payload["subject"] = subj; + payload["message"] = mes; + payload["sender_name"] = name; + payload["group_id"] = group_id; + payload["inventory_name"] = item_name; + payload["inventory_offer"] = info ? info->asLLSD() : LLSD(); + + LLSD args; + args["SUBJECT"] = subj; + args["MESSAGE"] = mes; + LLNotifications::instance().add(LLNotification::Params("GroupNotice").substitutions(args).payload(payload).time_stamp(timestamp)); } - else if (IM_GROUP_NOTICE_REQUESTED == dialog) + + // Also send down the old path for now. + if (IM_GROUP_NOTICE_REQUESTED == dialog) { - LLFloaterGroupInfo::showNotice(subj.c_str(),mes.c_str(),group_id,has_inventory,item_name,info); + + LLPanelGroup::showNotice(subj,mes,group_id,has_inventory,item_name,info); + } + else + { + delete info; } } break; @@ -1619,12 +2479,11 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if ((is_busy || is_muted)) { LLMessageSystem *msg = gMessageSystem; - join_group_callback(1, NULL); busy_message(msg,from_id); } else { - llinfos << "Received IM_GROUP_INVITATION message." << llendl; + LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; // Read the binary bucket for more information. struct invite_bucket_t { @@ -1635,25 +2494,33 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Make sure the binary bucket is the correct size. if (binary_bucket_size != sizeof(invite_bucket_t)) { - llwarns << "Malformed group invite binary bucket" << llendl; + LL_WARNS("Messaging") << "Malformed group invite binary bucket" << LL_ENDL; break; } invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; S32 membership_fee = ntohl(invite_bucket->membership_fee); - LLJoinGroupData* userdata = new LLJoinGroupData; - userdata->mTransactionID = session_id; - userdata->mGroupID = from_id; - userdata->mName.assign(name); - userdata->mMessage.assign(message); - userdata->mFee = membership_fee; - - LLString::format_map_t args; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("JoinGroup", args, - &join_group_callback, - (void*)userdata); + // IDEVO Clean up legacy name "Resident" in message constructed in + // lldatagroups.cpp + U32 pos = message.find(" has invited you to join a group.\n"); + if (pos != std::string::npos) + { + // use cleaned-up name from above + message = name + message.substr(pos); + } + + LLSD payload; + payload["transaction_id"] = session_id; + payload["group_id"] = from_id; + payload["name"] = name; + payload["message"] = message; + payload["fee"] = membership_fee; + + LLSD args; + args["MESSAGE"] = message; + // we shouldn't pass callback functor since it is registered in LLFunctorRegistration + LLNotificationsUtil::add("JoinGroup", args, payload); } } break; @@ -1663,7 +2530,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Someone has offered us some inventory. { LLOfferInfo* info = new LLOfferInfo; - if (IM_INVENTORY_OFFERED == dialog) { struct offer_agent_bucket_t @@ -1674,7 +2540,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if (sizeof(offer_agent_bucket_t) != binary_bucket_size) { - llwarns << "Malformed inventory offer from agent" << llendl; + LL_WARNS("Messaging") << "Malformed inventory offer from agent" << LL_ENDL; + delete info; break; } bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0]; @@ -1685,7 +2552,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { if (sizeof(S8) != binary_bucket_size) { - llwarns << "Malformed inventory offer from object" << llendl; + LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; + delete info; break; } info->mType = (LLAssetType::EType) binary_bucket[0]; @@ -1696,7 +2564,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) info->mFromID = from_id; info->mFromGroup = from_group; info->mTransactionID = session_id; - info->mFolderID = gInventory.findCategoryUUIDForType(info->mType); + info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); if (dialog == IM_TASK_INVENTORY_OFFERED) { @@ -1710,43 +2578,49 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) info->mDesc = message; info->mHost = msg->getSender(); //if (((is_busy && !is_owned_by_me) || is_muted)) - if ( is_muted ) + if (is_muted) { + // Prefetch the offered item so that it can be discarded by the appropriate observer. (EXT-4331) + LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); + fetch_item->startFetch(); + delete fetch_item; + // Same as closing window - inventory_offer_callback(-1, info); + info->forceResponse(IOR_DECLINE); } else { - inventory_offer_handler(info, dialog == IM_TASK_INVENTORY_OFFERED); + inventory_offer_handler(info); } } break; case IM_INVENTORY_ACCEPTED: { - args["[NAME]"] = name; - LLNotifyBox::showXml("InventoryAccepted", args); + args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; + LLSD payload; + payload["from_id"] = from_id; + LLNotificationsUtil::add("InventoryAccepted", args, payload); break; } case IM_INVENTORY_DECLINED: { - args["[NAME]"] = name; - LLNotifyBox::showXml("InventoryDeclined", args); + args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; + LLSD payload; + payload["from_id"] = from_id; + LLNotificationsUtil::add("InventoryDeclined", args, payload); break; } + // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856 case IM_GROUP_VOTE: - { - LLUUID *userdata = new LLUUID(session_id); - args["[NAME]"] = name; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("GroupVote", args, - &group_vote_callback, userdata); - } - break; + { + LL_WARNS("Messaging") << "Received IM: IM_GROUP_VOTE_DEPRECATED" << LL_ENDL; + } + break; case IM_GROUP_ELECTION_DEPRECATED: { - llwarns << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << llendl; + LL_WARNS("Messaging") << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << LL_ENDL; } break; @@ -1765,17 +2639,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } // standard message, not from system - char saved[MAX_STRING]; /* Flawfinder: ignore */ - saved[0] = '\0'; + std::string saved; if(offline == IM_OFFLINE) { - char time_buf[TIME_STR_LENGTH]; /* Flawfinder: ignore */ - snprintf(saved, /* Flawfinder: ignore */ - MAX_STRING, - "(Saved %s) ", - formatted_time(timestamp, time_buf)); + saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); } - snprintf(buffer, sizeof(buffer), "%s%s%s%s", name, separator_string, saved, (message+message_offset)); /* Flawfinder: ignore */ + buffer = saved + message; BOOL is_this_agent = FALSE; if(from_id == gAgentID) { @@ -1786,29 +2655,101 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, - (char*)binary_bucket, + ll_safe_string((char*)binary_bucket), IM_SESSION_INVITE, parent_estate_id, region_id, - position); - - snprintf(buffer, sizeof(buffer), "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset)); /* Flawfinder: ignore */ - chat.mText = buffer; - LLFloaterChat::addChat(chat, TRUE, is_this_agent); + position, + true); } break; case IM_FROM_TASK: - if (is_busy && !is_owned_by_me) { - return; + if (is_busy && !is_owned_by_me) + { + return; + } + + // Build a link to open the object IM info window. + std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size-1); + + if (session_id.notNull()) + { + chat.mFromID = session_id; + } + else + { + // This message originated on a region without the updated code for task id and slurl information. + // We just need a unique ID for this object that isn't the owner ID. + // If it is the owner ID it will overwrite the style that contains the link to that owner's profile. + // This isn't ideal - it will make 1 style for all objects owned by the the same person/group. + // This works because the only thing we can really do in this case is show the owner name and link to their profile. + chat.mFromID = from_id ^ gAgent.getSessionID(); + } + + chat.mSourceType = CHAT_SOURCE_OBJECT; + + if(SYSTEM_FROM == name) + { + // System's UUID is NULL (fixes EXT-4766) + chat.mFromID = LLUUID::null; + chat.mSourceType = CHAT_SOURCE_SYSTEM; + } + + // IDEVO Some messages have embedded resident names + message = clean_name_from_task_im(message, from_group); + + LLSD query_string; + query_string["owner"] = from_id; + query_string["slurl"] = location; + query_string["name"] = name; + if (from_group) + { + query_string["groupowned"] = "true"; + } + + chat.mURL = LLSLURL("objectim", session_id, "").getSLURLString(); + chat.mText = message; + + // Note: lie to Nearby Chat, pretending that this is NOT an IM, because + // IMs from obejcts don't open IM sessions. + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(SYSTEM_FROM != name && nearby_chat) + { + chat.mOwnerID = from_id; + LLSD args; + args["slurl"] = location; + args["type"] = LLNotificationsUI::NT_NEARBYCHAT; + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); + } + + + //Object IMs send with from name: 'Second Life' need to be displayed also in notification toasts (EXT-1590) + if (SYSTEM_FROM != name) break; + + LLSD substitutions; + substitutions["NAME"] = name; + substitutions["MSG"] = message; + + LLSD payload; + payload["object_id"] = session_id; + payload["owner_id"] = from_id; + payload["from_id"] = from_id; + payload["slurl"] = location; + payload["name"] = name; + std::string session_name; + if (from_group) + { + payload["group_owned"] = "true"; + } + + LLNotification::Params params("ServerObjectMessage"); + params.substitutions = substitutions; + params.payload = payload; + + LLPostponedNotification::add<LLPostponedServerObjectNotification>(params, from_id, from_group); } - snprintf(buffer, sizeof(buffer), "%s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */ - // Note: lie to LLFloaterChat::addChat(), pretending that this is NOT an IM, because - // IMs from objcts don't open IM sessions. - chat.mText = buffer; - chat.mSourceType = CHAT_SOURCE_OBJECT; - LLFloaterChat::addChat(chat, FALSE, FALSE); break; case IM_FROM_TASK_AS_ALERT: if (is_busy && !is_owned_by_me) @@ -1817,22 +2758,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } { // Construct a viewer alert for this message. - args["[NAME]"] = name; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("ObjectMessage", args); + args["NAME"] = name; + args["MESSAGE"] = message; + LLNotificationsUtil::add("ObjectMessage", args); } break; case IM_BUSY_AUTO_RESPONSE: - // fix for JIRA issue VWR-20 submitted 13-JAN-2007 - Paul Churchill if (is_muted) { - lldebugs << "Ignoring busy response from " << from_id << llendl; + LL_DEBUGS("Messaging") << "Ignoring busy response from " << from_id << LL_ENDL; return; } else { - // original code resumes - gIMMgr->addMessage(session_id, from_id, name, message); + // TODO: after LLTrans hits release, get "busy response" into translatable file + buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.c_str()); + gIMMgr->addMessage(session_id, from_id, name, buffer); } break; @@ -1848,75 +2789,107 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } else { - // *TODO:translate -> [FIRST] [LAST] (maybe) - LLLureInfo* info = new LLLureInfo(from_id, session_id, FALSE); - args["[NAME]"] = name; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("OfferTeleport", args, - lure_callback, (void*)info); + LLVector3 pos, look_at; + U64 region_handle; + U8 region_access = SIM_ACCESS_MIN; + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + } + + LLSD args; + // *TODO: Translate -> [FIRST] [LAST] (maybe) + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); + args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + LLSD payload; + payload["from_id"] = from_id; + payload["lure_id"] = session_id; + payload["godlike"] = FALSE; + + LLNotification::Params params("TeleportOffered"); + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add<LLPostponedOfferNotification>( params, from_id, false); } } break; case IM_GODLIKE_LURE_USER: { - LLLureInfo* info = new LLLureInfo(from_id, session_id, TRUE); + LLSD payload; + payload["from_id"] = from_id; + payload["lure_id"] = session_id; + payload["godlike"] = TRUE; // do not show a message box, because you're about to be // teleported. - lure_callback(0, (void *)info); + LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); } break; case IM_GOTO_URL: { + LLSD args; // n.b. this is for URLs sent by the system, not for // URLs sent by scripts (i.e. llLoadURL) if (binary_bucket_size <= 0) { - llwarns << "bad binary_bucket_size: " + LL_WARNS("Messaging") << "bad binary_bucket_size: " << binary_bucket_size - << " - aborting function." << llendl; + << " - aborting function." << LL_ENDL; return; } - char* url = new char[binary_bucket_size]; - if (url == NULL) - { - llerrs << "Memory Allocation failed" << llendl; - return; - } - - strncpy(url, (char*)binary_bucket, binary_bucket_size-1); /* Flawfinder: ignore */ - url[binary_bucket_size-1] = '\0'; - args["[MESSAGE]"] = message; - args["[URL]"] = url; - LLNotifyBox::showXml("GotoURL", args, - goto_url_callback, (void*)url); + std::string url; + + url.assign((char*)binary_bucket, binary_bucket_size-1); + args["MESSAGE"] = message; + args["URL"] = url; + LLSD payload; + payload["url"] = url; + LLNotificationsUtil::add("GotoURL", args, payload ); } break; case IM_FRIENDSHIP_OFFERED: { - LLFriendshipOffer* offer = new LLFriendshipOffer; - offer->mFromID = from_id; - offer->mTransactionID = session_id; - offer->mOnline = (offline == IM_ONLINE); - offer->mHost = msg->getSender(); + LLSD payload; + payload["from_id"] = from_id; + payload["session_id"] = session_id;; + payload["online"] = (offline == IM_ONLINE); + payload["sender"] = msg->getSender().getIPandPort(); if (is_busy) { busy_message(msg, from_id); - friendship_offer_callback(1, (void*)offer); + LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); } else if (is_muted) { - friendship_offer_callback(1, (void*)offer); + LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); } else { - args["[NAME]"] = name; - LLNotifyBox::showXml("OfferFriendship", args, - &friendship_offer_callback, (void*)offer); + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); + if(message.empty()) + { + //support for frienship offers from clients before July 2008 + LLNotificationsUtil::add("OfferFriendshipNoMessage", args, payload); + } + else + { + args["[MESSAGE]"] = message; + LLNotification::Params params("OfferFriendship"); + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add<LLPostponedOfferNotification>( params, from_id, false); + } } } break; @@ -1932,19 +2905,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) strings.push_back(from_id.asString()); send_generic_message("requestonlinenotification", strings); - args["[NAME]"] = name; - LLNotifyBox::showXml("FriendshipAccepted", args); + args["NAME"] = name; + LLSD payload; + payload["from_id"] = from_id; + LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback, + _1, + _2, + "FriendshipAccepted", + args, + payload)); } break; - case IM_FRIENDSHIP_DECLINED: - args["[NAME]"] = name; - LLNotifyBox::showXml("FriendshipDeclined", args); - break; - + case IM_FRIENDSHIP_DECLINED_DEPRECATED: default: - llwarns << "Instant message calling for unknown dialog " - << (S32)dialog << llendl; + LL_WARNS("Messaging") << "Instant message calling for unknown dialog " + << (S32)dialog << LL_ENDL; break; } @@ -1959,76 +2935,26 @@ void busy_message (LLMessageSystem* msg, LLUUID from_id) { if (gAgent.getBusy()) { - LLString response = gSavedPerAccountSettings.getText("BusyModeResponse"); + std::string my_name; + LLAgentUI::buildFullname(my_name); + std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); pack_instant_message( gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), from_id, - SYSTEM_FROM, - response.c_str(), + my_name, + response, IM_ONLINE, - IM_CONSOLE_AND_CHAT_HISTORY); + IM_BUSY_AUTO_RESPONSE); gAgent.sendReliableMessage(); } } -void friendship_offer_callback(S32 option, void* user_data) +bool callingcard_offer_callback(const LLSD& notification, const LLSD& response) { - LLFriendshipOffer* offer = (LLFriendshipOffer*)user_data; - if(!offer) return; - LLUUID fid; - LLMessageSystem* msg = gMessageSystem; - switch(option) - { - case 0: - // accept - LLAvatarTracker::formFriendship(offer->mFromID); - - fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); - - // This will also trigger an onlinenotification if the user is online - msg->newMessageFast(_PREHASH_AcceptFriendship); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, fid); - msg->sendReliable(offer->mHost); - break; - case 1: - // decline - msg->newMessageFast(_PREHASH_DeclineFriendship); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID); - msg->sendReliable(offer->mHost); - break; - default: - // close button probably, possibly timed out - break; - } - - delete offer; - offer = NULL; -} - -struct LLCallingCardOfferData -{ - LLUUID mTransactionID; - LLUUID mSourceID; - LLHost mHost; -}; - -void callingcard_offer_callback(S32 option, void* user_data) -{ - LLCallingCardOfferData* offerdata = (LLCallingCardOfferData*)user_data; - if(!offerdata) return; + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLUUID fid; LLUUID from_id; LLMessageSystem* msg = gMessageSystem; @@ -2041,11 +2967,11 @@ void callingcard_offer_callback(S32 option, void* user_data) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID); - fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); + msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); + fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); msg->nextBlockFast(_PREHASH_FolderData); msg->addUUIDFast(_PREHASH_FolderID, fid); - msg->sendReliable(offerdata->mHost); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); break; case 1: // decline @@ -2054,36 +2980,36 @@ void callingcard_offer_callback(S32 option, void* user_data) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID); - msg->sendReliable(offerdata->mHost); - busy_message(msg, offerdata->mSourceID); + msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); + busy_message(msg, notification["payload"]["source_id"].asUUID()); break; default: // close button probably, possibly timed out break; } - delete offerdata; - offerdata = NULL; + return false; } +static LLNotificationFunctorRegistration callingcard_offer_cb_reg("OfferCallingCard", callingcard_offer_callback); void process_offer_callingcard(LLMessageSystem* msg, void**) { // someone has offered to form a friendship - lldebugs << "callingcard offer" << llendl; + LL_DEBUGS("Messaging") << "callingcard offer" << LL_ENDL; LLUUID source_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id); LLUUID tid; msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid); - LLCallingCardOfferData* offerdata = new LLCallingCardOfferData; - offerdata->mTransactionID = tid; - offerdata->mSourceID = source_id; - offerdata->mHost = msg->getSender(); + LLSD payload; + payload["transaction_id"] = tid; + payload["source_id"] = source_id; + payload["sender"] = msg->getSender().getIPandPort(); LLViewerObject* source = gObjectList.findObject(source_id); - LLString::format_map_t args; + LLSD args; std::string source_name; if(source && source->isAvatar()) { @@ -2091,53 +3017,89 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) LLNameValue* nvlast = source->getNVPair("LastName"); if (nvfirst && nvlast) { - args["[FIRST]"] = nvfirst->getString(); - args["[LAST]"] = nvlast->getString(); - source_name = std::string(nvfirst->getString()) + " " + nvlast->getString(); + source_name = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); } } if(!source_name.empty()) { if (gAgent.getBusy() - || gMuteListp->isMuted(source_id, source_name, LLMute::flagTextChat)) + || LLMuteList::getInstance()->isMuted(source_id, source_name, LLMute::flagTextChat)) { // automatically decline offer - callingcard_offer_callback(1, (void*)offerdata); - offerdata = NULL; // pointer was freed by callback + LLNotifications::instance().forceResponse(LLNotification::Params("OfferCallingCard").payload(payload), 1); } else { - LLNotifyBox::showXml("OfferCallingCard", args, - &callingcard_offer_callback, (void*)offerdata); - offerdata = NULL; // pointer ownership transferred + LLNotificationsUtil::add("OfferCallingCard", args, payload); } } else { - llwarns << "Calling card offer from an unknown source." << llendl; + LL_WARNS("Messaging") << "Calling card offer from an unknown source." << LL_ENDL; } - - delete offerdata; // !=NULL if we didn't give ownership away - offerdata = NULL; } void process_accept_callingcard(LLMessageSystem* msg, void**) { - LLNotifyBox::showXml("CallingCardAccepted"); + LLNotificationsUtil::add("CallingCardAccepted"); } void process_decline_callingcard(LLMessageSystem* msg, void**) { - LLNotifyBox::showXml("CallingCardDeclined"); + LLNotificationsUtil::add("CallingCardDeclined"); } +class ChatTranslationReceiver : public LLTranslate::TranslationReceiver +{ +public : + ChatTranslationReceiver(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, + const LLChat &chat, const LLSD &toast_args) + : LLTranslate::TranslationReceiver(from_lang, to_lang), + m_chat(chat), + m_toastArgs(toast_args), + m_origMesg(mesg) + { + } + static boost::intrusive_ptr<ChatTranslationReceiver> build(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, const LLChat &chat, const LLSD &toast_args) + { + return boost::intrusive_ptr<ChatTranslationReceiver>(new ChatTranslationReceiver(from_lang, to_lang, mesg, chat, toast_args)); + } + +protected: + void handleResponse(const std::string &translation, const std::string &detected_language) + { + // filter out non-interesting responeses + if ( !translation.empty() + && (m_toLang != detected_language) + && (LLStringUtil::compareInsensitive(translation, m_origMesg) != 0) ) + { + m_chat.mText += " (" + translation + ")"; + } + + LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); + } + + void handleFailure() + { + LLTranslate::TranslationReceiver::handleFailure(); + m_chat.mText += " (?)"; + + LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); + } + +private: + LLChat m_chat; + std::string m_origMesg; + LLSD m_toastArgs; +}; void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { - LLChat chat; - char mesg[DB_CHAT_MSG_BUF_SIZE]; /* Flawfinder: ignore */ - char from_name[DB_FULL_NAME_BUF_SIZE]; /* Flawfinder: ignore */ + LLChat chat; + std::string mesg; + std::string from_name; U8 source_temp; U8 type_temp; U8 audible_temp; @@ -2147,15 +3109,14 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) BOOL is_owned_by_me = FALSE; LLViewerObject* chatter; - msg->getString("ChatData", "FromName", DB_FULL_NAME_BUF_SIZE, from_name); - chat.mFromName = from_name; + msg->getString("ChatData", "FromName", from_name); msg->getUUID("ChatData", "SourceID", from_id); chat.mFromID = from_id; // Object owner for objects msg->getUUID("ChatData", "OwnerID", owner_id); - + msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp); chat.mSourceType = (EChatSourceType)source_temp; @@ -2167,33 +3128,49 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) chat.mTime = LLFrameTimer::getElapsedSeconds(); - BOOL is_self = (from_id == gAgent.getID()); + // IDEVO Correct for new-style "Resident" names + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // I don't know if it's OK to change this here, if + // anything downstream does lookups by name, for instance + + LLAvatarName av_name; + if (LLAvatarNameCache::get(from_id, &av_name)) + { + chat.mFromName = av_name.mDisplayName; + } + else + { + chat.mFromName = LLCacheName::cleanFullName(from_name); + } + } + else + { + chat.mFromName = from_name; + } + BOOL is_busy = gAgent.getBusy(); - // Apparently you can receive chat before app is fully initialized, hence - // gMuteListp can be null. JC BOOL is_muted = FALSE; BOOL is_linden = FALSE; - if (gMuteListp) - { - is_muted = gMuteListp->isMuted( - from_id, - from_name, - LLMute::flagTextChat) - || gMuteListp->isMuted(owner_id, LLMute::flagTextChat); - is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && - gMuteListp->isLinden(from_name); - } + is_muted = LLMuteList::getInstance()->isMuted( + from_id, + from_name, + LLMute::flagTextChat) + || LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagTextChat); + is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && + LLMuteList::getInstance()->isLinden(from_name); BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); chatter = gObjectList.findObject(from_id); if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); - + // Make swirly things only for talking objects. (not script debug messages, though) if (chat.mSourceType == CHAT_SOURCE_OBJECT - && chat.mChatType != CHAT_TYPE_DEBUG_MSG) + && chat.mChatType != CHAT_TYPE_DEBUG_MSG + && gSavedSettings.getBOOL("EffectScriptChatParticles") ) { LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); psc->setSourceObject(chatter); @@ -2201,7 +3178,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) //We set the particles to be owned by the object's owner, //just in case they should be muted by the mute list psc->setOwnerUUID(owner_id); - gWorldPointer->mPartSim.addPartSource(psc); + LLViewerPartSim::getInstance()->addPartSource(psc); } // record last audible utterance @@ -2224,26 +3201,22 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) std::string verb; color.setVec(1.f,1.f,1.f,1.f); - msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, DB_CHAT_MSG_BUF_SIZE, mesg); + msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); BOOL ircstyle = FALSE; // Look for IRC-style emotes here so chatbubbles work - if (!strncmp(mesg, "/me ", 4) || !strncmp(mesg, "/me'", 4)) + std::string prefix = mesg.substr(0, 4); + if (prefix == "/me " || prefix == "/me'") { - chat.mText = from_name; - chat.mText += (mesg + 3); ircstyle = TRUE; } - else - { - chat.mText = mesg; - } + chat.mText = mesg; // Look for the start of typing so we can put "..." in the bubbles. if (CHAT_TYPE_START == chat.mChatType) { - gLocalSpeakerMgr->setSpeakerTyping(from_id, TRUE); + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, TRUE); // Might not have the avatar constructed yet, eg on login. if (chatter && chatter->isAvatar()) @@ -2254,7 +3227,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) } else if (CHAT_TYPE_STOP == chat.mChatType) { - gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE); + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); // Might not have the avatar constructed yet, eg on login. if (chatter && chatter->isAvatar()) @@ -2264,22 +3237,12 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) return; } - // We have a real utterance now, so can stop showing "..." and proceed. - if (chatter && chatter->isAvatar()) - { - gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE); - ((LLVOAvatar*)chatter)->stopTyping(); - - if (!is_muted && !is_busy) - { - visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles"); - ((LLVOAvatar*)chatter)->addChat(chat); - } - } - // Look for IRC-style emotes if (ircstyle) { + // set CHAT_STYLE_IRC to avoid adding Avatar Name as author of message. See EXT-656 + chat.mChatStyle = CHAT_STYLE_IRC; + // Do nothing, ircstyle is fixed above for chat bubbles } else @@ -2287,52 +3250,49 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) switch(chat.mChatType) { case CHAT_TYPE_WHISPER: - if (is_self) - { - verb = " whisper: "; - } - else - { - verb = " whispers: "; - } + verb = LLTrans::getString("whisper") + " "; break; case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: case CHAT_TYPE_NORMAL: - verb = ": "; + verb = ""; break; case CHAT_TYPE_SHOUT: - if (is_self) - { - verb = " shout: "; - } - else - { - verb = " shouts: "; - } + verb = LLTrans::getString("shout") + " "; break; case CHAT_TYPE_START: case CHAT_TYPE_STOP: - llwarns << "Got chat type start/stop in main chat processing." << llendl; + LL_WARNS("Messaging") << "Got chat type start/stop in main chat processing." << LL_ENDL; break; default: - llwarns << "Unknown type " << chat.mChatType << " in chat!" << llendl; - verb = " say, "; + LL_WARNS("Messaging") << "Unknown type " << chat.mChatType << " in chat!" << LL_ENDL; + verb = ""; break; } - if (is_self) - { - chat.mText = "You"; - } - else - { - chat.mText = from_name; - } + + chat.mText = ""; chat.mText += verb; chat.mText += mesg; } + // We have a real utterance now, so can stop showing "..." and proceed. + if (chatter && chatter->isAvatar()) + { + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); + ((LLVOAvatar*)chatter)->stopTyping(); + + if (!is_muted && !is_busy) + { + visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles"); + std::string formated_msg = ""; + LLViewerChat::formatChatMsg(chat, formated_msg); + LLChat chat_bubble = chat; + chat_bubble.mText = formated_msg; + ((LLVOAvatar*)chatter)->addChat(chat_bubble); + } + } + if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); @@ -2351,18 +3311,28 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // T * * * F Yes Yes chat.mMuted = is_muted && !is_linden; - - - if (!visible_in_chat_bubble - && (is_linden || !is_busy || is_owned_by_me)) + + // pass owner_id to chat so that we can display the remote + // object inspect for an object that is chatting with you + LLSD args; + args["type"] = LLNotificationsUI::NT_NEARBYCHAT; + chat.mOwnerID = owner_id; + + if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) { - // show on screen and add to history - LLFloaterChat::addChat(chat, FALSE, FALSE); + if (chat.mChatStyle == CHAT_STYLE_IRC) + { + mesg = mesg.substr(4, std::string::npos); + } + const std::string from_lang = ""; // leave empty to trigger autodetect + const std::string to_lang = LLTranslate::getTranslateLanguage(); + + LLHTTPClient::ResponderPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); + LLTranslate::translateMessage(result, from_lang, to_lang, mesg); } else { - // just add to chat history - LLFloaterChat::addChatHistory(chat); + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); } } } @@ -2379,13 +3349,15 @@ void process_teleport_start(LLMessageSystem *msg, void**) U32 teleport_flags = 0x0; msg->getU32("Info", "TeleportFlags", teleport_flags); + LL_DEBUGS("Messaging") << "Got TeleportStart with TeleportFlags=" << teleport_flags << ". gTeleportDisplay: " << gTeleportDisplay << ", gAgent.mTeleportState: " << gAgent.getTeleportState() << LL_ENDL; + if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) { - gViewerWindow->setProgressCancelButtonVisible(FALSE, ""); + gViewerWindow->setProgressCancelButtonVisible(FALSE); } else { - gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel"); + gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); } // Freeze the UI and show progress bar @@ -2397,6 +3369,7 @@ void process_teleport_start(LLMessageSystem *msg, void**) gAgent.setTeleportState( LLAgent::TELEPORT_START ); make_ui_sound("UISndTeleportOut"); + LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " << teleport_flags << LL_ENDL; // Don't call LLFirstUse::useTeleport here because this could be // due to being killed, which would send you home, not to a Telehub } @@ -2409,26 +3382,26 @@ void process_teleport_progress(LLMessageSystem* msg, void**) if((gAgent.getID() != agent_id) || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)) { - llwarns << "Unexpected teleport progress message." << llendl; + LL_WARNS("Messaging") << "Unexpected teleport progress message." << LL_ENDL; return; } U32 teleport_flags = 0x0; msg->getU32("Info", "TeleportFlags", teleport_flags); if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) { - gViewerWindow->setProgressCancelButtonVisible(FALSE, ""); + gViewerWindow->setProgressCancelButtonVisible(FALSE); } else { - gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel"); + gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); } - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - msg->getString("Info", "Message", MAX_STRING, buffer); - lldebugs << "teleport progress: " << buffer << llendl; + std::string buffer; + msg->getString("Info", "Message", buffer); + LL_DEBUGS("Messaging") << "teleport progress: " << buffer << LL_ENDL; //Sorta hacky...default to using simulator raw messages //if we don't find the coresponding mapping in our progress mappings - LLString message = buffer; + std::string message = buffer; if (LLAgent::sTeleportProgressMessages.find(buffer) != LLAgent::sTeleportProgressMessages.end() ) @@ -2442,7 +3415,9 @@ void process_teleport_progress(LLMessageSystem* msg, void**) class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver { public: - LLFetchInWelcomeArea() {} + LLFetchInWelcomeArea(const uuid_vec_t &ids) : + LLInventoryFetchDescendentsObserver(ids) + {} virtual void done() { LLIsType is_landmark(LLAssetType::AT_LANDMARK); @@ -2453,8 +3428,8 @@ public: LLInventoryModel::cat_array_t land_cats; LLInventoryModel::item_array_t land_items; - folder_ref_t::iterator it = mCompleteFolders.begin(); - folder_ref_t::iterator end = mCompleteFolders.end(); + uuid_vec_t::iterator it = mComplete.begin(); + uuid_vec_t::iterator end = mComplete.end(); for(; it != end; ++it) { gInventory.collectDescendentsIf( @@ -2470,18 +3445,18 @@ public: LLInventoryModel::EXCLUDE_TRASH, is_card); } - LLString::format_map_t args; + LLSD args; if ( land_items.count() > 0 ) { // Show notification that they can now teleport to landmarks. Use a random landmark from the inventory S32 random_land = ll_rand( land_items.count() - 1 ); - args["[NAME]"] = land_items[random_land]->getName(); - LLNotifyBox::showXml("TeleportToLandmark",args); + args["NAME"] = land_items[random_land]->getName(); + LLNotificationsUtil::add("TeleportToLandmark",args); } if ( card_items.count() > 0 ) { // Show notification that they can now contact people. Use a random calling card from the inventory S32 random_card = ll_rand( card_items.count() - 1 ); - args["[NAME]"] = card_items[random_card]->getName(); - LLNotifyBox::showXml("TeleportToPerson",args); + args["NAME"] = card_items[random_card]->getName(); + LLNotificationsUtil::add("TeleportToPerson",args); } gInventory.removeObserver(this); @@ -2515,19 +3490,18 @@ BOOL LLPostTeleportNotifiers::tick() if ( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) { // get callingcards and landmarks available to the user arriving. - LLInventoryFetchDescendentsObserver::folder_ref_t folders; - LLUUID folder_id; - folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); - if(folder_id.notNull()) - folders.push_back(folder_id); - folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK); + uuid_vec_t folders; + const LLUUID callingcard_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + if(callingcard_id.notNull()) + folders.push_back(callingcard_id); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); if(folder_id.notNull()) folders.push_back(folder_id); if(!folders.empty()) { - LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea; - fetcher->fetchDescendents(folders); - if(fetcher->isEverythingComplete()) + LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea(folders); + fetcher->startFetch(); + if(fetcher->isFinished()) { fetcher->done(); } @@ -2548,21 +3522,24 @@ BOOL LLPostTeleportNotifiers::tick() // We're going to pretend to be a new agent void process_teleport_finish(LLMessageSystem* msg, void**) { - //llinfos << "Got teleport location message" << llendl; + LL_DEBUGS("Messaging") << "Got teleport location message" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { - llwarns << "Got teleport notification for wrong agent!" << llendl; + LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; return; } + + // Teleport is finished; it can't be cancelled now. + gViewerWindow->setProgressCancelButtonVisible(FALSE); // Do teleport effect for where you're leaving // VEFFECT: TeleportStart - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); effectp->setPositionGlobal(gAgent.getPositionGlobal()); effectp->setColor(LLColor4U(gAgent.getEffectColor())); - gHUDManager->sendEffects(); + LLHUDManager::getInstance()->sendEffects(); U32 location_id; U32 sim_ip; @@ -2579,9 +3556,8 @@ void process_teleport_finish(LLMessageSystem* msg, void**) msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); - char seedCap[STD_STRING_BUF_SIZE]; /* Flawfinder: ignore */ - msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, - STD_STRING_BUF_SIZE, seedCap); + std::string seedCap; + msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, seedCap); // update home location if we are teleporting out of prelude - specific to teleporting to welcome area if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET) @@ -2598,23 +3574,22 @@ void process_teleport_finish(LLMessageSystem* msg, void**) // Viewer trusts the simulator. gMessageSystem->enableCircuit(sim_host, TRUE); - if(!gWorldp) return; - LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host); + LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); /* // send camera update to new region - gAgent.updateCamera(); + gAgentCamera.updateCamera(); // likewise make sure the camera is behind the avatar - gAgent.resetView(TRUE); + gAgentCamera.resetView(TRUE); LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal()); gAgent.setRegion(regionp); gObjectList.shiftObjects(shift_vector); - if (gAgent.getAvatarObject()) + if (isAgentAvatarValid()) { - gAgent.getAvatarObject()->clearChatText(); - gAgent.slamLookAt(look_at); + gAgentAvatarp->clearChatText(); + gAgentCamera.slamLookAt(look_at); } gAgent.setPositionAgent(pos); gAssetStorage->setUpstream(sim); @@ -2622,8 +3597,8 @@ void process_teleport_finish(LLMessageSystem* msg, void**) */ // now, use the circuit info to tell simulator about us! - llinfos << "process_teleport_finish() Enabling " - << sim_host << " with code " << msg->mOurCircuitCode << llendl; + LL_INFOS("Messaging") << "process_teleport_finish() Enabling " + << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode()); @@ -2635,7 +3610,7 @@ void process_teleport_finish(LLMessageSystem* msg, void**) gAgent.setTeleportState( LLAgent::TELEPORT_MOVING ); gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages["contacting"]); - regionp->setSeedCapability(std::string(seedCap)); + regionp->setSeedCapability(seedCap); // Don't send camera updates to the new region until we're // actually there... @@ -2643,18 +3618,15 @@ void process_teleport_finish(LLMessageSystem* msg, void**) // Now do teleport effect for where you're going. // VEFFECT: TeleportEnd - effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); effectp->setPositionGlobal(gAgent.getPositionGlobal()); effectp->setColor(LLColor4U(gAgent.getEffectColor())); - gHUDManager->sendEffects(); + LLHUDManager::getInstance()->sendEffects(); // gTeleportDisplay = TRUE; // gTeleportDisplayTimer.reset(); // gViewerWindow->setShowProgress(TRUE); - - // This could be first use of teleport, so test for that - LLFirstUse::useTeleport(); } // stuff we have to do every time we get an AvatarInitComplete from a sim @@ -2667,16 +3639,9 @@ void process_avatar_init_complete(LLMessageSystem* msg, void**) } */ -static void display_release_message(S32, void* data) -{ - std::string* msg = (std::string*)data; - LLFloaterReleaseMsg::displayMessage(msg->c_str()); - delete msg; -} - void process_agent_movement_complete(LLMessageSystem* msg, void**) { - gAgentMovementCompleted = TRUE; + gAgentMovementCompleted = true; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); @@ -2684,12 +3649,12 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) { - llwarns << "Incorrect id in process_agent_movement_complete()" - << llendl; + LL_WARNS("Messaging") << "Incorrect id in process_agent_movement_complete()" + << LL_ENDL; return; } - llinfos << "process_agent_movement_complete()" << llendl; + LL_DEBUGS("Messaging") << "process_agent_movement_complete()" << LL_ENDL; // *TODO: check timestamp to make sure the movement compleation // makes sense. @@ -2700,38 +3665,36 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) U64 region_handle; msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle); - char version_channel_char[MAX_STRING]; - msg->getString("SimData", "ChannelVersion", MAX_STRING, version_channel_char); + std::string version_channel; + msg->getString("SimData", "ChannelVersion", version_channel); - LLVOAvatar* avatarp = gAgent.getAvatarObject(); - if (!avatarp) + if (!isAgentAvatarValid()) { // Could happen if you were immediately god-teleported away on login, - // maybe other cases. Continue, but warn. JC - llwarns << "agent_movement_complete() with NULL avatarp." << llendl; + // maybe other cases. Continue, but warn. + LL_WARNS("Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; } F32 x, y; from_region_handle(region_handle, &x, &y); - if(!gWorldp) return; - LLViewerRegion* regionp = gWorldp->getRegionFromHandle(region_handle); + LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); if (!regionp) { if (gAgent.getRegion()) { - llwarns << "current region " << gAgent.getRegion()->getOriginGlobal() << llendl; + LL_WARNS("Messaging") << "current region " << gAgent.getRegion()->getOriginGlobal() << LL_ENDL; } - llwarns << "Agent being sent to invalid home region: " + LL_WARNS("Messaging") << "Agent being sent to invalid home region: " << x << ":" << y << " current pos " << gAgent.getPositionGlobal() - << llendl; - LLAppViewer::instance()->forceDisconnect("You were sent to an invalid region."); + << LL_ENDL; + LLAppViewer::instance()->forceDisconnect(LLTrans::getString("SentToInvalidRegion")); return; } - llinfos << "Changing home region to " << x << ":" << y << llendl; + LL_INFOS("Messaging") << "Changing home region to " << x << ":" << y << LL_ENDL; // set our upstream host the new simulator and shuffle things as // appropriate. @@ -2748,10 +3711,16 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) if( is_teleport ) { - // Force the camera back onto the agent, don't animate. JC - gAgent.setFocusOnAvatar(TRUE, FALSE); - gAgent.slamLookAt(look_at); - gAgent.updateCamera(); + if (gAgent.getTeleportKeepsLookAt()) + { + // *NOTE: the LookAt data we get from the sim here doesn't + // seem to be useful, so get it from the camera instead + look_at = LLViewerCamera::getInstance()->getAtAxis(); + } + // Force the camera back onto the agent, don't animate. + gAgentCamera.setFocusOnAvatar(TRUE, FALSE); + gAgentCamera.slamLookAt(look_at); + gAgentCamera.updateCamera(); gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL ); @@ -2759,17 +3728,24 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) // know what you look like. gAgent.sendAgentSetAppearance(); - if (avatarp) + if (isAgentAvatarValid()) { // Chat the "back" SLURL. (DEV-4907) - LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL()); - chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); + + LLSLURL slurl; + gAgent.getTeleportSourceSLURL(slurl); + LLSD substitution = LLSD().with("[T_SLURL]", slurl.getSLURLString()); + std::string completed_from = LLAgent::sTeleportProgressMessages["completed_from"]; + LLStringUtil::format(completed_from, substitution); + + LLSD args; + args["MESSAGE"] = completed_from; + LLNotificationsUtil::add("SystemMessageTip", args); // Set the new position - avatarp->setPositionAgent(agent_pos); - avatarp->clearChat(); - avatarp->slamPosition(); + gAgentAvatarp->setPositionAgent(agent_pos); + gAgentAvatarp->clearChat(); + gAgentAvatarp->slamPosition(); } } else @@ -2787,7 +3763,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { LLTracker::stopTracking(NULL); } - else if ( is_teleport ) + else if ( is_teleport && !gAgent.getTeleportKeepsLookAt() ) { //look at the beacon LLVector3 global_agent_pos = agent_pos; @@ -2795,7 +3771,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) global_agent_pos[1] += y; look_at = (LLVector3)beacon_pos - global_agent_pos; look_at.normVec(); - gAgent.slamLookAt(look_at); + gAgentCamera.slamLookAt(look_at); } } @@ -2829,18 +3805,53 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) gAgent.clearBusy(); } - if (avatarp) + if (isAgentAvatarValid()) { - avatarp->mFootPlane.clearVec(); + gAgentAvatarp->mFootPlane.clearVec(); } // send walk-vs-run status gAgent.sendWalkRun(gAgent.getRunning() || gAgent.getAlwaysRun()); - if (LLFloaterReleaseMsg::checkVersion(version_channel_char)) + // If the server version has changed, display an info box and offer + // to display the release notes, unless this is the initial log in. + if (gLastVersionChannel == version_channel) { - LLNotifyBox::showXml("ServerVersionChanged", display_release_message, new std::string(version_channel_char) ); + return; + } + + if (!gLastVersionChannel.empty()) + { + // work out the URL for this server's Release Notes + std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/"; + std::string server_version = version_channel; + std::vector<std::string> s_vect; + boost::algorithm::split(s_vect, server_version, isspace); + for(U32 i = 0; i < s_vect.size(); i++) + { + if (i != (s_vect.size() - 1)) + { + if(i != (s_vect.size() - 2)) + { + url += s_vect[i] + "_"; + } + else + { + url += s_vect[i] + "/"; + } + } + else + { + url += s_vect[i].substr(0,4); + } + } + + LLSD args; + args["URL"] = url; + LLNotificationsUtil::add("ServerVersionChanged", args); } + + gLastVersionChannel = version_channel; } void process_crossed_region(LLMessageSystem* msg, void**) @@ -2851,11 +3862,11 @@ void process_crossed_region(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) { - llwarns << "Incorrect id in process_crossed_region()" - << llendl; + LL_WARNS("Messaging") << "Incorrect id in process_crossed_region()" + << LL_ENDL; return; } - llinfos << "process_crossed_region()" << llendl; + LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; U32 sim_ip; msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); @@ -2865,14 +3876,13 @@ void process_crossed_region(LLMessageSystem* msg, void**) U64 region_handle; msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); - char seedCap[STD_STRING_BUF_SIZE]; /* Flawfinder: ignore */ - msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, STD_STRING_BUF_SIZE, seedCap); + std::string seedCap; + msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, seedCap); send_complete_agent_movement(sim_host); - if(!gWorldp) return; - LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host); - regionp->setSeedCapability(std::string(seedCap)); + LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); + regionp->setSeedCapability(seedCap); } @@ -2884,6 +3894,7 @@ const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less th const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot // between these values we delay the updates (but no more than one second) +static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE_SEND("Send Message"); void send_agent_update(BOOL force_send, BOOL send_reliable) { @@ -2900,6 +3911,12 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) return; } + // no region to send update to + if(gAgent.getRegion() == NULL) + { + return; + } + const F32 TRANSLATE_THRESHOLD = 0.01f; // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation @@ -2932,7 +3949,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion(); LLQuaternion head_rotation = gAgent.getHeadRotation(); - camera_pos_agent = gAgent.getCameraPositionAgent(); + camera_pos_agent = gAgentCamera.getCameraPositionAgent(); render_state = gAgent.getRenderState(); @@ -2940,7 +3957,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) U8 flag_change = 0; cam_center_chg = last_camera_pos_agent - camera_pos_agent; - cam_rot_chg = last_camera_at - gCamera->getAtAxis(); + cam_rot_chg = last_camera_at - LLViewerCamera::getInstance()->getAtAxis(); // If a modifier key is held down, turn off // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't @@ -2962,6 +3979,10 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) { flags |= AU_FLAGS_HIDETITLE; } + if (gAgent.getAutoPilot()) + { + flags |= AU_FLAGS_CLIENT_AUTOPILOT; + } flag_change = last_flags ^ flags; @@ -2978,24 +3999,24 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) /* if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) { - //llinfos << "head rot " << head_rotation << llendl; - llinfos << "head_rot_chg = " << head_rot_chg << llendl; + //LL_INFOS("Messaging") << "head rot " << head_rotation << LL_ENDL; + LL_INFOS("Messaging") << "head_rot_chg = " << head_rot_chg << LL_ENDL; } if (cam_rot_chg.magVec() > ROTATION_THRESHOLD) { - llinfos << "cam rot " << cam_rot_chg.magVec() << llendl; + LL_INFOS("Messaging") << "cam rot " << cam_rot_chg.magVec() << LL_ENDL; } if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) { - llinfos << "cam center " << cam_center_chg.magVec() << llendl; + LL_INFOS("Messaging") << "cam center " << cam_center_chg.magVec() << LL_ENDL; } // if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD) // { -// llinfos << "drag delta " << drag_delta_chg.magVec() << llendl; +// LL_INFOS("Messaging") << "drag delta " << drag_delta_chg.magVec() << LL_ENDL; // } if (control_flag_change) { - llinfos << "dcf = " << control_flag_change << llendl; + LL_INFOS("Messaging") << "dcf = " << control_flag_change << LL_ENDL; } */ @@ -3032,6 +4053,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) if (duplicate_count < DUP_MSGS && !gDisconnected) { + LLFastTimer t(FTM_AGENT_UPDATE_SEND); // Build the message msg->newMessageFast(_PREHASH_AgentUpdate); msg->nextBlockFast(_PREHASH_AgentData); @@ -3044,14 +4066,14 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) // if (camera_pos_agent.mV[VY] > 255.f) // { -// llinfos << "Sending camera center " << camera_pos_agent << llendl; +// LL_INFOS("Messaging") << "Sending camera center " << camera_pos_agent << LL_ENDL; // } msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent); - msg->addVector3Fast(_PREHASH_CameraAtAxis, gCamera->getAtAxis()); - msg->addVector3Fast(_PREHASH_CameraLeftAxis, gCamera->getLeftAxis()); - msg->addVector3Fast(_PREHASH_CameraUpAxis, gCamera->getUpAxis()); - msg->addF32Fast(_PREHASH_Far, gAgent.mDrawDistance); + msg->addVector3Fast(_PREHASH_CameraAtAxis, LLViewerCamera::getInstance()->getAtAxis()); + msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis()); + msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); + msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); msg->addU32Fast(_PREHASH_ControlFlags, control_flags); @@ -3059,12 +4081,12 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) { if (control_flags & AGENT_CONTROL_LBUTTON_DOWN) { - llinfos << "AgentUpdate left button down" << llendl; + LL_INFOS("Messaging") << "AgentUpdate left button down" << LL_ENDL; } if (control_flags & AGENT_CONTROL_LBUTTON_UP) { - llinfos << "AgentUpdate left button up" << llendl; + LL_INFOS("Messaging") << "AgentUpdate left button up" << LL_ENDL; } } @@ -3079,15 +4101,15 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) gAgent.sendReliableMessage(); } - //llinfos << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << llendl; +// LL_DEBUGS("Messaging") << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << LL_ENDL; // Copy the old data last_head_rot = head_rotation; last_render_state = render_state; last_camera_pos_agent = camera_pos_agent; - last_camera_at = gCamera->getAtAxis(); - last_camera_left = gCamera->getLeftAxis(); - last_camera_up = gCamera->getUpAxis(); + last_camera_at = LLViewerCamera::getInstance()->getAtAxis(); + last_camera_left = LLViewerCamera::getInstance()->getLeftAxis(); + last_camera_up = LLViewerCamera::getInstance()->getUpAxis(); last_control_flags = control_flags; last_flags = flags; } @@ -3114,7 +4136,6 @@ void process_object_update(LLMessageSystem *mesgsys, void **user_data) // Update the object... gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL); - stop_glerror(); } void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data) @@ -3132,7 +4153,6 @@ void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data // Update the object... gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED); - stop_glerror(); } void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) @@ -3150,7 +4170,6 @@ void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) // Update the object... gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED); - stop_glerror(); } @@ -3169,11 +4188,12 @@ void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED); } +static LLFastTimer::DeclareTimer FTM_PROCESS_OBJECTS("Process Objects"); void process_kill_object(LLMessageSystem *mesgsys, void **user_data) { - LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS); + LLFastTimer t(FTM_PROCESS_OBJECTS); LLUUID id; U32 local_id; @@ -3192,16 +4212,16 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) gMessageSystem->getSenderPort()); if (id == LLUUID::null) { - //llinfos << "Unknown kill for local " << local_id << llendl; + LL_DEBUGS("Messaging") << "Unknown kill for local " << local_id << LL_ENDL; gObjectList.mNumUnknownKills++; continue; } else { - //llinfos << "Kill message for local " << local_id << llendl; + LL_DEBUGS("Messaging") << "Kill message for local " << local_id << LL_ENDL; } - gSelectMgr->removeObjectFromSelections(id); + LLSelectMgr::getInstance()->removeObjectFromSelections(id); // ...don't kill the avatar if (!(id == gAgentID)) @@ -3228,7 +4248,7 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) } else { - llwarns << "Object in UUID lookup, but not on object list in kill!" << llendl; + LL_WARNS("Messaging") << "Object in UUID lookup, but not on object list in kill!" << LL_ENDL; gObjectList.mNumUnknownKills++; } } @@ -3255,11 +4275,10 @@ void process_time_synch(LLMessageSystem *mesgsys, void **user_data) mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction); mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity); - if(!gWorldp) return; - gWorldp->setSpaceTimeUSec(space_time_usec); + LLWorld::getInstance()->setSpaceTimeUSec(space_time_usec); - //lldebugs << "time_synch() - " << sun_direction << ", " << sun_ang_velocity - // << ", " << phase << llendl; + //LL_DEBUGS("Messaging") << "time_synch() - " << sun_direction << ", " << sun_ang_velocity + // << ", " << phase << LL_ENDL; gSky.setSunPhase(phase); gSky.setSunTargetDirection(sun_direction, sun_ang_velocity); @@ -3272,8 +4291,6 @@ void process_time_synch(LLMessageSystem *mesgsys, void **user_data) void process_sound_trigger(LLMessageSystem *msg, void **) { if (!gAudiop) return; - if (!gParcelMgr) return; - if (!gMuteListp) return; U64 region_handle = 0; F32 gain = 0; @@ -3299,23 +4316,28 @@ void process_sound_trigger(LLMessageSystem *msg, void **) // Don't play a trigger sound if you can't hear it due // to parcel "local audio only" settings. - if (!gParcelMgr->canHearSound(pos_global)) return; + if (!LLViewerParcelMgr::getInstance()->canHearSound(pos_global)) return; // Don't play sounds triggered by someone you muted. - if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return; + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; // Don't play sounds from an object you muted - if (gMuteListp->isMuted(object_id)) return; + if (LLMuteList::getInstance()->isMuted(object_id)) return; // Don't play sounds from an object whose parent you muted if (parent_id.notNull() - && gMuteListp->isMuted(parent_id)) + && LLMuteList::getInstance()->isMuted(parent_id)) { return; } - F32 volume = gSavedSettings.getBOOL("MuteSounds") ? 0.f : (gain * gSavedSettings.getF32("AudioLevelSFX")); - gAudiop->triggerSound(sound_id, owner_id, volume, pos_global); + // Don't play sounds from a region with maturity above current agent maturity + if( !gAgent.canAccessMaturityInRegion( region_handle ) ) + { + return; + } + + gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); } void process_preload_sound(LLMessageSystem *msg, void **user_data) @@ -3336,8 +4358,8 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data) LLViewerObject *objectp = gObjectList.findObject(object_id); if (!objectp) return; - if (gMuteListp->isMuted(object_id)) return; - if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return; + if (LLMuteList::getInstance()->isMuted(object_id)) return; + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; LLAudioSource *sourcep = objectp->getAudioSource(owner_id); if (!sourcep) return; @@ -3348,9 +4370,14 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data) // audio data into a buffer at this point, as it won't actually // help us out. + // Don't play sounds from a region with maturity above current agent maturity + LLVector3d pos_global = objectp->getPositionGlobal(); + if (gAgent.canAccessMaturityAtGlobal(pos_global)) + { // Add audioData starts a transfer internally. sourcep->addAudioData(datap, FALSE); } +} void process_attached_sound(LLMessageSystem *msg, void **user_data) { @@ -3373,10 +4400,18 @@ void process_attached_sound(LLMessageSystem *msg, void **user_data) return; } - if (gMuteListp->isMuted(object_id)) return; + if (LLMuteList::getInstance()->isMuted(object_id)) return; - if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return; + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; + + // Don't play sounds from a region with maturity above current agent maturity + LLVector3d pos = objectp->getPositionGlobal(); + if( !gAgent.canAccessMaturityAtGlobal(pos) ) + { + return; + } + objectp->setAttachedSound(sound_id, owner_id, gain, flags); } @@ -3426,83 +4461,111 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data) switch (stat_id) { case LL_SIM_STAT_TIME_DILATION: - gViewerStats->mSimTimeDilation.addValue(stat_value); + LLViewerStats::getInstance()->mSimTimeDilation.addValue(stat_value); break; case LL_SIM_STAT_FPS: - gViewerStats->mSimFPS.addValue(stat_value); + LLViewerStats::getInstance()->mSimFPS.addValue(stat_value); break; case LL_SIM_STAT_PHYSFPS: - gViewerStats->mSimPhysicsFPS.addValue(stat_value); + LLViewerStats::getInstance()->mSimPhysicsFPS.addValue(stat_value); break; case LL_SIM_STAT_AGENTUPS: - gViewerStats->mSimAgentUPS.addValue(stat_value); + LLViewerStats::getInstance()->mSimAgentUPS.addValue(stat_value); break; case LL_SIM_STAT_FRAMEMS: - gViewerStats->mSimFrameMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimFrameMsec.addValue(stat_value); break; case LL_SIM_STAT_NETMS: - gViewerStats->mSimNetMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimNetMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMOTHERMS: - gViewerStats->mSimSimOtherMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimSimOtherMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSMS: - gViewerStats->mSimSimPhysicsMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimSimPhysicsMsec.addValue(stat_value); break; case LL_SIM_STAT_AGENTMS: - gViewerStats->mSimAgentMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimAgentMsec.addValue(stat_value); break; case LL_SIM_STAT_IMAGESMS: - gViewerStats->mSimImagesMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimImagesMsec.addValue(stat_value); break; case LL_SIM_STAT_SCRIPTMS: - gViewerStats->mSimScriptMsec.addValue(stat_value); + LLViewerStats::getInstance()->mSimScriptMsec.addValue(stat_value); break; case LL_SIM_STAT_NUMTASKS: - gViewerStats->mSimObjects.addValue(stat_value); + LLViewerStats::getInstance()->mSimObjects.addValue(stat_value); break; case LL_SIM_STAT_NUMTASKSACTIVE: - gViewerStats->mSimActiveObjects.addValue(stat_value); + LLViewerStats::getInstance()->mSimActiveObjects.addValue(stat_value); break; case LL_SIM_STAT_NUMAGENTMAIN: - gViewerStats->mSimMainAgents.addValue(stat_value); + LLViewerStats::getInstance()->mSimMainAgents.addValue(stat_value); break; case LL_SIM_STAT_NUMAGENTCHILD: - gViewerStats->mSimChildAgents.addValue(stat_value); + LLViewerStats::getInstance()->mSimChildAgents.addValue(stat_value); break; case LL_SIM_STAT_NUMSCRIPTSACTIVE: - gViewerStats->mSimActiveScripts.addValue(stat_value); + LLViewerStats::getInstance()->mSimActiveScripts.addValue(stat_value); break; - case LL_SIM_STAT_LSLIPS: - gViewerStats->mSimLSLIPS.addValue(stat_value); + case LL_SIM_STAT_SCRIPT_EPS: + LLViewerStats::getInstance()->mSimScriptEPS.addValue(stat_value); break; case LL_SIM_STAT_INPPS: - gViewerStats->mSimInPPS.addValue(stat_value); + LLViewerStats::getInstance()->mSimInPPS.addValue(stat_value); break; case LL_SIM_STAT_OUTPPS: - gViewerStats->mSimOutPPS.addValue(stat_value); + LLViewerStats::getInstance()->mSimOutPPS.addValue(stat_value); break; case LL_SIM_STAT_PENDING_DOWNLOADS: - gViewerStats->mSimPendingDownloads.addValue(stat_value); + LLViewerStats::getInstance()->mSimPendingDownloads.addValue(stat_value); break; case LL_SIM_STAT_PENDING_UPLOADS: - gViewerStats->mSimPendingUploads.addValue(stat_value); + LLViewerStats::getInstance()->mSimPendingUploads.addValue(stat_value); break; case LL_SIM_STAT_PENDING_LOCAL_UPLOADS: - gViewerStats->mSimPendingLocalUploads.addValue(stat_value); + LLViewerStats::getInstance()->mSimPendingLocalUploads.addValue(stat_value); break; case LL_SIM_STAT_TOTAL_UNACKED_BYTES: - gViewerStats->mSimTotalUnackedBytes.addValue(stat_value / 1024.f); + LLViewerStats::getInstance()->mSimTotalUnackedBytes.addValue(stat_value / 1024.f); + break; + case LL_SIM_STAT_PHYSICS_PINNED_TASKS: + LLViewerStats::getInstance()->mPhysicsPinnedTasks.addValue(stat_value); + break; + case LL_SIM_STAT_PHYSICS_LOD_TASKS: + LLViewerStats::getInstance()->mPhysicsLODTasks.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSSTEPMS: + LLViewerStats::getInstance()->mSimSimPhysicsStepMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSSHAPEMS: + LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSOTHERMS: + LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSMEMORY: + LLViewerStats::getInstance()->mPhysicsMemoryAllocated.addValue(stat_value); + break; + case LL_SIM_STAT_SIMSPARETIME: + LLViewerStats::getInstance()->mSimSpareMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMSLEEPTIME: + LLViewerStats::getInstance()->mSimSleepMsec.addValue(stat_value); + break; + case LL_SIM_STAT_IOPUMPTIME: + LLViewerStats::getInstance()->mSimPumpIOMsec.addValue(stat_value); break; default: -// llwarns << "Unknown stat id" << stat_id << llendl; + // Used to be a commented out warning. + LL_DEBUGS("Messaging") << "Unknown stat id" << stat_id << LL_ENDL; break; } } /* msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation); - gViewerStats->mSimTDStat.addValue(time_dilation); + LLViewerStats::getInstance()->mSimTDStat.addValue(time_dilation); // Process information // { CpuUsage F32 } @@ -3517,9 +4580,9 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data) msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total); msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss); msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime); - gViewerStats->mSimCPUUsageStat.addValue(cpu_usage); - gViewerStats->mSimMemTotalStat.addValue(sim_mem_total); - gViewerStats->mSimMemRSSStat.addValue(sim_mem_rss); + LLViewerStats::getInstance()->mSimCPUUsageStat.addValue(cpu_usage); + LLViewerStats::getInstance()->mSimMemTotalStat.addValue(sim_mem_total); + LLViewerStats::getInstance()->mSimMemRSSStat.addValue(sim_mem_rss); */ // @@ -3562,7 +4625,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) if (!avatarp) { // no agent by this ID...error? - llwarns << "Received animation state for unknown avatar" << uuid << llendl; + LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL; return; } @@ -3571,7 +4634,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) avatarp->mSignaledAnimations.clear(); - if (avatarp->mIsSelf) + if (avatarp->isSelf()) { LLUUID object_id; @@ -3580,10 +4643,21 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); - //llinfos << "Anim sequence ID: " << anim_sequence_id << llendl; + LL_DEBUGS("Messaging") << "Anim sequence ID: " << anim_sequence_id << LL_ENDL; avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; + // *HACK: Disabling flying mode if it has been enabled shortly before the agent + // stand up animation is signaled. In this case we don't get a signal to start + // flying animation from server, the AGENT_CONTROL_FLY flag remains set but the + // avatar does not play flying animation, so we switch flying mode off. + // See LLAgent::setFlying(). This may cause "Stop Flying" button to blink. + // See EXT-2781. + if (animation_id == ANIM_AGENT_STANDUP && gAgent.getFlying()) + { + gAgent.setFlying(FALSE); + } + if (i < num_source_blocks) { mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i); @@ -3634,13 +4708,13 @@ void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); - if( avatarp ) + if (avatarp) { avatarp->processAvatarAppearance( mesgsys ); } else { - llwarns << "avatar_appearance sent for unknown avatar " << uuid << llendl; + LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; } } @@ -3649,7 +4723,7 @@ void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) LLVector4 cameraCollidePlane; mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane); - gAgent.setCameraCollidePlane(cameraCollidePlane); + gAgentCamera.setCameraCollidePlane(cameraCollidePlane); } void near_sit_object(BOOL success, void *data) @@ -3682,20 +4756,21 @@ void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data) BOOL force_mouselook; mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook); - LLVOAvatar* avatar = gAgent.getAvatarObject(); - - if (avatar && dist_vec_squared(camera_eye, camera_at) > 0.0001f) + if (isAgentAvatarValid() && dist_vec_squared(camera_eye, camera_at) > 0.0001f) { - gAgent.setSitCamera(sitObjectID, camera_eye, camera_at); + gAgentCamera.setSitCamera(sitObjectID, camera_eye, camera_at); } - gAgent.mForceMouselook = force_mouselook; + gAgentCamera.setForceMouselook(force_mouselook); + // Forcing turning off flying here to prevent flying after pressing "Stand" + // to stand up from an object. See EXT-1655. + gAgent.setFlying(FALSE); LLViewerObject* object = gObjectList.findObject(sitObjectID); if (object) { LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation()); - if (!use_autopilot || (avatar && avatar->mIsSitting && avatar->getRoot() == object->getRoot())) + if (!use_autopilot || isAgentAvatarValid() && gAgentAvatarp->isSitting() && gAgentAvatarp->getRoot() == object->getRoot()) { //we're already sitting on this object, so don't autopilot } @@ -3706,7 +4781,7 @@ void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data) } else { - llwarns << "Received sit approval for unknown object " << sitObjectID << llendl; + LL_WARNS("Messaging") << "Received sit approval for unknown object " << sitObjectID << LL_ENDL; } } @@ -3842,7 +4917,7 @@ void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_dat // Culled from newsim lltask.cpp void process_name_value(LLMessageSystem *mesgsys, void **user_data) { - char temp_str[NAME_VALUE_BUF_SIZE]; /* Flawfinder: ignore */ + std::string temp_str; LLUUID id; S32 i, num_blocks; @@ -3855,20 +4930,20 @@ void process_name_value(LLMessageSystem *mesgsys, void **user_data) num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); for (i = 0; i < num_blocks; i++) { - mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i); - llinfos << "Added to object Name Value: " << temp_str << llendl; + mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); + LL_INFOS("Messaging") << "Added to object Name Value: " << temp_str << LL_ENDL; object->addNVPair(temp_str); } } else { - llinfos << "Can't find object " << id << " to add name value pair" << llendl; + LL_INFOS("Messaging") << "Can't find object " << id << " to add name value pair" << LL_ENDL; } } void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data) { - char temp_str[NAME_VALUE_BUF_SIZE]; /* Flawfinder: ignore */ + std::string temp_str; LLUUID id; S32 i, num_blocks; @@ -3881,23 +4956,22 @@ void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data) num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); for (i = 0; i < num_blocks; i++) { - mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i); - llinfos << "Removed from object Name Value: " << temp_str << llendl; + mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); + LL_INFOS("Messaging") << "Removed from object Name Value: " << temp_str << LL_ENDL; object->removeNVPair(temp_str); } } else { - llinfos << "Can't find object " << id << " to remove name value pair" << llendl; + LL_INFOS("Messaging") << "Can't find object " << id << " to remove name value pair" << LL_ENDL; } } void process_kick_user(LLMessageSystem *msg, void** /*user_data*/) { - char message[2048]; /* Flawfinder: ignore */ - message[0] = '\0'; + std::string message; - msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, 2048, message); + msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, message); LLAppViewer::instance()->forceDisconnect(message); } @@ -3947,7 +5021,7 @@ void process_time_dilation(LLMessageSystem *msg, void **user_data) // get the pointer to the right region U32 ip = msg->getSenderIP(); U32 port = msg->getSenderPort(); - LLViewerRegion *regionp = gWorldp->getRegion(ip, port); + LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(ip, port); if (regionp) { regionp->setTimeDilation(time_dilation); @@ -3956,106 +5030,455 @@ void process_time_dilation(LLMessageSystem *msg, void **user_data) */ - void process_money_balance_reply( LLMessageSystem* msg, void** ) { S32 balance = 0; S32 credit = 0; S32 committed = 0; - char desc[STD_STRING_BUF_SIZE] = ""; /* Flawfinder: ignore */ + std::string desc; + LLUUID tid; + msg->getUUID("MoneyData", "TransactionID", tid); msg->getS32("MoneyData", "MoneyBalance", balance); msg->getS32("MoneyData", "SquareMetersCredit", credit); msg->getS32("MoneyData", "SquareMetersCommitted", committed); - msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, STD_STRING_BUF_SIZE, desc); - llinfos << "L$, credit, committed: " << balance << " " << credit << " " - << committed << llendl; - + msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, desc); + LL_INFOS("Messaging") << "L$, credit, committed: " << balance << " " << credit << " " + << committed << LL_ENDL; + if (gStatusBar) { - S32 old_balance = gStatusBar->getBalance(); + gStatusBar->setBalance(balance); + gStatusBar->setLandCredit(credit); + gStatusBar->setLandCommitted(committed); + } + + if (desc.empty() + || !gSavedSettings.getBOOL("NotifyMoneyChange")) + { + // ...nothing to display + return; + } - // This is an update, not the first transmission of balance - if (old_balance != 0) + // Suppress duplicate messages about the same transaction + static std::deque<LLUUID> recent; + if (std::find(recent.rbegin(), recent.rend(), tid) != recent.rend()) + { + return; + } + + // Once the 'recent' container gets large enough, chop some + // off the beginning. + const U32 MAX_LOOKBACK = 30; + const S32 POP_FRONT_SIZE = 12; + if(recent.size() > MAX_LOOKBACK) + { + LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; + recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); + } + //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; + recent.push_back(tid); + + if (msg->has("TransactionInfo")) + { + // ...message has extended info for localization + process_money_balance_reply_extended(msg); + } + else + { + // Only old dev grids will not supply the TransactionInfo block, + // so we can just use the hard-coded English string. + LLSD args; + args["MESSAGE"] = desc; + LLNotificationsUtil::add("SystemMessage", args); + } +} + +static std::string reason_from_transaction_type(S32 transaction_type, + const std::string& item_desc) +{ + // *NOTE: The keys for the reason strings are unusual because + // an earlier version of the code used English language strings + // extracted from hard-coded server English descriptions. + // Keeping them so we don't have to re-localize them. + switch (transaction_type) + { + case TRANS_OBJECT_SALE: { - // this is actually an update - if (balance > old_balance) + LLStringUtil::format_map_t arg; + arg["ITEM"] = item_desc; + return LLTrans::getString("for item", arg); + } + case TRANS_LAND_SALE: + return LLTrans::getString("for a parcel of land"); + + case TRANS_LAND_PASS_SALE: + return LLTrans::getString("for a land access pass"); + + case TRANS_GROUP_LAND_DEED: + return LLTrans::getString("for deeding land"); + + case TRANS_GROUP_CREATE: + return LLTrans::getString("to create a group"); + + case TRANS_GROUP_JOIN: + return LLTrans::getString("to join a group"); + + case TRANS_UPLOAD_CHARGE: + return LLTrans::getString("to upload"); + + case TRANS_CLASSIFIED_CHARGE: + return LLTrans::getString("to publish a classified ad"); + + // These have no reason to display, but are expected and should not + // generate warnings + case TRANS_GIFT: + case TRANS_PAY_OBJECT: + case TRANS_OBJECT_PAYS: + return std::string(); + + default: + llwarns << "Unknown transaction type " + << transaction_type << llendl; + return std::string(); + } +} + +static void money_balance_group_notify(const LLUUID& group_id, + const std::string& name, + bool is_group, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} + +static void money_balance_avatar_notify(const LLUUID& agent_id, + const LLAvatarName& av_name, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} + +static void process_money_balance_reply_extended(LLMessageSystem* msg) +{ + // Added in server 1.40 and viewer 2.1, support for localization + // and agent ids for name lookup. + S32 transaction_type = 0; + LLUUID source_id; + BOOL is_source_group = FALSE; + LLUUID dest_id; + BOOL is_dest_group = FALSE; + S32 amount = 0; + std::string item_description; + + msg->getS32("TransactionInfo", "TransactionType", transaction_type); + msg->getUUID("TransactionInfo", "SourceID", source_id); + msg->getBOOL("TransactionInfo", "IsSourceGroup", is_source_group); + msg->getUUID("TransactionInfo", "DestID", dest_id); + msg->getBOOL("TransactionInfo", "IsDestGroup", is_dest_group); + msg->getS32("TransactionInfo", "Amount", amount); + msg->getString("TransactionInfo", "ItemDescription", item_description); + LL_INFOS("Money") << "MoneyBalanceReply source " << source_id + << " dest " << dest_id + << " type " << transaction_type + << " item " << item_description << LL_ENDL; + + if (source_id.isNull() && dest_id.isNull()) + { + // this is a pure balance update, no notification required + return; + } + + std::string source_slurl; + if (is_source_group) + { + source_slurl = + LLSLURL( "group", source_id, "inspect").getSLURLString(); + } + else + { + source_slurl = + LLSLURL( "agent", source_id, "completename").getSLURLString(); + } + + std::string dest_slurl; + if (is_dest_group) + { + dest_slurl = + LLSLURL( "group", dest_id, "inspect").getSLURLString(); + } + else + { + dest_slurl = + LLSLURL( "agent", dest_id, "completename").getSLURLString(); + } + + std::string reason = + reason_from_transaction_type(transaction_type, item_description); + + LLStringUtil::format_map_t args; + args["REASON"] = reason; // could be empty + args["AMOUNT"] = llformat("%d", amount); + + // Need to delay until name looked up, so need to know whether or not + // is group + bool is_name_group = false; + LLUUID name_id; + std::string message; + std::string notification; + LLSD final_args; + LLSD payload; + + bool you_paid_someone = (source_id == gAgentID); + if (you_paid_someone) + { + args["NAME"] = dest_slurl; + is_name_group = is_dest_group; + name_id = dest_id; + if (!reason.empty()) + { + if (dest_id.notNull()) { - LLFirstUse::useBalanceIncrease(balance - old_balance); + message = LLTrans::getString("you_paid_ldollars", args); } - else if (balance < old_balance) + else { - LLFirstUse::useBalanceDecrease(balance - old_balance); + // transaction fee to the system, eg, to create a group + message = LLTrans::getString("you_paid_ldollars_no_name", args); } } + else + { + if (dest_id.notNull()) + { + message = LLTrans::getString("you_paid_ldollars_no_reason", args); + } + else + { + // no target, no reason, you just paid money + message = LLTrans::getString("you_paid_ldollars_no_info", args); + } + } + final_args["MESSAGE"] = message; + notification = "PaymentSent"; + } + else { + // ...someone paid you + args["NAME"] = source_slurl; + is_name_group = is_source_group; + name_id = source_id; + if (!reason.empty()) + { + message = LLTrans::getString("paid_you_ldollars", args); + } + else { + message = LLTrans::getString("paid_you_ldollars_no_reason", args); + } + final_args["MESSAGE"] = message; - gStatusBar->setBalance(balance); - gStatusBar->setLandCredit(credit); - gStatusBar->setLandCommitted(committed); + // make notification loggable + payload["from_id"] = source_id; + notification = "PaymentReceived"; } - LLUUID tid; - msg->getUUID("MoneyData", "TransactionID", tid); - static std::deque<LLUUID> recent; - if(desc[0] && gSavedSettings.getBOOL("NotifyMoneyChange") - && (std::find(recent.rbegin(), recent.rend(), tid) == recent.rend())) + // Despite using SLURLs, wait until the name is available before + // showing the notification, otherwise the UI layout is strange and + // the user sees a "Loading..." message + if (is_name_group) + { + gCacheName->getGroup(name_id, + boost::bind(&money_balance_group_notify, + _1, _2, _3, + notification, final_args, payload)); + } + else { + LLAvatarNameCache::get(name_id, + boost::bind(&money_balance_avatar_notify, + _1, _2, + notification, final_args, payload)); + } +} + + + +bool handle_special_notification_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) { - // Make the user confirm the transaction, since they might - // have missed something during an event. - // *TODO:translate - LLString::format_map_t args; - args["[MESSAGE]"] = desc; - LLNotifyBox::showXml("SystemMessage", args); + // set the preference to the maturity of the region we're calling + int preferredMaturity = notification["payload"]["_region_access"].asInteger(); + gSavedSettings.setU32("PreferredMaturity", preferredMaturity); + gAgent.sendMaturityPreferenceToServer(preferredMaturity); - // Once the 'recent' container gets large enough, chop some - // off the beginning. - const U32 MAX_LOOKBACK = 30; - const S32 POP_FRONT_SIZE = 12; - if(recent.size() > MAX_LOOKBACK) + // notify user that the maturity preference has been changed + LLSD args; + args["RATING"] = LLViewerRegion::accessToString(preferredMaturity); + LLNotificationsUtil::add("PreferredMaturityChanged", args); + } + + return false; +} + +// some of the server notifications need special handling. This is where we do that. +bool handle_special_notification(std::string notificationID, LLSD& llsdBlock) +{ + int regionAccess = llsdBlock["_region_access"].asInteger(); + llsdBlock["REGIONMATURITY"] = LLViewerRegion::accessToString(regionAccess); + + // we're going to throw the LLSD in there in case anyone ever wants to use it + LLNotificationsUtil::add(notificationID+"_Notify", llsdBlock); + + if (regionAccess == SIM_ACCESS_MATURE) + { + if (gAgent.isTeen()) + { + LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); + return true; + } + else if (gAgent.prefersPG()) + { + LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); + return true; + } + } + else if (regionAccess == SIM_ACCESS_ADULT) + { + if (!gAgent.isAdult()) { - lldebugs << "Removing oldest transaction records" << llendl; - recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); + LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); + return true; + } + else if (gAgent.prefersPG() || gAgent.prefersMature()) + { + LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); + return true; } - //lldebugs << "Pushing back transaction " << tid << llendl; - recent.push_back(tid); } + return false; } -void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data) +bool attempt_standard_notification(LLMessageSystem* msgsystem) { - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer); - BOOL modal = FALSE; - msgsystem->getBOOL("AlertData", "Modal", modal); - process_alert_core(buffer, modal); + // if we have additional alert data + if (msgsystem->has(_PREHASH_AlertInfo) && msgsystem->getNumberOfBlocksFast(_PREHASH_AlertInfo) > 0) + { + // notification was specified using the new mechanism, so we can just handle it here + std::string notificationID; + std::string llsdRaw; + LLSD llsdBlock; + msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, notificationID); + msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsdRaw); + if (llsdRaw.length()) + { + std::istringstream llsdData(llsdRaw); + if (!LLSDSerialize::deserialize(llsdBlock, llsdData, llsdRaw.length())) + { + llwarns << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << llendl; + } + } + + if ( + (notificationID == "RegionEntryAccessBlocked") || + (notificationID == "LandClaimAccessBlocked") || + (notificationID == "LandBuyAccessBlocked") + ) + { + /*--------------------------------------------------------------------- + (Commented so a grep will find the notification strings, since + we construct them on the fly; if you add additional notifications, + please update the comment.) + + Could throw any of the following notifications: + + RegionEntryAccessBlocked + RegionEntryAccessBlocked_Notify + RegionEntryAccessBlocked_Change + RegionEntryAccessBlocked_KB + LandClaimAccessBlocked + LandClaimAccessBlocked_Notify + LandClaimAccessBlocked_Change + LandClaimAccessBlocked_KB + LandBuyAccessBlocked + LandBuyAccessBlocked_Notify + LandBuyAccessBlocked_Change + LandBuyAccessBlocked_KB + + -----------------------------------------------------------------------*/ + if (handle_special_notification(notificationID, llsdBlock)) + { + return true; + } + } + + LLNotificationsUtil::add(notificationID, llsdBlock); + return true; + } + return false; } -void process_alert_message(LLMessageSystem *msgsystem, void **user_data) + +void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data) { - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer); - BOOL modal = FALSE; - process_alert_core(buffer, modal); + // make sure the cursor is back to the usual default since the + // alert is probably due to some kind of error. + gViewerWindow->getWindow()->resetBusyCount(); + + if (!attempt_standard_notification(msgsystem)) + { + BOOL modal = FALSE; + msgsystem->getBOOL("AlertData", "Modal", modal); + std::string buffer; + msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); + process_alert_core(buffer, modal); + } } -void process_alert_core(const std::string& message, BOOL modal) +// The only difference between this routine and the previous is the fact that +// for this routine, the modal parameter is always false. Sadly, for the message +// handled by this routine, there is no "Modal" parameter on the message, and +// there's no API to tell if a message has the given parameter or not. +// So we can't handle the messages with the same handler. +void process_alert_message(LLMessageSystem *msgsystem, void **user_data) { // make sure the cursor is back to the usual default since the // alert is probably due to some kind of error. gViewerWindow->getWindow()->resetBusyCount(); + + if (!attempt_standard_notification(msgsystem)) + { + BOOL modal = FALSE; + std::string buffer; + msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); + process_alert_core(buffer, modal); + } +} - // HACK -- handle callbacks for specific alerts +void process_alert_core(const std::string& message, BOOL modal) +{ + // HACK -- handle callbacks for specific alerts. It also is localized in notifications.xml if ( message == "You died and have been teleported to your home location") { - gViewerStats->incStat(LLViewerStats::ST_KILLED_COUNT); + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_KILLED_COUNT); } else if( message == "Home position set." ) { // save the home location image to disk - LLString snap_filename = gDirUtilp->getLindenUserDir(); + std::string snap_filename = gDirUtilp->getLindenUserDir(); snap_filename += gDirUtilp->getDirDelimiter(); snap_filename += SCREEN_HOME_FILENAME; - gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, FALSE); + gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, FALSE); } const std::string ALERT_PREFIX("ALERT: "); @@ -4065,54 +5488,54 @@ void process_alert_core(const std::string& message, BOOL modal) // Allow the server to spawn a named alert so that server alerts can be // translated out of English. std::string alert_name(message.substr(ALERT_PREFIX.length())); - LLAlertDialog::showXml(alert_name); + LLNotificationsUtil::add(alert_name); } else if (message.find(NOTIFY_PREFIX) == 0) { // Allow the server to spawn a named notification so that server notifications can be // translated out of English. std::string notify_name(message.substr(NOTIFY_PREFIX.length())); - LLNotifyBox::showXml(notify_name); + LLNotificationsUtil::add(notify_name); } else if (message[0] == '/') { // System message is important, show in upper-right box not tip - LLString text(message.substr(1)); - LLString::format_map_t args; + std::string text(message.substr(1)); + LLSD args; if (text.substr(0,17) == "RESTART_X_MINUTES") { S32 mins = 0; - LLString::convertToS32(text.substr(18), mins); - args["[MINUTES]"] = llformat("%d",mins); - LLNotifyBox::showXml("RegionRestartMinutes", args); + LLStringUtil::convertToS32(text.substr(18), mins); + args["MINUTES"] = llformat("%d",mins); + LLNotificationsUtil::add("RegionRestartMinutes", args); } else if (text.substr(0,17) == "RESTART_X_SECONDS") { S32 secs = 0; - LLString::convertToS32(text.substr(18), secs); - args["[SECONDS]"] = llformat("%d",secs); - LLNotifyBox::showXml("RegionRestartSeconds", args); + LLStringUtil::convertToS32(text.substr(18), secs); + args["SECONDS"] = llformat("%d",secs); + LLNotificationsUtil::add("RegionRestartSeconds", args); } else { - // *TODO:translate - args["[MESSAGE]"] = text; - LLNotifyBox::showXml("SystemMessage", args); + std::string new_msg =LLNotifications::instance().getGlobalString(text); + args["MESSAGE"] = new_msg; + LLNotificationsUtil::add("SystemMessage", args); } } else if (modal) { - // *TODO:translate - LLString::format_map_t args; - args["[ERROR_MESSAGE]"] = message; - gViewerWindow->alertXml("ErrorMessage", args); + LLSD args; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + args["ERROR_MESSAGE"] = new_msg; + LLNotificationsUtil::add("ErrorMessage", args); } else { - // *TODO:translate - LLString::format_map_t args; - args["[MESSAGE]"] = message; - LLNotifyBox::showXml("SystemMessageTip", args); + LLSD args; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + args["MESSAGE"] = new_msg; + LLNotificationsUtil::add("SystemMessageTip", args); } } @@ -4125,22 +5548,22 @@ void handle_show_mean_events(void *) { return; } - - LLFloaterBump::show(NULL); + LLFloaterReg::showInstance("bumps"); + //LLFloaterBump::showInstance(); } -void mean_name_callback(const LLUUID &id, const char *first, const char *last, BOOL always_false, void* data) +void mean_name_callback(const LLUUID &id, const std::string& full_name, bool is_group) { if (gNoRender) { return; } - static const int max_collision_list_size = 20; + static const U32 max_collision_list_size = 20; if (gMeanCollisionList.size() > max_collision_list_size) { mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); - for (S32 i=0; i<max_collision_list_size; i++) iter++; + for (U32 i=0; i<max_collision_list_size; i++) iter++; for_each(iter, gMeanCollisionList.end(), DeletePointer()); gMeanCollisionList.erase(iter, gMeanCollisionList.end()); } @@ -4151,10 +5574,7 @@ void mean_name_callback(const LLUUID &id, const char *first, const char *last, B LLMeanCollisionData *mcd = *iter; if (mcd->mPerp == id) { - strncpy(mcd->mFirstName, first, DB_FIRST_NAME_BUF_SIZE -1); /* Flawfinder: ignore */ - mcd->mFirstName[DB_FIRST_NAME_BUF_SIZE -1] = '\0'; - strncpy(mcd->mLastName, last, DB_LAST_NAME_BUF_SIZE -1); /* Flawfinder: ignore */ - mcd->mLastName[DB_LAST_NAME_BUF_SIZE -1] = '\0'; + mcd->mFullName = full_name; } } } @@ -4163,7 +5583,7 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use { if (gAgent.inPrelude()) { - // JC: In prelude, bumping is OK. This dialog is rather confusing to + // In prelude, bumping is OK. This dialog is rather confusing to // newbies, so we don't show it. Drop the packet on the floor. return; } @@ -4208,8 +5628,7 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use { LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag); gMeanCollisionList.push_front(mcd); - const BOOL is_group = FALSE; - gCacheName->get(perp, is_group, mean_name_callback); + gCacheName->get(perp, false, boost::bind(&mean_name_callback, _1, _2, _3)); } } } @@ -4235,34 +5654,19 @@ void process_frozen_message(LLMessageSystem *msgsystem, void **user_data) // do some extra stuff once we get our economy data void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) { - LLGlobalEconomy::processEconomyData(msg, (void**)gGlobalEconomy); - - S32 upload_cost = gGlobalEconomy->getPriceUpload(); - LLFloaterImagePreview::setUploadAmount(upload_cost); + LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance()); - gMenuHolder->childSetLabelArg("Upload Image", "[COST]", llformat("%d", upload_cost)); - gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", llformat("%d", upload_cost)); - gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", llformat("%d", upload_cost)); - gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", llformat("%d", upload_cost)); -} + S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); -class LLScriptQuestionCBData -{ -public: - LLScriptQuestionCBData(const LLUUID &taskid, const LLUUID &itemid, const LLHost &sender, S32 questions, const char *object_name, const char *owner_name) - : mTaskID(taskid), mItemID(itemid), mSender(sender), mQuestions(questions), mObjectName(object_name), mOwnerName(owner_name) - { - } + LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; - LLUUID mTaskID; - LLUUID mItemID; - LLHost mSender; - S32 mQuestions; - LLString mObjectName; - LLString mOwnerName; -}; + gMenuHolder->getChild<LLUICtrl>("Upload Image")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild<LLUICtrl>("Upload Sound")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild<LLUICtrl>("Upload Animation")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild<LLUICtrl>("Bulk Upload")->setLabelArg("[COST]", llformat("%d", upload_cost)); +} -void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_questions, BOOL granted) +void notify_cautioned_script_question(const LLSD& notification, const LLSD& response, S32 orig_questions, BOOL granted) { // only continue if at least some permissions were requested if (orig_questions) @@ -4273,16 +5677,16 @@ void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_q // located in [REGIONNAME] at [REGIONPOS], // has been <granted|denied> permission to: [PERMISSIONS]." - LLUIString notice(LLNotifyBox::getTemplateMessage(granted ? "ScriptQuestionCautionChatGranted" : "ScriptQuestionCautionChatDenied")); + LLUIString notice(LLTrans::getString(granted ? "ScriptQuestionCautionChatGranted" : "ScriptQuestionCautionChatDenied")); // always include the object name and owner name - notice.setArg("[OBJECTNAME]", cbdata->mObjectName); - notice.setArg("[OWNERNAME]", cbdata->mOwnerName); + notice.setArg("[OBJECTNAME]", notification["payload"]["object_name"].asString()); + notice.setArg("[OWNERNAME]", notification["payload"]["owner_name"].asString()); // try to lookup viewerobject that corresponds to the object that // requested permissions (here, taskid->requesting object id) BOOL foundpos = FALSE; - LLViewerObject* viewobj = gObjectList.findObject(cbdata->mTaskID); + LLViewerObject* viewobj = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); if (viewobj) { // found the viewerobject, get it's position in its region @@ -4294,7 +5698,7 @@ void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_q { // got the region, so include the region and 3d coordinates of the object notice.setArg("[REGIONNAME]", viewregion->getName()); - LLString formatpos = llformat("%.1f, %.1f,%.1f", objpos[VX], objpos[VY], objpos[VZ]); + std::string formatpos = llformat("%.1f, %.1f,%.1f", objpos[VX], objpos[VY], objpos[VZ]); notice.setArg("[REGIONPOS]", formatpos); foundpos = TRUE; @@ -4312,10 +5716,10 @@ void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_q // permission that has been flagged as a caution permission BOOL caution = FALSE; S32 count = 0; - LLString perms; + std::string perms; for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) { - if ((orig_questions & LSCRIPTRunTimePermissionBits[i]) && LLNotifyBox::getTemplateIsCaution(SCRIPT_QUESTIONS[i])) + if ((orig_questions & LSCRIPTRunTimePermissionBits[i]) && SCRIPT_QUESTION_IS_CAUTION[i]) { count++; caution = TRUE; @@ -4327,7 +5731,7 @@ void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_q perms.append(", "); } - perms.append(LLNotifyBox::getTemplateMessage(SCRIPT_QUESTIONS[i])); + perms.append(LLTrans::getString(SCRIPT_QUESTIONS[i])); } } @@ -4338,48 +5742,17 @@ void notify_cautioned_script_question(LLScriptQuestionCBData* cbdata, S32 orig_q if (caution) { LLChat chat(notice.getString()); - LLFloaterChat::addChat(chat, FALSE, FALSE); + // LLFloaterChat::addChat(chat, FALSE, FALSE); } } } -void script_question_decline_cb(S32 option, void* user_data) +bool script_question_cb(const LLSD& notification, const LLSD& response) { + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLMessageSystem *msg = gMessageSystem; - LLScriptQuestionCBData *cbdata = (LLScriptQuestionCBData *)user_data; - - // remember the permissions requested so they can be checked - // when it comes time to log a chat message - S32 orig = cbdata->mQuestions; - - // this callback will always decline all permissions requested - // (any question flags set in the ScriptAnswerYes message - // will be interpreted as having been granted, so clearing all - // the bits will deny every permission) - cbdata->mQuestions = 0; - - // respond with the permissions denial - msg->newMessageFast(_PREHASH_ScriptAnswerYes); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_Data); - msg->addUUIDFast(_PREHASH_TaskID, cbdata->mTaskID); - msg->addUUIDFast(_PREHASH_ItemID, cbdata->mItemID); - msg->addS32Fast(_PREHASH_Questions, cbdata->mQuestions); - msg->sendReliable(cbdata->mSender); - - // log a chat message, if appropriate - notify_cautioned_script_question(cbdata, orig, FALSE); - - delete cbdata; -} - -void script_question_cb(S32 option, void* user_data) -{ - LLMessageSystem *msg = gMessageSystem; - LLScriptQuestionCBData *cbdata = (LLScriptQuestionCBData *)user_data; - S32 orig = cbdata->mQuestions; + S32 orig = notification["payload"]["questions"].asInteger(); + S32 new_questions = orig; // check whether permissions were granted or denied BOOL allowed = TRUE; @@ -4387,83 +5760,115 @@ void script_question_cb(S32 option, void* user_data) // if any other button was clicked, the permissions were denied if (option != 0) { - cbdata->mQuestions = 0; + new_questions = 0; allowed = FALSE; } + LLUUID task_id = notification["payload"]["task_id"].asUUID(); + LLUUID item_id = notification["payload"]["item_id"].asUUID(); + // reply with the permissions granted or denied msg->newMessageFast(_PREHASH_ScriptAnswerYes); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Data); - msg->addUUIDFast(_PREHASH_TaskID, cbdata->mTaskID); - msg->addUUIDFast(_PREHASH_ItemID, cbdata->mItemID); - msg->addS32Fast(_PREHASH_Questions, cbdata->mQuestions); - msg->sendReliable(cbdata->mSender); + msg->addUUIDFast(_PREHASH_TaskID, task_id); + msg->addUUIDFast(_PREHASH_ItemID, item_id); + msg->addS32Fast(_PREHASH_Questions, new_questions); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); // only log a chat message if caution prompts are enabled if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) { // log a chat message, if appropriate - notify_cautioned_script_question(cbdata, orig, allowed); + notify_cautioned_script_question(notification, response, orig, allowed); } - if ( option == 2 ) // mute + if ( response["Mute"] ) // mute { - gMuteListp->add(LLMute(cbdata->mItemID, cbdata->mObjectName, LLMute::OBJECT)); + LLMuteList::getInstance()->add(LLMute(item_id, notification["payload"]["object_name"].asString(), LLMute::OBJECT)); // purge the message queue of any previously queued requests from the same source. DEV-4879 - class OfferMatcher : public LLNotifyBoxView::Matcher + class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - BOOL matches(LLNotifyBox::notify_callback_t callback, void* cb_data) const + bool matches(const LLNotificationPtr notification) const { - return callback == script_question_cb && ((LLScriptQuestionCBData*)cb_data)->mItemID == blocked_id; + if (notification->getName() == "ScriptQuestionCaution" + || notification->getName() == "ScriptQuestion") + { + return (notification->getPayload()["item_id"].asUUID() == blocked_id); + } + return false; } private: const LLUUID& blocked_id; }; - gNotifyBoxView->purgeMessagesMatching(OfferMatcher(cbdata->mItemID)); + + LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( + gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(item_id)); + } + + if (response["Details"]) + { + // respawn notification... + LLNotificationsUtil::add(notification["name"], notification["substitutions"], notification["payload"]); + + // ...with description on top + LLNotificationsUtil::add("DebitPermissionDetails"); } - delete cbdata; + return false; } +static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb); +static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb); void process_script_question(LLMessageSystem *msg, void **user_data) { - // *TODO:translate owner name -> [FIRST] [LAST] + // *TODO: Translate owner name -> [FIRST] [LAST] LLHost sender = msg->getSender(); LLUUID taskid; LLUUID itemid; S32 questions; - char object_name[255]; /* Flawfinder: ignore */ - char owner_name[DB_FULL_NAME_BUF_SIZE]; /* Flawfinder: ignore */ + std::string object_name; + std::string owner_name; // taskid -> object key of object requesting permissions msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid ); // itemid -> script asset key of script requesting permissions msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid ); - msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, 255, object_name); - msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, DB_FULL_NAME_BUF_SIZE, owner_name); + msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, object_name); + msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name); msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions ); - // don't display permission requests if this object is muted - JS. - if (gMuteListp->isMuted(taskid)) return; - + // Special case. If the objects are owned by this agent, throttle per-object instead + // of per-owner. It's common for residents to reset a ton of scripts that re-request + // permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa, + // so we'll reuse the same namespace for both throttle types. + std::string throttle_name = owner_name; + std::string self_name; + LLAgentUI::buildFullname( self_name ); + if( owner_name == self_name ) + { + throttle_name = taskid.getString(); + } + + // don't display permission requests if this object is muted + if (LLMuteList::getInstance()->isMuted(taskid)) return; + // throttle excessive requests from any specific user's scripts - LLString throttle_owner_name = owner_name; - typedef LLKeyThrottle<LLString> LLStringThrottle; + typedef LLKeyThrottle<std::string> LLStringThrottle; static LLStringThrottle question_throttle( LLREQUEST_PERMISSION_THROTTLE_LIMIT, LLREQUEST_PERMISSION_THROTTLE_INTERVAL ); - switch (question_throttle.noteAction(throttle_owner_name)) + switch (question_throttle.noteAction(throttle_name)) { case LLStringThrottle::THROTTLE_NEWLY_BLOCKED: - llinfos << "process_script_question throttled" + LL_INFOS("Messaging") << "process_script_question throttled" << " owner_name:" << owner_name - << llendl; + << LL_ENDL; // Fall through case LLStringThrottle::THROTTLE_BLOCKED: @@ -4474,14 +5879,14 @@ void process_script_question(LLMessageSystem *msg, void **user_data) break; } - LLString script_question; + std::string script_question; if (questions) { BOOL caution = FALSE; S32 count = 0; - LLString::format_map_t args; - args["[OBJECTNAME]"] = object_name; - args["[NAME]"] = owner_name; + LLSD args; + args["OBJECTNAME"] = object_name; + args["NAME"] = LLCacheName::cleanFullName(owner_name); // check the received permission flags against each permission for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) @@ -4489,34 +5894,32 @@ void process_script_question(LLMessageSystem *msg, void **user_data) if (questions & LSCRIPTRunTimePermissionBits[i]) { count++; - script_question += " " + LLNotifyBox::getTemplateMessage(SCRIPT_QUESTIONS[i]) + "\n"; + script_question += " " + LLTrans::getString(SCRIPT_QUESTIONS[i]) + "\n"; // check whether permission question should cause special caution dialog - caution |= LLNotifyBox::getTemplateIsCaution(SCRIPT_QUESTIONS[i]); + caution |= (SCRIPT_QUESTION_IS_CAUTION[i]); } } - args["[QUESTIONS]"] = script_question; + args["QUESTIONS"] = script_question; - LLScriptQuestionCBData *cbdata = new LLScriptQuestionCBData(taskid, itemid, sender, questions, object_name, owner_name); + LLSD payload; + payload["task_id"] = taskid; + payload["item_id"] = itemid; + payload["sender"] = sender.getIPandPort(); + payload["questions"] = questions; + payload["object_name"] = object_name; + payload["owner_name"] = owner_name; // check whether cautions are even enabled or not if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) { - if (caution) - { - // display the caution permissions prompt - LLNotifyBox::showXml("ScriptQuestionCaution", args, TRUE, script_question_cb, cbdata); - } - else - { - // display the permissions request normally - LLNotifyBox::showXml("ScriptQuestion", args, FALSE, script_question_cb, cbdata); - } + // display the caution permissions prompt + LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload); } else { // fall back to default behavior if cautions are entirely disabled - LLNotifyBox::showXml("ScriptQuestion", args, FALSE, script_question_cb, cbdata); + LLNotificationsUtil::add("ScriptQuestion", args, payload); } } @@ -4525,42 +5928,41 @@ void process_script_question(LLMessageSystem *msg, void **user_data) void process_derez_container(LLMessageSystem *msg, void**) { - llwarns << "call to deprecated process_derez_container" << llendl; + LL_WARNS("Messaging") << "call to deprecated process_derez_container" << LL_ENDL; } void container_inventory_arrived(LLViewerObject* object, - InventoryObjectList* inventory, + LLInventoryObject::object_list_t* inventory, S32 serial_num, void* data) { - llinfos << "container_inventory_arrived()" << llendl; - if( gAgent.cameraMouselook() ) + LL_DEBUGS("Messaging") << "container_inventory_arrived()" << LL_ENDL; + if( gAgentCamera.cameraMouselook() ) { - gAgent.changeCameraToDefault(); + gAgentCamera.changeCameraToDefault(); } - LLInventoryView* view = LLInventoryView::getActiveInventory(); + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); if (inventory->size() > 2) { // create a new inventory category to put this in LLUUID cat_id; - cat_id = gInventory.createNewCategory(gAgent.getInventoryRootID(), - LLAssetType::AT_NONE, - "Acquired Items"); + cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), + LLFolderType::FT_NONE, + LLTrans::getString("AcquiredItems")); - InventoryObjectList::const_iterator it = inventory->begin(); - InventoryObjectList::const_iterator end = inventory->end(); + LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); + LLInventoryObject::object_list_t::const_iterator end = inventory->end(); for ( ; it != end; ++it) { - if ((*it)->getType() != LLAssetType::AT_CATEGORY && - (*it)->getType() != LLAssetType::AT_ROOT_CATEGORY) + if ((*it)->getType() != LLAssetType::AT_CATEGORY) { LLInventoryObject* obj = (LLInventoryObject*)(*it); LLInventoryItem* item = (LLInventoryItem*)(obj); LLUUID item_id; item_id.generate(); - S32 creation_date_utc = time_corrected(); + time_t creation_date_utc = time_corrected(); LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item_id, cat_id, @@ -4578,29 +5980,28 @@ void container_inventory_arrived(LLViewerObject* object, } } gInventory.notifyObservers(); - if(view) + if(active_panel) { - view->getPanel()->setSelection(cat_id, TAKE_FOCUS_NO); + active_panel->setSelection(cat_id, TAKE_FOCUS_NO); } } else if (inventory->size() == 2) { // we're going to get one fake root category as well as the // one actual object - InventoryObjectList::iterator it = inventory->begin(); + LLInventoryObject::object_list_t::iterator it = inventory->begin(); - if ((*it)->getType() == LLAssetType::AT_CATEGORY || - (*it)->getType() == LLAssetType::AT_ROOT_CATEGORY) + if ((*it)->getType() == LLAssetType::AT_CATEGORY) { ++it; } LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it)); - LLUUID category = gInventory.findCategoryUUIDForType(item->getType()); + const LLUUID category = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType())); LLUUID item_id; item_id.generate(); - S32 creation_date_utc = time_corrected(); + time_t creation_date_utc = time_corrected(); LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item_id, category, item->getPermissions(), @@ -4615,9 +6016,9 @@ void container_inventory_arrived(LLViewerObject* object, new_item->updateServer(TRUE); gInventory.updateItem(new_item); gInventory.notifyObservers(); - if(view) + if(active_panel) { - view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO); + active_panel->setSelection(item_id, TAKE_FOCUS_NO); } } @@ -4638,34 +6039,90 @@ void container_inventory_arrived(LLViewerObject* object, } } -// method to format the time. Buffer should be at least -// TIME_STR_LENGTH long, and the function returns buffer (for use in -// sprintf and the like) -char* formatted_time(const time_t& the_time, char* buffer) +// method to format the time. +std::string formatted_time(const time_t& the_time) { - LLString::copy(buffer, ctime(&the_time), TIME_STR_LENGTH); - buffer[24] = '\0'; - return buffer; + std::string dateStr = "["+LLTrans::getString("LTimeWeek")+"] [" + +LLTrans::getString("LTimeMonth")+"] [" + +LLTrans::getString("LTimeDay")+"] [" + +LLTrans::getString("LTimeHour")+"]:[" + +LLTrans::getString("LTimeMin")+"]:[" + +LLTrans::getString("LTimeSec")+"] [" + +LLTrans::getString("LTimeYear")+"]"; + + LLSD substitution; + substitution["datetime"] = (S32) the_time; + LLStringUtil::format (dateStr, substitution); + return dateStr; } void process_teleport_failed(LLMessageSystem *msg, void**) { - char reason[STD_STRING_BUF_SIZE]; /* Flawfinder: ignore */ - msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, STD_STRING_BUF_SIZE, reason); + std::string reason; + std::string big_reason; + LLSD args; + + // if we have additional alert data + if (msg->has(_PREHASH_AlertInfo) && msg->getSizeFast(_PREHASH_AlertInfo, _PREHASH_Message) > 0) + { + // Get the message ID + msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, reason); + big_reason = LLAgent::sTeleportErrorMessages[reason]; + if ( big_reason.size() > 0 ) + { // Substitute verbose reason from the local map + args["REASON"] = big_reason; + } + else + { // Nothing found in the map - use what the server returned in the original message block + msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); + args["REASON"] = reason; + } + + LLSD llsd_block; + std::string llsd_raw; + msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsd_raw); + if (llsd_raw.length()) + { + std::istringstream llsd_data(llsd_raw); + if (!LLSDSerialize::deserialize(llsd_block, llsd_data, llsd_raw.length())) + { + llwarns << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << llendl; + } + else + { + // change notification name in this special case + if (handle_special_notification("RegionEntryAccessBlocked", llsd_block)) + { + if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) + { + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + } + return; + } + } + } - LLStringBase<char>::format_map_t args; - LLString big_reason = LLAgent::sTeleportErrorMessages[reason]; - if ( big_reason.size() > 0 ) - { // Substitute verbose reason from the local map - args["[REASON]"] = big_reason; } else - { // Nothing found in the map - use what the server returned - args["[REASON]"] = reason; + { + msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); + + big_reason = LLAgent::sTeleportErrorMessages[reason]; + if ( big_reason.size() > 0 ) + { // Substitute verbose reason from the local map + args["REASON"] = big_reason; + } + else + { // Nothing found in the map - use what the server returned + args["REASON"] = reason; + } } - gViewerWindow->alertXml("CouldNotTeleportReason", args); + LLNotificationsUtil::add("CouldNotTeleportReason", args); + + // Let the interested parties know that teleport failed. + LLViewerParcelMgr::getInstance()->onTeleportFailed(); if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { @@ -4679,7 +6136,7 @@ void process_teleport_local(LLMessageSystem *msg,void**) msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { - llwarns << "Got teleport notification for wrong agent!" << llendl; + LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; return; } @@ -4693,7 +6150,18 @@ void process_teleport_local(LLMessageSystem *msg,void**) if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL ) + { + // To prevent TeleportStart messages re-activating the progress screen right + // after tp, keep the teleport state and let progress screen clear it after a short delay + // (progress screen is active but not visible) *TODO: remove when SVC-5290 is fixed + gTeleportDisplayTimer.reset(); + gTeleportDisplay = TRUE; + } + else + { + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + } } // Sim tells us whether the new position is off the ground @@ -4707,26 +6175,33 @@ void process_teleport_local(LLMessageSystem *msg,void**) } gAgent.setPositionAgent(pos); - gAgent.slamLookAt(look_at); + gAgentCamera.slamLookAt(look_at); - // likewise make sure the camera is behind the avatar - gAgent.resetView(TRUE); + if ( !(gAgent.getTeleportKeepsLookAt() && LLViewerJoystick::getInstance()->getOverrideCamera()) ) + { + gAgentCamera.resetView(TRUE, TRUE); + } // send camera update to new region - gAgent.updateCamera(); + gAgentCamera.updateCamera(); send_agent_update(TRUE, TRUE); + + // Let the interested parties know we've teleported. + // Vadim *HACK: Agent position seems to get reset (to render position?) + // on each frame, so we have to pass the new position manually. + LLViewerParcelMgr::getInstance()->onTeleportFinished(true, gAgent.getPosGlobalFromAgent(pos)); } void send_simple_im(const LLUUID& to_id, - const char* message, + const std::string& message, EInstantMessage dialog, const LLUUID& id) { std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); send_improved_im(to_id, - my_name.c_str(), + my_name, message, IM_ONLINE, dialog, @@ -4737,15 +6212,15 @@ void send_simple_im(const LLUUID& to_id, } void send_group_notice(const LLUUID& group_id, - const char* subject, - const char* message, + const std::string& subject, + const std::string& message, const LLInventoryItem* item) { // Put this notice into an instant message form. // This will mean converting the item to a binary bucket, // and the subject/message into a single field. std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); // Combine subject + message into a single string. std::ostringstream subject_and_message; @@ -4777,8 +6252,8 @@ void send_group_notice(const LLUUID& group_id, send_improved_im( group_id, - my_name.c_str(), - subject_and_message.str().c_str(), + my_name, + subject_and_message.str(), IM_ONLINE, IM_GROUP_NOTICE, LLUUID::null, @@ -4787,9 +6262,13 @@ void send_group_notice(const LLUUID& group_id, bin_bucket_size); } -void handle_lure_callback(S32 option, const LLString& text, void* userdata) +bool handle_lure_callback(const LLSD& notification, const LLSD& response) { - LLDynamicArray<LLUUID>* invitees = (LLDynamicArray<LLUUID>*)userdata; + std::string text = response["message"].asString(); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) { @@ -4800,22 +6279,35 @@ void handle_lure_callback(S32 option, const LLString& text, void* userdata) msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Info); msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. - msg->addStringFast(_PREHASH_Message, text.c_str()); - for(LLDynamicArray<LLUUID>::iterator itr = invitees->begin(); itr != invitees->end(); ++itr) + msg->addStringFast(_PREHASH_Message, text); + for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); + it != notification["payload"]["ids"].endArray(); + ++it) { + LLUUID target_id = it->asUUID(); + msg->nextBlockFast(_PREHASH_TargetData); - msg->addUUIDFast(_PREHASH_TargetID, *itr); + msg->addUUIDFast(_PREHASH_TargetID, target_id); + + // Record the offer. + { + std::string target_name; + gCacheName->getFullName(target_id, target_name); // for im log filenames + LLSD args; + args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; + + LLSD payload; + + //*TODO please rewrite all keys to the same case, lower or upper + payload["from_id"] = target_id; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("TeleportOfferSent", args, payload); + } } gAgent.sendReliableMessage(); } - delete invitees; - invitees = NULL; -} - -void handle_lure_callback_godlike(S32 option, void* userdata) -{ - handle_lure_callback(option, LLString::null, userdata); + return false; } void handle_lure(const LLUUID& invitee) @@ -4826,30 +6318,36 @@ void handle_lure(const LLUUID& invitee) } // Prompt for a message to the invited user. -void handle_lure(LLDynamicArray<LLUUID>& ids) +void handle_lure(const uuid_vec_t& ids) { - LLDynamicArray<LLUUID>* userdata = new LLDynamicArray<LLUUID>(ids); + if (ids.empty()) return; + + if (!gAgent.getRegion()) return; + + LLSD edit_args; + edit_args["REGION"] = gAgent.getRegion()->getName(); - LLString::format_map_t edit_args; - edit_args["[REGION]"] = gAgent.getRegion()->getName(); + LLSD payload; + for (LLDynamicArray<LLUUID>::const_iterator it = ids.begin(); + it != ids.end(); + ++it) + { + payload["ids"].append(*it); + } if (gAgent.isGodlike()) { - gViewerWindow->alertXmlEditText("OfferTeleportFromGod", edit_args, - &handle_lure_callback_godlike, userdata, - NULL, NULL, edit_args); + LLNotificationsUtil::add("OfferTeleportFromGod", edit_args, payload, handle_lure_callback); } else { - gViewerWindow->alertXmlEditText("OfferTeleport", edit_args, - NULL, NULL, - handle_lure_callback, userdata, edit_args); + LLNotificationsUtil::add("OfferTeleport", edit_args, payload, handle_lure_callback); } } void send_improved_im(const LLUUID& to_id, - const char* name, - const char* message, + const std::string& name, + const std::string& message, U8 offline, EInstantMessage dialog, const LLUUID& id, @@ -4880,10 +6378,10 @@ void send_improved_im(const LLUUID& to_id, void send_places_query(const LLUUID& query_id, const LLUUID& trans_id, - const char* query_text, + const std::string& query_text, U32 query_flags, S32 category, - const char* sim_name) + const std::string& sim_name) { LLMessageSystem* msg = gMessageSystem; @@ -4909,18 +6407,16 @@ void process_user_info_reply(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "process_user_info_reply - " - << "wrong agent id." << llendl; + LL_WARNS("Messaging") << "process_user_info_reply - " + << "wrong agent id." << LL_ENDL; } BOOL im_via_email; msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email); - char email[DB_USER_EMAIL_ADDR_BUF_SIZE]; /* Flawfinder: ignore */ - msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, DB_USER_EMAIL_ADDR_BUF_SIZE, - email); - char dir_visibility[MAX_STRING]; /* Flawfinder: ignore */ - msg->getString( - "UserData", "DirectoryVisibility", MAX_STRING, dir_visibility); + std::string email; + msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, email); + std::string dir_visibility; + msg->getString( "UserData", "DirectoryVisibility", dir_visibility); LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email); LLFloaterPostcard::updateUserInfo(email); @@ -4936,21 +6432,13 @@ const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24; const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512; const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; -struct ScriptDialogInfo +bool callback_script_dialog(const LLSD& notification, const LLSD& response) { - LLHost mSender; - LLUUID mObjectID; - S32 mChatChannel; - std::vector<LLString> mButtons; -}; - -void callback_script_dialog(S32 option, void* data) -{ - ScriptDialogInfo* info = (ScriptDialogInfo*)data; - if (!info) return; - + LLNotificationForm form(notification["form"]); + std::string button = LLNotification::getSelectedOptionName(response); + S32 button_idx = LLNotification::getSelectedOption(notification, response); // Didn't click "Ignore" - if (0 != option) + if (button_idx != -1) { LLMessageSystem* msg = gMessageSystem; msg->newMessage("ScriptDialogReply"); @@ -4958,141 +6446,138 @@ void callback_script_dialog(S32 option, void* data) msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); msg->nextBlock("Data"); - msg->addUUID("ObjectID", info->mObjectID); - msg->addS32("ChatChannel", info->mChatChannel); - msg->addS32("ButtonIndex", option); - msg->addString("ButtonLabel", info->mButtons[option-1]); - msg->sendReliable(info->mSender); + msg->addUUID("ObjectID", notification["payload"]["object_id"].asUUID()); + msg->addS32("ChatChannel", notification["payload"]["chat_channel"].asInteger()); + msg->addS32("ButtonIndex", button_idx); + msg->addString("ButtonLabel", button); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); } - delete info; + return false; } +static LLNotificationFunctorRegistration callback_script_dialog_reg_1("ScriptDialog", callback_script_dialog); +static LLNotificationFunctorRegistration callback_script_dialog_reg_2("ScriptDialogGroup", callback_script_dialog); void process_script_dialog(LLMessageSystem* msg, void**) { S32 i; + LLSD payload; - ScriptDialogInfo* info = new ScriptDialogInfo; + LLUUID object_id; + msg->getUUID("Data", "ObjectID", object_id); - const S32 messageLength = SCRIPT_DIALOG_MAX_MESSAGE_SIZE + sizeof(SCRIPT_DIALOG_HEADER); - char message[messageLength]; /* Flawfinder: ignore */ // Account for size of "Script Dialog:\n" + if (LLMuteList::getInstance()->isMuted(object_id)) + { + return; + } - char first_name[DB_FIRST_NAME_BUF_SIZE]; /* Flawfinder: ignore */ - char last_name[DB_GROUP_NAME_BUF_SIZE]; /* Flawfinder: ignore */ - char title[DB_INV_ITEM_NAME_BUF_SIZE]; /* Flawfinder: ignore */ - info->mSender = msg->getSender(); + std::string message; + std::string first_name; + std::string last_name; + std::string title; - msg->getUUID("Data", "ObjectID", info->mObjectID); - msg->getString("Data", "FirstName", DB_FIRST_NAME_BUF_SIZE, first_name); - msg->getString("Data", "LastName", DB_LAST_NAME_BUF_SIZE, last_name); - msg->getString("Data", "ObjectName", DB_INV_ITEM_NAME_BUF_SIZE, title); - msg->getString("Data", "Message", SCRIPT_DIALOG_MAX_MESSAGE_SIZE, message); - msg->getS32("Data", "ChatChannel", info->mChatChannel); + S32 chat_channel; + msg->getString("Data", "FirstName", first_name); + msg->getString("Data", "LastName", last_name); + msg->getString("Data", "ObjectName", title); + msg->getString("Data", "Message", message); + msg->getS32("Data", "ChatChannel", chat_channel); // unused for now LLUUID image_id; msg->getUUID("Data", "ImageID", image_id); + payload["sender"] = msg->getSender().getIPandPort(); + payload["object_id"] = object_id; + payload["chat_channel"] = chat_channel; + + // build up custom form S32 button_count = msg->getNumberOfBlocks("Buttons"); if (button_count > SCRIPT_DIALOG_MAX_BUTTONS) { + llwarns << "Too many script dialog buttons - omitting some" << llendl; button_count = SCRIPT_DIALOG_MAX_BUTTONS; } + LLNotificationForm form; for (i = 0; i < button_count; i++) { - char tdesc[SCRIPT_DIALOG_BUTTON_STR_SIZE+1]; /* Flawfinder: ignore */ - msg->getString("Buttons", "ButtonLabel", SCRIPT_DIALOG_BUTTON_STR_SIZE + 1, tdesc, i); - info->mButtons.push_back(LLString(tdesc)); + std::string tdesc; + msg->getString("Buttons", "ButtonLabel", tdesc, i); + form.addElement("button", std::string(tdesc)); } - LLStringBase<char>::format_map_t args; - args["[TITLE]"] = title; - args["[MESSAGE]"] = message; - if (strlen(first_name) > 0) /* Flawfinder: ignore */ + LLSD args; + args["TITLE"] = title; + args["MESSAGE"] = message; + LLNotificationPtr notification; + if (!first_name.empty()) { - args["[FIRST]"] = first_name; - args["[LAST]"] = last_name; - LLNotifyBox::showXml("ScriptDialog", args, - callback_script_dialog, info, - info->mButtons, - TRUE); + args["NAME"] = LLCacheName::buildFullName(first_name, last_name); + notification = LLNotifications::instance().add( + LLNotification::Params("ScriptDialog").substitutions(args).payload(payload).form_elements(form.asLLSD())); } else { - args["[GROUPNAME]"] = last_name; - LLNotifyBox::showXml("ScriptDialogGroup", args, - callback_script_dialog, info, - info->mButtons, - TRUE); + args["GROUPNAME"] = last_name; + notification = LLNotifications::instance().add( + LLNotification::Params("ScriptDialogGroup").substitutions(args).payload(payload).form_elements(form.asLLSD())); } } //--------------------------------------------------------------------------- -struct LoadUrlInfo -{ - LLUUID mObjectID; - LLUUID mOwnerID; - BOOL mOwnerIsGroup; - char mObjectName[256]; /* Flawfinder: ignore */ - char mMessage[256]; /* Flawfinder: ignore */ - char mUrl[256]; /* Flawfinder: ignore */ -}; -std::vector<LoadUrlInfo*> gLoadUrlList; +std::vector<LLSD> gLoadUrlList; -void callback_load_url(S32 option, void* data) +bool callback_load_url(const LLSD& notification, const LLSD& response) { - LoadUrlInfo* infop = (LoadUrlInfo*)data; - if (!infop) return; + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option) { - LLWeb::loadURL(infop->mUrl); + LLWeb::loadURL(notification["payload"]["url"].asString()); } - delete infop; - infop = NULL; + return false; } +static LLNotificationFunctorRegistration callback_load_url_reg("LoadWebPage", callback_load_url); // We've got the name of the person who owns the object hurling the url. // Display confirmation dialog. -void callback_load_url_name(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* data) +void callback_load_url_name(const LLUUID& id, const std::string& full_name, bool is_group) { - std::vector<LoadUrlInfo*>::iterator it; + std::vector<LLSD>::iterator it; for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); ) { - LoadUrlInfo* infop = *it; - if (infop->mOwnerID == id) + LLSD load_url_info = *it; + if (load_url_info["owner_id"].asUUID() == id) { it = gLoadUrlList.erase(it); - std::string owner_name(first); + std::string owner_name; if (is_group) { - owner_name += " (group)"; + owner_name = full_name + LLTrans::getString("Group"); } else { - owner_name += " "; - owner_name += last; + owner_name = full_name; } // For legacy name-only mutes. - if (gMuteListp->isMuted(LLUUID::null, owner_name)) + if (LLMuteList::getInstance()->isMuted(LLUUID::null, owner_name)) { - delete infop; - infop = NULL; continue; } - LLString::format_map_t args; - args["[URL]"] = infop->mUrl; - args["[MESSAGE]"] = infop->mMessage; - args["[OBJECTNAME]"] = infop->mObjectName; - args["[NAME]"] = owner_name; - LLNotifyBox::showXml("LoadWebPage", args, callback_load_url, infop); + LLSD args; + args["URL"] = load_url_info["url"].asString(); + args["MESSAGE"] = load_url_info["message"].asString();; + args["OBJECTNAME"] = load_url_info["object_name"].asString(); + args["NAME"] = owner_name; + + LLNotificationsUtil::add("LoadWebPage", args, load_url_info); } else { @@ -5103,42 +6588,52 @@ void callback_load_url_name(const LLUUID& id, const char* first, const char* las void process_load_url(LLMessageSystem* msg, void**) { - LoadUrlInfo* infop = new LoadUrlInfo; - - msg->getString("Data", "ObjectName", 256, infop->mObjectName); - msg->getUUID( "Data", "ObjectID", infop->mObjectID); - msg->getUUID( "Data", "OwnerID", infop->mOwnerID); - msg->getBOOL( "Data", "OwnerIsGroup", infop->mOwnerIsGroup); - msg->getString("Data", "Message", 256, infop->mMessage); - msg->getString("Data", "URL", 256, infop->mUrl); + LLUUID object_id; + LLUUID owner_id; + BOOL owner_is_group; + char object_name[256]; /* Flawfinder: ignore */ + char message[256]; /* Flawfinder: ignore */ + char url[256]; /* Flawfinder: ignore */ + + msg->getString("Data", "ObjectName", 256, object_name); + msg->getUUID( "Data", "ObjectID", object_id); + msg->getUUID( "Data", "OwnerID", owner_id); + msg->getBOOL( "Data", "OwnerIsGroup", owner_is_group); + msg->getString("Data", "Message", 256, message); + msg->getString("Data", "URL", 256, url); + + LLSD payload; + payload["object_id"] = object_id; + payload["owner_id"] = owner_id; + payload["owner_is_group"] = owner_is_group; + payload["object_name"] = object_name; + payload["message"] = message; + payload["url"] = url; // URL is safety checked in load_url above // Check if object or owner is muted - if (gMuteListp && - (gMuteListp->isMuted(infop->mObjectID, infop->mObjectName) || - gMuteListp->isMuted(infop->mOwnerID)) - ) - { - llinfos<<"Ignoring load_url from muted object/owner."<<llendl; - delete infop; - infop = NULL; + if (LLMuteList::getInstance()->isMuted(object_id, object_name) || + LLMuteList::getInstance()->isMuted(owner_id)) + { + LL_INFOS("Messaging")<<"Ignoring load_url from muted object/owner."<<LL_ENDL; return; } // Add to list of pending name lookups - gLoadUrlList.push_back(infop); + gLoadUrlList.push_back(payload); - gCacheName->get(infop->mOwnerID, infop->mOwnerIsGroup, callback_load_url_name); + gCacheName->get(owner_id, owner_is_group, + boost::bind(&callback_load_url_name, _1, _2, _3)); } void callback_download_complete(void** data, S32 result, LLExtStat ext_status) { - LLString* filepath = (LLString*)data; - LLString::format_map_t args; - args["[DOWNLOAD_PATH]"] = *filepath; - gViewerWindow->alertXml("FinishedRawDownload", args); + std::string* filepath = (std::string*)data; + LLSD args; + args["DOWNLOAD_PATH"] = *filepath; + LLNotificationsUtil::add("FinishedRawDownload", args); delete filepath; } @@ -5149,71 +6644,108 @@ void process_initiate_download(LLMessageSystem* msg, void**) msg->getUUID("AgentData", "AgentID", agent_id); if (agent_id != gAgent.getID()) { - llwarns << "Initiate download for wrong agent" << llendl; + LL_WARNS("Messaging") << "Initiate download for wrong agent" << LL_ENDL; return; } - char sim_filename[MAX_PATH]; /* Flawfinder: ignore */ - char viewer_filename[MAX_PATH]; /* Flawfinder: ignore */ - msg->getString("FileData", "SimFilename", MAX_PATH, sim_filename); - msg->getString("FileData", "ViewerFilename", MAX_PATH, viewer_filename); + std::string sim_filename; + std::string viewer_filename; + msg->getString("FileData", "SimFilename", sim_filename); + msg->getString("FileData", "ViewerFilename", viewer_filename); + if (!gXferManager->validateFileForRequest(viewer_filename)) + { + llwarns << "SECURITY: Unauthorized download to local file " << viewer_filename << llendl; + return; + } gXferManager->requestFile(viewer_filename, sim_filename, LL_PATH_NONE, msg->getSender(), FALSE, // don't delete remote callback_download_complete, - (void**)new LLString(viewer_filename)); + (void**)new std::string(viewer_filename)); } void process_script_teleport_request(LLMessageSystem* msg, void**) { - char object_name[256]; /* Flawfinder: ignore */ - char sim_name[256]; /* Flawfinder: ignore */ + std::string object_name; + std::string sim_name; LLVector3 pos; LLVector3 look_at; - msg->getString("Data", "ObjectName", 255, object_name); - msg->getString( "Data", "SimName", 255, sim_name); + msg->getString("Data", "ObjectName", object_name); + msg->getString("Data", "SimName", sim_name); msg->getVector3("Data", "SimPosition", pos); msg->getVector3("Data", "LookAt", look_at); - // gFloaterWorldMap->trackURL(sim_name, (U32)pos.mV[VX], (U32)pos.mV[VY], (U32)pos.mV[VZ]); - // LLFloaterWorldMap::show(NULL, TRUE); - - LLURLDispatcher::dispatch(LLURLDispatcher::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); + LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance(); + if(instance) + { + instance->trackURL( + sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); + LLFloaterReg::showInstance("world_map", "center"); + } + + // remove above two lines and replace with below line + // to re-enable parcel browser for llMapDestination() + // LLURLDispatcher::dispatch(LLSLURL::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); } void process_covenant_reply(LLMessageSystem* msg, void**) { LLUUID covenant_id, estate_owner_id; - char estate_name[MAX_STRING]; /* Flawfinder: ignore */ + std::string estate_name; U32 covenant_timestamp; msg->getUUID("Data", "CovenantID", covenant_id); msg->getU32("Data", "CovenantTimestamp", covenant_timestamp); - msg->getString("Data", "EstateName", MAX_STRING, estate_name); + msg->getString("Data", "EstateName", estate_name); msg->getUUID("Data", "EstateOwnerID", estate_owner_id); LLPanelEstateCovenant::updateEstateName(estate_name); LLPanelLandCovenant::updateEstateName(estate_name); LLFloaterBuyLand::updateEstateName(estate_name); + std::string owner_name = + LLSLURL("agent", estate_owner_id, "inspect").getSLURLString(); + LLPanelEstateCovenant::updateEstateOwnerName(owner_name); + LLPanelLandCovenant::updateEstateOwnerName(owner_name); + LLFloaterBuyLand::updateEstateOwnerName(owner_name); + + LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel<LLPanelPlaceProfile>("panel_place_profile"); + if (panel) + { + panel->updateEstateName(estate_name); + panel->updateEstateOwnerName(owner_name); + } + // standard message, not from system - char last_modified[MAX_STRING]; /* Flawfinder: ignore */ - last_modified[0] = '\0'; - char time_buf[TIME_STR_LENGTH]; /* Flawfinder: ignore */ - snprintf(last_modified, MAX_STRING, "Last Modified %s", /* Flawfinder: ignore */ - formatted_time((time_t)covenant_timestamp, time_buf)); + std::string last_modified; + if (covenant_timestamp == 0) + { + last_modified = LLTrans::getString("covenant_last_modified")+LLTrans::getString("never_text"); + } + else + { + last_modified = LLTrans::getString("covenant_last_modified")+"[" + +LLTrans::getString("LTimeWeek")+"] [" + +LLTrans::getString("LTimeMonth")+"] [" + +LLTrans::getString("LTimeDay")+"] [" + +LLTrans::getString("LTimeHour")+"]:[" + +LLTrans::getString("LTimeMin")+"]:[" + +LLTrans::getString("LTimeSec")+"] [" + +LLTrans::getString("LTimeYear")+"]"; + LLSD substitution; + substitution["datetime"] = (S32) covenant_timestamp; + LLStringUtil::format (last_modified, substitution); + } LLPanelEstateCovenant::updateLastModified(last_modified); LLPanelLandCovenant::updateLastModified(last_modified); LLFloaterBuyLand::updateLastModified(last_modified); - gCacheName->getName(estate_owner_id, callbackCacheEstateOwnerName); - // load the actual covenant asset data const BOOL high_priority = TRUE; if (covenant_id.notNull()) @@ -5234,48 +6766,28 @@ void process_covenant_reply(LLMessageSystem* msg, void**) if (estate_owner_id.isNull()) { // mainland - covenant_text = "There is no Covenant provided for this Estate."; + covenant_text = LLTrans::getString("RegionNoCovenant"); } else { - covenant_text = "There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details."; + covenant_text = LLTrans::getString("RegionNoCovenantOtherOwner"); } LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id); + if (panel) + { + panel->updateCovenantText(covenant_text); + } } } -void callbackCacheEstateOwnerName( - const LLUUID& id, - const char* first, - const char* last, - BOOL is_group, - void*) -{ - std::string name; - - if (id.isNull()) - { - name = "(none)"; - } - else - { - name = first; - name += " "; - name += last; - } - LLPanelEstateCovenant::updateEstateOwnerName(name); - LLPanelLandCovenant::updateEstateOwnerName(name); - LLFloaterBuyLand::updateEstateOwnerName(name); -} - void onCovenantLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { - llinfos << "onCovenantLoadComplete()" << llendl; + LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; std::string covenant_text; if(0 == status) { @@ -5283,27 +6795,20 @@ void onCovenantLoadComplete(LLVFS *vfs, S32 file_length = file.getSize(); - char* buffer = new char[file_length+1]; - if (buffer == NULL) - { - llerrs << "Memory Allocation failed" << llendl; - return; - } - - file.read((U8*)buffer, file_length); /* Flawfinder: ignore */ - + std::vector<char> buffer(file_length+1); + file.read((U8*)&buffer[0], file_length); // put a EOS at the end - buffer[file_length] = 0; + buffer[file_length] = '\0'; - if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) ) + if( (file_length > 19) && !strncmp( &buffer[0], "Linden text version", 19 ) ) { - LLViewerTextEditor* editor = - new LLViewerTextEditor("temp", - LLRect(0,0,0,0), - file_length+1); - if( !editor->importBuffer( buffer ) ) + LLViewerTextEditor::Params params; + params.name("temp"); + params.max_text_length(file_length+1); + LLViewerTextEditor * editor = LLUICtrlFactory::create<LLViewerTextEditor> (params); + if( !editor->importBuffer( &buffer[0], file_length+1 ) ) { - llwarns << "Problem importing estate covenant." << llendl; + LL_WARNS("Messaging") << "Problem importing estate covenant." << LL_ENDL; covenant_text = "Problem importing estate covenant."; } else @@ -5311,21 +6816,17 @@ void onCovenantLoadComplete(LLVFS *vfs, // Version 0 (just text, doesn't include version number) covenant_text = editor->getText(); } - delete[] buffer; delete editor; } else { - llwarns << "Problem importing estate covenant: Covenant file format error." << llendl; + LL_WARNS("Messaging") << "Problem importing estate covenant: Covenant file format error." << LL_ENDL; covenant_text = "Problem importing estate covenant: Covenant file format error."; } } else { - if( gViewerStats ) - { - gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); - } + LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || LL_ERR_FILE_EMPTY == status) @@ -5341,11 +6842,17 @@ void onCovenantLoadComplete(LLVFS *vfs, covenant_text = "Unable to load estate covenant at this time."; } - llwarns << "Problem loading notecard: " << status << llendl; + LL_WARNS("Messaging") << "Problem loading notecard: " << status << LL_ENDL; } LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid); LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid); + + LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel<LLPanelPlaceProfile>("panel_place_profile"); + if (panel) + { + panel->updateCovenantText(covenant_text); + } } @@ -5354,12 +6861,12 @@ void process_feature_disabled_message(LLMessageSystem* msg, void**) // Handle Blacklisted feature simulator response... LLUUID agentID; LLUUID transactionID; - char messageText[MAX_STRING]; /* Flawfinder: ignore */ - msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage,MAX_STRING,&messageText[0],0); + std::string messageText; + msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage, messageText,0); msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID); msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID); - llwarns << "Blacklisted Feature Response:" << &messageText[0] << llendl; + LL_WARNS("Messaging") << "Blacklisted Feature Response:" << messageText << LL_ENDL; } // ------------------------------------------------------------ @@ -5374,4 +6881,12 @@ void invalid_message_callback(LLMessageSystem* msg, } // Please do not add more message handlers here. This file is huge. -// Put them in a file related to the functionality you are implementing. JC +// Put them in a file related to the functionality you are implementing. + +void LLOfferInfo::forceResponse(InventoryOfferResponse response) +{ + LLNotification::Params params("UserGiveItem"); + params.functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2)); + LLNotifications::instance().forceResponse(params, response); +} + |
