llaisapi.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. /**
  2. * @file llaisapi.cpp
  3. * @brief classes and functions implementation for interfacing with the v3+ ais
  4. * inventory service.
  5. *
  6. * $LicenseInfo:firstyear=2000&license=viewergpl$
  7. *
  8. * Copyright (c) 2000-2009, Linden Research, Inc.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include <utility> // For std::move
  35. #include "llaisapi.h"
  36. #include "llcallbacklist.h"
  37. #include "llnotifications.h"
  38. #include "llsdutil.h"
  39. #include "llagent.h"
  40. #include "llappviewer.h" // For gDisconnected
  41. #include "llinventorymodel.h"
  42. #include "llviewercontrol.h"
  43. #include "llviewerregion.h"
  44. // AIS3 allows '*' requests, but in reality those will be cut at some point.
  45. // Specify our own depth to be able to anticipate it and mark folders as
  46. // incomplete.
  47. constexpr U32 MAX_FOLDER_DEPTH_REQUEST = 50;
  48. //static
  49. std::list<AISAPI::ais_query_item_t> AISAPI::sPostponedQuery;
  50. //-----------------------------------------------------------------------------
  51. // Classes for AISv3 support.
  52. //-----------------------------------------------------------------------------
  53. //static
  54. bool AISAPI::isAvailable(bool override_setting)
  55. {
  56. static LLCachedControl<bool> use_ais(gSavedSettings, "UseAISForInventory");
  57. bool available = (override_setting || use_ais) &&
  58. gAgent.hasRegionCapability("InventoryAPIv3");
  59. static bool pool_created = false;
  60. if (available && !pool_created)
  61. {
  62. pool_created = true;
  63. LLCoprocedureManager::getInstance()->initializePool("AIS");
  64. }
  65. return available;
  66. }
  67. //static
  68. bool AISAPI::getInvCap(std::string& cap)
  69. {
  70. cap = gAgent.getRegionCapability("InventoryAPIv3");
  71. return !cap.empty();
  72. }
  73. //static
  74. bool AISAPI::getLibCap(std::string& cap)
  75. {
  76. cap = gAgent.getRegionCapability("LibraryAPIv3");
  77. return !cap.empty();
  78. }
  79. // I may be suffering from golden hammer here, but the first part of this bind
  80. // is actually a static cast for &HttpCoroutineAdapter::postAndSuspend so that
  81. // the compiler can identify the correct signature to select. Reads as follows:
  82. // LLSD : method returning LLSD
  83. // (LLCoreHttpUtil::HttpCoroutineAdapter::*): pointer to member function of
  84. // HttpCoroutineAdapter
  85. // (const std::string&, const LLSD&, LLCore::HttpOptions::ptr_t,
  86. // LLCore::HttpHeaders::ptr_t) : method signature
  87. #define COROCAST(T) static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(const std::string&, const LLSD&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>(T)
  88. #define COROCAST2(T) static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>(T)
  89. #define COROCAST3(T) static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(const std::string&, const std::string, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>(T)
  90. //static
  91. void AISAPI::createInventory(const LLUUID& parent_id, const LLSD& inventory,
  92. completion_t callback)
  93. {
  94. std::string url;
  95. if (!getInvCap(url))
  96. {
  97. llwarns << "No cap found" << llendl;
  98. if (callback)
  99. {
  100. callback(LLUUID::null);
  101. }
  102. return;
  103. }
  104. LLUUID tid;
  105. tid.generate();
  106. url += "/category/" + parent_id.asString() + "?tid=" + tid.asString();
  107. LL_DEBUGS("Inventory") << "url: " << url << " - New inventory:\n"
  108. << ll_pretty_print_sd(inventory) << LL_ENDL;
  109. invokationFn_t postfn =
  110. boost::bind(COROCAST(&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend),
  111. // _1 -> adapter
  112. // _2 -> url
  113. // _3 -> body
  114. // _4 -> options
  115. // _5 -> headers
  116. _1, _2, _3, _4, _5);
  117. LLCoprocedureManager::coprocedure_t
  118. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, postfn, url,
  119. parent_id, inventory, callback, CREATEINVENTORY));
  120. enqueueAISCommand("createInventory", proc);
  121. }
  122. //static
  123. void AISAPI::slamFolder(const LLUUID& folder_id, const LLSD& new_inventory,
  124. completion_t callback)
  125. {
  126. std::string url;
  127. if (!getInvCap(url))
  128. {
  129. llwarns << "No cap found" << llendl;
  130. if (callback)
  131. {
  132. callback(LLUUID::null);
  133. }
  134. return;
  135. }
  136. LLUUID tid;
  137. tid.generate();
  138. url += "/category/" + folder_id.asString() + "/links?tid=" +
  139. tid.asString();
  140. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  141. invokationFn_t putfn =
  142. boost::bind(COROCAST(&LLCoreHttpUtil::HttpCoroutineAdapter::putAndSuspend),
  143. // _1 -> adapter
  144. // _2 -> url
  145. // _3 -> body
  146. // _4 -> options
  147. // _5 -> headers
  148. _1, _2, _3, _4, _5);
  149. LLCoprocedureManager::coprocedure_t
  150. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, putfn, url,
  151. folder_id, new_inventory, callback, SLAMFOLDER));
  152. enqueueAISCommand("slamFolder", proc);
  153. }
  154. //static
  155. void AISAPI::removeCategory(const LLUUID& cat_id, completion_t callback)
  156. {
  157. std::string url;
  158. if (!getInvCap(url))
  159. {
  160. llwarns << "No cap found" << llendl;
  161. if (callback)
  162. {
  163. callback(LLUUID::null);
  164. }
  165. return;
  166. }
  167. url += "/category/" + cat_id.asString();
  168. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  169. invokationFn_t delfn =
  170. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend),
  171. // _1 -> adapter
  172. // _2 -> url
  173. // _3 -> body
  174. // _4 -> options
  175. // _5 -> headers
  176. _1, _2, _4, _5);
  177. LLCoprocedureManager::coprocedure_t
  178. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, delfn, url,
  179. cat_id, LLSD(), callback, REMOVECATEGORY));
  180. enqueueAISCommand("removeCategory", proc);
  181. }
  182. //static
  183. void AISAPI::removeItem(const LLUUID& item_id, completion_t callback)
  184. {
  185. std::string url;
  186. if (!getInvCap(url))
  187. {
  188. llwarns << "No cap found" << llendl;
  189. if (callback)
  190. {
  191. callback(LLUUID::null);
  192. }
  193. return;
  194. }
  195. url += "/item/" + item_id.asString();
  196. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  197. invokationFn_t delfn =
  198. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend),
  199. // _1 -> adapter
  200. // _2 -> url
  201. // _3 -> body
  202. // _4 -> options
  203. // _5 -> headers
  204. _1, _2, _4, _5);
  205. LLCoprocedureManager::coprocedure_t
  206. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, delfn, url,
  207. item_id, LLSD(), callback, REMOVEITEM));
  208. enqueueAISCommand("RemoveItem", proc);
  209. }
  210. //static
  211. void AISAPI::copyLibraryCategory(const LLUUID& source_id,
  212. const LLUUID& dest_id, bool copy_subfolders,
  213. completion_t callback)
  214. {
  215. std::string url;
  216. if (!getLibCap(url))
  217. {
  218. llwarns << "No cap found" << llendl;
  219. if (callback)
  220. {
  221. callback(LLUUID::null);
  222. }
  223. return;
  224. }
  225. LL_DEBUGS("Inventory") << "Copying library category: " << source_id
  226. << " => " << dest_id << LL_ENDL;
  227. LLUUID tid;
  228. tid.generate();
  229. url += "/category/" + source_id.asString() + "?tid=" + tid.asString();
  230. if (!copy_subfolders)
  231. {
  232. url += ",depth=0";
  233. }
  234. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  235. std::string destination = dest_id.asString();
  236. invokationFn_t copyfn =
  237. boost::bind(COROCAST3(&LLCoreHttpUtil::HttpCoroutineAdapter::copyAndSuspend),
  238. // _1 -> adapter
  239. // _2 -> url
  240. // _3 -> body
  241. // _4 -> options
  242. // _5 -> headers
  243. _1, _2, destination, _4, _5);
  244. LLCoprocedureManager::coprocedure_t
  245. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, copyfn, url,
  246. dest_id, LLSD(), callback, COPYLIBRARYCATEGORY));
  247. enqueueAISCommand("copyLibraryCategory", proc);
  248. }
  249. //static
  250. void AISAPI::purgeDescendents(const LLUUID& cat_id, completion_t callback)
  251. {
  252. std::string url;
  253. if (!getInvCap(url))
  254. {
  255. llwarns << "No cap found" << llendl;
  256. if (callback)
  257. {
  258. callback(LLUUID::null);
  259. }
  260. return;
  261. }
  262. url += "/category/" + cat_id.asString() + "/children";
  263. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  264. invokationFn_t delfn =
  265. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend),
  266. // _1 -> adapter
  267. // _2 -> url
  268. // _3 -> body
  269. // _4 -> options
  270. // _5 -> headers
  271. _1, _2, _4, _5);
  272. LLCoprocedureManager::coprocedure_t
  273. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, delfn, url,
  274. cat_id, LLSD(), callback, PURGEDESCENDENTS));
  275. enqueueAISCommand("purgeDescendents", proc);
  276. }
  277. //static
  278. void AISAPI::updateCategory(const LLUUID& cat_id, const LLSD& updates,
  279. completion_t callback)
  280. {
  281. std::string url;
  282. if (!getInvCap(url))
  283. {
  284. llwarns << "No cap found" << llendl;
  285. if (callback)
  286. {
  287. callback(LLUUID::null);
  288. }
  289. return;
  290. }
  291. url += "/category/" + cat_id.asString();
  292. LL_DEBUGS("Inventory") << "url: " << url << " - Request:\n"
  293. << ll_pretty_print_sd(updates) << LL_ENDL;
  294. invokationFn_t patchfn =
  295. boost::bind(COROCAST(&LLCoreHttpUtil::HttpCoroutineAdapter::patchAndSuspend),
  296. // _1 -> adapter
  297. // _2 -> url
  298. // _3 -> body
  299. // _4 -> options
  300. // _5 -> headers
  301. _1, _2, _3, _4, _5);
  302. LLCoprocedureManager::coprocedure_t
  303. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, patchfn, url,
  304. cat_id, updates, callback, UPDATECATEGORY));
  305. enqueueAISCommand("updateCategory", proc);
  306. }
  307. //static
  308. void AISAPI::updateItem(const LLUUID& item_id, const LLSD& updates,
  309. completion_t callback)
  310. {
  311. std::string url;
  312. if (!getInvCap(url))
  313. {
  314. llwarns << "No cap found" << llendl;
  315. if (callback)
  316. {
  317. callback(LLUUID::null);
  318. }
  319. return;
  320. }
  321. url += "/item/" + item_id.asString();
  322. LL_DEBUGS("Inventory") << "url: " << url << " - Request:\n"
  323. << ll_pretty_print_sd(updates) << LL_ENDL;
  324. invokationFn_t patchfn =
  325. boost::bind(COROCAST(&LLCoreHttpUtil::HttpCoroutineAdapter::patchAndSuspend),
  326. // _1 -> adapter
  327. // _2 -> url
  328. // _3 -> body
  329. // _4 -> options
  330. // _5 -> headers
  331. _1, _2, _3, _4, _5);
  332. LLCoprocedureManager::coprocedure_t
  333. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, patchfn, url,
  334. item_id, updates, callback, UPDATEITEM));
  335. enqueueAISCommand("updateItem", proc);
  336. }
  337. //static
  338. void AISAPI::fetchItem(const LLUUID& item_id, bool library,
  339. completion_t callback)
  340. {
  341. std::string url;
  342. bool has_cap = library ? getLibCap(url) : getInvCap(url);
  343. if (!has_cap)
  344. {
  345. llwarns << "No cap found" << llendl;
  346. if (callback)
  347. {
  348. callback(LLUUID::null);
  349. }
  350. return;
  351. }
  352. url += "/item/" + item_id.asString();
  353. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  354. invokationFn_t getfn =
  355. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  356. // _1 -> adapter
  357. // _2 -> url
  358. // _3 -> body
  359. // _4 -> options
  360. // _5 -> headers
  361. _1, _2, _4, _5);
  362. LLCoprocedureManager::coprocedure_t
  363. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  364. item_id, LLSD(), callback, FETCHITEM));
  365. enqueueAISCommand("fetchItem", proc);
  366. }
  367. //static
  368. void AISAPI::fetchCategoryChildren(const LLUUID& cat_id, bool library,
  369. bool recursive, completion_t callback,
  370. U32 depth)
  371. {
  372. std::string url;
  373. bool has_cap = library ? getLibCap(url) : getInvCap(url);
  374. if (!has_cap)
  375. {
  376. llwarns << "No cap found" << llendl;
  377. if (callback)
  378. {
  379. callback(LLUUID::null);
  380. }
  381. return;
  382. }
  383. url += "/category/" + cat_id.asString() + "/children";
  384. if (recursive || depth > MAX_FOLDER_DEPTH_REQUEST)
  385. {
  386. // Can specify depth=*, but server side is going to cap requests and
  387. // reject everything "over the top".
  388. depth = MAX_FOLDER_DEPTH_REQUEST;
  389. }
  390. url += llformat("?depth=%u" , depth);
  391. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  392. invokationFn_t getfn =
  393. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  394. // _1 -> adapter
  395. // _2 -> url
  396. // _3 -> body
  397. // _4 -> options
  398. // _5 -> headers
  399. _1, _2, _4, _5);
  400. // getAndSuspend() does not use a body, so we can pass additional data.
  401. LLSD body;
  402. body["depth"] = depth;
  403. LLCoprocedureManager::coprocedure_t
  404. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  405. cat_id, body, callback, FETCHCATEGORYCHILDREN));
  406. enqueueAISCommand("fetchCategoryChildren", proc);
  407. }
  408. //static
  409. void AISAPI::fetchCategoryCategories(const LLUUID& cat_id, bool library,
  410. bool recursive, completion_t callback,
  411. U32 depth)
  412. {
  413. std::string url;
  414. bool has_cap = library ? getLibCap(url) : getInvCap(url);
  415. if (!has_cap)
  416. {
  417. llwarns << "No cap found" << llendl;
  418. if (callback)
  419. {
  420. callback(LLUUID::null);
  421. }
  422. return;
  423. }
  424. url += "/category/" + cat_id.asString() + "/categories";
  425. if (recursive || depth > MAX_FOLDER_DEPTH_REQUEST)
  426. {
  427. // Can specify depth=*, but server side is going to cap requests and
  428. // reject everything "over the top".
  429. depth = MAX_FOLDER_DEPTH_REQUEST;
  430. }
  431. url += llformat("?depth=%u" , depth);
  432. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  433. invokationFn_t getfn =
  434. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  435. // _1 -> adapter
  436. // _2 -> url
  437. // _3 -> body
  438. // _4 -> options
  439. // _5 -> headers
  440. _1, _2, _4, _5);
  441. // getAndSuspend() does not use a body, so we can pass additional data.
  442. LLSD body;
  443. body["depth"] = depth;
  444. LLCoprocedureManager::coprocedure_t
  445. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  446. cat_id, body, callback, FETCHCATEGORYCATEGORIES));
  447. enqueueAISCommand("fetchCategoryCategories", proc);
  448. }
  449. //static
  450. void AISAPI::fetchCategorySubset(const LLUUID& cat_id,
  451. const uuid_vec_t& children, bool library,
  452. bool recursive, completion_t callback,
  453. U32 depth)
  454. {
  455. if (children.empty())
  456. {
  457. llwarns << "Empty request" << llendl;
  458. if (callback)
  459. {
  460. callback(LLUUID::null);
  461. }
  462. return;
  463. }
  464. std::string url;
  465. bool has_cap = library ? getLibCap(url) : getInvCap(url);
  466. if (!has_cap)
  467. {
  468. llwarns << "No cap found" << llendl;
  469. if (callback)
  470. {
  471. callback(LLUUID::null);
  472. }
  473. return;
  474. }
  475. url += "/category/" + cat_id.asString() + "/children";
  476. if (recursive || depth > MAX_FOLDER_DEPTH_REQUEST)
  477. {
  478. // Can specify depth=*, but server side is going to cap requests and
  479. // reject everything "over the top".
  480. depth = MAX_FOLDER_DEPTH_REQUEST;
  481. }
  482. url += llformat("?depth=%u&children=" , depth);
  483. for (U32 i = 0, count = children.size(); i < count; ++i)
  484. {
  485. if (i)
  486. {
  487. url.append(",");
  488. }
  489. url += children[i].asString();
  490. }
  491. // RFC documentation specifies a maximum length of 2048
  492. constexpr size_t MAX_URL_LENGTH = 2000;
  493. if (url.size() > MAX_URL_LENGTH)
  494. {
  495. llwarns << "Request url is too long, url: " << url << llendl;
  496. }
  497. else
  498. {
  499. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  500. }
  501. invokationFn_t getfn =
  502. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  503. // _1 -> adapter
  504. // _2 -> url
  505. // _3 -> body
  506. // _4 -> options
  507. // _5 -> headers
  508. _1, _2, _4, _5);
  509. // getAndSuspend() does not use a body, so we can pass additional data.
  510. LLSD body;
  511. body["depth"] = depth;
  512. LLCoprocedureManager::coprocedure_t
  513. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  514. cat_id, body, callback, FETCHCATEGORYSUBSET));
  515. enqueueAISCommand("fetchCategorySubset", proc);
  516. }
  517. //static
  518. void AISAPI::fetchCategoryLinks(const LLUUID& cat_id, completion_t callback)
  519. {
  520. std::string url;
  521. if (!getInvCap(url))
  522. {
  523. llwarns << "No cap found" << llendl;
  524. if (callback)
  525. {
  526. callback(LLUUID::null);
  527. }
  528. return;
  529. }
  530. url += "/category/" + cat_id.asString() + "/links";
  531. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  532. invokationFn_t getfn =
  533. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  534. // _1 -> adapter
  535. // _2 -> url
  536. // _3 -> body
  537. // _4 -> options
  538. // _5 -> headers
  539. _1, _2, _4, _5);
  540. // getAndSuspend() does not use a body, so we can pass additional data.
  541. LLSD body;
  542. body["depth"] = 0;
  543. LLCoprocedureManager::coprocedure_t
  544. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  545. cat_id, body, callback, FETCHCATEGORYLINKS));
  546. enqueueAISCommand("fetchCategoryLinks", proc);
  547. }
  548. //static
  549. void AISAPI::fetchCOF(completion_t callback)
  550. {
  551. std::string url;
  552. if (!getInvCap(url))
  553. {
  554. llwarns << "No cap found" << llendl;
  555. if (callback)
  556. {
  557. callback(LLUUID::null);
  558. }
  559. return;
  560. }
  561. url += "/category/current/links";
  562. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  563. invokationFn_t getfn =
  564. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  565. // _1 -> adapter
  566. // _2 -> url
  567. // _3 -> body
  568. // _4 -> options
  569. // _5 -> headers
  570. _1, _2, _4, _5);
  571. // getAndSuspend() does not use a body, so we can pass additional data.
  572. LLSD body;
  573. body["depth"] = 0;
  574. LLCoprocedureManager::coprocedure_t
  575. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  576. LLUUID::null, body, callback, FETCHCOF));
  577. enqueueAISCommand("fetchCOF", proc);
  578. }
  579. //static
  580. void AISAPI::fetchOrphans(completion_t callback)
  581. {
  582. std::string url;
  583. if (!getInvCap(url))
  584. {
  585. llwarns << "No cap found" << llendl;
  586. if (callback)
  587. {
  588. callback(LLUUID::null);
  589. }
  590. return;
  591. }
  592. url += "/orphans";
  593. LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
  594. invokationFn_t getfn =
  595. boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
  596. // _1 -> adapter
  597. // _2 -> url
  598. // _3 -> body
  599. // _4 -> options
  600. // _5 -> headers
  601. _1, _2, _4, _5);
  602. LLCoprocedureManager::coprocedure_t
  603. proc(boost::bind(&AISAPI::invokeAISCommandCoro, _1, getfn, url,
  604. LLUUID::null, LLSD(), callback, FETCHORPHANS));
  605. enqueueAISCommand("fetchOrphans", proc);
  606. }
  607. //static
  608. void AISAPI::enqueueAISCommand(const std::string& proc_name,
  609. LLCoprocedureManager::coprocedure_t proc)
  610. {
  611. if (!sPostponedQuery.empty())
  612. {
  613. sPostponedQuery.emplace_back("AIS(" + proc_name + ")", proc);
  614. llinfos << "Queue not empty. Postponing: " << proc_name << llendl;
  615. return;
  616. }
  617. LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
  618. LLUUID id = cpmgr->enqueueCoprocedure("AIS", "AIS(" + proc_name + ")",
  619. proc);
  620. if (id.isNull()) // Failure to enqueue !
  621. {
  622. llinfos << "Will retry: " << proc_name << llendl;
  623. sPostponedQuery.emplace_back("AIS(" + proc_name + ")", proc);
  624. gIdleCallbacks.addFunction(onIdle, NULL);
  625. }
  626. }
  627. //static
  628. void AISAPI::onIdle(void*)
  629. {
  630. LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
  631. while (!sPostponedQuery.empty())
  632. {
  633. ais_query_item_t& item = sPostponedQuery.front();
  634. LLUUID id = cpmgr->enqueueCoprocedure("AIS", item.first, item.second);
  635. if (id.isNull()) // Failure to enqueue !
  636. {
  637. llinfos << "Will retry: " << item.first << llendl;
  638. break;
  639. }
  640. sPostponedQuery.pop_front();
  641. }
  642. if (sPostponedQuery.empty())
  643. {
  644. gIdleCallbacks.deleteFunction(onIdle, NULL);
  645. }
  646. }
  647. //static
  648. void AISAPI::invokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter,
  649. invokationFn_t invoke, std::string url,
  650. LLUUID target_id, LLSD body,
  651. completion_t callback, U32 type)
  652. {
  653. if (gDisconnected)
  654. {
  655. if (callback)
  656. {
  657. callback(LLUUID::null);
  658. }
  659. return;
  660. }
  661. LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
  662. LLCore::HttpHeaders::ptr_t headers;
  663. constexpr U32 AIS_TIMEOUT = 180;
  664. options->setTimeout(AIS_TIMEOUT);
  665. LL_DEBUGS("Inventory") << "Target: " << target_id << " - Command type: "
  666. << (S32)type << " - URL: " << url << LL_ENDL;
  667. LLSD result = invoke(adapter, url, body, options, headers);
  668. LLCore::HttpStatus status =
  669. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  670. if (!status || !result.isMap())
  671. {
  672. if (!result.isMap())
  673. {
  674. status = gStatusInternalError;
  675. }
  676. llwarns << "Inventory error: " << status.toString() << " - Result:\n"
  677. << ll_pretty_print_sd(result) << llendl;
  678. if (status.getType() == 410) // Gone
  679. {
  680. // Item does not exist or was already deleted from server; parent
  681. // folder is out of sync.
  682. if (type == REMOVECATEGORY)
  683. {
  684. LLViewerInventoryCategory* catp =
  685. gInventory.getCategory(target_id);
  686. if (catp)
  687. {
  688. llwarns << "Purge failed (folder no longer exists on server) for: "
  689. << catp->getName()
  690. << " - Local version: " << catp->getVersion()
  691. << " - Descendents count: server="
  692. << catp->getDescendentCount() << " - viewer="
  693. << catp->getViewerDescendentCount() << llendl;
  694. gInventory.fetchDescendentsOf(catp->getParentUUID());
  695. }
  696. }
  697. else if (type == REMOVEITEM)
  698. {
  699. LLViewerInventoryItem* itemp = gInventory.getItem(target_id);
  700. if (itemp)
  701. {
  702. llwarns << "Purge failed (item no longer exists on server) for: "
  703. << itemp->getName() << llendl;
  704. gInventory.onObjectDeletedFromServer(target_id);
  705. }
  706. }
  707. }
  708. else if (status == gStatusForbidden) // 403
  709. {
  710. if (type == FETCHCATEGORYCHILDREN)
  711. {
  712. if (body.has("depth") && !body["depth"].asInteger())
  713. {
  714. // Cannot fetch a single folder with depth 0; folder is too
  715. // big.
  716. llwarns << "Fetch failed, content is over limit, url: "
  717. << url << llendl;
  718. static U32 warned = 0;
  719. const char* notification =
  720. warned++ ? "AISInventoryLimitReached"
  721. : "AISInventoryLimitReachedAlert";
  722. gNotifications.add(notification);
  723. }
  724. }
  725. }
  726. }
  727. // Parse update LLSD into stuff to do.
  728. AISUpdate ais_update(result, type, body);
  729. // Execute the updates in the appropriate order.
  730. ais_update.doUpdate();
  731. if (!callback || callback.empty())
  732. {
  733. return;
  734. }
  735. switch (type)
  736. {
  737. case COPYLIBRARYCATEGORY:
  738. case FETCHCATEGORYCHILDREN:
  739. case FETCHCATEGORYCATEGORIES:
  740. case FETCHCATEGORYSUBSET:
  741. case FETCHCOF:
  742. case FETCHCATEGORYLINKS:
  743. {
  744. LLUUID id;
  745. if (result.has("category_id"))
  746. {
  747. id = result["category_id"];
  748. }
  749. callback(id);
  750. break;
  751. }
  752. case FETCHITEM:
  753. {
  754. LLUUID id;
  755. if (result.has("item_id"))
  756. {
  757. // Error message might contain an item_id !
  758. id = result["item_id"];
  759. }
  760. if (result.has("linked_id"))
  761. {
  762. id = result["linked_id"];
  763. }
  764. callback(id);
  765. break;
  766. }
  767. case CREATEINVENTORY:
  768. {
  769. if (result.has("_created_categories"))
  770. {
  771. const LLSD& cats = result["_created_categories"];
  772. for (LLSD::array_const_iterator it = cats.beginArray(),
  773. end = cats.endArray();
  774. it != end; ++it)
  775. {
  776. callback(it->asUUID());
  777. }
  778. }
  779. if (result.has("_created_items"))
  780. {
  781. const LLSD& items = result["_created_items"];
  782. for (LLSD::array_const_iterator it = items.beginArray(),
  783. end = items.endArray();
  784. it != end; ++it)
  785. {
  786. callback(it->asUUID());
  787. }
  788. }
  789. }
  790. default: // No callback needed.
  791. break;
  792. }
  793. }
  794. constexpr F32 CORO_YIELD_SECONDS = 1.f / 120.f;
  795. AISUpdate::AISUpdate(const LLSD& update, U32 type, const LLSD& body)
  796. : mType(type),
  797. mFetch(type >= AISAPI::FETCHITEM),
  798. mFetchDepth(MAX_FOLDER_DEPTH_REQUEST)
  799. {
  800. LL_DEBUGS("Inventory") << "Applying updates for command type: " << type
  801. << LL_ENDL;
  802. if (mFetch && body.has("depth"))
  803. {
  804. mFetchDepth = body["depth"].asInteger();
  805. }
  806. mTimer.setTimerExpirySec(CORO_YIELD_SECONDS);
  807. parseUpdate(update);
  808. }
  809. void AISUpdate::checkTimeout()
  810. {
  811. if (mTimer.hasExpired())
  812. {
  813. llcoro::suspend();
  814. mTimer.setTimerExpirySec(CORO_YIELD_SECONDS);
  815. }
  816. }
  817. void AISUpdate::clearParseResults()
  818. {
  819. mCatDescendentDeltas.clear();
  820. mCatDescendentsKnown.clear();
  821. mCatVersionsUpdated.clear();
  822. mItemsCreated.clear();
  823. mItemsUpdated.clear();
  824. mItemsLost.clear();
  825. mCategoriesCreated.clear();
  826. mCategoriesUpdated.clear();
  827. mObjectsDeletedIds.clear();
  828. mItemIds.clear();
  829. mCategoryIds.clear();
  830. }
  831. void AISUpdate::parseUpdate(const LLSD& update)
  832. {
  833. clearParseResults();
  834. parseMeta(update);
  835. parseContent(update);
  836. }
  837. void AISUpdate::parseMeta(const LLSD& update)
  838. {
  839. LL_DEBUGS("Inventory") << "Meta data:\n" << ll_pretty_print_sd(update)
  840. << LL_ENDL;
  841. // Parse _categories_removed -> mObjectsDeletedIds
  842. uuid_list_t cat_ids;
  843. parseUUIDArray(update, "_categories_removed", cat_ids);
  844. for (uuid_list_t::const_iterator it = cat_ids.begin();
  845. it != cat_ids.end(); ++it)
  846. {
  847. LLViewerInventoryCategory* catp = gInventory.getCategory(*it);
  848. if (catp)
  849. {
  850. --mCatDescendentDeltas[catp->getParentUUID()];
  851. mObjectsDeletedIds.emplace(*it);
  852. }
  853. else
  854. {
  855. llwarns << "Removed category " << *it << " not found." << llendl;
  856. }
  857. }
  858. // Parse _categories_items_removed -> mObjectsDeletedIds
  859. uuid_list_t item_ids;
  860. parseUUIDArray(update, "_category_items_removed", item_ids);
  861. parseUUIDArray(update, "_removed_items", item_ids);
  862. for (uuid_list_t::const_iterator it = item_ids.begin();
  863. it != item_ids.end(); ++it)
  864. {
  865. LLViewerInventoryItem* itemp = gInventory.getItem(*it);
  866. if (itemp)
  867. {
  868. --mCatDescendentDeltas[itemp->getParentUUID()];
  869. mObjectsDeletedIds.emplace(*it);
  870. }
  871. else
  872. {
  873. llwarns << "Removed item " << *it << " not found." << llendl;
  874. }
  875. }
  876. // Parse _broken_links_removed -> mObjectsDeletedIds
  877. uuid_list_t broken_link_ids;
  878. parseUUIDArray(update, "_broken_links_removed", broken_link_ids);
  879. for (uuid_list_t::const_iterator it = broken_link_ids.begin();
  880. it != broken_link_ids.end(); ++it)
  881. {
  882. LLViewerInventoryItem* itemp = gInventory.getItem(*it);
  883. if (itemp)
  884. {
  885. --mCatDescendentDeltas[itemp->getParentUUID()];
  886. mObjectsDeletedIds.emplace(*it);
  887. }
  888. else
  889. {
  890. llwarns << "Removed broken link " << *it << " not found."
  891. << llendl;
  892. }
  893. }
  894. // Parse _created_items
  895. parseUUIDArray(update, "_created_items", mItemIds);
  896. // Parse _created_categories
  897. parseUUIDArray(update, "_created_categories", mCategoryIds);
  898. // Parse updated category versions.
  899. const std::string& ucv = "_updated_category_versions";
  900. if (update.has(ucv))
  901. {
  902. LLUUID cat_id;
  903. for (LLSD::map_const_iterator it = update[ucv].beginMap(),
  904. end = update[ucv].endMap();
  905. it != end; ++it)
  906. {
  907. cat_id.set(it->first, false);
  908. mCatVersionsUpdated[cat_id] = it->second.asInteger();
  909. }
  910. }
  911. }
  912. void AISUpdate::parseContent(const LLSD& update)
  913. {
  914. LL_DEBUGS("Inventory") << "Update data:\n" << ll_pretty_print_sd(update)
  915. << LL_ENDL;
  916. // Errors from a fetch request might contain an item id without full item
  917. // or folder. *TODO: depending on error we might want to do something, like
  918. // removing the item on 404, or refetching the parent folder.
  919. if (update.has("parent_id"))
  920. {
  921. if (update.has("linked_id"))
  922. {
  923. parseLink(update, mFetchDepth);
  924. }
  925. else if (update.has("item_id"))
  926. {
  927. parseItem(update);
  928. }
  929. }
  930. if (mType == AISAPI::FETCHCATEGORYSUBSET)
  931. {
  932. // Initial category is incomplete, do not process it and go for
  933. // contents instead.
  934. if (update.has("_embedded"))
  935. {
  936. parseEmbedded(update["_embedded"], mFetchDepth - 1);
  937. }
  938. }
  939. else if (update.has("category_id") && update.has("parent_id"))
  940. {
  941. parseCategory(update, mFetchDepth);
  942. }
  943. else if (update.has("_embedded"))
  944. {
  945. parseEmbedded(update["_embedded"], mFetchDepth);
  946. }
  947. }
  948. void AISUpdate::parseItem(const LLSD& item_map)
  949. {
  950. LL_DEBUGS("Inventory") << "Item map:\n" << ll_pretty_print_sd(item_map)
  951. << LL_ENDL;
  952. LLUUID item_id = item_map["item_id"].asUUID();
  953. LLPointer<LLViewerInventoryItem> new_itemp(new LLViewerInventoryItem);
  954. LLViewerInventoryItem* cur_itemp = gInventory.getItem(item_id);
  955. if (cur_itemp && new_itemp)
  956. {
  957. // Default to current values where not provided.
  958. new_itemp->copyViewerItem(cur_itemp);
  959. }
  960. if (!new_itemp || !new_itemp->unpackMessage(item_map))
  961. {
  962. llwarns << "Invalid data, cannot parse: " << item_map << llendl;
  963. gNotifications.add("AISFailure");
  964. return;
  965. }
  966. if (mFetch)
  967. {
  968. new_itemp->setComplete(true);
  969. if (new_itemp->getParentUUID().isNull())
  970. {
  971. mItemsLost.emplace(item_id, new_itemp);
  972. }
  973. // Do not use new_itemp after this ! HB
  974. mItemsCreated.emplace(item_id, std::move(new_itemp));
  975. }
  976. else if (cur_itemp)
  977. {
  978. // This statement is here to cause a new entry with 0 delta to be
  979. // created if it does not already exist; otherwise has no effect.
  980. mCatDescendentDeltas[new_itemp->getParentUUID()];
  981. // Do not use new_itemp after this ! HB
  982. mItemsUpdated.emplace(item_id, std::move(new_itemp));
  983. }
  984. else
  985. {
  986. new_itemp->setComplete(true);
  987. ++mCatDescendentDeltas[new_itemp->getParentUUID()];
  988. // Do not use new_itemp after this ! HB
  989. mItemsCreated.emplace(item_id, std::move(new_itemp));
  990. }
  991. }
  992. void AISUpdate::parseLink(const LLSD& link_map, U32 depth)
  993. {
  994. LL_DEBUGS("Inventory") << "Link map:\n" << ll_pretty_print_sd(link_map)
  995. << LL_ENDL;
  996. LLUUID item_id = link_map["item_id"].asUUID();
  997. LLPointer<LLViewerInventoryItem> new_linkp(new LLViewerInventoryItem);
  998. LLViewerInventoryItem* cur_linkp = gInventory.getItem(item_id);
  999. if (cur_linkp && new_linkp)
  1000. {
  1001. // Default to current values where not provided.
  1002. new_linkp->copyViewerItem(cur_linkp);
  1003. }
  1004. if (!new_linkp || !new_linkp->unpackMessage(link_map))
  1005. {
  1006. llwarns << "Invalid data, cannot parse: " << link_map << llendl;
  1007. gNotifications.add("AISFailure");
  1008. return;
  1009. }
  1010. const LLUUID& parent_id = new_linkp->getParentUUID();
  1011. if (mFetch)
  1012. {
  1013. LLPermissions perms;
  1014. perms.init(gAgentID, gAgentID, LLUUID::null, LLUUID::null);
  1015. perms.initMasks(PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
  1016. new_linkp->setPermissions(perms);
  1017. LLSaleInfo default_sale_info;
  1018. new_linkp->setSaleInfo(default_sale_info);
  1019. new_linkp->setComplete(true);
  1020. if (new_linkp->getParentUUID().isNull())
  1021. {
  1022. mItemsLost.emplace(item_id, new_linkp);
  1023. }
  1024. // Do not use new_linkp after this ! HB
  1025. mItemsCreated.emplace(item_id, std::move(new_linkp));
  1026. }
  1027. else if (cur_linkp)
  1028. {
  1029. // This statement is here to cause a new entry with 0 delta to be
  1030. // created if it does not already exist; otherwise has no effect.
  1031. mCatDescendentDeltas[parent_id];
  1032. // Do not use new_linkp after this ! HB
  1033. mItemsUpdated.emplace(item_id, std::move(new_linkp));
  1034. }
  1035. else
  1036. {
  1037. ++mCatDescendentDeltas[parent_id];
  1038. LLPermissions perms;
  1039. perms.init(gAgentID, gAgentID, LLUUID::null, LLUUID::null);
  1040. perms.initMasks(PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
  1041. new_linkp->setPermissions(perms);
  1042. LLSaleInfo default_sale_info;
  1043. new_linkp->setSaleInfo(default_sale_info);
  1044. new_linkp->setComplete(true);
  1045. // Do not use new_linkp after this ! HB
  1046. mItemsCreated.emplace(item_id, std::move(new_linkp));
  1047. }
  1048. if (link_map.has("_embedded"))
  1049. {
  1050. parseEmbedded(link_map["_embedded"], depth);
  1051. }
  1052. }
  1053. void AISUpdate::parseCategory(const LLSD& category_map, U32 depth)
  1054. {
  1055. LLUUID cat_id = category_map["category_id"].asUUID();
  1056. S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;
  1057. if (category_map.has("version"))
  1058. {
  1059. version = category_map["version"].asInteger();
  1060. }
  1061. LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  1062. if (catp && version > LLViewerInventoryCategory::VERSION_UNKNOWN &&
  1063. catp->getVersion() > version && !catp->isDescendentCountUnknown())
  1064. {
  1065. llwarns << "Got stale folder data for " << cat_id
  1066. << ". Current version is " << catp->getVersion()
  1067. << " and received data version was " << version
  1068. << ". Ignoring." << llendl;
  1069. return;
  1070. }
  1071. LLPointer<LLViewerInventoryCategory> new_catp;
  1072. if (catp)
  1073. {
  1074. // Default to current values where not provided.
  1075. new_catp = new LLViewerInventoryCategory(catp);
  1076. }
  1077. else if (category_map.has("agent_id"))
  1078. {
  1079. new_catp =
  1080. new LLViewerInventoryCategory(category_map["agent_id"].asUUID());
  1081. }
  1082. else
  1083. {
  1084. new_catp = new LLViewerInventoryCategory(LLUUID::null);
  1085. LL_DEBUGS("Inventory") << "No owner provided, folder "
  1086. << new_catp->getUUID()
  1087. << " might be assigned wrong owner" << LL_ENDL;
  1088. }
  1089. // Note: unpackMessage() does not unpack version or descendent count.
  1090. if (!new_catp || !new_catp->unpackMessage(category_map))
  1091. {
  1092. gNotifications.add("AISFailure");
  1093. return;
  1094. }
  1095. // Check descendent count first, as it may be needed to populate newly
  1096. // created categories
  1097. if (category_map.has("_embedded"))
  1098. {
  1099. LLFolderType::EType type = new_catp->getPreferredType();
  1100. bool links_only = type == LLFolderType::FT_CURRENT_OUTFIT ||
  1101. type == LLFolderType::FT_OUTFIT;
  1102. parseDescendentCount(cat_id, links_only, category_map["_embedded"]);
  1103. }
  1104. if (mFetch)
  1105. {
  1106. uuid_int_map_t::const_iterator it = mCatDescendentsKnown.find(cat_id);
  1107. if (it != mCatDescendentsKnown.end())
  1108. {
  1109. S32 descendent_count = it->second;
  1110. LL_DEBUGS("Inventory") << "Setting descendents count to "
  1111. << descendent_count << " for category "
  1112. << cat_id << LL_ENDL;
  1113. new_catp->setDescendentCount(descendent_count);
  1114. // Set the version only if we are sure this update has full data
  1115. // and embeded items since the viewer uses version to decide if
  1116. // folder and contents still need fetching.
  1117. if (depth >= 0 &&
  1118. version > LLViewerInventoryCategory::VERSION_UNKNOWN)
  1119. {
  1120. if (catp && catp->getVersion() > version)
  1121. {
  1122. llwarns << "Version for category " << cat_id << " was "
  1123. << catp->getVersion()
  1124. << ", but fetch returned version " << version
  1125. << llendl;
  1126. }
  1127. LL_DEBUGS("Inventory") << "Setting version to " << version
  1128. << " for category " << cat_id
  1129. << LL_ENDL;
  1130. new_catp->setVersion(version);
  1131. }
  1132. }
  1133. // Do not use new_catp after this ! HB
  1134. mCategoriesCreated.emplace(cat_id, std::move(new_catp));
  1135. }
  1136. else if (catp)
  1137. {
  1138. // This statement is here to cause a new entry with 0 delta to be
  1139. // created if it does not already exist; otherwise has no effect.
  1140. mCatDescendentDeltas[new_catp->getParentUUID()];
  1141. // Capture update for the category itself as well.
  1142. mCatDescendentDeltas[cat_id];
  1143. // Do not use new_catp after this ! HB
  1144. mCategoriesUpdated.emplace(cat_id, std::move(new_catp));
  1145. }
  1146. else
  1147. {
  1148. uuid_int_map_t::const_iterator it = mCatDescendentsKnown.find(cat_id);
  1149. if (it != mCatDescendentsKnown.end())
  1150. {
  1151. S32 descendent_count = it->second;
  1152. LL_DEBUGS("Inventory") << "Setting descendents count to "
  1153. << descendent_count << " for new category "
  1154. << cat_id << LL_ENDL;
  1155. new_catp->setDescendentCount(descendent_count);
  1156. // Since we got a proper children count, we can set the version.
  1157. if (version > LLViewerInventoryCategory::VERSION_UNKNOWN)
  1158. {
  1159. LL_DEBUGS("Inventory") << "Setting version to " << version
  1160. << " for category " << cat_id
  1161. << LL_ENDL;
  1162. new_catp->setVersion(version);
  1163. }
  1164. }
  1165. ++mCatDescendentDeltas[new_catp->getParentUUID()];
  1166. // Do not use new_catp after this ! HB
  1167. mCategoriesCreated.emplace(cat_id, std::move(new_catp));
  1168. }
  1169. // Check for more embedded content.
  1170. if (category_map.has("_embedded"))
  1171. {
  1172. parseEmbedded(category_map["_embedded"], depth - 1);
  1173. }
  1174. }
  1175. void AISUpdate::parseDescendentCount(const LLUUID& cat_id, bool links_only,
  1176. const LLSD& embedded)
  1177. {
  1178. // We can only determine true descendent count if this contains all
  1179. // descendent types.
  1180. if (embedded.has("categories") && embedded.has("links") &&
  1181. embedded.has("items"))
  1182. {
  1183. S32 count = embedded["categories"].size() + embedded["links"].size() +
  1184. embedded["items"].size();
  1185. mCatDescendentsKnown[cat_id] = count;
  1186. }
  1187. // For folders that *should* only contain links, such as the COF, we only
  1188. // need to ensure links are present.
  1189. else if (links_only && mFetch && embedded.has("links"))
  1190. {
  1191. mCatDescendentsKnown[cat_id] = embedded["links"].size();
  1192. }
  1193. }
  1194. void AISUpdate::parseEmbedded(const LLSD& embedded, U32 depth)
  1195. {
  1196. checkTimeout();
  1197. #if 0
  1198. if (embedded.has("link"))
  1199. {
  1200. parseEmbeddedLinks(embedded["link"], depth);
  1201. }
  1202. #endif
  1203. if (embedded.has("links")) // _embedded in a category
  1204. {
  1205. parseEmbeddedLinks(embedded["links"], depth);
  1206. }
  1207. if (embedded.has("items")) // _embedded in a category
  1208. {
  1209. parseEmbeddedItems(embedded["items"]);
  1210. }
  1211. if (embedded.has("item")) // _embedded in a link
  1212. {
  1213. parseEmbeddedItem(embedded["item"]);
  1214. }
  1215. if (embedded.has("categories")) // _embedded in a category
  1216. {
  1217. parseEmbeddedCategories(embedded["categories"], depth);
  1218. }
  1219. if (embedded.has("category")) // _embedded in a link
  1220. {
  1221. parseEmbeddedCategory(embedded["category"], depth);
  1222. }
  1223. }
  1224. void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name,
  1225. uuid_list_t& ids)
  1226. {
  1227. if (content.has(name))
  1228. {
  1229. for (LLSD::array_const_iterator it = content[name].beginArray(),
  1230. end = content[name].endArray();
  1231. it != end; ++it)
  1232. {
  1233. ids.emplace(it->asUUID());
  1234. }
  1235. }
  1236. }
  1237. void AISUpdate::parseEmbeddedLinks(const LLSD& links, U32 depth)
  1238. {
  1239. for (LLSD::map_const_iterator it = links.beginMap(), end = links.endMap();
  1240. it != end; ++it)
  1241. {
  1242. const LLUUID id(it->first);
  1243. if (mFetch || mItemIds.count(id))
  1244. {
  1245. parseLink(it->second, depth);
  1246. }
  1247. else
  1248. {
  1249. LL_DEBUGS("Inventory") << "Ignoring link not in items list: " << id
  1250. << LL_ENDL;
  1251. }
  1252. }
  1253. }
  1254. void AISUpdate::parseEmbeddedItem(const LLSD& item)
  1255. {
  1256. // A single item (_embedded in a link)
  1257. if (item.has("item_id") &&
  1258. (mFetch || mItemIds.count(item["item_id"].asUUID())))
  1259. {
  1260. parseItem(item);
  1261. }
  1262. }
  1263. void AISUpdate::parseEmbeddedItems(const LLSD& items)
  1264. {
  1265. // A map of items (_embedded in a category)
  1266. for (LLSD::map_const_iterator it = items.beginMap(), end = items.endMap();
  1267. it != end; ++it)
  1268. {
  1269. const LLUUID id(it->first);
  1270. if (mFetch || mItemIds.count(id))
  1271. {
  1272. parseItem(it->second);
  1273. }
  1274. else
  1275. {
  1276. LL_DEBUGS("Inventory") << "Ignoring item not in items list: " << id
  1277. << LL_ENDL;
  1278. }
  1279. }
  1280. }
  1281. void AISUpdate::parseEmbeddedCategory(const LLSD& category, U32 depth)
  1282. {
  1283. // A single category (_embedded in a link)
  1284. if (category.has("category_id") &&
  1285. (mFetch || mCategoryIds.count(category["category_id"].asUUID())))
  1286. {
  1287. parseCategory(category, depth);
  1288. }
  1289. }
  1290. void AISUpdate::parseEmbeddedCategories(const LLSD& categories, U32 depth)
  1291. {
  1292. // A map of categories (_embedded in a category)
  1293. for (LLSD::map_const_iterator it = categories.beginMap(),
  1294. end = categories.endMap();
  1295. it != end; ++it)
  1296. {
  1297. const LLUUID id(it->first);
  1298. if (mFetch || mCategoryIds.count(id))
  1299. {
  1300. parseCategory(it->second, depth);
  1301. }
  1302. else
  1303. {
  1304. LL_DEBUGS("Inventory") << "Ignoring category not in categories list: "
  1305. << id << LL_ENDL;
  1306. }
  1307. }
  1308. }
  1309. void AISUpdate::doUpdate()
  1310. {
  1311. checkTimeout();
  1312. // Do version/descendent accounting.
  1313. for (uuid_int_map_t::const_iterator it = mCatDescendentDeltas.begin(),
  1314. end = mCatDescendentDeltas.end();
  1315. it != end; ++it)
  1316. {
  1317. const LLUUID& cat_id = it->first;
  1318. LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  1319. LL_DEBUGS("Inventory") << "Descendent accounting for category "
  1320. << (catp ? catp->getName() : "NOT FOUND")
  1321. << " (" << cat_id << ")" << LL_ENDL;
  1322. // Do not account for update if we just created this category
  1323. if (mCategoriesCreated.count(cat_id))
  1324. {
  1325. LL_DEBUGS("Inventory") << "Skipping version increment for new category "
  1326. << (catp ? catp->getName() : "NOT FOUND")
  1327. << " (" << cat_id << ")" << LL_ENDL;
  1328. continue;
  1329. }
  1330. // Do not account for update unless AIS told us it updated that
  1331. // category
  1332. if (!mCatVersionsUpdated.count(cat_id))
  1333. {
  1334. LL_DEBUGS("Inventory") << "Skipping version increment for non-updated category "
  1335. << (catp ? catp->getName() : "NOT FOUND")
  1336. << " (" << cat_id << ")" << LL_ENDL;
  1337. continue;
  1338. }
  1339. // If we have a known descendent count, set that now.
  1340. if (catp)
  1341. {
  1342. S32 descendent_delta = it->second;
  1343. LL_DEBUGS("Inventory") << "Updating descendent count for "
  1344. << catp->getName() << " (" << cat_id
  1345. << ") with delta " << descendent_delta;
  1346. S32 old_count = catp->getDescendentCount();
  1347. LL_CONT << " from " << old_count << " to "
  1348. << old_count + descendent_delta << LL_ENDL;
  1349. LLInventoryModel::LLCategoryUpdate up(cat_id, descendent_delta);
  1350. gInventory.accountForUpdate(up);
  1351. }
  1352. else
  1353. {
  1354. LL_DEBUGS("Inventory") << "Skipping version accounting for unknown category "
  1355. << cat_id << LL_ENDL;
  1356. }
  1357. }
  1358. // CREATE CATEGORIES
  1359. for (deferred_category_map_t::const_iterator
  1360. it = mCategoriesCreated.begin(), end = mCategoriesCreated.end();
  1361. it != end; ++it)
  1362. {
  1363. LL_DEBUGS("Inventory") << "Creating category " << it->first << LL_ENDL;
  1364. LLPointer<LLViewerInventoryCategory> new_catp = it->second;
  1365. gInventory.updateCategory(new_catp, LLInventoryObserver::CREATE);
  1366. // Fetching can receive massive amount of items and folders
  1367. if (gInventory.getChangedIDs().size() > MAX_FOLDER_DEPTH_REQUEST)
  1368. {
  1369. gInventory.notifyObservers();
  1370. checkTimeout();
  1371. }
  1372. }
  1373. // UPDATE CATEGORIES
  1374. for (deferred_category_map_t::const_iterator
  1375. it = mCategoriesUpdated.begin(), end = mCategoriesUpdated.end();
  1376. it != end; ++it)
  1377. {
  1378. const LLUUID& cat_id = it->first;
  1379. LLPointer<LLViewerInventoryCategory> new_catp = it->second;
  1380. // Since this is a copy of the category *before* the accounting update,
  1381. // above, we need to transfer back the updated version/descendent
  1382. // count.
  1383. LLViewerInventoryCategory* cur_catp =
  1384. gInventory.getCategory(new_catp->getUUID());
  1385. if (cur_catp)
  1386. {
  1387. LL_DEBUGS("Inventory") << "Updating category: "
  1388. << new_catp->getName() << " - Id: "
  1389. << cat_id << LL_ENDL;
  1390. new_catp->setVersion(cur_catp->getVersion());
  1391. new_catp->setDescendentCount(cur_catp->getDescendentCount());
  1392. gInventory.updateCategory(new_catp);
  1393. }
  1394. else
  1395. {
  1396. llwarns << "Failed to update unknown category "
  1397. << new_catp->getUUID() << llendl;
  1398. }
  1399. }
  1400. // LOST ITEMS
  1401. if (!mItemsLost.empty())
  1402. {
  1403. const LLUUID& laf = gInventory.getLostAndFoundID();
  1404. for (deferred_item_map_t::const_iterator it = mItemsLost.begin(),
  1405. end = mItemsLost.end();
  1406. it != end; ++it)
  1407. {
  1408. LL_DEBUGS("Inventory") << "Lost item " << it->first << LL_ENDL;
  1409. LLPointer<LLViewerInventoryItem> new_itemp = it->second;
  1410. new_itemp->setParent(laf);
  1411. new_itemp->updateParentOnServer(false);
  1412. }
  1413. }
  1414. // CREATE ITEMS
  1415. for (deferred_item_map_t::const_iterator it = mItemsCreated.begin(),
  1416. end = mItemsCreated.end();
  1417. it != end; ++it)
  1418. {
  1419. LL_DEBUGS("Inventory") << "Creating item " << it->first << LL_ENDL;
  1420. LLPointer<LLViewerInventoryItem> new_itemp = it->second;
  1421. // *FIXME: risky function since it calls updateServer() in some cases.
  1422. // Maybe break out the update/create cases, in which case this is
  1423. // create.
  1424. gInventory.updateItem(new_itemp, LLInventoryObserver::CREATE);
  1425. // Fetching can receive massive amount of items and folders
  1426. if (gInventory.getChangedIDs().size() > MAX_FOLDER_DEPTH_REQUEST)
  1427. {
  1428. gInventory.notifyObservers();
  1429. checkTimeout();
  1430. }
  1431. }
  1432. // UPDATE ITEMS
  1433. for (deferred_item_map_t::const_iterator it = mItemsUpdated.begin(),
  1434. end = mItemsUpdated.end();
  1435. it != end; ++it)
  1436. {
  1437. LL_DEBUGS("Inventory") << "Updating item " << it->first << LL_ENDL;
  1438. LLPointer<LLViewerInventoryItem> new_itemp = it->second;
  1439. // *FIXME: risky function since it calls updateServer() in some cases.
  1440. // Maybe break out the update/create cases, in which case this is
  1441. // update.
  1442. gInventory.updateItem(new_itemp);
  1443. }
  1444. // DELETE OBJECTS
  1445. for (uuid_list_t::const_iterator it = mObjectsDeletedIds.begin(),
  1446. end = mObjectsDeletedIds.end();
  1447. it != end; ++it)
  1448. {
  1449. const LLUUID& item_id = *it;
  1450. LL_DEBUGS("Inventory") << "Deleting item " << item_id << LL_ENDL;
  1451. gInventory.onObjectDeletedFromServer(item_id, false, false, false);
  1452. }
  1453. // *TODO: how can we use this version info ? Need to be sure all changes
  1454. // are going through AIS first, or at least through something with a
  1455. // reliable responder. Notes by HB: this is mostly irrelevant: the AIS
  1456. // updates can be mixed up with legacy UDP inventory updates, the latter
  1457. // also causing version increments (UPDATE: as of 28/05/20016 there
  1458. // *should* not be mixed-up AIS/UDP operations any more now: all inventory
  1459. // ops should now have been enabled with AIS). Beside, several requests
  1460. // launched in a raw can see their replies arriving in a different order
  1461. // (because TCP/IP networking does not guarantee that bunches of packets
  1462. // sent in sequence will arrive in the same order) and a race condition
  1463. // ensues, falsely producing category versions mismatches (UPDATE: as of
  1464. // 28/05/20016 this is still a problem).
  1465. // It may however help tracking down bad version accounting in code and
  1466. // was therefore kept as a debug feature (UPDATE as of 24/05/2017 this is
  1467. // also used for the added 'catp->fetch()', backported from viewer-neko,
  1468. // and therefore no more just a debug feature).
  1469. LL_DEBUGS("Inventory") << "Checking updated category versions...";
  1470. for (uuid_int_map_t::iterator it = mCatVersionsUpdated.begin(),
  1471. end = mCatVersionsUpdated.end();
  1472. it != end; ++it)
  1473. {
  1474. S32 version = it->second;
  1475. LLViewerInventoryCategory* catp = gInventory.getCategory(it->first);
  1476. if (catp && catp->getVersion() != version)
  1477. {
  1478. LL_CONT << "\nPossible version mismatch for category: "
  1479. << catp->getName()
  1480. << " - Viewer-side version: "
  1481. << catp->getVersion()
  1482. << " - Server-side version: "
  1483. << version;
  1484. if (version == LLViewerInventoryCategory::VERSION_UNKNOWN)
  1485. {
  1486. catp->fetch();
  1487. }
  1488. #if 1 // 02/10/2023: AIS has been revamped, with more operations moved to
  1489. // it (meaning less potential occurrences for mixed UDP and HTTP
  1490. // operations arriving/occurring out of order and messing up the
  1491. // folders version and descendents count), serious version checks
  1492. // and stale data detection. Let's now allow to set the folder
  1493. // version according to AIS' idea of what it should be... HB
  1494. else
  1495. {
  1496. catp->setVersion(version);
  1497. }
  1498. #endif
  1499. }
  1500. }
  1501. LL_CONT << "\nChecks done." << LL_ENDL;
  1502. gInventory.notifyObservers();
  1503. checkTimeout();
  1504. }