llavatarnamecache.cpp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. /**
  2. * @file llavatarnamecache.cpp
  3. * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
  4. * ("James Cook") from avatar UUIDs.
  5. *
  6. * $LicenseInfo:firstyear=2010&license=viewergpl$
  7. *
  8. * Copyright (c) 2010, 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 "linden_common.h"
  34. #include "boost/tokenizer.hpp"
  35. #include "llavatarnamecache.h"
  36. #include "llcachename.h" // we wrap this system
  37. #include "lldate.h"
  38. #include "llframetimer.h"
  39. #include "llsd.h"
  40. #include "llsdserialize.h"
  41. ///////////////////////////////////////////////////////////////////////////////
  42. // Helper function
  43. static const std::string MAX_AGE("max-age");
  44. static const boost::char_separator<char> EQUALS_SEPARATOR("=");
  45. static const boost::char_separator<char> COMMA_SEPARATOR(",");
  46. // Parse a cache-control header to get the max-age delta-seconds. Returns true
  47. // if header has max-age param and it parses correctly. Exported here to ease
  48. // unit testing.
  49. static bool max_age_from_cache_control(const std::string& cache_control,
  50. S32* max_age)
  51. {
  52. // Split the string on "," to get a list of directives
  53. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  54. tokenizer directives(cache_control, COMMA_SEPARATOR);
  55. for (tokenizer::iterator token_it = directives.begin(),
  56. end = directives.end();
  57. token_it != end; ++token_it)
  58. {
  59. // Tokens may have leading or trailing whitespace
  60. std::string token = *token_it;
  61. LLStringUtil::trim(token);
  62. if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
  63. {
  64. // ...this token starts with max-age, so let's chop it up by "="
  65. tokenizer subtokens(token, EQUALS_SEPARATOR);
  66. tokenizer::iterator subtoken_it = subtokens.begin();
  67. // Must have a token
  68. if (subtoken_it == subtokens.end()) return false;
  69. std::string subtoken = *subtoken_it;
  70. // Must exactly equal "max-age"
  71. LLStringUtil::trim(subtoken);
  72. if (subtoken != MAX_AGE) return false;
  73. // Must have another token
  74. ++subtoken_it;
  75. if (subtoken_it == subtokens.end()) return false;
  76. subtoken = *subtoken_it;
  77. // Must be a valid integer
  78. // *NOTE: atoi() returns 0 for invalid values, so we have to check
  79. // the string first.
  80. // *TODO: Do servers ever send "0000" for zero? We don't handle it
  81. LLStringUtil::trim(subtoken);
  82. if (subtoken == "0")
  83. {
  84. *max_age = 0;
  85. return true;
  86. }
  87. S32 val = atoi(subtoken.c_str());
  88. if (val > 0 && val < S32_MAX)
  89. {
  90. *max_age = val;
  91. return true;
  92. }
  93. return false;
  94. }
  95. }
  96. return false;
  97. }
  98. ///////////////////////////////////////////////////////////////////////////////
  99. // LLAvatarName class
  100. ///////////////////////////////////////////////////////////////////////////////
  101. bool LLAvatarName::sOmitResidentAsLastName = false;
  102. bool LLAvatarName::sLegacyNamesForFriends = true;
  103. bool LLAvatarName::sLegacyNamesForSpeakers = true;
  104. // Store these in pre-built std::strings to avoid memory allocations in
  105. // LLSD map lookups
  106. static const std::string USERNAME("username");
  107. static const std::string DISPLAY_NAME("display_name");
  108. static const std::string LEGACY_FIRST_NAME("legacy_first_name");
  109. static const std::string LEGACY_LAST_NAME("legacy_last_name");
  110. static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default");
  111. static const std::string DISPLAY_NAME_EXPIRES("display_name_expires");
  112. static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update");
  113. LLAvatarName::LLAvatarName()
  114. : mUsername(),
  115. mDisplayName(),
  116. mLegacyFirstName(),
  117. mLegacyLastName(),
  118. mIsDisplayNameDefault(false),
  119. mIsTemporaryName(false),
  120. mExpires(F64_MAX),
  121. mNextUpdate(0.0)
  122. {
  123. }
  124. bool LLAvatarName::operator<(const LLAvatarName& rhs) const
  125. {
  126. if (LL_LIKELY(mUsername != rhs.mUsername))
  127. {
  128. return mUsername < rhs.mUsername;
  129. }
  130. else
  131. {
  132. return mDisplayName < rhs.mDisplayName;
  133. }
  134. }
  135. LLSD LLAvatarName::asLLSD() const
  136. {
  137. LLSD sd;
  138. sd[USERNAME] = mUsername;
  139. sd[DISPLAY_NAME] = mDisplayName;
  140. sd[LEGACY_FIRST_NAME] = mLegacyFirstName;
  141. sd[LEGACY_LAST_NAME] = mLegacyLastName;
  142. sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault;
  143. sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires);
  144. sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate);
  145. return sd;
  146. }
  147. void LLAvatarName::fromLLSD(const LLSD& sd)
  148. {
  149. mUsername = sd[USERNAME].asString();
  150. mDisplayName = sd[DISPLAY_NAME].asString();
  151. mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString();
  152. mLegacyLastName = sd[LEGACY_LAST_NAME].asString();
  153. mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean();
  154. LLDate expires = sd[DISPLAY_NAME_EXPIRES];
  155. mExpires = expires.secondsSinceEpoch();
  156. LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE];
  157. mNextUpdate = next_update.secondsSinceEpoch();
  158. }
  159. std::string LLAvatarName::getCompleteName() const
  160. {
  161. std::string name;
  162. if (mUsername.empty() || mIsDisplayNameDefault)
  163. {
  164. // If the display name feature is off
  165. // OR this particular display name is defaulted (i.e. based
  166. // on user name), then display only the easier to read instance
  167. // of the person's name.
  168. name = mDisplayName;
  169. }
  170. else
  171. {
  172. name = mDisplayName + " (" + mUsername + ")";
  173. }
  174. return name;
  175. }
  176. std::string LLAvatarName::getLegacyName(bool full) const
  177. {
  178. std::string name;
  179. name.reserve(mLegacyFirstName.size() + 1 + mLegacyLastName.size());
  180. name = mLegacyFirstName;
  181. if (full || !sOmitResidentAsLastName || mLegacyLastName != "Resident")
  182. {
  183. name += " ";
  184. name += mLegacyLastName;
  185. }
  186. return name;
  187. }
  188. std::string LLAvatarName::getNames(bool linefeed) const
  189. {
  190. std::string name = getLegacyName();
  191. if (!mIsTemporaryName && !mUsername.empty() && name != mDisplayName)
  192. {
  193. if (linefeed)
  194. {
  195. name = mDisplayName + "\n[" + name + "]";
  196. }
  197. else
  198. {
  199. name = mDisplayName + " [" + name + "]";
  200. }
  201. }
  202. return name;
  203. }
  204. ///////////////////////////////////////////////////////////////////////////////
  205. // LLAvatarNameCache class
  206. ///////////////////////////////////////////////////////////////////////////////
  207. // Time-to-live for a temp cache entry.
  208. constexpr F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0;
  209. // Maximum time an unrefreshed cache entry is allowed
  210. constexpr F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0;
  211. // static variables initialization
  212. bool LLAvatarNameCache::sRunning = false;
  213. std::string LLAvatarNameCache::sNameLookupURL;
  214. U32 LLAvatarNameCache::sUseDisplayNames = 0;
  215. LLFrameTimer LLAvatarNameCache::sRequestTimer;
  216. LLAvatarNameCache::use_display_name_signal_t LLAvatarNameCache::sUseDisplayNamesSignal;
  217. uuid_list_t LLAvatarNameCache::sAskQueue;
  218. LLAvatarNameCache::pending_queue_t LLAvatarNameCache::sPendingQueue;
  219. LLAvatarNameCache::signal_map_t LLAvatarNameCache::sSignalMap;
  220. LLAvatarNameCache::cache_t LLAvatarNameCache::sCache;
  221. F64 LLAvatarNameCache::sLastExpireCheck = 0.0;
  222. S32 LLAvatarNameCache::sPendingRequests = 0;
  223. S32 LLAvatarNameCache::sMaximumRequests = 32;
  224. LLCore::HttpRequest::ptr_t LLAvatarNameCache::sHttpRequest;
  225. LLCore::HttpHeaders::ptr_t LLAvatarNameCache::sHttpHeaders;
  226. LLCore::HttpOptions::ptr_t LLAvatarNameCache::sHttpOptions;
  227. /* Sample response:
  228. <?xml version="1.0"?>
  229. <llsd>
  230. <map>
  231. <key>agents</key>
  232. <array>
  233. <map>
  234. <key>display_name_next_update</key>
  235. <date>2010-04-16T21:34:02+00:00Z</date>
  236. <key>display_name_expires</key>
  237. <date>2010-04-16T21:32:26.142178+00:00Z</date>
  238. <key>display_name</key>
  239. <string>MickBot390 LLQABot</string>
  240. <key>sl_id</key>
  241. <string>mickbot390.llqabot</string>
  242. <key>id</key>
  243. <string>0012809d-7d2d-4c24-9609-af1230a37715</string>
  244. <key>is_display_name_default</key>
  245. <boolean>false</boolean>
  246. </map>
  247. <map>
  248. <key>display_name_next_update</key>
  249. <date>2010-04-16T21:34:02+00:00Z</date>
  250. <key>display_name_expires</key>
  251. <date>2010-04-16T21:32:26.142178+00:00Z</date>
  252. <key>display_name</key>
  253. <string>Bjork Gudmundsdottir</string>
  254. <key>sl_id</key>
  255. <string>sardonyx.linden</string>
  256. <key>id</key>
  257. <string>3941037e-78ab-45f0-b421-bd6e77c1804d</string>
  258. <key>is_display_name_default</key>
  259. <boolean>true</boolean>
  260. </map>
  261. </array>
  262. </map>
  263. </llsd>
  264. */
  265. //static
  266. void LLAvatarNameCache::requestAvatarNameCacheCoro(const std::string& url,
  267. uuid_vec_t agent_ids)
  268. {
  269. LL_DEBUGS("NameCache") << "Entering coroutine: " << LLCoros::getName()
  270. << " - URL: " << url << " - Requesting "
  271. << agent_ids.size() << " agent IDs." << LL_ENDL;
  272. if (!sHttpRequest || !sHttpOptions || !sHttpHeaders)
  273. {
  274. llwarns << "Trying to request name cache when http parameters are not initialized"
  275. << llendl;
  276. return;
  277. }
  278. LLCoreHttpUtil::HttpCoroutineAdapter adapter("NameCache", sHttpRequest);
  279. ++sPendingRequests;
  280. LLSD result = adapter.getAndSuspend(url, sHttpOptions, sHttpHeaders);
  281. --sPendingRequests;
  282. const LLSD& http_results =
  283. result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
  284. LLCore::HttpStatus status =
  285. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(http_results);
  286. bool success = bool(status);
  287. if (!success)
  288. {
  289. llwarns << "Fetch error: " << status.toString() << llendl;
  290. }
  291. else if (!http_results.has("success") ||
  292. !http_results["success"].asBoolean())
  293. {
  294. llwarns << "Request error " << http_results["status"] << ": "
  295. << http_results["message"] << llendl;
  296. success = false;
  297. }
  298. if (!success)
  299. {
  300. // On any sort of failure add dummy records for any agent IDs in this
  301. // request that we do not have cached already
  302. for (S32 i = 0, count = agent_ids.size(); i < count; ++i)
  303. {
  304. const LLUUID& agent_id = agent_ids[i];
  305. handleAgentError(agent_id);
  306. }
  307. return;
  308. }
  309. // Pull expiration out of headers if available
  310. const LLSD& headers = http_results["headers"];
  311. F64 expires = nameExpirationFromHeaders(headers);
  312. LLAvatarNameCache::handleAvNameCacheSuccess(result, expires);
  313. }
  314. //static
  315. void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD& data, F64 expires)
  316. {
  317. F64 now = LLFrameTimer::getTotalSeconds();
  318. if (data.has("agents"))
  319. {
  320. const LLSD& agents = data["agents"];
  321. for (LLSD::array_const_iterator it = agents.beginArray(),
  322. end = agents.endArray();
  323. it != end; ++it)
  324. {
  325. const LLSD& row = *it;
  326. LLUUID agent_id = row["id"].asUUID();
  327. LLAvatarName av_name;
  328. av_name.fromLLSD(row);
  329. // Use expiration time from header
  330. av_name.mExpires = expires;
  331. // Some avatars don't have explicit display names set
  332. if (av_name.mDisplayName.empty())
  333. {
  334. av_name.mDisplayName = av_name.mUsername;
  335. }
  336. LL_DEBUGS("NameCache") << "Result for " << agent_id
  337. << " - username '" << av_name.mUsername
  338. << "' - displayname '"
  339. << av_name.mDisplayName << "' - expires in "
  340. << expires - now << " seconds" << LL_ENDL;
  341. // Cache it and fire signals
  342. processName(agent_id, av_name, true);
  343. }
  344. }
  345. if (data.has("bad_ids"))
  346. {
  347. // Same logic as error response case
  348. const LLSD& unresolved_agents = data["bad_ids"];
  349. S32 num_unresolved = unresolved_agents.size();
  350. if (num_unresolved > 0)
  351. {
  352. llwarns << num_unresolved << " unresolved ids; expires in "
  353. << expires - now << " seconds" << llendl;
  354. for (LLSD::array_const_iterator
  355. it = unresolved_agents.beginArray(),
  356. end = unresolved_agents.endArray();
  357. it != end; ++it)
  358. {
  359. const LLUUID& agent_id = *it;
  360. llwarns << "Failed id " << agent_id << llendl;
  361. handleAgentError(agent_id);
  362. }
  363. }
  364. }
  365. LL_DEBUGS("NameCache") << sCache.size() << " cached names" << LL_ENDL;
  366. }
  367. // Provide some fallback for agents that return errors
  368. //static
  369. void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id)
  370. {
  371. cache_t::iterator existing = sCache.find(agent_id);
  372. if (existing == sCache.end())
  373. {
  374. // There is no existing cache entry, so make a temporary name from
  375. // legacy
  376. llwarns << "Get legacy for agent " << agent_id << llendl;
  377. if (gCacheNamep)
  378. {
  379. gCacheNamep->get(agent_id, false, legacyNameCallback);
  380. }
  381. return;
  382. }
  383. // We have a cached (but probably expired) entry - since that would have
  384. // been returned by the get method, there is no need to signal anyone
  385. // Clear this agent from the pending list
  386. sPendingQueue.erase(agent_id);
  387. LLAvatarName& av_name = existing->second;
  388. LL_DEBUGS("NameCache") << "Use cache for agent " << agent_id
  389. << "user '" << av_name.mUsername << "' "
  390. << "display '" << av_name.mDisplayName
  391. << "' expires in "
  392. << av_name.mExpires - LLFrameTimer::getTotalSeconds()
  393. << " seconds" << LL_ENDL;
  394. // Reset expiry time so we do not constantly rerequest.
  395. av_name.mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME;
  396. }
  397. //static
  398. void LLAvatarNameCache::processName(const LLUUID& agent_id,
  399. const LLAvatarName& av_name,
  400. bool add_to_cache)
  401. {
  402. if (agent_id.isNull())
  403. {
  404. return;
  405. }
  406. if (add_to_cache)
  407. {
  408. sCache[agent_id] = av_name;
  409. }
  410. sPendingQueue.erase(agent_id);
  411. // signal everyone waiting on this name
  412. signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
  413. if (sig_it != sSignalMap.end())
  414. {
  415. callback_signal_t* signal = sig_it->second;
  416. (*signal)(agent_id, av_name);
  417. sSignalMap.erase(agent_id);
  418. delete signal;
  419. signal = NULL;
  420. }
  421. }
  422. //static
  423. void LLAvatarNameCache::requestNamesViaCapability()
  424. {
  425. if (sPendingRequests >= sMaximumRequests || sNameLookupURL.empty())
  426. {
  427. return;
  428. }
  429. F64 now = LLFrameTimer::getTotalSeconds();
  430. // URL format is like:
  431. // http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0
  432. //
  433. // Apache can handle URLs of 4096 chars, but let's be conservative
  434. constexpr U32 NAME_URL_MAX = 4096;
  435. constexpr U32 NAME_URL_SEND_THRESHOLD = 3500;
  436. std::string url;
  437. url.reserve(NAME_URL_MAX);
  438. std::vector<LLUUID> agent_ids;
  439. agent_ids.reserve(sAskQueue.size());
  440. U32 ids = 0;
  441. uuid_list_t::const_iterator it;
  442. while (!sAskQueue.empty())
  443. {
  444. it = sAskQueue.begin();
  445. LLUUID agent_id = *it;
  446. sAskQueue.hset_erase(it);
  447. if (agent_id.isNull()) continue; // It happens...
  448. if (url.empty())
  449. {
  450. // ...starting new request
  451. url += sNameLookupURL;
  452. url += "?ids=";
  453. ids = 1;
  454. }
  455. else
  456. {
  457. // ...continuing existing request
  458. url += "&ids=";
  459. ++ids;
  460. }
  461. url += agent_id.asString();
  462. agent_ids.push_back(agent_id);
  463. // Mark request as pending
  464. sPendingQueue[agent_id] = now;
  465. if (url.size() > NAME_URL_SEND_THRESHOLD)
  466. {
  467. break;
  468. }
  469. }
  470. if (!url.empty())
  471. {
  472. LL_DEBUGS("NameCache") << "Requested " << ids << " ids" << LL_ENDL;
  473. gCoros.launch("LLAvatarNameCache::requestAvatarNameCacheCoro",
  474. boost::bind(&LLAvatarNameCache::requestAvatarNameCacheCoro,
  475. url, agent_ids));
  476. }
  477. }
  478. //static
  479. void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id,
  480. const std::string& full_name,
  481. bool is_group)
  482. {
  483. // Construct a dummy record for this name. By convention, SLID is blank
  484. // Never expires, but not written to disk, so lasts until end of session.
  485. LLAvatarName av_name;
  486. LL_DEBUGS("NameCache") << "Callback for agent " << agent_id
  487. << " - full name '" << full_name
  488. << (is_group ? "' (group)" : "'") << LL_ENDL;
  489. buildLegacyName(full_name, &av_name);
  490. // Add to cache, because if we don't we'll keep rerequesting the same
  491. // record forever. buildLegacyName should always guarantee that these
  492. // records expire reasonably soon (in TEMP_CACHE_ENTRY_LIFETIME seconds),
  493. // so if the failure was due to something temporary we will eventually
  494. // request and get the right data.
  495. processName(agent_id, av_name, true);
  496. }
  497. //static
  498. void LLAvatarNameCache::requestNamesViaLegacy()
  499. {
  500. if (!gCacheNamep)
  501. {
  502. llwarns_once << "Cache name not initialized or already deleted !"
  503. << llendl;
  504. return;
  505. }
  506. constexpr S32 MAX_REQUESTS = 100;
  507. F64 now = LLFrameTimer::getTotalSeconds();
  508. std::string full_name;
  509. uuid_list_t::const_iterator it;
  510. for (S32 requests = 0; requests < MAX_REQUESTS && !sAskQueue.empty();
  511. ++requests)
  512. {
  513. it = sAskQueue.begin();
  514. LLUUID agent_id = *it;
  515. sAskQueue.hset_erase(it);
  516. if (agent_id.isNull()) continue; // It happens...
  517. // Mark as pending first, just in case the callback is immediately
  518. // invoked below. This should never happen in practice.
  519. sPendingQueue[agent_id] = now;
  520. LL_DEBUGS("NameCache") << "Requesting name for agent " << agent_id
  521. << LL_ENDL;
  522. gCacheNamep->get(agent_id, false, legacyNameCallback);
  523. }
  524. }
  525. //static
  526. void LLAvatarNameCache::initClass()
  527. {
  528. sHttpRequest = DEFAULT_HTTP_REQUEST;
  529. sHttpHeaders = DEFAULT_HTTP_HEADERS;
  530. sHttpOptions = DEFAULT_HTTP_OPTIONS;
  531. }
  532. //static
  533. void LLAvatarNameCache::cleanupClass()
  534. {
  535. sHttpRequest.reset();
  536. sHttpHeaders.reset();
  537. sHttpOptions.reset();
  538. sCache.clear();
  539. }
  540. //static
  541. bool LLAvatarNameCache::importFile(std::istream& istr)
  542. {
  543. LLSD data;
  544. if (LLSDSerialize::fromXMLDocument(data, istr) == LLSDParser::PARSE_FAILURE)
  545. {
  546. return false;
  547. }
  548. // by convention LLSD storage is a map
  549. // we only store one entry in the map
  550. LLSD agents = data["agents"];
  551. LLUUID agent_id;
  552. LLAvatarName av_name;
  553. for (LLSD::map_const_iterator it = agents.beginMap(),
  554. end = agents.endMap();
  555. it != end; ++it)
  556. {
  557. agent_id.set(it->first);
  558. av_name.fromLLSD(it->second);
  559. sCache[agent_id] = av_name;
  560. }
  561. llinfos << "Loaded " << sCache.size() << " avatar names." << llendl;
  562. return true;
  563. }
  564. //static
  565. void LLAvatarNameCache::exportFile(std::ostream& ostr)
  566. {
  567. LLSD agents;
  568. F64 max_unrefreshed = LLFrameTimer::getTotalSeconds() - MAX_UNREFRESHED_TIME;
  569. for (cache_t::const_iterator it = sCache.begin(), end = sCache.end();
  570. it != end; ++it)
  571. {
  572. const LLUUID& agent_id = it->first;
  573. const LLAvatarName& av_name = it->second;
  574. // Do not write temporary or expired entries to the stored cache
  575. if (!av_name.mIsTemporaryName && av_name.mExpires >= max_unrefreshed)
  576. {
  577. // key must be a string
  578. agents[agent_id.asString()] = av_name.asLLSD();
  579. }
  580. }
  581. LLSD data;
  582. data["agents"] = agents;
  583. LLSDSerialize::toPrettyXML(data, ostr);
  584. }
  585. //static
  586. void LLAvatarNameCache::setNameLookupURL(const std::string& name_lookup_url)
  587. {
  588. sNameLookupURL = name_lookup_url;
  589. }
  590. //static
  591. bool LLAvatarNameCache::hasNameLookupURL()
  592. {
  593. return !sNameLookupURL.empty();
  594. }
  595. //static
  596. void LLAvatarNameCache::setMaximumRequests(U32 num)
  597. {
  598. sMaximumRequests = num;
  599. }
  600. //static
  601. void LLAvatarNameCache::idle()
  602. {
  603. // By convention, start running at first idle() call
  604. sRunning = true;
  605. constexpr F32 SECS_BETWEEN_REQUESTS = 0.1f;
  606. if (!sRequestTimer.hasExpired())
  607. {
  608. return;
  609. }
  610. if (!sAskQueue.empty())
  611. {
  612. if (useDisplayNames())
  613. {
  614. requestNamesViaCapability();
  615. }
  616. else
  617. {
  618. // Fall back to legacy name cache system
  619. requestNamesViaLegacy();
  620. }
  621. }
  622. if (sAskQueue.empty())
  623. {
  624. // Cleared the list, reset the request timer.
  625. sRequestTimer.resetWithExpiry(SECS_BETWEEN_REQUESTS);
  626. }
  627. // Erase anything that has not been refreshed for more than
  628. // MAX_UNREFRESHED_TIME
  629. eraseUnrefreshed();
  630. }
  631. //static
  632. bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
  633. {
  634. pending_queue_t::const_iterator it = sPendingQueue.find(agent_id);
  635. if (it == sPendingQueue.end())
  636. {
  637. return false;
  638. }
  639. // In the list of requests in flight, retry if too old
  640. constexpr F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
  641. F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
  642. return it->second > expire_time;
  643. }
  644. //static
  645. void LLAvatarNameCache::eraseUnrefreshed()
  646. {
  647. F64 now = LLFrameTimer::getTotalSeconds();
  648. F64 max_unrefreshed = now - MAX_UNREFRESHED_TIME;
  649. if (!sLastExpireCheck || sLastExpireCheck < max_unrefreshed)
  650. {
  651. sLastExpireCheck = now;
  652. cache_t::iterator it = sCache.begin();
  653. while (it != sCache.end())
  654. {
  655. cache_t::iterator cur = it++;
  656. const LLAvatarName& av_name = cur->second;
  657. if (av_name.mExpires < max_unrefreshed)
  658. {
  659. LL_DEBUGS("NameCache") << cur->first << " user '"
  660. << av_name.mUsername << "' expired "
  661. << now - av_name.mExpires << " secs ago"
  662. << LL_ENDL;
  663. sCache.erase(cur);
  664. }
  665. }
  666. llinfos << sCache.size() << " cached avatar names" << llendl;
  667. }
  668. }
  669. //static
  670. void LLAvatarNameCache::buildLegacyName(const std::string& full_name,
  671. LLAvatarName* av_name)
  672. {
  673. llassert(av_name);
  674. av_name->mUsername = "";
  675. av_name->mDisplayName = full_name;
  676. size_t i = full_name.find(' ');
  677. if (i > 0)
  678. {
  679. av_name->mLegacyFirstName = full_name.substr(0, i);
  680. av_name->mLegacyLastName = full_name.substr(i + 1);
  681. }
  682. else
  683. {
  684. // Should never happen... Just in case.
  685. av_name->mLegacyFirstName = full_name;
  686. av_name->mLegacyLastName = "Resident";
  687. }
  688. av_name->mIsDisplayNameDefault = true;
  689. av_name->mIsTemporaryName = true;
  690. av_name->mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME;
  691. LL_DEBUGS("NameCache") << "Processed " << full_name << LL_ENDL;
  692. }
  693. // Fills in av_name if it has it in the cache, even if expired (can check
  694. // expiry time). Returns bool specifying if av_name was filled, false
  695. // otherwise.
  696. //static
  697. bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName* av_name)
  698. {
  699. if (sRunning)
  700. {
  701. // ...only do immediate lookups when cache is running
  702. if (useDisplayNames())
  703. {
  704. // ...use display names cache
  705. cache_t::iterator it = sCache.find(agent_id);
  706. if (it != sCache.end())
  707. {
  708. *av_name = it->second;
  709. // re-request name if entry is expired
  710. if (av_name->mExpires < LLFrameTimer::getTotalSeconds())
  711. {
  712. if (!isRequestPending(agent_id))
  713. {
  714. LL_DEBUGS("NameCache") << "Refreshing cache for agent "
  715. << agent_id << LL_ENDL;
  716. sAskQueue.emplace(agent_id);
  717. }
  718. }
  719. return true;
  720. }
  721. }
  722. else if (gCacheNamep)
  723. {
  724. // ...use legacy names cache
  725. std::string full_name;
  726. if (gCacheNamep->getFullName(agent_id, full_name))
  727. {
  728. buildLegacyName(full_name, av_name);
  729. return true;
  730. }
  731. }
  732. }
  733. if (!isRequestPending(agent_id))
  734. {
  735. LL_DEBUGS("NameCache") << "Request queued for agent " << agent_id
  736. << LL_ENDL;
  737. sAskQueue.emplace(agent_id);
  738. }
  739. return false;
  740. }
  741. //static
  742. void LLAvatarNameCache::fireSignal(const LLUUID& agent_id,
  743. const callback_slot_t& slot,
  744. const LLAvatarName& av_name)
  745. {
  746. callback_signal_t signal;
  747. signal.connect(slot);
  748. signal(agent_id, av_name);
  749. }
  750. //static
  751. LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& agent_id,
  752. callback_slot_t slot)
  753. {
  754. callback_connection_t connection;
  755. if (sRunning)
  756. {
  757. // ...only do immediate lookups when cache is running
  758. if (useDisplayNames())
  759. {
  760. // ...use new cache
  761. cache_t::iterator it = sCache.find(agent_id);
  762. if (it != sCache.end())
  763. {
  764. const LLAvatarName& av_name = it->second;
  765. if (av_name.mExpires > LLFrameTimer::getTotalSeconds())
  766. {
  767. // ...name already exists in cache, fire callback now
  768. fireSignal(agent_id, slot, av_name);
  769. return connection;
  770. }
  771. }
  772. }
  773. else if (gCacheNamep)
  774. {
  775. // ...use old name system
  776. std::string full_name;
  777. if (gCacheNamep->getFullName(agent_id, full_name))
  778. {
  779. LLAvatarName av_name;
  780. buildLegacyName(full_name, &av_name);
  781. fireSignal(agent_id, slot, av_name);
  782. return connection;
  783. }
  784. }
  785. }
  786. // Schedule a request
  787. if (!isRequestPending(agent_id))
  788. {
  789. sAskQueue.emplace(agent_id);
  790. }
  791. // Always store additional callback, even if request is pending
  792. signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
  793. if (sig_it == sSignalMap.end())
  794. {
  795. // New callback for this Id
  796. callback_signal_t* signal = new callback_signal_t();
  797. connection = signal->connect(slot);
  798. sSignalMap.emplace(agent_id, signal);
  799. }
  800. else
  801. {
  802. // Existing callback, bind additional slot
  803. callback_signal_t* signal = sig_it->second;
  804. connection = signal->connect(slot);
  805. }
  806. return connection;
  807. }
  808. //static
  809. void LLAvatarNameCache::setUseDisplayNames(U32 use)
  810. {
  811. if (use != sUseDisplayNames)
  812. {
  813. if (use > 2)
  814. {
  815. sUseDisplayNames = 1;
  816. }
  817. else
  818. {
  819. sUseDisplayNames = use;
  820. }
  821. // Flush our cache
  822. sCache.clear();
  823. sUseDisplayNamesSignal();
  824. }
  825. }
  826. //static
  827. U32 LLAvatarNameCache::useDisplayNames()
  828. {
  829. // Must be both manually set on and able to look up names.
  830. if (sNameLookupURL.empty())
  831. {
  832. return 0;
  833. }
  834. else
  835. {
  836. return sUseDisplayNames;
  837. }
  838. }
  839. //static
  840. void LLAvatarNameCache::erase(const LLUUID& agent_id)
  841. {
  842. sCache.erase(agent_id);
  843. }
  844. //static
  845. void LLAvatarNameCache::insert(const LLUUID& agent_id,
  846. const LLAvatarName& av_name)
  847. {
  848. // *TODO: update timestamp if zero ?
  849. sCache[agent_id] = av_name;
  850. }
  851. //static
  852. F64 LLAvatarNameCache::nameExpirationFromHeaders(const LLSD& headers)
  853. {
  854. F64 expires = 0.0;
  855. if (expirationFromCacheControl(headers, &expires))
  856. {
  857. return expires;
  858. }
  859. else
  860. {
  861. // With no expiration info, default to an hour
  862. constexpr F64 DEFAULT_EXPIRES = 60.0 * 60.0;
  863. F64 now = LLFrameTimer::getTotalSeconds();
  864. return now + DEFAULT_EXPIRES;
  865. }
  866. }
  867. //static
  868. bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers,
  869. F64* expires)
  870. {
  871. bool fromCacheControl = false;
  872. F64 now = LLFrameTimer::getTotalSeconds();
  873. // Allow the header to override the default
  874. std::string cache_control;
  875. if (headers.has(HTTP_IN_HEADER_CACHE_CONTROL))
  876. {
  877. cache_control = headers[HTTP_IN_HEADER_CACHE_CONTROL].asString();
  878. }
  879. if (!cache_control.empty())
  880. {
  881. S32 max_age = 0;
  882. if (max_age_from_cache_control(cache_control, &max_age))
  883. {
  884. *expires = now + (F64)max_age;
  885. fromCacheControl = true;
  886. }
  887. }
  888. LL_DEBUGS("NameCache") << (fromCacheControl ? "Expires based on cache control "
  889. : "default expiration ")
  890. << "in " << *expires - now << " seconds" << LL_ENDL;
  891. return fromCacheControl;
  892. }
  893. //static
  894. void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
  895. {
  896. sUseDisplayNamesSignal.connect(cb);
  897. }