llinventorymodelfetch.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428
  1. /**
  2. * @file llinventorymodelfetch.cpp
  3. * @brief Implementation of the inventory fetcher.
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2010, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llinventorymodelfetch.h"
  34. #include "indra_constants.h" // For ALEXANDRIA_LINDEN_ID
  35. #include "llcallbacklist.h"
  36. #include "llcorehttputil.h"
  37. #include "llsdutil.h" // For ll_pretty_print_sd()
  38. #include "llnotifications.h"
  39. #include "llsdserialize.h"
  40. #include "llagent.h"
  41. #include "llaisapi.h"
  42. #include "llappviewer.h"
  43. #include "llinventorymodel.h"
  44. #include "llstartup.h"
  45. #include "llviewercontrol.h"
  46. #include "llviewerinventory.h"
  47. #include "llviewermessage.h"
  48. // IMPORTANT NOTE: do *NOT* add calls to gInventory.notifyObservers() into
  49. // *ANY* of the methods of this module: these would cause recursive calls to
  50. // gInventory.notifyObservers() in observers callbacks, and result in failed
  51. // inventory items status updates (such as worn items listed as not worn). Such
  52. // calls are *USELESS* anyway, since gInventory.idleNotifyObservers() is called
  53. // at *each frame* from llappviewer.cpp, after the idle callbacks invocation,
  54. // and it itself calls gInventory.notifyObservers() at a point where recursion
  55. // does not risk to happen. HB
  56. //----------------------------------------------------------------------------
  57. // Helper class BGItemHttpHandler
  58. //----------------------------------------------------------------------------
  59. // HTTP request handler class for single inventory item requests.
  60. //
  61. // We will use a handler-per-request pattern here rather than a shared handler.
  62. // Mainly convenient as this was converted from a Responder class model.
  63. //
  64. // Derives from and is identical to the normal FetchItemHttpHandler except
  65. // that: 1) it uses the background request object which is updated more slowly
  66. // than the foreground and: 2) keeps a count of active requests on the
  67. // LLInventoryModelFetch object to indicate outstanding operations are
  68. // in-flight.
  69. class BGItemHttpHandler : public LLInventoryModel::FetchItemHttpHandler
  70. {
  71. LOG_CLASS(BGItemHttpHandler);
  72. public:
  73. BGItemHttpHandler(const LLSD& request_sd)
  74. : LLInventoryModel::FetchItemHttpHandler(request_sd)
  75. {
  76. LLInventoryModelFetch::getInstance()->incrFetchCount(1);
  77. }
  78. ~BGItemHttpHandler() override
  79. {
  80. LLInventoryModelFetch::getInstance()->incrFetchCount(-1);
  81. }
  82. BGItemHttpHandler(const BGItemHttpHandler&) = delete;
  83. void operator=(const BGItemHttpHandler&) = delete;
  84. static void postRequest(const std::string& url, const LLSD& request_sd,
  85. bool is_library = false);
  86. };
  87. //static
  88. void BGItemHttpHandler::postRequest(const std::string& url,
  89. const LLSD& request_sd, bool is_library)
  90. {
  91. static const char* lib_item_str = "library item";
  92. static const char* inv_item_str = "inventory item";
  93. LLCore::HttpHandler::ptr_t handler(new BGItemHttpHandler(request_sd));
  94. gInventory.requestPost(false, url, request_sd, handler,
  95. is_library ? lib_item_str : inv_item_str);
  96. }
  97. //----------------------------------------------------------------------------
  98. // Helper class BGFolderHttpHandler
  99. //----------------------------------------------------------------------------
  100. // HTTP request handler class for folders.
  101. //
  102. // Handler for FetchInventoryDescendents2 and FetchLibDescendents2 caps
  103. // requests for folders.
  104. class BGFolderHttpHandler : public LLCore::HttpHandler
  105. {
  106. LOG_CLASS(BGFolderHttpHandler);
  107. public:
  108. BGFolderHttpHandler(const LLSD& request_sd,
  109. const uuid_vec_t& recursive_cats)
  110. : LLCore::HttpHandler(),
  111. mRequestSD(request_sd),
  112. mRecursiveCatUUIDs(recursive_cats)
  113. {
  114. LLInventoryModelFetch::getInstance()->incrFetchCount(1);
  115. }
  116. ~BGFolderHttpHandler() override
  117. {
  118. LLInventoryModelFetch::getInstance()->incrFetchCount(-1);
  119. }
  120. BGFolderHttpHandler(const BGFolderHttpHandler&) = delete;
  121. void operator=(const BGFolderHttpHandler&) = delete;
  122. void onCompleted(LLCore::HttpHandle handle,
  123. LLCore::HttpResponse* responsep) override;
  124. bool getIsRecursive(const LLUUID& cat_id) const;
  125. static void postRequest(const std::string& url, const LLSD& request_sd,
  126. const uuid_vec_t& recursive_cats,
  127. bool is_library = false);
  128. private:
  129. void processFailure(LLCore::HttpStatus status,
  130. LLCore::HttpResponse* responsep);
  131. void processFailure(const char* const reason,
  132. LLCore::HttpResponse* responsep);
  133. private:
  134. LLSD mRequestSD;
  135. // *HACK for storing away which cat fetches are recursive:
  136. const uuid_vec_t mRecursiveCatUUIDs;
  137. };
  138. //static
  139. void BGFolderHttpHandler::postRequest(const std::string& url,
  140. const LLSD& request_sd,
  141. const uuid_vec_t& recursive_cats,
  142. bool is_library)
  143. {
  144. static const char* lib_folder_str = "library folder";
  145. static const char* inv_folder_str = "inventory folder";
  146. LLCore::HttpHandler::ptr_t handler(new BGFolderHttpHandler(request_sd,
  147. recursive_cats));
  148. gInventory.requestPost(false, url, request_sd, handler,
  149. is_library ? lib_folder_str : inv_folder_str);
  150. }
  151. void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle,
  152. LLCore::HttpResponse* responsep)
  153. {
  154. LLCore::HttpStatus status = responsep->getStatus();
  155. if (!status)
  156. {
  157. processFailure(status, responsep);
  158. return;
  159. }
  160. // Response body should be present.
  161. LLCore::BufferArray* bodyp = responsep->getBody();
  162. if (!bodyp || ! bodyp->size())
  163. {
  164. llwarns << "Missing data in inventory folder query." << llendl;
  165. processFailure("HTTP response missing expected body", responsep);
  166. return;
  167. }
  168. // Could test 'Content-Type' header but probably unreliable.
  169. // Convert response to LLSD
  170. LLSD body_llsd;
  171. if (!LLCoreHttpUtil::responseToLLSD(responsep, true, body_llsd))
  172. {
  173. // INFOS-level logging will occur on the parsed failure
  174. processFailure("HTTP response contained malformed LLSD", responsep);
  175. return;
  176. }
  177. // Expect top-level structure to be a map
  178. if (!body_llsd.isMap())
  179. {
  180. processFailure("LLSD response not a map", responsep);
  181. return;
  182. }
  183. // Check for 200-with-error failures. See comments in llinventorymodel.cpp
  184. // about this mode of error.
  185. if (body_llsd.has("error"))
  186. {
  187. processFailure("Inventory application error (200-with-error)",
  188. responsep);
  189. return;
  190. }
  191. // Okay, process data if possible
  192. LLInventoryModelFetch* fetcherp = LLInventoryModelFetch::getInstance();
  193. const LLUUID& laf_id = gInventory.getLostAndFoundID();
  194. // API V2 and earlier should probably be testing for "error" map in
  195. // response as an application-level error. Instead, we assume success and
  196. // attempt to extract information.
  197. if (body_llsd.has("folders"))
  198. {
  199. const LLSD& folders = body_llsd["folders"];
  200. for (LLSD::array_const_iterator folder_it = folders.beginArray(),
  201. folder_end = folders.endArray();
  202. folder_it != folder_end; ++folder_it)
  203. {
  204. const LLSD& folder_sd = *folder_it;
  205. LLUUID parent_id = folder_sd["folder_id"];
  206. LLUUID owner_id = folder_sd["owner_id"];
  207. S32 version = folder_sd["version"].asInteger();
  208. S32 descendents = folder_sd["descendents"].asInteger();
  209. if (parent_id.isNull() && laf_id.notNull() &&
  210. folder_sd.has("items"))
  211. {
  212. const LLSD& items = folder_sd["items"];
  213. LLPointer<LLViewerInventoryItem> itemp =
  214. new LLViewerInventoryItem;
  215. for (LLSD::array_const_iterator item_it = items.beginArray(),
  216. item_end = items.endArray();
  217. item_it != item_end; ++item_it)
  218. {
  219. const LLSD& item_llsd = *item_it;
  220. itemp->unpackMessage(item_llsd);
  221. LLInventoryModel::update_list_t update;
  222. update.emplace_back(laf_id, 1);
  223. gInventory.accountForUpdate(update);
  224. itemp->setParent(laf_id);
  225. itemp->updateParentOnServer(false);
  226. gInventory.updateItem(itemp);
  227. }
  228. }
  229. if (!gInventory.getCategory(parent_id))
  230. {
  231. continue;
  232. }
  233. if (folder_sd.has("categories"))
  234. {
  235. LLPointer<LLViewerInventoryCategory> catp =
  236. new LLViewerInventoryCategory(owner_id);
  237. const LLSD& categories = folder_sd["categories"];
  238. for (LLSD::array_const_iterator it = categories.beginArray(),
  239. end = categories.endArray();
  240. it != end; ++it)
  241. {
  242. LLSD category = *it;
  243. catp->fromLLSD(category);
  244. if (getIsRecursive(catp->getUUID()))
  245. {
  246. fetcherp->addRequestAtBack(catp->getUUID(), true,
  247. true);
  248. }
  249. else if (!gInventory.isCategoryComplete(catp->getUUID()))
  250. {
  251. gInventory.updateCategory(catp);
  252. }
  253. }
  254. }
  255. if (folder_sd.has("items"))
  256. {
  257. const LLSD& items = folder_sd["items"];
  258. LLPointer<LLViewerInventoryItem> itemp =
  259. new LLViewerInventoryItem;
  260. for (LLSD::array_const_iterator it = items.beginArray(),
  261. end = items.endArray();
  262. it != end; ++it)
  263. {
  264. const LLSD item_llsd = *it;
  265. itemp->unpackMessage(item_llsd);
  266. gInventory.updateItem(itemp);
  267. }
  268. // Set version and descendentcount according to message.
  269. LLViewerInventoryCategory* catp =
  270. gInventory.getCategory(parent_id);
  271. if (catp)
  272. {
  273. catp->setVersion(version);
  274. catp->setDescendentCount(descendents);
  275. }
  276. }
  277. }
  278. }
  279. if (body_llsd.has("bad_folders"))
  280. {
  281. const LLSD& bad_folders = body_llsd["bad_folders"];
  282. LL_DEBUGS("InventoryFetch") << "Bad folders LLSD:\n"
  283. << ll_pretty_print_sd(bad_folders)
  284. << LL_ENDL;
  285. for (LLSD::array_const_iterator it = bad_folders.beginArray(),
  286. end = bad_folders.endArray();
  287. it != end; ++it)
  288. {
  289. const LLSD& folder_sd = *it;
  290. // These folders failed on the dataserver. We probably do not want
  291. // to retry them.
  292. if (folder_sd.has("folder_id"))
  293. {
  294. llwarns << "Folder: " << folder_sd["folder_id"].asString()
  295. << " - Error: " << folder_sd["error"].asString()
  296. << llendl;
  297. }
  298. }
  299. }
  300. if (fetcherp->isBulkFetchProcessingComplete())
  301. {
  302. fetcherp->setAllFoldersFetched();
  303. }
  304. }
  305. void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status,
  306. LLCore::HttpResponse* responsep)
  307. {
  308. if (gDisconnected || LLApp::isExiting())
  309. {
  310. return;
  311. }
  312. const std::string& ct = responsep->getContentType();
  313. llwarns << "Inventory folder fetch failure - Status: "
  314. << status.toTerseString() << " - Reason: " << status.toString()
  315. << " - Content-type: " << ct << " - Content (abridged): "
  316. << LLCoreHttpUtil::responseToString(responsep) << llendl;
  317. // Could use a 404 test here to try to detect revoked caps...
  318. if (status != gStatusForbidden) // 403
  319. {
  320. LLInventoryModelFetch* fetcherp = LLInventoryModelFetch::getInstance();
  321. if (fetcherp->isBulkFetchProcessingComplete())
  322. {
  323. fetcherp->setAllFoldersFetched();
  324. }
  325. return;
  326. }
  327. // 403 error processing.
  328. const std::string& url =
  329. gAgent.getRegionCapability("FetchInventoryDescendents2");
  330. if (url.empty())
  331. {
  332. llwarns << "Fetch failed. No FetchInventoryDescendents2 capability."
  333. << llendl;
  334. return;
  335. }
  336. size_t size = mRequestSD["folders"].size();
  337. if (!size)
  338. {
  339. static U32 warned = 0;
  340. const char* notification = warned++ ? "AISInventoryLimitReached"
  341. : "AISInventoryLimitReachedAlert";
  342. gNotifications.add(notification);
  343. }
  344. // We can split. Also assume that this is not the library
  345. LLSD folders;
  346. uuid_vec_t recursive_cats;
  347. LLUUID folder_id;
  348. for (LLSD::array_iterator it = mRequestSD["folders"].beginArray(),
  349. end = mRequestSD["folders"].endArray();
  350. it != end; )
  351. {
  352. folders.append(*it++);
  353. folder_id = it->get("folder_id").asUUID();
  354. if (getIsRecursive(folder_id))
  355. {
  356. recursive_cats.emplace_back(folder_id);
  357. }
  358. if (it == end || folders.size() == size / 2)
  359. {
  360. LLSD request_body;
  361. request_body["folders"] = folders;
  362. postRequest(url, request_body, recursive_cats);
  363. recursive_cats.clear();
  364. folders.clear();
  365. }
  366. }
  367. }
  368. void BGFolderHttpHandler::processFailure(const char* const reason,
  369. LLCore::HttpResponse* responsep)
  370. {
  371. llwarns << "Inventory folder fetch failure - Status: internal error - Reason: "
  372. << reason << " - Content (abridged): "
  373. << LLCoreHttpUtil::responseToString(responsep) << llendl;
  374. LLInventoryModelFetch* fetcherp = LLInventoryModelFetch::getInstance();
  375. // Reverse of previous processFailure() method, this is invoked when
  376. // response structure is found to be invalid. Original always re-issued the
  377. // request (without limit). This does the same but be aware that this may
  378. // be a source of problems. Philosophy is that inventory folders are so
  379. // essential to operation that this is a reasonable action.
  380. for (LLSD::array_const_iterator it = mRequestSD["folders"].beginArray(),
  381. end = mRequestSD["folders"].endArray();
  382. it != end; ++it)
  383. {
  384. LLSD folder_sd = *it;
  385. LLUUID cat_id = folder_sd["folder_id"].asUUID();
  386. fetcherp->addRequestAtFront(cat_id, getIsRecursive(cat_id), true);
  387. }
  388. }
  389. bool BGFolderHttpHandler::getIsRecursive(const LLUUID& cat_id) const
  390. {
  391. return std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(),
  392. cat_id) != mRecursiveCatUUIDs.end();
  393. }
  394. ///////////////////////////////////////////////////////////////////////////////
  395. // LLInventoryModelFetch class proper
  396. ///////////////////////////////////////////////////////////////////////////////
  397. //static
  398. bool LLInventoryModelFetch::sUseAISFetching = true;
  399. //static
  400. bool LLInventoryModelFetch::useAISFetching()
  401. {
  402. return sUseAISFetching && AISAPI::isAvailable();
  403. }
  404. LLInventoryModelFetch::LLInventoryModelFetch()
  405. : mBackgroundFetchActive(false),
  406. mFolderFetchActive(false),
  407. mFetchCount(0),
  408. mLastFetchCount(0),
  409. mFetchFolderCount(0),
  410. mAllRecursiveFoldersFetched(false),
  411. mRecursiveInventoryFetchStarted(false),
  412. mRecursiveLibraryFetchStarted(false)
  413. {
  414. }
  415. bool LLInventoryModelFetch::isBulkFetchProcessingComplete() const
  416. {
  417. return mFetchCount <= 0 && mFetchFolderQueue.empty() &&
  418. mFetchItemQueue.empty();
  419. }
  420. bool LLInventoryModelFetch::isFolderFetchProcessingComplete() const
  421. {
  422. return mFetchFolderCount <= 0 && mFetchFolderQueue.empty();
  423. }
  424. bool LLInventoryModelFetch::libraryFetchCompleted() const
  425. {
  426. return mRecursiveLibraryFetchStarted &&
  427. fetchQueueContainsNoDescendentsOf(gInventory.getLibraryRootFolderID());
  428. }
  429. bool LLInventoryModelFetch::inventoryFetchCompleted() const
  430. {
  431. return mRecursiveInventoryFetchStarted &&
  432. fetchQueueContainsNoDescendentsOf(gInventory.getRootFolderID());
  433. }
  434. void LLInventoryModelFetch::addRequestAtFront(const LLUUID& id, bool recursive,
  435. bool is_category)
  436. {
  437. U32 type = recursive ? FT_RECURSIVE : FT_DEFAULT;
  438. if (is_category)
  439. {
  440. mFetchFolderQueue.emplace_front(id, type, is_category);
  441. }
  442. else
  443. {
  444. mFetchItemQueue.emplace_front(id, type, is_category);
  445. }
  446. }
  447. void LLInventoryModelFetch::addRequestAtBack(const LLUUID& id, bool recursive,
  448. bool is_category)
  449. {
  450. U32 type = recursive ? FT_RECURSIVE : FT_DEFAULT;
  451. if (is_category)
  452. {
  453. mFetchFolderQueue.emplace_back(id, type, is_category);
  454. }
  455. else
  456. {
  457. mFetchItemQueue.emplace_back(id, type, is_category);
  458. }
  459. }
  460. void LLInventoryModelFetch::start(const LLUUID& id, bool recursive)
  461. {
  462. bool is_cat = id.notNull() && gInventory.getCategory(id);
  463. if (is_cat || (!mAllRecursiveFoldersFetched && id.isNull()))
  464. {
  465. // It is a folder: do a bulk fetch.
  466. LL_DEBUGS("InventoryFetch") << "Start fetching category: " << id
  467. << ", recursive: " << recursive << LL_ENDL;
  468. mBackgroundFetchActive = mFolderFetchActive = true;
  469. U32 fetch_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
  470. if (id.isNull()) // Root folder fetch request
  471. {
  472. if (!mRecursiveInventoryFetchStarted)
  473. {
  474. mRecursiveInventoryFetchStarted |= recursive;
  475. const LLUUID& root_id = gInventory.getRootFolderID();
  476. if (recursive && useAISFetching())
  477. {
  478. // Not only root folder can be massive, but most system
  479. // folders will be requested independently, so request root
  480. // folder and content separately.
  481. mFetchFolderQueue.emplace_front(root_id,
  482. FT_FOLDER_AND_CONTENT,
  483. true);
  484. }
  485. else
  486. {
  487. mFetchFolderQueue.emplace_back(root_id, fetch_type, true);
  488. }
  489. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  490. }
  491. if (!mRecursiveLibraryFetchStarted)
  492. {
  493. mRecursiveLibraryFetchStarted |= recursive;
  494. const LLUUID& lib_id = gInventory.getLibraryRootFolderID();
  495. mFetchFolderQueue.emplace_back(lib_id, fetch_type, true);
  496. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  497. }
  498. }
  499. else
  500. {
  501. if (useAISFetching())
  502. {
  503. if (mFetchFolderQueue.empty() ||
  504. mFetchFolderQueue.back().mUUID != id)
  505. {
  506. // With AIS, make sure root goes to the top and follow up
  507. // recursive fetches, not individual requests.
  508. mFetchFolderQueue.emplace_back(id, fetch_type, true);
  509. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  510. }
  511. }
  512. else if (mFetchFolderQueue.empty() ||
  513. mFetchFolderQueue.front().mUUID != id)
  514. {
  515. // Specific folder requests go to front of queue.
  516. mFetchFolderQueue.emplace_front(id, fetch_type, true);
  517. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  518. }
  519. if (id == gInventory.getLibraryRootFolderID())
  520. {
  521. mRecursiveLibraryFetchStarted |= recursive;
  522. }
  523. if (id == gInventory.getRootFolderID())
  524. {
  525. mRecursiveInventoryFetchStarted |= recursive;
  526. }
  527. }
  528. }
  529. else if (LLViewerInventoryItem* itemp = gInventory.getItem(id))
  530. {
  531. if (!itemp->isFinished())
  532. {
  533. scheduleItemFetch(id);
  534. }
  535. }
  536. }
  537. void LLInventoryModelFetch::scheduleFolderFetch(const LLUUID& id,
  538. bool force)
  539. {
  540. if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != id)
  541. {
  542. mBackgroundFetchActive = true;
  543. U32 fetch_type = force ? FT_FORCED : FT_DEFAULT;
  544. // Specific folder requests go to front of queue.
  545. mFetchFolderQueue.emplace_front(id, fetch_type, true);
  546. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  547. LL_DEBUGS("InventoryFetch") << "Scheduled category " << id
  548. << (force ? " for forced fetch."
  549. : " for fetch.") << LL_ENDL;
  550. }
  551. }
  552. void LLInventoryModelFetch::scheduleItemFetch(const LLUUID& id, bool force)
  553. {
  554. if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != id)
  555. {
  556. mBackgroundFetchActive = true;
  557. U32 fetch_type = force ? FT_FORCED : FT_DEFAULT;
  558. mFetchItemQueue.emplace_front(id, fetch_type, true);
  559. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  560. LL_DEBUGS("InventoryFetch") << "Scheduled item " << id
  561. << (force ? " for forced fetch."
  562. : " for fetch.") << LL_ENDL;
  563. }
  564. }
  565. void LLInventoryModelFetch::findLostItems()
  566. {
  567. mBackgroundFetchActive = mFolderFetchActive = true;
  568. mFetchFolderQueue.emplace_back(LLUUID::null, FT_RECURSIVE, true);
  569. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  570. }
  571. void LLInventoryModelFetch::setAllFoldersFetched()
  572. {
  573. if (mRecursiveInventoryFetchStarted && mRecursiveLibraryFetchStarted)
  574. {
  575. mAllRecursiveFoldersFetched = true;
  576. }
  577. mFolderFetchActive = false;
  578. if (isBulkFetchProcessingComplete())
  579. {
  580. mBackgroundFetchActive = false;
  581. #if 1 // Avoids pointless idle callbacks when nothing is left to do. HB
  582. gIdleCallbacks.deleteFunction(backgroundFetchCB, NULL);
  583. #endif
  584. }
  585. // Try and rebuild any broken links in the inventory now.
  586. gInventory.rebuildBrokenLinks();
  587. llinfos << "Inventory background fetch completed" << llendl;
  588. }
  589. //static
  590. void LLInventoryModelFetch::backgroundFetchCB(void*)
  591. {
  592. LLInventoryModelFetch::getInstance()->backgroundFetch();
  593. }
  594. void LLInventoryModelFetch::backgroundFetch()
  595. {
  596. // Wait until we receive the agent region capabilities. HB
  597. if (!gAgent.regionCapabilitiesReceived())
  598. {
  599. return;
  600. }
  601. if (useAISFetching())
  602. {
  603. bulkFetchAIS();
  604. return;
  605. }
  606. const std::string& url =
  607. gAgent.getRegionCapability("FetchInventoryDescendents2");
  608. if (!url.empty())
  609. {
  610. bulkFetch(url);
  611. return;
  612. }
  613. // This should never happen any more, including in OpenSim (unless a grid
  614. // is running an antediluvian server version). HB
  615. llwarns_sparse << "Missing capability: cannot perform bulk fetch !"
  616. << llendl;
  617. }
  618. void LLInventoryModelFetch::incrFetchCount(S32 fetching)
  619. {
  620. mFetchCount += fetching;
  621. if (mFetchCount < 0)
  622. {
  623. llwarns_sparse << "Inventory fetch count fell below zero." << llendl;
  624. mFetchCount = 0;
  625. }
  626. }
  627. void LLInventoryModelFetch::incrFetchFolderCount(S32 fetching)
  628. {
  629. incrFetchCount(fetching);
  630. mFetchFolderCount += fetching;
  631. if (mFetchFolderCount < 0)
  632. {
  633. llwarns_sparse << "Inventory categories fetch count fell below zero."
  634. << llendl;
  635. mFetchFolderCount = 0;
  636. }
  637. }
  638. void LLInventoryModelFetch::onAISContentsCallback(const uuid_vec_t& ids_vec,
  639. const LLUUID& response_id)
  640. {
  641. // Do not push_front on failure: there is a chance it was fired from inside
  642. // bulkFetchAIS().
  643. incrFetchFolderCount(-1);
  644. for (U32 i = 0, count = ids_vec.size(); i < count; ++i)
  645. {
  646. const LLUUID& cat_id = ids_vec[i];
  647. mExpectedFolderIds.erase(cat_id);
  648. LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  649. if (catp)
  650. {
  651. catp->setFetching(LLViewerInventoryCategory::FETCH_NONE);
  652. }
  653. if (response_id.isNull())
  654. {
  655. // Failed to fetch; get it individually.
  656. mFetchFolderQueue.emplace_back(cat_id, FT_RECURSIVE, true);
  657. continue;
  658. }
  659. // Push descendant back to verify they are fetched fully (e.g. we did
  660. // not encounter depth limit).
  661. LLInventoryModel::cat_array_t* categories;
  662. LLInventoryModel::item_array_t* items;
  663. gInventory.getDirectDescendentsOf(cat_id, categories, items);
  664. if (categories)
  665. {
  666. for (LLInventoryModel::cat_array_t::const_iterator
  667. it = categories->begin(), end = categories->end();
  668. it != end; ++it)
  669. {
  670. mFetchFolderQueue.emplace_back((*it)->getUUID(), FT_RECURSIVE,
  671. true);
  672. }
  673. }
  674. }
  675. if (!mFetchFolderQueue.empty())
  676. {
  677. mBackgroundFetchActive = mFolderFetchActive = true;
  678. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  679. }
  680. }
  681. void LLInventoryModelFetch::onAISFolderCallback(const LLUUID& cat_id,
  682. const LLUUID& response_id,
  683. U32 fetch_type)
  684. {
  685. if (!mExpectedFolderIds.erase(cat_id))
  686. {
  687. llwarns << "Unexpected folder response for: " << cat_id << llendl;
  688. }
  689. if (cat_id.isNull())
  690. {
  691. // Orphan: no other actions needed.
  692. // Note: return is done on purpose before incrFetchFolderCount(-1),
  693. // below since we did not incrFetchFolderCount(1) for orphans request,
  694. // to avoid requests number mismatch when no reply is received for
  695. // orphans. HB
  696. return;
  697. }
  698. // Do not push_front on failure: there is a chance it was fired from inside
  699. // bulkFetchAIS().
  700. incrFetchFolderCount(-1);
  701. if (response_id.isNull()) // Failed to fetch
  702. {
  703. if (fetch_type == FT_RECURSIVE)
  704. {
  705. // A full recursive request failed; try requesting folder and
  706. // nested contents separately.
  707. mFetchFolderQueue.emplace_back(cat_id, FT_CONTENT_RECURSIVE, true);
  708. }
  709. else if (fetch_type == FT_FOLDER_AND_CONTENT)
  710. {
  711. llwarns << "Failed to download folder: " << cat_id
  712. << " - Requesting known content separately." << llendl;
  713. mFetchFolderQueue.emplace_back(cat_id, FT_CONTENT_RECURSIVE, true);
  714. // Set folder version to prevent viewer from trying to request
  715. // folder indefinetely.
  716. LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  717. if (catp && catp->isVersionUnknown())
  718. {
  719. catp->setVersion(0);
  720. }
  721. }
  722. }
  723. else if (fetch_type == FT_RECURSIVE)
  724. {
  725. // Got the folder and contents, now verify contents. Request contents
  726. // even for FT_RECURSIVE in case of changes, failures or if depth limit
  727. // gets imlemented. This should not re-download folders if they already
  728. // have a known version.
  729. LL_DEBUGS("InventoryFetch") << "Got folder: " << cat_id
  730. << " - Requesting its contents."
  731. << LL_ENDL;
  732. // Push descendant back to verify they are fetched fully (e.g. we did
  733. // not encounter depth limit).
  734. LLInventoryModel::cat_array_t* categories;
  735. LLInventoryModel::item_array_t* items;
  736. gInventory.getDirectDescendentsOf(cat_id, categories, items);
  737. if (categories)
  738. {
  739. for (LLInventoryModel::cat_array_t::const_iterator
  740. it = categories->begin(), end = categories->end();
  741. it != end; ++it)
  742. {
  743. mFetchFolderQueue.emplace_back((*it)->getUUID(), FT_RECURSIVE,
  744. true);
  745. }
  746. }
  747. }
  748. else if (fetch_type == FT_FOLDER_AND_CONTENT)
  749. {
  750. // Read folder for contents request.
  751. mFetchFolderQueue.emplace_front(cat_id, FT_CONTENT_RECURSIVE, true);
  752. }
  753. else
  754. {
  755. LL_DEBUGS("InventoryFetch") << "Got folder: " << cat_id << LL_ENDL;
  756. }
  757. if (!mFetchFolderQueue.empty())
  758. {
  759. mBackgroundFetchActive = mFolderFetchActive = true;
  760. gIdleCallbacks.addFunction(backgroundFetchCB, NULL);
  761. }
  762. LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  763. if (catp)
  764. {
  765. catp->setFetching(LLViewerInventoryCategory::FETCH_NONE);
  766. }
  767. }
  768. void LLInventoryModelFetch::bulkFetchAIS()
  769. {
  770. if (gDisconnected || LLApp::isExiting())
  771. {
  772. gIdleCallbacks.deleteFunction(backgroundFetchCB, NULL);
  773. return;
  774. }
  775. static LLCachedControl<U32> ais_pool(gSavedSettings, "PoolSizeAIS");
  776. // Do not launch too many requests at once; AIS throttles. Also, reserve
  777. // one request for actions outside of fetch (like renames).
  778. S32 max_fetches = llclamp((S32)ais_pool, 2, 51) - 1;
  779. // Do not loop for too long (in case of large, fully loaded inventory)
  780. mFetchTimer.reset();
  781. bool short_timeout = LLStartUp::getStartupState() > STATE_WEARABLES_WAIT;
  782. mFetchTimer.setTimerExpirySec(short_timeout ? 0.005f : 1.f);
  783. S32 last_fetch_count = mFetchCount;
  784. while (!mFetchFolderQueue.empty() && mFetchCount < max_fetches &&
  785. !mFetchTimer.hasExpired())
  786. {
  787. const FetchQueueInfo& fetch_info = mFetchFolderQueue.front();
  788. bulkFetchAIS(fetch_info);
  789. mFetchFolderQueue.pop_front();
  790. }
  791. // Ideally we should not fetch items if recursive fetch is not done, but
  792. // there is a chance some request will start timing out and recursive fetch
  793. // would then get stuck on a single folder, so we need to keep item fetches
  794. // going to avoid such an issue.
  795. while (!mFetchItemQueue.empty() && mFetchCount < max_fetches &&
  796. !mFetchTimer.hasExpired())
  797. {
  798. const FetchQueueInfo& fetch_info = mFetchItemQueue.front();
  799. bulkFetchAIS(fetch_info);
  800. mFetchItemQueue.pop_front();
  801. }
  802. if (mFetchCount != last_fetch_count || mFetchCount != mLastFetchCount)
  803. {
  804. LL_DEBUGS("InventoryFetch") << "Total active fetches went from "
  805. << mLastFetchCount << " to " << mFetchCount
  806. << " with " << mFetchFolderQueue.size()
  807. << " scheduled folder fetches and "
  808. << mFetchItemQueue.size()
  809. << " scheduled item fetches." << LL_ENDL;
  810. mLastFetchCount = mFetchCount;
  811. }
  812. if (mFolderFetchActive && isFolderFetchProcessingComplete())
  813. {
  814. setAllFoldersFetched();
  815. }
  816. if (isBulkFetchProcessingComplete())
  817. {
  818. mBackgroundFetchActive = false;
  819. }
  820. }
  821. static void ais_simple_item_cb(const LLUUID& response_id)
  822. {
  823. LL_DEBUGS("InventoryFetch") << "Got simple response Id:" << response_id
  824. << LL_ENDL;
  825. LLInventoryModelFetch::getInstance()->incrFetchCount(-1);
  826. }
  827. // I do not like lambdas... HB
  828. static void fetch_orphans_cb(const LLUUID& response_id)
  829. {
  830. if (gDisconnected || LLApp::isExiting())
  831. {
  832. return;
  833. }
  834. LL_DEBUGS("InventoryFetch") << "Got orphans reply Id: " << response_id
  835. << LL_ENDL;
  836. // Note: the '0' below is for FT_DEFAULT; should it actually be recursive ?
  837. LLInventoryModelFetch::getInstance()->onAISFolderCallback(LLUUID::null,
  838. response_id, 0);
  839. }
  840. static void fetch_contents_cb(uuid_vec_t children, const LLUUID& response_id)
  841. {
  842. if (gDisconnected || LLApp::isExiting())
  843. {
  844. return;
  845. }
  846. LL_DEBUGS("InventoryFetch") << "Got contents reply Id: " << response_id
  847. << LL_ENDL;
  848. LLInventoryModelFetch::getInstance()->onAISContentsCallback(children,
  849. response_id);
  850. }
  851. static void fetch_folder_cb(LLUUID cat_id, U32 type, const LLUUID& response_id)
  852. {
  853. if (gDisconnected || LLApp::isExiting())
  854. {
  855. return;
  856. }
  857. LL_DEBUGS("InventoryFetch") << "Got folder reply Id: " << response_id
  858. << LL_ENDL;
  859. LLInventoryModelFetch::getInstance()->onAISFolderCallback(cat_id,
  860. response_id,
  861. type);
  862. }
  863. void LLInventoryModelFetch::bulkFetchAIS(const FetchQueueInfo& fetch_info)
  864. {
  865. const LLUUID& id = fetch_info.mUUID;
  866. if (!fetch_info.mIsCategory) // If this is an inventory item...
  867. {
  868. bool needs_fetch = false;
  869. bool is_library = false;
  870. LLViewerInventoryItem* itemp = gInventory.getItem(id);
  871. if (!itemp)
  872. {
  873. // We do not know it at all, so assume it is incomplete.
  874. needs_fetch = true;
  875. }
  876. else if (!itemp->isFinished() || fetch_info.mFetchType == FT_FORCED)
  877. {
  878. needs_fetch = true;
  879. is_library = itemp->getPermissions().getOwner() != gAgentID;
  880. }
  881. if (needs_fetch)
  882. {
  883. ++mFetchCount;
  884. AISAPI::fetchItem(id, is_library, ais_simple_item_cb);
  885. }
  886. return;
  887. }
  888. // Inventory category cases.
  889. if (id.isNull()) // Lost & found case.
  890. {
  891. #if 0 // Do NOT increment the count for this request: it may not receive any
  892. // reply when there are no orphans and we would be left in indefinitely
  893. // "loading" inventory state. HB
  894. incrFetchFolderCount(1);
  895. #endif
  896. mExpectedFolderIds.emplace(id);
  897. AISAPI::fetchOrphans(fetch_orphans_cb);
  898. return;
  899. }
  900. LLViewerInventoryCategory* catp = gInventory.getCategory(id);
  901. if (!catp)
  902. {
  903. return; // Could try and fetch it in another way instead ?
  904. }
  905. bool is_library = catp->getOwnerID() == ALEXANDRIA_LINDEN_ID;
  906. U32 fetch_type = fetch_info.mFetchType;
  907. if (fetch_type == FT_CONTENT_RECURSIVE)
  908. {
  909. static LLCachedControl<U32> ais_batch(gSavedSettings, "BatchSizeAIS3");
  910. // Top limit is 'as many as you can put into an URL'.
  911. size_t batch_limit = llclamp((size_t)ais_batch, 1, 40);
  912. U32 target_state = LLViewerInventoryCategory::FETCH_RECURSIVE;
  913. bool content_done = true;
  914. // Fetch contents only, ignoring the category itself
  915. uuid_vec_t children;
  916. LLInventoryModel::cat_array_t* categories;
  917. LLInventoryModel::item_array_t* items;
  918. gInventory.getDirectDescendentsOf(id, categories, items);
  919. if (categories)
  920. {
  921. for (LLInventoryModel::cat_array_t::const_iterator
  922. it = categories->begin(), end = categories->end();
  923. it != end; ++it)
  924. {
  925. LLViewerInventoryCategory* childp = *it;
  926. if (childp->getFetching() >= target_state ||
  927. !childp->isVersionUnknown())
  928. {
  929. continue;
  930. }
  931. if (childp->getPreferredType() ==
  932. LLFolderType::FT_MARKETPLACE_LISTINGS)
  933. {
  934. // Fetch marketplace alone; should it actually be
  935. // fetched as FT_FOLDER_AND_CONTENT ?
  936. if (!children.empty())
  937. {
  938. // Ignore it now so that it can instead be fetched
  939. // alone on next run(s).
  940. content_done = false;
  941. continue;
  942. }
  943. // This will cause to break from the loop below, after
  944. // registering this only marketplace folder for fetch. HB
  945. batch_limit = 0;
  946. }
  947. const LLUUID& child_id = childp->getUUID();
  948. children.emplace_back(child_id);
  949. mExpectedFolderIds.emplace(child_id);
  950. childp->setFetching(target_state);
  951. if (children.size() >= batch_limit)
  952. {
  953. content_done = false;
  954. break;
  955. }
  956. }
  957. if (!children.empty())
  958. {
  959. // Increment before call in case of immediate callback
  960. incrFetchFolderCount(1);
  961. AISAPI::fetchCategorySubset(id, children, is_library, true,
  962. boost::bind(fetch_contents_cb,
  963. children, _1));
  964. }
  965. if (content_done)
  966. {
  967. // This will have a bit of overlap with onAISContentCallback(),
  968. // but something else might have dowloaded folders, so verify
  969. // every child that is complete has its children done as well.
  970. for (LLInventoryModel::cat_array_t::const_iterator
  971. it = categories->begin(), end = categories->end();
  972. it != end; ++it)
  973. {
  974. LLViewerInventoryCategory* childp = *it;
  975. if (!childp->isVersionUnknown())
  976. {
  977. mFetchFolderQueue.emplace_back(childp->getUUID(),
  978. FT_RECURSIVE, true);
  979. }
  980. }
  981. }
  982. else
  983. {
  984. // Send it back to get the rest
  985. mFetchFolderQueue.emplace_back(id, FT_CONTENT_RECURSIVE, true);
  986. }
  987. }
  988. }
  989. else if (fetch_type == FT_FORCED || catp->isVersionUnknown())
  990. {
  991. U32 target_state;
  992. if (fetch_type > FT_CONTENT_RECURSIVE)
  993. {
  994. target_state = LLViewerInventoryCategory::FETCH_RECURSIVE;
  995. }
  996. else
  997. {
  998. target_state = LLViewerInventoryCategory::FETCH_NORMAL;
  999. }
  1000. // Start again if we did a non-recursive fetch before to get all
  1001. // children in a single request.
  1002. if (catp->getFetching() < target_state)
  1003. {
  1004. // Increment before call in case of immediate callback
  1005. incrFetchFolderCount(1);
  1006. catp->setFetching(target_state);
  1007. mExpectedFolderIds.emplace(id);
  1008. bool recurse = fetch_type == FT_RECURSIVE;
  1009. AISAPI::fetchCategoryChildren(id, is_library, recurse,
  1010. boost::bind(fetch_folder_cb, id,
  1011. fetch_type, _1));
  1012. }
  1013. }
  1014. // Already fetched, check if anything inside needs fetching.
  1015. else if (fetch_type == FT_RECURSIVE || fetch_type == FT_FOLDER_AND_CONTENT)
  1016. {
  1017. LLInventoryModel::cat_array_t* categories;
  1018. LLInventoryModel::item_array_t* items;
  1019. gInventory.getDirectDescendentsOf(id, categories, items);
  1020. if (categories)
  1021. {
  1022. for (LLInventoryModel::cat_array_t::const_iterator
  1023. it = categories->begin(), end = categories->end();
  1024. it != end; ++it)
  1025. {
  1026. // Send it back to get the rest (not front, to avoid an
  1027. // infinite loop).
  1028. mFetchFolderQueue.emplace_back((*it)->getUUID(), FT_RECURSIVE,
  1029. true);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. // Bundle up a bunch of requests to send all at once.
  1035. //static
  1036. void LLInventoryModelFetch::bulkFetch(const std::string& url)
  1037. {
  1038. // Background fetch is called from gIdleCallbacks in a loop until
  1039. // background fetch is stopped. If there are items in mFetch*Queue, we want
  1040. // to check the time since the last bulkFetch was sent. If it exceeds our
  1041. // retry time, go ahead and fire off another batch.
  1042. // *TODO: these values could be tweaked at runtime to effect a fast/slow
  1043. // fetch throttle. Once login is complete and the scene is mostly loaded,
  1044. // we could turn up the throttle and fill missing inventory quicker.
  1045. constexpr U32 max_batch_size = 10;
  1046. // Outstanding requests, not connections
  1047. constexpr S32 max_concurrent_fetches = 12;
  1048. if (gDisconnected || LLApp::isExiting())
  1049. {
  1050. gIdleCallbacks.deleteFunction(backgroundFetchCB, NULL);
  1051. return; // Just bail if we are disconnected
  1052. }
  1053. if (mFetchCount)
  1054. {
  1055. // Process completed background HTTP requests
  1056. gInventory.handleResponses(false);
  1057. }
  1058. if (mFetchCount > max_concurrent_fetches)
  1059. {
  1060. return;
  1061. }
  1062. U32 item_count = 0;
  1063. U32 folder_count = 0;
  1064. static LLCachedControl<U32> inventory_sort_order(gSavedSettings,
  1065. "InventorySortOrder");
  1066. U32 sort_order = (U32)inventory_sort_order & 0x1;
  1067. uuid_vec_t recursive_cats;
  1068. uuid_list_t all_cats; // Duplicate avoidance.
  1069. LLSD folder_request_body;
  1070. LLSD folder_request_body_lib;
  1071. LLSD item_request_body;
  1072. LLSD item_request_body_lib;
  1073. const LLUUID& lib_owner_id = gInventory.getLibraryOwnerID();
  1074. while (!mFetchFolderQueue.empty() &&
  1075. item_count + folder_count < max_batch_size)
  1076. {
  1077. const FetchQueueInfo& fetch_info = mFetchFolderQueue.front();
  1078. const LLUUID& cat_id = fetch_info.mUUID;
  1079. if (all_cats.count(cat_id))
  1080. {
  1081. // Duplicate, skip.
  1082. mFetchFolderQueue.pop_front();
  1083. continue;
  1084. }
  1085. all_cats.emplace(cat_id);
  1086. if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
  1087. {
  1088. recursive_cats.emplace_back(cat_id);
  1089. }
  1090. if (cat_id.isNull()) // DEV-17797
  1091. {
  1092. LLSD folder_sd;
  1093. folder_sd["folder_id"] = LLUUID::null.asString();
  1094. folder_sd["owner_id"] = gAgentID;
  1095. folder_sd["sort_order"] = (LLSD::Integer)sort_order;
  1096. folder_sd["fetch_folders"] = false;
  1097. folder_sd["fetch_items"] = true;
  1098. folder_request_body["folders"].append(folder_sd);
  1099. ++folder_count;
  1100. mFetchFolderQueue.pop_front();
  1101. continue;
  1102. }
  1103. const LLViewerInventoryCategory* catp = gInventory.getCategory(cat_id);
  1104. if (!catp)
  1105. {
  1106. mFetchFolderQueue.pop_front();
  1107. continue;
  1108. }
  1109. if (catp->isVersionUnknown())
  1110. {
  1111. LLSD folder_sd;
  1112. folder_sd["folder_id"] = cat_id;
  1113. folder_sd["owner_id"] = catp->getOwnerID();
  1114. folder_sd["sort_order"] = (LLSD::Integer)sort_order;
  1115. // (LLSD::Boolean)sFullFetchStarted;
  1116. folder_sd["fetch_folders"] = true;
  1117. folder_sd["fetch_items"] = true;
  1118. if (catp->getOwnerID() == lib_owner_id)
  1119. {
  1120. folder_request_body_lib["folders"].append(folder_sd);
  1121. }
  1122. else
  1123. {
  1124. folder_request_body["folders"].append(folder_sd);
  1125. }
  1126. ++folder_count;
  1127. }
  1128. else
  1129. {
  1130. // May already have this folder, but append child folders to list.
  1131. if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
  1132. {
  1133. LLInventoryModel::cat_array_t* categories;
  1134. LLInventoryModel::item_array_t* items;
  1135. gInventory.getDirectDescendentsOf(cat_id, categories, items);
  1136. if (categories)
  1137. {
  1138. for (LLInventoryModel::cat_array_t::const_iterator
  1139. it = categories->begin(), end = categories->end();
  1140. it != end; ++it)
  1141. {
  1142. mFetchFolderQueue.emplace_back((*it)->getUUID(),
  1143. fetch_info.mFetchType,
  1144. true);
  1145. }
  1146. }
  1147. }
  1148. }
  1149. mFetchFolderQueue.pop_front();
  1150. }
  1151. while (!mFetchItemQueue.empty() &&
  1152. item_count + folder_count < max_batch_size)
  1153. {
  1154. const FetchQueueInfo& fetch_info = mFetchItemQueue.front();
  1155. const LLUUID& item_id = fetch_info.mUUID;
  1156. LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
  1157. if (itemp)
  1158. {
  1159. LLSD item_sd;
  1160. item_sd["owner_id"] = itemp->getPermissions().getOwner();
  1161. item_sd["item_id"] = item_id;
  1162. if (itemp->getPermissions().getOwner() == gAgentID)
  1163. {
  1164. item_request_body.append(item_sd);
  1165. }
  1166. else
  1167. {
  1168. item_request_body_lib.append(item_sd);
  1169. }
  1170. #if 0
  1171. itemp->fetchFromServer();
  1172. #endif
  1173. ++item_count;
  1174. }
  1175. mFetchItemQueue.pop_front();
  1176. }
  1177. if (!item_count && !folder_count)
  1178. {
  1179. if (isBulkFetchProcessingComplete())
  1180. {
  1181. setAllFoldersFetched();
  1182. }
  1183. return;
  1184. }
  1185. // Issue HTTP POST requests to fetch folders and items
  1186. if (folder_request_body["folders"].size())
  1187. {
  1188. BGFolderHttpHandler::postRequest(url, folder_request_body,
  1189. recursive_cats);
  1190. }
  1191. if (folder_request_body_lib["folders"].size())
  1192. {
  1193. const std::string& url =
  1194. gAgent.getRegionCapability("FetchLibDescendents2");
  1195. if (!url.empty())
  1196. {
  1197. BGFolderHttpHandler::postRequest(url, folder_request_body_lib,
  1198. recursive_cats, true);
  1199. }
  1200. }
  1201. if (item_request_body.size())
  1202. {
  1203. const std::string& url = gAgent.getRegionCapability("FetchInventory2");
  1204. if (!url.empty())
  1205. {
  1206. LLSD body;
  1207. body["items"] = item_request_body;
  1208. BGItemHttpHandler::postRequest(url, body);
  1209. }
  1210. }
  1211. if (item_request_body_lib.size())
  1212. {
  1213. const std::string& url = gAgent.getRegionCapability("FetchLib2");
  1214. if (!url.empty())
  1215. {
  1216. LLSD body;
  1217. body["items"] = item_request_body_lib;
  1218. BGItemHttpHandler::postRequest(url, body, true);
  1219. }
  1220. }
  1221. }
  1222. bool LLInventoryModelFetch::fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const
  1223. {
  1224. for (fetch_queue_t::const_iterator it = mFetchFolderQueue.begin(),
  1225. end = mFetchFolderQueue.end();
  1226. it != end; ++it)
  1227. {
  1228. const LLUUID& fetch_id = it->mUUID;
  1229. if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
  1230. {
  1231. return false;
  1232. }
  1233. }
  1234. for (fetch_queue_t::const_iterator it = mFetchItemQueue.begin(),
  1235. end = mFetchItemQueue.end();
  1236. it != end; ++it)
  1237. {
  1238. const LLUUID& fetch_id = it->mUUID;
  1239. if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
  1240. {
  1241. return false;
  1242. }
  1243. }
  1244. return true;
  1245. }
  1246. //static
  1247. void LLInventoryModelFetch::forceFetchFolder(const LLUUID& cat_id)
  1248. {
  1249. LLInventoryModelFetch* self = getInstance();
  1250. self->scheduleFolderFetch(cat_id, true); // true = force
  1251. self->start(cat_id, false); // false = not recursive
  1252. }
  1253. //static
  1254. void LLInventoryModelFetch::forceFetchItem(const LLUUID& item_id)
  1255. {
  1256. forceFetchItem(gInventory.getItem(item_id));
  1257. }
  1258. //static
  1259. void LLInventoryModelFetch::forceFetchItem(const LLInventoryItem* itemp)
  1260. {
  1261. if (!itemp)
  1262. {
  1263. return;
  1264. }
  1265. LLInventoryModelFetch* self = getInstance();
  1266. self->scheduleItemFetch(itemp->getUUID(), true); // true = force
  1267. if (useAISFetching())
  1268. {
  1269. // Scheduling is not enough with AIS3: we need to trigger the fetch on
  1270. // the parent folder as well. HB
  1271. const LLUUID& parent_id = itemp->getParentUUID();
  1272. self->scheduleFolderFetch(parent_id, true); // true = force
  1273. self->start(parent_id, false); // false = not recursive
  1274. }
  1275. }