PageRenderTime 52ms CodeModel.GetById 13ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llgiveinventory.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 550 lines | 439 code | 47 blank | 64 comment | 48 complexity | 47548af15d744c738ef8f6aa5bc85f1e MD5 | raw file
  1/**
  2 * @file llgiveinventory.cpp
  3 * @brief LLGiveInventory class implementation
  4 *
  5 * $LicenseInfo:firstyear=2010&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 "llgiveinventory.h"
 29
 30// library includes
 31#include "llnotificationsutil.h"
 32#include "lltrans.h"
 33
 34// newview includes
 35#include "llagent.h"
 36#include "llagentdata.h"
 37#include "llagentui.h"
 38#include "llagentwearables.h"
 39#include "llfloatertools.h" // for gFloaterTool
 40#include "llhudeffecttrail.h"
 41#include "llhudmanager.h"
 42#include "llimview.h"
 43#include "llinventory.h"
 44#include "llinventoryfunctions.h"
 45#include "llmutelist.h"
 46#include "llrecentpeople.h"
 47#include "llviewerobjectlist.h"
 48#include "llvoavatarself.h"
 49
 50// MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES
 51// or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a
 52// bit from there to give some pad.
 53const S32 MAX_ITEMS = 42;
 54
 55class LLGiveable : public LLInventoryCollectFunctor
 56{
 57public:
 58	LLGiveable() : mCountLosing(0) {}
 59	virtual ~LLGiveable() {}
 60	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
 61
 62	S32 countNoCopy() const { return mCountLosing; }
 63protected:
 64	S32 mCountLosing;
 65};
 66
 67bool LLGiveable::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
 68{
 69	// All categories can be given.
 70	if (cat)
 71		return true;
 72
 73	bool allowed = false;
 74	if (item)
 75	{
 76		allowed = itemTransferCommonlyAllowed(item);
 77		if (allowed &&
 78		   !item->getPermissions().allowOperationBy(PERM_TRANSFER,
 79							    gAgent.getID()))
 80		{
 81			allowed = FALSE;
 82		}
 83		if (allowed &&
 84		   !item->getPermissions().allowCopyBy(gAgent.getID()))
 85		{
 86			++mCountLosing;
 87		}
 88	}
 89	return allowed;
 90}
 91
 92class LLUncopyableItems : public LLInventoryCollectFunctor
 93{
 94public:
 95	LLUncopyableItems() {}
 96	virtual ~LLUncopyableItems() {}
 97	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
 98};
 99
100bool LLUncopyableItems::operator()(LLInventoryCategory* cat,
101								   LLInventoryItem* item)
102{
103	bool uncopyable = false;
104	if (item)
105	{
106		if (itemTransferCommonlyAllowed(item) &&
107			!item->getPermissions().allowCopyBy(gAgent.getID()))
108		{
109			uncopyable = true;
110		}
111	}
112	return uncopyable;
113}
114
115// static
116bool LLGiveInventory::isInventoryGiveAcceptable(const LLInventoryItem* item)
117{
118	if (!item) return false;
119
120	if (!isAgentAvatarValid()) return false;
121
122	if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID))
123	{
124		return false;
125	}
126
127	bool acceptable = true;
128	switch(item->getType())
129	{
130	case LLAssetType::AT_OBJECT:
131		if (get_is_item_worn(item->getUUID()))
132		{
133			acceptable = false;
134		}
135		break;
136	case LLAssetType::AT_BODYPART:
137	case LLAssetType::AT_CLOTHING:
138		{
139			BOOL copyable = false;
140			if (item->getPermissions().allowCopyBy(gAgentID)) copyable = true;
141
142			if (!copyable && get_is_item_worn(item->getUUID()))
143			{
144				acceptable = false;
145			}
146		}
147		break;
148	default:
149		break;
150	}
151	return acceptable;
152}
153
154// static
155bool LLGiveInventory::isInventoryGroupGiveAcceptable(const LLInventoryItem* item)
156{
157	if (!item) return false;
158
159	if (!isAgentAvatarValid()) return false;
160
161	// These permissions are double checked in the simulator in
162	// LLGroupNoticeInventoryItemFetch::result().
163	if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID))
164	{
165		return false;
166	}
167	if (!item->getPermissions().allowCopyBy(gAgent.getID()))
168	{
169		return false;
170	}
171
172
173	bool acceptable = true;
174	switch(item->getType())
175	{
176	case LLAssetType::AT_OBJECT:
177		if (gAgentAvatarp->isWearingAttachment(item->getUUID()))
178		{
179			acceptable = false;
180		}
181		break;
182	default:
183		break;
184	}
185	return acceptable;
186}
187
188// static
189bool LLGiveInventory::doGiveInventoryItem(const LLUUID& to_agent,
190									  const LLInventoryItem* item,
191									  const LLUUID& im_session_id/* = LLUUID::null*/)
192
193{
194	bool res = true;
195	llinfos << "LLGiveInventory::giveInventory()" << llendl;
196	if (!isInventoryGiveAcceptable(item))
197	{
198		return false;
199	}
200	if (item->getPermissions().allowCopyBy(gAgentID))
201	{
202		// just give it away.
203		LLGiveInventory::commitGiveInventoryItem(to_agent, item, im_session_id);
204	}
205	else
206	{
207		// ask if the agent is sure.
208		LLSD substitutions;
209		substitutions["ITEMS"] = item->getName();
210		LLSD payload;
211		payload["agent_id"] = to_agent;
212		LLSD items = LLSD::emptyArray();
213		items.append(item->getUUID());
214		payload["items"] = items;
215		LLNotificationsUtil::add("CannotCopyWarning", substitutions, payload,
216			&LLGiveInventory::handleCopyProtectedItem);
217		res = false;
218	}
219
220	return res;
221}
222
223void LLGiveInventory::doGiveInventoryCategory(const LLUUID& to_agent,
224											  const LLInventoryCategory* cat,
225											  const LLUUID& im_session_id)
226
227{
228	if (!cat) return;
229	llinfos << "LLGiveInventory::giveInventoryCategory() - "
230		<< cat->getUUID() << llendl;
231
232	if (!isAgentAvatarValid()) return;
233
234	// Test out how many items are being given.
235	LLViewerInventoryCategory::cat_array_t cats;
236	LLViewerInventoryItem::item_array_t items;
237	LLGiveable giveable;
238	gInventory.collectDescendentsIf (cat->getUUID(),
239		cats,
240		items,
241		LLInventoryModel::EXCLUDE_TRASH,
242		giveable);
243	S32 count = cats.count();
244	bool complete = true;
245	for(S32 i = 0; i < count; ++i)
246	{
247		if (!gInventory.isCategoryComplete(cats.get(i)->getUUID()))
248		{
249			complete = false;
250			break;
251		}
252	}
253	if (!complete)
254	{
255		LLNotificationsUtil::add("IncompleteInventory");
256		return;
257	}
258	count = items.count() + cats.count();
259	if (count > MAX_ITEMS)
260	{
261		LLNotificationsUtil::add("TooManyItems");
262		return;
263	}
264	else if (count == 0)
265	{
266		LLNotificationsUtil::add("NoItems");
267		return;
268	}
269	else
270	{
271		if (0 == giveable.countNoCopy())
272		{
273			LLGiveInventory::commitGiveInventoryCategory(to_agent, cat, im_session_id);
274		}
275		else
276		{
277			LLSD args;
278			args["COUNT"] = llformat("%d",giveable.countNoCopy());
279			LLSD payload;
280			payload["agent_id"] = to_agent;
281			payload["folder_id"] = cat->getUUID();
282			LLNotificationsUtil::add("CannotCopyCountItems", args, payload, &LLGiveInventory::handleCopyProtectedCategory);
283		}
284	}
285}
286
287//////////////////////////////////////////////////////////////////////////
288//     PRIVATE METHODS
289//////////////////////////////////////////////////////////////////////////
290
291//static
292void LLGiveInventory::logInventoryOffer(const LLUUID& to_agent, const LLUUID &im_session_id)
293{
294	// compute id of possible IM session with agent that has "to_agent" id
295	LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, to_agent);
296	// If this item was given by drag-and-drop into an IM panel, log this action in the IM panel chat.
297	LLSD args;
298	args["user_id"] = to_agent;
299	if (im_session_id.notNull())
300	{
301		gIMMgr->addSystemMessage(im_session_id, "inventory_item_offered", args);
302	}
303	// If this item was given by drag-and-drop on avatar while IM panel was open, log this action in the IM panel chat.
304	else if (LLIMModel::getInstance()->findIMSession(session_id))
305	{
306		gIMMgr->addSystemMessage(session_id, "inventory_item_offered", args);
307	}
308	// If this item was given by drag-and-drop on avatar while IM panel wasn't open, log this action to IM history.
309	else
310	{
311		std::string full_name;
312		if (gCacheName->getFullName(to_agent, full_name))
313		{
314			// Build a new format username or firstname_lastname for legacy names
315			// to use it for a history log filename.
316			full_name = LLCacheName::buildUsername(full_name);
317			LLIMModel::instance().logToFile(full_name, LLTrans::getString("SECOND_LIFE"), im_session_id, LLTrans::getString("inventory_item_offered-im"));
318		}
319	}
320}
321
322// static
323bool LLGiveInventory::handleCopyProtectedItem(const LLSD& notification, const LLSD& response)
324{
325	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
326	LLSD itmes = notification["payload"]["items"];
327	LLInventoryItem* item = NULL;
328	switch(option)
329	{
330	case 0:  // "Yes"
331		for (LLSD::array_iterator it = itmes.beginArray(); it != itmes.endArray(); it++)
332		{
333			item = gInventory.getItem((*it).asUUID());
334			if (item)
335			{
336				LLGiveInventory::commitGiveInventoryItem(notification["payload"]["agent_id"].asUUID(),
337					item);
338				// delete it for now - it will be deleted on the server
339				// quickly enough.
340				gInventory.deleteObject(item->getUUID());
341				gInventory.notifyObservers();
342			}
343			else
344			{
345				LLNotificationsUtil::add("CannotGiveItem");
346			}
347		}
348		break;
349
350	default: // no, cancel, whatever, who cares, not yes.
351		LLNotificationsUtil::add("TransactionCancelled");
352		break;
353	}
354	return false;
355}
356
357// static
358void LLGiveInventory::commitGiveInventoryItem(const LLUUID& to_agent,
359												const LLInventoryItem* item,
360												const LLUUID& im_session_id)
361{
362	if (!item) return;
363	std::string name;
364	LLAgentUI::buildFullname(name);
365	LLUUID transaction_id;
366	transaction_id.generate();
367	const S32 BUCKET_SIZE = sizeof(U8) + UUID_BYTES;
368	U8 bucket[BUCKET_SIZE];
369	bucket[0] = (U8)item->getType();
370	memcpy(&bucket[1], &(item->getUUID().mData), UUID_BYTES);		/* Flawfinder: ignore */
371	pack_instant_message(
372		gMessageSystem,
373		gAgentID,
374		FALSE,
375		gAgentSessionID,
376		to_agent,
377		name,
378		item->getName(),
379		IM_ONLINE,
380		IM_INVENTORY_OFFERED,
381		transaction_id,
382		0,
383		LLUUID::null,
384		gAgent.getPositionAgent(),
385		NO_TIMESTAMP,
386		bucket,
387		BUCKET_SIZE);
388	gAgent.sendReliableMessage();
389
390	// VEFFECT: giveInventory
391	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
392	effectp->setSourceObject(gAgentAvatarp);
393	effectp->setTargetObject(gObjectList.findObject(to_agent));
394	effectp->setDuration(LL_HUD_DUR_SHORT);
395	effectp->setColor(LLColor4U(gAgent.getEffectColor()));
396	gFloaterTools->dirty();
397
398	LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
399
400	logInventoryOffer(to_agent, im_session_id);
401
402	// add buddy to recent people list
403	LLRecentPeople::instance().add(to_agent);
404}
405
406// static
407bool LLGiveInventory::handleCopyProtectedCategory(const LLSD& notification, const LLSD& response)
408{
409	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
410	LLInventoryCategory* cat = NULL;
411	switch(option)
412	{
413	case 0:  // "Yes"
414		cat = gInventory.getCategory(notification["payload"]["folder_id"].asUUID());
415		if (cat)
416		{
417			LLGiveInventory::commitGiveInventoryCategory(notification["payload"]["agent_id"].asUUID(),
418				cat);
419			LLViewerInventoryCategory::cat_array_t cats;
420			LLViewerInventoryItem::item_array_t items;
421			LLUncopyableItems remove;
422			gInventory.collectDescendentsIf (cat->getUUID(),
423				cats,
424				items,
425				LLInventoryModel::EXCLUDE_TRASH,
426				remove);
427			S32 count = items.count();
428			for(S32 i = 0; i < count; ++i)
429			{
430				gInventory.deleteObject(items.get(i)->getUUID());
431			}
432			gInventory.notifyObservers();
433		}
434		else
435		{
436			LLNotificationsUtil::add("CannotGiveCategory");
437		}
438		break;
439
440	default: // no, cancel, whatever, who cares, not yes.
441		LLNotificationsUtil::add("TransactionCancelled");
442		break;
443	}
444	return false;
445}
446
447// static
448void LLGiveInventory::commitGiveInventoryCategory(const LLUUID& to_agent,
449													const LLInventoryCategory* cat,
450													const LLUUID& im_session_id)
451
452{
453	if (!cat) return;
454	llinfos << "LLGiveInventory::commitGiveInventoryCategory() - "
455		<< cat->getUUID() << llendl;
456
457	// add buddy to recent people list
458	LLRecentPeople::instance().add(to_agent);
459
460	// Test out how many items are being given.
461	LLViewerInventoryCategory::cat_array_t cats;
462	LLViewerInventoryItem::item_array_t items;
463	LLGiveable giveable;
464	gInventory.collectDescendentsIf (cat->getUUID(),
465		cats,
466		items,
467		LLInventoryModel::EXCLUDE_TRASH,
468		giveable);
469
470	// MAX ITEMS is based on (sizeof(uuid)+2) * count must be <
471	// MTUBYTES or 18 * count < 1200 => count < 1200/18 =>
472	// 66. I've cut it down a bit from there to give some pad.
473	S32 count = items.count() + cats.count();
474	if (count > MAX_ITEMS)
475	{
476		LLNotificationsUtil::add("TooManyItems");
477		return;
478	}
479	else if (count == 0)
480	{
481		LLNotificationsUtil::add("NoItems");
482		return;
483	}
484	else
485	{
486		std::string name;
487		LLAgentUI::buildFullname(name);
488		LLUUID transaction_id;
489		transaction_id.generate();
490		S32 bucket_size = (sizeof(U8) + UUID_BYTES) * (count + 1);
491		U8* bucket = new U8[bucket_size];
492		U8* pos = bucket;
493		U8 type = (U8)cat->getType();
494		memcpy(pos, &type, sizeof(U8));		/* Flawfinder: ignore */
495		pos += sizeof(U8);
496		memcpy(pos, &(cat->getUUID()), UUID_BYTES);		/* Flawfinder: ignore */
497		pos += UUID_BYTES;
498		S32 i;
499		count = cats.count();
500		for(i = 0; i < count; ++i)
501		{
502			memcpy(pos, &type, sizeof(U8));		/* Flawfinder: ignore */
503			pos += sizeof(U8);
504			memcpy(pos, &(cats.get(i)->getUUID()), UUID_BYTES);		/* Flawfinder: ignore */
505			pos += UUID_BYTES;
506		}
507		count = items.count();
508		for(i = 0; i < count; ++i)
509		{
510			type = (U8)items.get(i)->getType();
511			memcpy(pos, &type, sizeof(U8));		/* Flawfinder: ignore */
512			pos += sizeof(U8);
513			memcpy(pos, &(items.get(i)->getUUID()), UUID_BYTES);		/* Flawfinder: ignore */
514			pos += UUID_BYTES;
515		}
516		pack_instant_message(
517			gMessageSystem,
518			gAgent.getID(),
519			FALSE,
520			gAgent.getSessionID(),
521			to_agent,
522			name,
523			cat->getName(),
524			IM_ONLINE,
525			IM_INVENTORY_OFFERED,
526			transaction_id,
527			0,
528			LLUUID::null,
529			gAgent.getPositionAgent(),
530			NO_TIMESTAMP,
531			bucket,
532			bucket_size);
533		gAgent.sendReliableMessage();
534		delete[] bucket;
535
536		// VEFFECT: giveInventoryCategory
537		LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
538		effectp->setSourceObject(gAgentAvatarp);
539		effectp->setTargetObject(gObjectList.findObject(to_agent));
540		effectp->setDuration(LL_HUD_DUR_SHORT);
541		effectp->setColor(LLColor4U(gAgent.getEffectColor()));
542		gFloaterTools->dirty();
543
544		LLMuteList::getInstance()->autoRemove(to_agent, LLMuteList::AR_INVENTORY);
545
546		logInventoryOffer(to_agent, im_session_id);
547	}
548}
549
550// EOF