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