PageRenderTime 1367ms CodeModel.GetById 181ms app.highlight 556ms RepoModel.GetById 611ms app.codeStats 1ms

/indra/newview/llviewermessage.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2181 lines | 1546 code | 231 blank | 404 comment | 190 complexity | 8f1f020fd1913110055cba2576d7da61 MD5 | raw file

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

   1/** 
   2 * @file llviewermessage.cpp
   3 * @brief Dumping ground for viewer-side message system callbacks.
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28#include "llviewermessage.h"
  29#include "boost/lexical_cast.hpp"
  30
  31// Linden libraries
  32#include "llanimationstates.h"
  33#include "llaudioengine.h" 
  34#include "llavataractions.h"
  35#include "llavatarnamecache.h"		// IDEVO HACK
  36#include "lscript_byteformat.h"
  37#include "lleconomy.h"
  38#include "lleventtimer.h"
  39#include "llfloaterreg.h"
  40#include "llfolderview.h"
  41#include "llfollowcamparams.h"
  42#include "llinventorydefines.h"
  43#include "lllslconstants.h"
  44#include "llregionhandle.h"
  45#include "llsdserialize.h"
  46#include "llteleportflags.h"
  47#include "lltransactionflags.h"
  48#include "llvfile.h"
  49#include "llvfs.h"
  50#include "llxfermanager.h"
  51#include "mean_collision_data.h"
  52
  53#include "llagent.h"
  54#include "llagentcamera.h"
  55#include "llcallingcard.h"
  56#include "llbuycurrencyhtml.h"
  57#include "llfirstuse.h"
  58#include "llfloaterbuyland.h"
  59#include "llfloaterland.h"
  60#include "llfloaterregioninfo.h"
  61#include "llfloaterlandholdings.h"
  62#include "llfloaterpreference.h"
  63#include "llfloatersidepanelcontainer.h"
  64#include "llfloatersnapshot.h"
  65#include "llhudeffecttrail.h"
  66#include "llhudmanager.h"
  67#include "llinventoryfunctions.h"
  68#include "llinventoryobserver.h"
  69#include "llinventorypanel.h"
  70#include "llnearbychat.h"
  71#include "llnotifications.h"
  72#include "llnotificationsutil.h"
  73#include "llpanelgrouplandmoney.h"
  74#include "llrecentpeople.h"
  75#include "llscriptfloater.h"
  76#include "llselectmgr.h"
  77#include "llstartup.h"
  78#include "llsky.h"
  79#include "llslurl.h"
  80#include "llstatenums.h"
  81#include "llstatusbar.h"
  82#include "llimview.h"
  83#include "llspeakers.h"
  84#include "lltrans.h"
  85#include "lltranslate.h"
  86#include "llviewerfoldertype.h"
  87#include "llvoavatar.h"				// IDEVO HACK
  88#include "lluri.h"
  89#include "llviewergenericmessage.h"
  90#include "llviewermenu.h"
  91#include "llviewerinventory.h"
  92#include "llviewerjoystick.h"
  93#include "llviewerobjectlist.h"
  94#include "llviewerparcelmgr.h"
  95#include "llviewerstats.h"
  96#include "llviewertexteditor.h"
  97#include "llviewerthrottle.h"
  98#include "llviewerwindow.h"
  99#include "llvlmanager.h"
 100#include "llvoavatarself.h"
 101#include "llworld.h"
 102#include "pipeline.h"
 103#include "llfloaterworldmap.h"
 104#include "llviewerdisplay.h"
 105#include "llkeythrottle.h"
 106#include "llgroupactions.h"
 107#include "llagentui.h"
 108#include "llpanelblockedlist.h"
 109#include "llpanelplaceprofile.h"
 110
 111#include <boost/algorithm/string/split.hpp> //
 112#include <boost/regex.hpp>
 113
 114#include "llnotificationmanager.h" //
 115
 116#if LL_MSVC
 117// disable boost::lexical_cast warning
 118#pragma warning (disable:4702)
 119#endif
 120
 121//
 122// Constants
 123//
 124const F32 BIRD_AUDIBLE_RADIUS = 32.0f;
 125const F32 SIT_DISTANCE_FROM_TARGET = 0.25f;
 126const F32 CAMERA_POSITION_THRESHOLD_SQUARED = 0.001f * 0.001f;
 127static const F32 LOGOUT_REPLY_TIME = 3.f;	// Wait this long after LogoutReply before quitting.
 128
 129// Determine how quickly residents' scripts can issue question dialogs
 130// Allow bursts of up to 5 dialogs in 10 seconds. 10*2=20 seconds recovery if throttle kicks in
 131static const U32 LLREQUEST_PERMISSION_THROTTLE_LIMIT	= 5;     // requests
 132static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL	= 10.0f; // seconds
 133
 134extern BOOL gDebugClicks;
 135
 136// function prototypes
 137bool check_offer_throttle(const std::string& from_name, bool check_only);
 138static void process_money_balance_reply_extended(LLMessageSystem* msg);
 139
 140//inventory offer throttle globals
 141LLFrameTimer gThrottleTimer;
 142const U32 OFFER_THROTTLE_MAX_COUNT=5; //number of items per time period
 143const F32 OFFER_THROTTLE_TIME=10.f; //time period in seconds
 144
 145//script permissions
 146const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = 
 147	{ 
 148		"ScriptTakeMoney",
 149		"ActOnControlInputs",
 150		"RemapControlInputs",
 151		"AnimateYourAvatar",
 152		"AttachToYourAvatar",
 153		"ReleaseOwnership",
 154		"LinkAndDelink",
 155		"AddAndRemoveJoints",
 156		"ChangePermissions",
 157		"TrackYourCamera",
 158		"ControlYourCamera"
 159	};
 160
 161const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = 
 162{
 163	TRUE,	// ScriptTakeMoney,
 164	FALSE,	// ActOnControlInputs
 165	FALSE,	// RemapControlInputs
 166	FALSE,	// AnimateYourAvatar
 167	FALSE,	// AttachToYourAvatar
 168	FALSE,	// ReleaseOwnership,
 169	FALSE,	// LinkAndDelink,
 170	FALSE,	// AddAndRemoveJoints
 171	FALSE,	// ChangePermissions
 172	FALSE,	// TrackYourCamera,
 173	FALSE	// ControlYourCamera
 174};
 175
 176bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
 177{
 178	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 179	LLMessageSystem* msg = gMessageSystem;
 180	const LLSD& payload = notification["payload"];
 181
 182	// add friend to recent people list
 183	LLRecentPeople::instance().add(payload["from_id"]);
 184
 185	switch(option)
 186	{
 187	case 0:
 188	{
 189		// accept
 190		LLAvatarTracker::formFriendship(payload["from_id"]);
 191
 192		const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD);
 193
 194		// This will also trigger an onlinenotification if the user is online
 195		msg->newMessageFast(_PREHASH_AcceptFriendship);
 196		msg->nextBlockFast(_PREHASH_AgentData);
 197		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 198		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 199		msg->nextBlockFast(_PREHASH_TransactionBlock);
 200		msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
 201		msg->nextBlockFast(_PREHASH_FolderData);
 202		msg->addUUIDFast(_PREHASH_FolderID, fid);
 203		msg->sendReliable(LLHost(payload["sender"].asString()));
 204
 205		LLSD payload = notification["payload"];
 206		payload["SUPPRESS_TOAST"] = true;
 207		LLNotificationsUtil::add("FriendshipAcceptedByMe",
 208				notification["substitutions"], payload);
 209		break;
 210	}
 211	case 1: // Decline
 212	{
 213		LLSD payload = notification["payload"];
 214		payload["SUPPRESS_TOAST"] = true;
 215		LLNotificationsUtil::add("FriendshipDeclinedByMe",
 216				notification["substitutions"], payload);
 217	}
 218	// fall-through
 219	case 2: // Send IM - decline and start IM session
 220		{
 221			// decline
 222			// We no longer notify other viewers, but we DO still send
 223			// the rejection to the simulator to delete the pending userop.
 224			msg->newMessageFast(_PREHASH_DeclineFriendship);
 225			msg->nextBlockFast(_PREHASH_AgentData);
 226			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 227			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 228			msg->nextBlockFast(_PREHASH_TransactionBlock);
 229			msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
 230			msg->sendReliable(LLHost(payload["sender"].asString()));
 231
 232			// start IM session
 233			if(2 == option)
 234			{
 235				LLAvatarActions::startIM(payload["from_id"].asUUID());
 236			}
 237	}
 238	default:
 239		// close button probably, possibly timed out
 240		break;
 241	}
 242
 243	return false;
 244}
 245static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback);
 246static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback);
 247
 248//const char BUSY_AUTO_RESPONSE[] =	"The Resident you messaged is in 'busy mode' which means they have "
 249//									"requested not to be disturbed. Your message will still be shown in their IM "
 250//									"panel for later viewing.";
 251
 252//
 253// Functions
 254//
 255
 256void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group,
 257				S32 trx_type, const std::string& desc)
 258{
 259	if(0 == amount || !region) return;
 260	amount = abs(amount);
 261	LL_INFOS("Messaging") << "give_money(" << uuid << "," << amount << ")"<< LL_ENDL;
 262	if(can_afford_transaction(amount))
 263	{
 264//		gStatusBar->debitBalance(amount);
 265		LLMessageSystem* msg = gMessageSystem;
 266		msg->newMessageFast(_PREHASH_MoneyTransferRequest);
 267		msg->nextBlockFast(_PREHASH_AgentData);
 268		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 269        msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 270		msg->nextBlockFast(_PREHASH_MoneyData);
 271		msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() );
 272		msg->addUUIDFast(_PREHASH_DestID, uuid);
 273		msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group));
 274		msg->addS32Fast(_PREHASH_Amount, amount);
 275		msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
 276		msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
 277		msg->addS32Fast(_PREHASH_TransactionType, trx_type );
 278		msg->addStringFast(_PREHASH_Description, desc);
 279		msg->sendReliable(region->getHost());
 280	}
 281	else
 282	{
 283		LLStringUtil::format_map_t args;
 284		args["AMOUNT"] = llformat("%d", amount);
 285		LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("giving", args), amount );
 286	}
 287}
 288
 289void send_complete_agent_movement(const LLHost& sim_host)
 290{
 291	LLMessageSystem* msg = gMessageSystem;
 292	msg->newMessageFast(_PREHASH_CompleteAgentMovement);
 293	msg->nextBlockFast(_PREHASH_AgentData);
 294	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 295	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 296	msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode);
 297	msg->sendReliable(sim_host);
 298}
 299
 300void process_logout_reply(LLMessageSystem* msg, void**)
 301{
 302	// The server has told us it's ok to quit.
 303	LL_DEBUGS("Messaging") << "process_logout_reply" << LL_ENDL;
 304
 305	LLUUID agent_id;
 306	msg->getUUID("AgentData", "AgentID", agent_id);
 307	LLUUID session_id;
 308	msg->getUUID("AgentData", "SessionID", session_id);
 309	if((agent_id != gAgent.getID()) || (session_id != gAgent.getSessionID()))
 310	{
 311		LL_WARNS("Messaging") << "Bogus Logout Reply" << LL_ENDL;
 312	}
 313
 314	LLInventoryModel::update_map_t parents;
 315	S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData );
 316	for(S32 i = 0; i < count; ++i)
 317	{
 318		LLUUID item_id;
 319		msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
 320
 321		if( (1 == count) && item_id.isNull() )
 322		{
 323			// Detect dummy item.  Indicates an empty list.
 324			break;
 325		}
 326
 327		// We do not need to track the asset ids, just account for an
 328		// updated inventory version.
 329		LL_INFOS("Messaging") << "process_logout_reply itemID=" << item_id << LL_ENDL;
 330		LLInventoryItem* item = gInventory.getItem( item_id );
 331		if( item )
 332		{
 333			parents[item->getParentUUID()] = 0;
 334			gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
 335		}
 336		else
 337		{
 338			LL_INFOS("Messaging") << "process_logout_reply item not found: " << item_id << LL_ENDL;
 339		}
 340	}
 341    LLAppViewer::instance()->forceQuit();
 342}
 343
 344void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
 345{
 346	LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender());
 347
 348	if(!regionp)
 349	{
 350		llwarns << "Invalid region for layer data." << llendl;
 351		return;
 352	}
 353	S32 size;
 354	S8 type;
 355
 356	mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type);
 357	size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data);
 358	if (0 == size)
 359	{
 360		LL_WARNS("Messaging") << "Layer data has zero size." << LL_ENDL;
 361		return;
 362	}
 363	if (size < 0)
 364	{
 365		// getSizeFast() is probably trying to tell us about an error
 366		LL_WARNS("Messaging") << "getSizeFast() returned negative result: "
 367			<< size
 368			<< LL_ENDL;
 369		return;
 370	}
 371	U8 *datap = new U8[size];
 372	mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size);
 373	LLVLData *vl_datap = new LLVLData(regionp, type, datap, size);
 374	if (mesgsys->getReceiveCompressedSize())
 375	{
 376		gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize());
 377	}
 378	else
 379	{
 380		gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize());
 381	}
 382}
 383
 384// S32 exported_object_count = 0;
 385// S32 exported_image_count = 0;
 386// S32 current_object_count = 0;
 387// S32 current_image_count = 0;
 388
 389// extern LLNotifyBox *gExporterNotify;
 390// extern LLUUID gExporterRequestID;
 391// extern std::string gExportDirectory;
 392
 393// extern LLUploadDialog *gExportDialog;
 394
 395// std::string gExportedFile;
 396
 397// std::map<LLUUID, std::string> gImageChecksums;
 398
 399// void export_complete()
 400// {
 401// 		LLUploadDialog::modalUploadFinished();
 402// 		gExporterRequestID.setNull();
 403// 		gExportDirectory = "";
 404
 405// 		LLFILE* fXML = LLFile::fopen(gExportedFile, "rb");		/* Flawfinder: ignore */
 406// 		fseek(fXML, 0, SEEK_END);
 407// 		long length = ftell(fXML);
 408// 		fseek(fXML, 0, SEEK_SET);
 409// 		U8 *buffer = new U8[length + 1];
 410// 		size_t nread = fread(buffer, 1, length, fXML);
 411// 		if (nread < (size_t) length)
 412// 		{
 413// 			LL_WARNS("Messaging") << "Short read" << LL_ENDL;
 414// 		}
 415// 		buffer[nread] = '\0';
 416// 		fclose(fXML);
 417
 418// 		char *pos = (char *)buffer;
 419// 		while ((pos = strstr(pos+1, "<sl:image ")) != 0)
 420// 		{
 421// 			char *pos_check = strstr(pos, "checksum=\"");
 422
 423// 			if (pos_check)
 424// 			{
 425// 				char *pos_uuid = strstr(pos_check, "\">");
 426
 427// 				if (pos_uuid)
 428// 				{
 429// 					char image_uuid_str[UUID_STR_SIZE];		/* Flawfinder: ignore */
 430// 					memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1);		/* Flawfinder: ignore */
 431// 					image_uuid_str[UUID_STR_SIZE-1] = 0;
 432					
 433// 					LLUUID image_uuid(image_uuid_str);
 434
 435// 					LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL;
 436
 437// 					std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid);
 438// 					if (itor != gImageChecksums.end())
 439// 					{
 440// 						LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL;
 441// 						if (!itor->second.empty())
 442// 						{
 443// 							memcpy(&pos_check[10], itor->second.c_str(), 32);		/* Flawfinder: ignore */
 444// 						}
 445// 					}
 446// 				}
 447// 			}
 448// 		}
 449
 450// 		LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb");		/* Flawfinder: ignore */
 451// 		if (fwrite(buffer, 1, length, fXMLOut) != length)
 452// 		{
 453// 			LL_WARNS("Messaging") << "Short write" << LL_ENDL;
 454// 		}
 455// 		fclose(fXMLOut);
 456
 457// 		delete [] buffer;
 458// }
 459
 460
 461// void exported_item_complete(const LLTSCode status, void *user_data)
 462// {
 463// 	//std::string *filename = (std::string *)user_data;
 464
 465// 	if (status < LLTS_OK)
 466// 	{
 467// 		LL_WARNS("Messaging") << "Export failed!" << LL_ENDL;
 468// 	}
 469// 	else
 470// 	{
 471// 		++current_object_count;
 472// 		if (current_image_count == exported_image_count && current_object_count == exported_object_count)
 473// 		{
 474// 			LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL;
 475
 476// 			export_complete();
 477// 		}
 478// 		else
 479// 		{
 480// 			gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
 481// 		}
 482// 	}
 483// }
 484
 485// struct exported_image_info
 486// {
 487// 	LLUUID image_id;
 488// 	std::string filename;
 489// 	U32 image_num;
 490// };
 491
 492// void exported_j2c_complete(const LLTSCode status, void *user_data)
 493// {
 494// 	exported_image_info *info = (exported_image_info *)user_data;
 495// 	LLUUID image_id = info->image_id;
 496// 	U32 image_num = info->image_num;
 497// 	std::string filename = info->filename;
 498// 	delete info;
 499
 500// 	if (status < LLTS_OK)
 501// 	{
 502// 		LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL;
 503// 	}
 504// 	else
 505// 	{
 506// 		LLFILE* fIn = LLFile::fopen(filename, "rb");		/* Flawfinder: ignore */
 507// 		if (fIn) 
 508// 		{
 509// 			LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
 510// 			LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
 511
 512// 			fseek(fIn, 0, SEEK_END);
 513// 			S32 length = ftell(fIn);
 514// 			fseek(fIn, 0, SEEK_SET);
 515// 			U8 *buffer = ImageUtility->allocateData(length);
 516// 			if (fread(buffer, 1, length, fIn) != length)
 517// 			{
 518// 				LL_WARNS("Messaging") << "Short read" << LL_ENDL;
 519// 			}
 520// 			fclose(fIn);
 521// 			LLFile::remove(filename);
 522
 523// 			// Convert to TGA
 524// 			LLPointer<LLImageRaw> image = new LLImageRaw();
 525
 526// 			ImageUtility->updateData();
 527// 			ImageUtility->decode(image, 100000.0f);
 528			
 529// 			TargaUtility->encode(image);
 530// 			U8 *data = TargaUtility->getData();
 531// 			S32 data_size = TargaUtility->getDataSize();
 532
 533// 			std::string file_path = gDirUtilp->getDirName(filename);
 534			
 535// 			std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename;
 536// 			//S32 name_len = output_file.length();
 537// 			//strcpy(&output_file[name_len-3], "tga");
 538// 			LLFILE* fOut = LLFile::fopen(output_file, "wb");		/* Flawfinder: ignore */
 539// 			char md5_hash_string[33];		/* Flawfinder: ignore */
 540// 			strcpy(md5_hash_string, "00000000000000000000000000000000");		/* Flawfinder: ignore */
 541// 			if (fOut)
 542// 			{
 543// 				if (fwrite(data, 1, data_size, fOut) != data_size)
 544// 				{
 545// 					LL_WARNS("Messaging") << "Short write" << LL_ENDL;
 546// 				}
 547// 				fseek(fOut, 0, SEEK_SET);
 548// 				fclose(fOut);
 549// 				fOut = LLFile::fopen(output_file, "rb");		/* Flawfinder: ignore */
 550// 				LLMD5 my_md5_hash(fOut);
 551// 				my_md5_hash.hex_digest(md5_hash_string);
 552// 			}
 553
 554// 			gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string));
 555// 		}
 556// 	}
 557
 558// 	++current_image_count;
 559// 	if (current_image_count == exported_image_count && current_object_count == exported_object_count)
 560// 	{
 561// 		LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL;
 562// 			export_complete();
 563// 	}
 564// 	else
 565// 	{
 566// 		gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
 567// 	}
 568//}
 569
 570void process_derez_ack(LLMessageSystem*, void**)
 571{
 572	if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();
 573}
 574
 575void process_places_reply(LLMessageSystem* msg, void** data)
 576{
 577	LLUUID query_id;
 578
 579	msg->getUUID("AgentData", "QueryID", query_id);
 580	if (query_id.isNull())
 581	{
 582		LLFloaterLandHoldings::processPlacesReply(msg, data);
 583	}
 584	else if(gAgent.isInGroup(query_id))
 585	{
 586		LLPanelGroupLandMoney::processPlacesReply(msg, data);
 587	}
 588	else
 589	{
 590		LL_WARNS("Messaging") << "Got invalid PlacesReply message" << LL_ENDL;
 591	}
 592}
 593
 594void send_sound_trigger(const LLUUID& sound_id, F32 gain)
 595{
 596	if (sound_id.isNull() || gAgent.getRegion() == NULL)
 597	{
 598		// disconnected agent or zero guids don't get sent (no sound)
 599		return;
 600	}
 601
 602	LLMessageSystem* msg = gMessageSystem;
 603	msg->newMessageFast(_PREHASH_SoundTrigger);
 604	msg->nextBlockFast(_PREHASH_SoundData);
 605	msg->addUUIDFast(_PREHASH_SoundID, sound_id);
 606	// Client untrusted, ids set on sim
 607	msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null );
 608	msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null );
 609	msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null );
 610
 611	msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle());
 612
 613	LLVector3 position = gAgent.getPositionAgent();
 614	msg->addVector3Fast(_PREHASH_Position, position);
 615	msg->addF32Fast(_PREHASH_Gain, gain);
 616
 617	gAgent.sendMessage();
 618}
 619
 620bool join_group_response(const LLSD& notification, const LLSD& response)
 621{
 622	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 623	BOOL delete_context_data = TRUE;
 624	bool accept_invite = false;
 625
 626	LLUUID group_id = notification["payload"]["group_id"].asUUID();
 627	LLUUID transaction_id = notification["payload"]["transaction_id"].asUUID();
 628	std::string name = notification["payload"]["name"].asString();
 629	std::string message = notification["payload"]["message"].asString();
 630	S32 fee = notification["payload"]["fee"].asInteger();
 631
 632	if (option == 2 && !group_id.isNull())
 633	{
 634		LLGroupActions::show(group_id);
 635		LLSD args;
 636		args["MESSAGE"] = message;
 637		LLNotificationsUtil::add("JoinGroup", args, notification["payload"]);
 638		return false;
 639	}
 640	if(option == 0 && !group_id.isNull())
 641	{
 642		// check for promotion or demotion.
 643		S32 max_groups = gMaxAgentGroups;
 644		if(gAgent.isInGroup(group_id)) ++max_groups;
 645
 646		if(gAgent.mGroups.count() < max_groups)
 647		{
 648			accept_invite = true;
 649		}
 650		else
 651		{
 652			delete_context_data = FALSE;
 653			LLSD args;
 654			args["NAME"] = name;
 655			LLNotificationsUtil::add("JoinedTooManyGroupsMember", args, notification["payload"]);
 656		}
 657	}
 658
 659	if (accept_invite)
 660	{
 661		// If there is a fee to join this group, make
 662		// sure the user is sure they want to join.
 663		if (fee > 0)
 664		{
 665			delete_context_data = FALSE;
 666			LLSD args;
 667			args["COST"] = llformat("%d", fee);
 668			// Set the fee for next time to 0, so that we don't keep
 669			// asking about a fee.
 670			LLSD next_payload = notification["payload"];
 671			next_payload["fee"] = 0;
 672			LLNotificationsUtil::add("JoinGroupCanAfford",
 673									args,
 674									next_payload);
 675		}
 676		else
 677		{
 678			send_improved_im(group_id,
 679							 std::string("name"),
 680							 std::string("message"),
 681							IM_ONLINE,
 682							IM_GROUP_INVITATION_ACCEPT,
 683							transaction_id);
 684		}
 685	}
 686	else
 687	{
 688		send_improved_im(group_id,
 689						 std::string("name"),
 690						 std::string("message"),
 691						IM_ONLINE,
 692						IM_GROUP_INVITATION_DECLINE,
 693						transaction_id);
 694	}
 695
 696	return false;
 697}
 698
 699static void highlight_inventory_objects_in_panel(const std::vector<LLUUID>& items, LLInventoryPanel *inventory_panel)
 700{
 701	if (NULL == inventory_panel) return;
 702
 703	for (std::vector<LLUUID>::const_iterator item_iter = items.begin();
 704		item_iter != items.end();
 705		++item_iter)
 706	{
 707		const LLUUID& item_id = (*item_iter);
 708		if(!highlight_offered_object(item_id))
 709		{
 710			continue;
 711		}
 712
 713		LLInventoryObject* item = gInventory.getObject(item_id);
 714		llassert(item);
 715		if (!item) {
 716			continue;
 717		}
 718
 719		LL_DEBUGS("Inventory_Move") << "Highlighting inventory item: " << item->getName() << ", " << item_id  << LL_ENDL;
 720		LLFolderView* fv = inventory_panel->getRootFolder();
 721		if (fv)
 722		{
 723			LLFolderViewItem* fv_item = fv->getItemByID(item_id);
 724			if (fv_item)
 725			{
 726				LLFolderViewItem* fv_folder = fv_item->getParentFolder();
 727				if (fv_folder)
 728				{
 729					// Parent folders can be different in case of 2 consecutive drag and drop
 730					// operations when the second one is started before the first one completes.
 731					LL_DEBUGS("Inventory_Move") << "Open folder: " << fv_folder->getName() << LL_ENDL;
 732					fv_folder->setOpen(TRUE);
 733					if (fv_folder->isSelected())
 734					{
 735						fv->changeSelection(fv_folder, FALSE);
 736					}
 737				}
 738				fv->changeSelection(fv_item, TRUE);
 739			}
 740		}
 741	}
 742}
 743
 744static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response);
 745static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response);
 746static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response);
 747
 748
 749//-----------------------------------------------------------------------------
 750// Instant Message
 751//-----------------------------------------------------------------------------
 752class LLOpenAgentOffer : public LLInventoryFetchItemsObserver
 753{
 754public:
 755	LLOpenAgentOffer(const LLUUID& object_id,
 756					 const std::string& from_name) : 
 757		LLInventoryFetchItemsObserver(object_id),
 758		mFromName(from_name) {}
 759	/*virtual*/ void startFetch()
 760	{
 761		for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it)
 762		{
 763			LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
 764			if (cat)
 765			{
 766				mComplete.push_back((*it));
 767			}
 768		}
 769		LLInventoryFetchItemsObserver::startFetch();
 770	}
 771	/*virtual*/ void done()
 772	{
 773		open_inventory_offer(mComplete, mFromName);
 774		gInventory.removeObserver(this);
 775		delete this;
 776	}
 777private:
 778	std::string mFromName;
 779};
 780
 781/**
 782 * Class to observe adding of new items moved from the world to user's inventory to select them in inventory.
 783 *
 784 * We can't create it each time items are moved because "drop" event is sent separately for each
 785 * element even while multi-dragging. We have to have the only instance of the observer. See EXT-4347.
 786 */
 787class LLViewerInventoryMoveFromWorldObserver : public LLInventoryAddItemByAssetObserver
 788{
 789public:
 790	LLViewerInventoryMoveFromWorldObserver()
 791		: LLInventoryAddItemByAssetObserver()
 792	{
 793
 794	}
 795
 796	void setMoveIntoFolderID(const LLUUID& into_folder_uuid) {mMoveIntoFolderID = into_folder_uuid; }
 797
 798private:
 799	/*virtual */void onAssetAdded(const LLUUID& asset_id)
 800	{
 801		// Store active Inventory panel.
 802		if (LLInventoryPanel::getActiveInventoryPanel())
 803		{
 804			mActivePanel = LLInventoryPanel::getActiveInventoryPanel()->getHandle();
 805		}
 806
 807		// Store selected items (without destination folder)
 808		mSelectedItems.clear();
 809		if (LLInventoryPanel::getActiveInventoryPanel())
 810		{
 811			mSelectedItems = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList();
 812		}
 813		mSelectedItems.erase(mMoveIntoFolderID);
 814	}
 815
 816	/**
 817	 * Selects added inventory items watched by their Asset UUIDs if selection was not changed since
 818	 * all items were started to watch (dropped into a folder).
 819	 */
 820	void done()
 821	{
 822		LLInventoryPanel* active_panel = dynamic_cast<LLInventoryPanel*>(mActivePanel.get());
 823
 824		// if selection is not changed since watch started lets hightlight new items.
 825		if (active_panel && !isSelectionChanged())
 826		{
 827			LL_DEBUGS("Inventory_Move") << "Selecting new items..." << LL_ENDL;
 828			active_panel->clearSelection();
 829			highlight_inventory_objects_in_panel(mAddedItems, active_panel);
 830		}
 831	}
 832
 833	/**
 834	 * Returns true if selected inventory items were changed since moved inventory items were started to watch.
 835	 */
 836	bool isSelectionChanged()
 837	{	
 838		LLInventoryPanel* active_panel = dynamic_cast<LLInventoryPanel*>(mActivePanel.get());
 839
 840		if (NULL == active_panel)
 841		{
 842			return true;
 843		}
 844
 845		// get selected items (without destination folder)
 846		selected_items_t selected_items = active_panel->getRootFolder()->getSelectionList();
 847		selected_items.erase(mMoveIntoFolderID);
 848
 849		// compare stored & current sets of selected items
 850		selected_items_t different_items;
 851		std::set_symmetric_difference(mSelectedItems.begin(), mSelectedItems.end(),
 852			selected_items.begin(), selected_items.end(), std::inserter(different_items, different_items.begin()));
 853
 854		LL_DEBUGS("Inventory_Move") << "Selected firstly: " << mSelectedItems.size()
 855			<< ", now: " << selected_items.size() << ", difference: " << different_items.size() << LL_ENDL;
 856
 857		return different_items.size() > 0;
 858	}
 859
 860	LLHandle<LLPanel> mActivePanel;
 861	typedef std::set<LLUUID> selected_items_t;
 862	selected_items_t mSelectedItems;
 863
 864	/**
 865	 * UUID of FolderViewFolder into which watched items are moved.
 866	 *
 867	 * Destination FolderViewFolder becomes selected while mouse hovering (when dragged items are dropped).
 868	 * 
 869	 * If mouse is moved out it set unselected and number of selected items is changed 
 870	 * even if selected items in Inventory stay the same.
 871	 * So, it is used to update stored selection list.
 872	 *
 873	 * @see onAssetAdded()
 874	 * @see isSelectionChanged()
 875	 */
 876	LLUUID mMoveIntoFolderID;
 877};
 878
 879LLViewerInventoryMoveFromWorldObserver* gInventoryMoveObserver = NULL;
 880
 881void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid)
 882{
 883	start_new_inventory_observer();
 884
 885	gInventoryMoveObserver->setMoveIntoFolderID(into_folder_uuid);
 886	gInventoryMoveObserver->watchAsset(inv_item->getAssetUUID());
 887}
 888
 889
 890/**
 891 * Class to observe moving of items and to select them in inventory.
 892 *
 893 * Used currently for dragging from inbox to regular inventory folders
 894 */
 895
 896class LLViewerInventoryMoveObserver : public LLInventoryObserver
 897{
 898public:
 899
 900	LLViewerInventoryMoveObserver(const LLUUID& object_id)
 901		: LLInventoryObserver()
 902		, mObjectID(object_id)
 903	{
 904		if (LLInventoryPanel::getActiveInventoryPanel())
 905		{
 906			mActivePanel = LLInventoryPanel::getActiveInventoryPanel()->getHandle();
 907		}
 908	}
 909
 910	virtual ~LLViewerInventoryMoveObserver() {}
 911	virtual void changed(U32 mask);
 912	
 913private:
 914	LLUUID mObjectID;
 915	LLHandle<LLPanel> mActivePanel;
 916
 917};
 918
 919void LLViewerInventoryMoveObserver::changed(U32 mask)
 920{
 921	LLInventoryPanel* active_panel = dynamic_cast<LLInventoryPanel*>(mActivePanel.get());
 922
 923	if (NULL == active_panel)
 924	{
 925		gInventory.removeObserver(this);
 926		return;
 927	}
 928
 929	if((mask & (LLInventoryObserver::STRUCTURE)) != 0)
 930	{
 931		const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
 932
 933		std::set<LLUUID>::const_iterator id_it = changed_items.begin();
 934		std::set<LLUUID>::const_iterator id_end = changed_items.end();
 935		for (;id_it != id_end; ++id_it)
 936		{
 937			if ((*id_it) == mObjectID)
 938			{
 939				active_panel->clearSelection();			
 940				std::vector<LLUUID> items;
 941				items.push_back(mObjectID);
 942				highlight_inventory_objects_in_panel(items, active_panel);
 943				active_panel->getRootFolder()->scrollToShowSelection();
 944				
 945				gInventory.removeObserver(this);
 946				break;
 947			}
 948		}
 949	}
 950}
 951
 952void set_dad_inbox_object(const LLUUID& object_id)
 953{
 954	LLViewerInventoryMoveObserver* move_observer = new LLViewerInventoryMoveObserver(object_id);
 955	gInventory.addObserver(move_observer);
 956}
 957
 958//unlike the FetchObserver for AgentOffer, we only make one 
 959//instance of the AddedObserver for TaskOffers
 960//and it never dies.  We do this because we don't know the UUID of 
 961//task offers until they are accepted, so we don't wouldn't 
 962//know what to watch for, so instead we just watch for all additions.
 963class LLOpenTaskOffer : public LLInventoryAddedObserver
 964{
 965protected:
 966	/*virtual*/ void done()
 967	{
 968		for (uuid_vec_t::iterator it = mAdded.begin(); it != mAdded.end();)
 969		{
 970			const LLUUID& item_uuid = *it;
 971			bool was_moved = false;
 972			LLInventoryObject* added_object = gInventory.getObject(item_uuid);
 973			if (added_object)
 974			{
 975				// cast to item to get Asset UUID
 976				LLInventoryItem* added_item = dynamic_cast<LLInventoryItem*>(added_object);
 977				if (added_item)
 978				{
 979					const LLUUID& asset_uuid = added_item->getAssetUUID();
 980					if (gInventoryMoveObserver->isAssetWatched(asset_uuid))
 981					{
 982						LL_DEBUGS("Inventory_Move") << "Found asset UUID: " << asset_uuid << LL_ENDL;
 983						was_moved = true;
 984					}
 985				}
 986			}
 987
 988			if (was_moved)
 989			{
 990				it = mAdded.erase(it);
 991			}
 992			else ++it;
 993		}
 994
 995		open_inventory_offer(mAdded, "");
 996		mAdded.clear();
 997	}
 998 };
 999
1000class LLOpenTaskGroupOffer : public LLInventoryAddedObserver
1001{
1002protected:
1003	/*virtual*/ void done()
1004	{
1005		open_inventory_offer(mAdded, "group_offer");
1006		mAdded.clear();
1007		gInventory.removeObserver(this);
1008		delete this;
1009	}
1010};
1011
1012//one global instance to bind them
1013LLOpenTaskOffer* gNewInventoryObserver=NULL;
1014class LLNewInventoryHintObserver : public LLInventoryAddedObserver
1015{
1016protected:
1017	/*virtual*/ void done()
1018	{
1019		LLFirstUse::newInventory();
1020	}
1021};
1022
1023LLNewInventoryHintObserver* gNewInventoryHintObserver=NULL;
1024
1025void start_new_inventory_observer()
1026{
1027	if (!gNewInventoryObserver) //task offer observer 
1028	{
1029		// Observer is deleted by gInventory
1030		gNewInventoryObserver = new LLOpenTaskOffer;
1031		gInventory.addObserver(gNewInventoryObserver);
1032	}
1033
1034	if (!gInventoryMoveObserver) //inventory move from the world observer 
1035	{
1036		// Observer is deleted by gInventory
1037		gInventoryMoveObserver = new LLViewerInventoryMoveFromWorldObserver;
1038		gInventory.addObserver(gInventoryMoveObserver);
1039	}
1040
1041	if (!gNewInventoryHintObserver)
1042	{
1043		// Observer is deleted by gInventory
1044		gNewInventoryHintObserver = new LLNewInventoryHintObserver();
1045		gInventory.addObserver(gNewInventoryHintObserver);
1046	}
1047}
1048
1049class LLDiscardAgentOffer : public LLInventoryFetchItemsObserver
1050{
1051	LOG_CLASS(LLDiscardAgentOffer);
1052
1053public:
1054	LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) :
1055		LLInventoryFetchItemsObserver(object_id),
1056		mFolderID(folder_id),
1057		mObjectID(object_id) {}
1058
1059	virtual void done()
1060	{
1061		LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL;
1062
1063		// We're invoked from LLInventoryModel::notifyObservers().
1064		// If we now try to remove the inventory item, it will cause a nested
1065		// notifyObservers() call, which won't work.
1066		// So defer moving the item to trash until viewer gets idle (in a moment).
1067		LLAppViewer::instance()->addOnIdleCallback(boost::bind(&LLInventoryModel::removeItem, &gInventory, mObjectID));
1068		gInventory.removeObserver(this);
1069		delete this;
1070	}
1071
1072protected:
1073	LLUUID mFolderID;
1074	LLUUID mObjectID;
1075};
1076
1077
1078//Returns TRUE if we are OK, FALSE if we are throttled
1079//Set check_only true if you want to know the throttle status 
1080//without registering a hit
1081bool check_offer_throttle(const std::string& from_name, bool check_only)
1082{
1083	static U32 throttle_count;
1084	static bool throttle_logged;
1085	LLChat chat;
1086	std::string log_message;
1087
1088	if (!gSavedSettings.getBOOL("ShowNewInventory"))
1089		return false;
1090
1091	if (check_only)
1092	{
1093		return gThrottleTimer.hasExpired();
1094	}
1095	
1096	if(gThrottleTimer.checkExpirationAndReset(OFFER_THROTTLE_TIME))
1097	{
1098		LL_DEBUGS("Messaging") << "Throttle Expired" << LL_ENDL;
1099		throttle_count=1;
1100		throttle_logged=false;
1101		return true;
1102	}
1103	else //has not expired
1104	{
1105		LL_DEBUGS("Messaging") << "Throttle Not Expired, Count: " << throttle_count << LL_ENDL;
1106		// When downloading the initial inventory we get a lot of new items
1107		// coming in and can't tell that from spam.
1108		if (LLStartUp::getStartupState() >= STATE_STARTED
1109			&& throttle_count >= OFFER_THROTTLE_MAX_COUNT)
1110		{
1111			if (!throttle_logged)
1112			{
1113				// Use the name of the last item giver, who is probably the person
1114				// spamming you.
1115
1116				LLStringUtil::format_map_t arg;
1117				std::string log_msg;
1118				std::ostringstream time ;
1119				time<<OFFER_THROTTLE_TIME;
1120
1121				arg["APP_NAME"] = LLAppViewer::instance()->getSecondLifeTitle();
1122				arg["TIME"] = time.str();
1123
1124				if (!from_name.empty())
1125				{
1126					arg["FROM_NAME"] = from_name;
1127					log_msg = LLTrans::getString("ItemsComingInTooFastFrom", arg);
1128				}
1129				else
1130				{
1131					log_msg = LLTrans::getString("ItemsComingInTooFast", arg);
1132				}
1133				
1134				//this is kinda important, so actually put it on screen
1135				LLSD args;
1136				args["MESSAGE"] = log_msg;
1137				LLNotificationsUtil::add("SystemMessage", args);
1138
1139				throttle_logged=true;
1140			}
1141			return false;
1142		}
1143		else
1144		{
1145			throttle_count++;
1146			return true;
1147		}
1148	}
1149}
1150 
1151void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_name)
1152{
1153	for (uuid_vec_t::const_iterator obj_iter = objects.begin();
1154		 obj_iter != objects.end();
1155		 ++obj_iter)
1156	{
1157		const LLUUID& obj_id = (*obj_iter);
1158		if(!highlight_offered_object(obj_id))
1159		{
1160			continue;
1161		}
1162
1163		const LLInventoryObject *obj = gInventory.getObject(obj_id);
1164		if (!obj)
1165		{
1166			llwarns << "Cannot find object [ itemID:" << obj_id << " ] to open." << llendl;
1167			continue;
1168		}
1169
1170		const LLAssetType::EType asset_type = obj->getActualType();
1171
1172		// Either an inventory item or a category.
1173		const LLInventoryItem* item = dynamic_cast<const LLInventoryItem*>(obj);
1174		if (item)
1175		{
1176			////////////////////////////////////////////////////////////////////////////////
1177			// Special handling for various types.
1178			if (check_offer_throttle(from_name, false)) // If we are throttled, don't display
1179			{
1180				LL_DEBUGS("Messaging") << "Highlighting inventory item: " << item->getUUID()  << LL_ENDL;
1181				// If we opened this ourselves, focus it
1182				const BOOL take_focus = from_name.empty() ? TAKE_FOCUS_YES : TAKE_FOCUS_NO;
1183				switch(asset_type)
1184				{
1185					case LLAssetType::AT_NOTECARD:
1186					{
1187						LLFloaterReg::showInstance("preview_notecard", LLSD(obj_id), take_focus);
1188						break;
1189					}
1190					case LLAssetType::AT_LANDMARK:
1191					{
1192						LLInventoryCategory* parent_folder = gInventory.getCategory(item->getParentUUID());
1193						if ("inventory_handler" == from_name)
1194						{
1195							LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id", item->getUUID()));
1196						}
1197						else if("group_offer" == from_name)
1198						{
1199							// "group_offer" is passed by LLOpenTaskGroupOffer
1200							// Notification about added landmark will be generated under the "from_name.empty()" called from LLOpenTaskOffer::done().
1201							LLSD args;
1202							args["type"] = "landmark";
1203							args["id"] = obj_id;
1204							LLFloaterSidePanelContainer::showPanel("places", args);
1205
1206							continue;
1207						}
1208						else if(from_name.empty())
1209						{
1210							std::string folder_name;
1211							if (parent_folder)
1212							{
1213								// Localize folder name.
1214								// *TODO: share this code?
1215								folder_name = parent_folder->getName();
1216								if (LLFolderType::lookupIsProtectedType(parent_folder->getPreferredType()))
1217								{
1218									LLTrans::findString(folder_name, "InvFolder " + folder_name);
1219								}
1220							}
1221							else
1222							{
1223								 folder_name = LLTrans::getString("Unknown");
1224							}
1225
1226							// we receive a message from LLOpenTaskOffer, it mean that new landmark has been added.
1227							LLSD args;
1228							args["LANDMARK_NAME"] = item->getName();
1229							args["FOLDER_NAME"] = folder_name;
1230							LLNotificationsUtil::add("LandmarkCreated", args);
1231						}
1232					}
1233					break;
1234					case LLAssetType::AT_TEXTURE:
1235					{
1236						LLFloaterReg::showInstance("preview_texture", LLSD(obj_id), take_focus);
1237						break;
1238					}
1239					case LLAssetType::AT_ANIMATION:
1240						LLFloaterReg::showInstance("preview_anim", LLSD(obj_id), take_focus);
1241						break;
1242					case LLAssetType::AT_SCRIPT:
1243						LLFloaterReg::showInstance("preview_script", LLSD(obj_id), take_focus);
1244						break;
1245					case LLAssetType::AT_SOUND:
1246						LLFloaterReg::showInstance("preview_sound", LLSD(obj_id), take_focus);
1247						break;
1248					default:
1249						break;
1250				}
1251			}
1252		}
1253
1254		////////////////////////////////////////////////////////////////////////////////
1255		// Highlight item
1256		const BOOL auto_open = 
1257			gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false
1258			!from_name.empty(); // don't open if it's not from anyone.
1259		LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id);
1260	}
1261}
1262
1263bool highlight_offered_object(const LLUUID& obj_id)
1264{
1265	const LLInventoryObject* obj = gInventory.getObject(obj_id);
1266	if(!obj)
1267	{
1268		LL_WARNS("Messaging") << "Unable to show inventory item: " << obj_id << LL_ENDL;
1269		return false;
1270	}
1271
1272	////////////////////////////////////////////////////////////////////////////////
1273	// Don't highlight if it's in certain "quiet" folders which don't need UI
1274	// notification (e.g. trash, cof, lost-and-found).
1275	if(!gAgent.getAFK())
1276	{
1277		const LLViewerInventoryCategory *parent = gInventory.getFirstNondefaultParent(obj_id);
1278		if (parent)
1279		{
1280			const LLFolderType::EType parent_type = parent->getPreferredType();
1281			if (LLViewerFolderType::lookupIsQuietType(parent_type))
1282			{
1283				return false;
1284			}
1285		}
1286	}
1287
1288	return true;
1289}
1290
1291void inventory_offer_mute_callback(const LLUUID& blocked_id,
1292								   const std::string& full_name,
1293								   bool is_group)
1294{
1295	// *NOTE: blocks owner if the offer came from an object
1296	LLMute::EType mute_type = is_group ? LLMute::GROUP : LLMute::AGENT;
1297
1298	LLMute mute(blocked_id, full_name, mute_type);
1299	if (LLMuteList::getInstance()->add(mute))
1300	{
1301		LLPanelBlockedList::showPanelAndSelect(blocked_id);
1302	}
1303
1304	// purge the message queue of any previously queued inventory offers from the same source.
1305	class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher
1306	{
1307	public:
1308		OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {}
1309		bool matches(const LLNotificationPtr notification) const
1310		{
1311			if(notification->getName() == "ObjectGiveItem" 
1312				|| notification->getName() == "OwnObjectGiveItem"
1313				|| notification->getName() == "UserGiveItem")
1314			{
1315				return (notification->getPayload()["from_id"].asUUID() == blocked_id);
1316			}
1317			return FALSE;
1318		}
1319	private:
1320		const LLUUID& blocked_id;
1321	};
1322
1323	LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID(
1324			gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id));
1325}
1326
1327LLOfferInfo::LLOfferInfo()
1328 : LLNotificationResponderInterface()
1329 , mFromGroup(FALSE)
1330 , mFromObject(FALSE)
1331 , mIM(IM_NOTHING_SPECIAL)
1332 , mType(LLAssetType::AT_NONE)
1333 , mPersist(false)
1334{
1335}
1336
1337LLOfferInfo::LLOfferInfo(const LLSD& sd)
1338{
1339	mIM = (EInstantMessage)sd["im_type"].asInteger();
1340	mFromID = sd["from_id"].asUUID();
1341	mFromGroup = sd["from_group"].asBoolean();
1342	mFromObject = sd["from_object"].asBoolean();
1343	mTransactionID = sd["transaction_id"].asUUID();
1344	mFolderID = sd["folder_id"].asUUID();
1345	mObjectID = sd["object_id"].asUUID();
1346	mType = LLAssetType::lookup(sd["type"].asString().c_str());
1347	mFromName = sd["from_name"].asString();
1348	mDesc = sd["description"].asString();
1349	mHost = LLHost(sd["sender"].asString());
1350	mPersist = sd["persist"].asBoolean();
1351}
1352
1353LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
1354{
1355	mIM = info.mIM;
1356	mFromID = info.mFromID;
1357	mFromGroup = info.mFromGroup;
1358	mFromObject = info.mFromObject;
1359	mTransactionID = info.mTransactionID;
1360	mFolderID = info.mFolderID;
1361	mObjectID = info.mObjectID;
1362	mType = info.mType;
1363	mFromName = info.mFromName;
1364	mDesc = info.mDesc;
1365	mHost = info.mHost;
1366	mPersist = info.mPersist;
1367}
1368
1369LLSD LLOfferInfo::asLLSD()
1370{
1371	LLSD sd;
1372	sd["im_type"] = mIM;
1373	sd["from_id"] = mFromID;
1374	sd["from_group"] = mFromGroup;
1375	sd["from_object"] = mFromObject;
1376	sd["transaction_id"] = mTransactionID;
1377	sd["folder_id"] = mFolderID;
1378	sd["object_id"] = mObjectID;
1379	sd["type"] = LLAssetType::lookup(mType);
1380	sd["from_name"] = mFromName;
1381	sd["description"] = mDesc;
1382	sd["sender"] = mHost.getIPandPort();
1383	sd["persist"] = mPersist;
1384	return sd;
1385}
1386
1387void LLOfferInfo::fromLLSD(const LLSD& params)
1388{
1389	*this = params;
1390}
1391
1392void LLOfferInfo::send_auto_receive_response(void)
1393{	
1394	LLMessageSystem* msg = gMessageSystem;
1395	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
1396	msg->nextBlockFast(_PREHASH_AgentData);
1397	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1398	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1399	msg->nextBlockFast(_PREHASH_MessageBlock);
1400	msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
1401	msg->addUUIDFast(_PREHASH_ToAgentID, mFromID);
1402	msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
1403	msg->addUUIDFast(_PREHASH_ID, mTransactionID);
1404	msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
1405	std::string name;
1406	LLAgentUI::buildFullname(name);
1407	msg->addStringFast(_PREHASH_FromAgentName, name);
1408	msg->addStringFast(_PREHASH_Message, ""); 
1409	msg->addU32Fast(_PREHASH_ParentEstateID, 0);
1410	msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
1411	msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
1412	
1413	// Auto Receive Message. The math for the dialog works, because the accept
1414	// for inventory_offered, task_inventory_offer or
1415	// group_notice_inventory is 1 greater than the offer integer value.
1416	// Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, 
1417	// or IM_GROUP_NOTICE_INVENTORY_ACCEPTED
1418	msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1));
1419	msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData),
1420						   sizeof(mFolderID.mData));
1421	// send the message
1422	msg->sendReliable(mHost);
1423	
1424	if(IM_INVENTORY_OFFERED == mIM)
1425	{
1426		// add buddy to recent people list
1427		LLRecentPeople::instance().add(mFromID);
1428	}
1429}
1430
1431void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response)
1432{
1433	initRespondFunctionMap();
1434
1435	const std::string name = notification["name"].asString();
1436	if(mRespondFunctions.find(name) == mRespondFunctions.end())
1437	{
1438		llwarns << "Unexpected notification name : " << name << llendl;
1439		llassert(!"Unexpected notification name");
1440		return;
1441	}
1442
1443	mRespondFunctions[name](notification, response);
1444}
1445
1446bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)
1447{
1448	LLChat chat;
1449	std::string log_message;
1450	S32 button = LLNotificationsUtil::getSelectedOption(notification, response);
1451
1452	LLInventoryObserver* opener = NULL;
1453	LLViewerInventoryCategory* catp = NULL;
1454	catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID);
1455	LLViewerInventoryItem* itemp = NULL;
1456	if(!catp)
1457	{
1458		itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID);
1459	}
1460	 
1461	// For muting, we need to add the mute, then decline the offer.
1462	// This must be done here because:
1463	// * callback may be called immediately,
1464	// * adding the mute sends a message,
1465	// * we can't build two messages at once.
1466	if (2 == button) // Block
1467	{
1468		LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID());
1469
1470		llassert(notification_ptr != NULL);
1471		if (notification_ptr != NULL)
1472		{
1473			gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback, _1, _2, _3));
1474		}
1475	}
1476
1477	std::string from_string; // Used in the pop-up.
1478	std::string chatHistory_string;  // Used in chat history.
1479	
1480	// TODO: when task inventory offers can also be handled the new way, migrate the code that sets these strings here:
1481	from_string = chatHistory_string = mFromName;
1482	
1483	bool busy = gAgent.getBusy();
1484	
1485	switch(button)
1486	{
1487	case IOR_SHOW:
1488		// we will want to open this item when it comes back.
1489		LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID
1490				 << LL_ENDL;
1491		switch (mIM)
1492		{
1493		case IM_INVENTORY_OFFERED:
1494			{
1495				// This is an offer from an agent. In this case, the back
1496				// end has already copied the items into your inventory,
1497				// so we can fetch it out of our inventory.
1498				if (gSavedSettings.getBOOL("ShowOfferedInventory"))
1499				{
1500					LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(mObjectID, from_string);
1501					open_agent_offer->startFetch();
1502					if(catp || (itemp && itemp->isFinished()))
1503					{
1504						open_agent_offer->done();
1505					}
1506					else
1507					{
1508						opener = open_agent_offer;
1509					}
1510				}
1511			}
1512			break;
1513		case IM_GROUP_NOTICE:
1514			opener = new LLOpenTaskGroupOffer;
1515			send_auto_receive_response();
1516			break;
1517		case IM_TASK_INVENTORY_OFFERED:
1518		case IM_GROUP_NOTICE_REQUESTED:
1519			// This is an offer from a task or group.
1520			// We don't use a new instance of an opener
1521			// We instead use the singular observer gOpenTaskOffer
1522			// Since it already exists, we don't need to actually do anything
1523			break;
1524		default:
1525			LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL;
1526			break;
1527		}
1528		break;
1529		// end switch (mIM)
1530			
1531	case IOR_ACCEPT:
1532		//don't spam them if they are getting flooded
1533		if (check_offer_throttle(mFromName, true))
1534		{
1535			log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString(".");
1536			LLSD args;
1537			args["MESSAGE"] = log_message;
1538			LLNotificationsUtil::add("SystemMessageTip", args);
1539		}
1540		break;
1541
1542	case IOR_MUTE:
1543		// MUTE falls through to decline
1544	case IOR_DECLINE:
1545		{
1546			{
1547				LLStringUtil::format_map_t log_message_args;
1548				log_message_args["DESC"] = mDesc;
1549				log_message_args["NAME"] = mFromName;
1550				log_message = LLTrans::getString("InvOfferDecline", log_message_args);
1551			}
1552			chat.mText = log_message;
1553			if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) )  // muting for SL-42269
1554			{
1555				chat.mMuted = TRUE;
1556			}
1557
1558			// *NOTE dzaporozhan
1559			// Disabled logging to old chat floater to fix crash in group notices - EXT-4149
1560			// LLFloaterChat::addChatHistory(chat);
1561			
1562			LLDiscardAgentOffer* discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID);
1563			discard_agent_offer->startFetch();
1564			if (catp || (itemp && itemp->isFinished()))
1565			{
1566				discard_agent_offer->done();
1567			}
1568			else
1569			{
1570				opener = discard_agent_offer;
1571			}
1572			
1573			
1574			if (busy &&	(!mFromGroup && !mFromObject))
1575			{
1576				busy_message(gMessageSystem, mFromID);
1577			}
1578			break;
1579		}
1580	default:
1581		// close button probably
1582		// The item has already been fetched and is in your inventory, we simply won't highlight it
1583		// OR delete it if the notification gets killed, since we don't want that to be a vector for 
1584		// losing inventory offers.
1585		break;
1586	}
1587
1588	if(opener)
1589	{
1590		gInventory.addObserver(opener);
1591	}
1592
1593	if(!mPersist)
1594	{
1595		delete this;
1596	}
1597	return false;
1598}
1599
1600bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const LLSD& response)
1601{
1602	LLChat chat;
1603	std::string log_message;
1604	S32 button = LLNotification::getSelectedOption(notification, response);
1605	
1606	// For muting, we need to add the mute, then decline the offer.
1607	// This must be done here because:
1608	// * callback may be called immediately,
1609	// * adding the mute sends a message,
1610	// * we can't build two messages at once.
1611	if (2 == button)
1612	{
1613		LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID());
1614
1615		llassert(notification_ptr != NULL);
1616		if (notification_ptr != NULL)
1617		{
1618			gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback, _1, _2, _3));
1619		}
1620	}
1621	
1622	LLMessageSystem* msg = gMessageSystem;
1623	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
1624	msg->nextBlockFast(_PREHASH_AgentData);
1625	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1626	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1627	msg->nextBlockFast(_PREHASH_MessageBlock);
1628	msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
1629	msg->addUUIDFast(_PREHASH_ToAgentID, mFromID);
1630	msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
1631	msg->addUUIDFast(_PREHASH_ID, mTransactionID);
1632	msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
1633	std::string name;
1634	LLAgentUI::buildFullname(name);
1635	msg->addStringFast(_PREHASH_FromAgentName, name);
1636	msg->addStringFast(_PREHASH_Message, ""); 
1637	msg->addU32Fast(_PREHASH_ParentEstateID, 0);
1638	msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
1639	msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
1640	LLInventoryObserver* opener = NULL;
1641	
1642	std::string from_string; // Used in the pop-up.
1643	std::string chatHistory_string;  // Used in chat history.
1644	if (mFromObject == TRUE)
1645	{
1646		if (mFromGroup)
1647		{
1648			std::string group_name;
1649			if (gCacheName->getGroupName(mFromID, group_name))
1650			{
1651				from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" 
1652				+ mFromName + LLTrans::getString("'") +" " + LLTrans::getString("InvOfferOwnedByGroup") 
1653				+ " "+ "'" + group_name + "'";
1654				
1655				chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByGroup") 
1656				+ " " + group_name + "'";
1657			}
1658			else
1659			{
1660				from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'"
1661				+ mFromName +"'"+ " " + LLTrans::getString("InvOfferOwnedByUnknownGroup");
1662				chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwn…

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