rlz_lib.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. //
  5. // A library to manage RLZ information for access-points shared
  6. // across different client applications.
  7. #include "rlz/lib/rlz_lib.h"
  8. #include <algorithm>
  9. #include "base/strings/string_util.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "base/syslog_logging.h"
  12. #include "base/threading/platform_thread.h"
  13. #include "base/time/time.h"
  14. #include "build/build_config.h"
  15. #include "build/chromeos_buildflags.h"
  16. #include "net/base/backoff_entry.h"
  17. #include "rlz/lib/assert.h"
  18. #include "rlz/lib/financial_ping.h"
  19. #include "rlz/lib/lib_values.h"
  20. #include "rlz/lib/net_response_check.h"
  21. #include "rlz/lib/rlz_value_store.h"
  22. #include "rlz/lib/string_utils.h"
  23. #include "services/network/public/mojom/url_loader_factory.mojom.h"
  24. #if BUILDFLAG(IS_WIN)
  25. #include "rlz/lib/machine_deal_win.h"
  26. #endif
  27. namespace {
  28. // Event information returned from ping response.
  29. struct ReturnedEvent {
  30. rlz_lib::AccessPoint access_point;
  31. rlz_lib::Event event_type;
  32. };
  33. // Helper functions
  34. bool IsAccessPointSupported(rlz_lib::AccessPoint point) {
  35. switch (point) {
  36. case rlz_lib::NO_ACCESS_POINT:
  37. case rlz_lib::LAST_ACCESS_POINT:
  38. case rlz_lib::MOBILE_IDLE_SCREEN_BLACKBERRY:
  39. case rlz_lib::MOBILE_IDLE_SCREEN_WINMOB:
  40. case rlz_lib::MOBILE_IDLE_SCREEN_SYMBIAN:
  41. // These AP's are never available on Windows PCs.
  42. return false;
  43. case rlz_lib::IE_DEFAULT_SEARCH:
  44. case rlz_lib::IE_HOME_PAGE:
  45. case rlz_lib::IETB_SEARCH_BOX:
  46. case rlz_lib::QUICK_SEARCH_BOX:
  47. case rlz_lib::GD_DESKBAND:
  48. case rlz_lib::GD_SEARCH_GADGET:
  49. case rlz_lib::GD_WEB_SERVER:
  50. case rlz_lib::GD_OUTLOOK:
  51. case rlz_lib::CHROME_OMNIBOX:
  52. case rlz_lib::CHROME_HOME_PAGE:
  53. // TODO: Figure out when these settings are set to Google.
  54. default:
  55. return true;
  56. }
  57. }
  58. // Current RLZ can only use [a-zA-Z0-9_\-]
  59. // We will be more liberal and allow some additional chars, but not url meta
  60. // chars.
  61. bool IsGoodRlzChar(const char ch) {
  62. if (base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch))
  63. return true;
  64. switch (ch) {
  65. case '_':
  66. case '-':
  67. case '!':
  68. case '@':
  69. case '$':
  70. case '*':
  71. case '(':
  72. case ')':
  73. case ';':
  74. case '.':
  75. case '<':
  76. case '>':
  77. return true;
  78. }
  79. return false;
  80. }
  81. // This function will remove bad rlz chars and also limit the max rlz to some
  82. // reasonable size. It also assumes that normalized_rlz is at least
  83. // kMaxRlzLength+1 long.
  84. void NormalizeRlz(const char* raw_rlz, char* normalized_rlz) {
  85. size_t index = 0;
  86. for (; raw_rlz[index] != 0 && index < rlz_lib::kMaxRlzLength; ++index) {
  87. char current = raw_rlz[index];
  88. if (IsGoodRlzChar(current)) {
  89. normalized_rlz[index] = current;
  90. } else {
  91. normalized_rlz[index] = '.';
  92. }
  93. }
  94. normalized_rlz[index] = 0;
  95. }
  96. void GetEventsFromResponseString(
  97. const std::string& response_line,
  98. const std::string& field_header,
  99. std::vector<ReturnedEvent>* event_array) {
  100. // Get the string of events.
  101. std::string events = response_line.substr(field_header.size());
  102. base::TrimWhitespaceASCII(events, base::TRIM_LEADING, &events);
  103. int events_length = events.find_first_of("\r\n ");
  104. if (events_length < 0)
  105. events_length = events.size();
  106. events = events.substr(0, events_length);
  107. // Break this up into individual events
  108. int event_end_index = -1;
  109. do {
  110. int event_begin = event_end_index + 1;
  111. event_end_index = events.find(rlz_lib::kEventsCgiSeparator, event_begin);
  112. int event_end = event_end_index;
  113. if (event_end < 0)
  114. event_end = events_length;
  115. std::string event_string = events.substr(event_begin,
  116. event_end - event_begin);
  117. if (event_string.size() != 3) // 3 = 2(AP) + 1(E)
  118. continue;
  119. rlz_lib::AccessPoint point = rlz_lib::NO_ACCESS_POINT;
  120. rlz_lib::Event event = rlz_lib::INVALID_EVENT;
  121. if (!GetAccessPointFromName(event_string.substr(0, 2).c_str(), &point) ||
  122. point == rlz_lib::NO_ACCESS_POINT) {
  123. continue;
  124. }
  125. if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(),
  126. &event) || event == rlz_lib::INVALID_EVENT) {
  127. continue;
  128. }
  129. ReturnedEvent current_event = {point, event};
  130. event_array->push_back(current_event);
  131. } while (event_end_index >= 0);
  132. }
  133. // Event storage functions.
  134. bool RecordStatefulEvent(rlz_lib::Product product, rlz_lib::AccessPoint point,
  135. rlz_lib::Event event) {
  136. rlz_lib::ScopedRlzValueStoreLock lock;
  137. rlz_lib::RlzValueStore* store = lock.GetStore();
  138. if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
  139. return false;
  140. // Write the new event to the value store.
  141. const char* point_name = GetAccessPointName(point);
  142. const char* event_name = GetEventName(event);
  143. if (!point_name || !event_name)
  144. return false;
  145. if (!point_name[0] || !event_name[0])
  146. return false;
  147. std::string new_event_value;
  148. base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
  149. return store->AddStatefulEvent(product, new_event_value.c_str());
  150. }
  151. bool GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi,
  152. size_t cgi_size,
  153. rlz_lib::RlzValueStore* store) {
  154. // Prepend the CGI param key to the buffer.
  155. std::string cgi_arg;
  156. base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable);
  157. if (cgi_size <= cgi_arg.size())
  158. return false;
  159. size_t index;
  160. for (index = 0; index < cgi_arg.size(); ++index)
  161. cgi[index] = cgi_arg[index];
  162. // Read stored events.
  163. std::vector<std::string> events;
  164. if (!store->ReadProductEvents(product, &events))
  165. return false;
  166. // Append the events to the buffer.
  167. size_t num_values = 0;
  168. for (num_values = 0; num_values < events.size(); ++num_values) {
  169. cgi[index] = '\0';
  170. int divider = num_values > 0 ? 1 : 0;
  171. int size = cgi_size - (index + divider);
  172. if (size <= 0)
  173. return cgi_size >= (rlz_lib::kMaxCgiLength + 1);
  174. strncpy(cgi + index + divider, events[num_values].c_str(), size);
  175. if (divider)
  176. cgi[index] = rlz_lib::kEventsCgiSeparator;
  177. index += std::min((int)events[num_values].length(), size) + divider;
  178. }
  179. cgi[index] = '\0';
  180. return num_values > 0;
  181. }
  182. } // namespace
  183. namespace rlz_lib {
  184. #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
  185. bool SetURLLoaderFactory(network::mojom::URLLoaderFactory* factory) {
  186. return FinancialPing::SetURLLoaderFactory(factory);
  187. }
  188. #endif
  189. bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) {
  190. if (!cgi || cgi_size <= 0) {
  191. ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer");
  192. return false;
  193. }
  194. cgi[0] = 0;
  195. ScopedRlzValueStoreLock lock;
  196. RlzValueStore* store = lock.GetStore();
  197. if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
  198. return false;
  199. size_t size_local = std::min(
  200. static_cast<size_t>(kMaxCgiLength + 1), cgi_size);
  201. bool result = GetProductEventsAsCgiHelper(product, cgi, size_local, store);
  202. if (!result) {
  203. ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size");
  204. cgi[0] = 0;
  205. return false;
  206. }
  207. return true;
  208. }
  209. bool RecordProductEvent(Product product, AccessPoint point, Event event) {
  210. ScopedRlzValueStoreLock lock;
  211. RlzValueStore* store = lock.GetStore();
  212. if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
  213. return false;
  214. // Get this event's value.
  215. const char* point_name = GetAccessPointName(point);
  216. const char* event_name = GetEventName(event);
  217. if (!point_name || !event_name)
  218. return false;
  219. if (!point_name[0] || !event_name[0])
  220. return false;
  221. std::string new_event_value;
  222. base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
  223. // Check whether this event is a stateful event. If so, don't record it.
  224. if (store->IsStatefulEvent(product, new_event_value.c_str())) {
  225. // For a stateful event we skip recording, this function is also
  226. // considered successful.
  227. return true;
  228. }
  229. // Write the new event to the value store.
  230. return store->AddProductEvent(product, new_event_value.c_str());
  231. }
  232. bool ClearProductEvent(Product product, AccessPoint point, Event event) {
  233. ScopedRlzValueStoreLock lock;
  234. RlzValueStore* store = lock.GetStore();
  235. if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
  236. return false;
  237. // Get the event's value store value and delete it.
  238. const char* point_name = GetAccessPointName(point);
  239. const char* event_name = GetEventName(event);
  240. if (!point_name || !event_name)
  241. return false;
  242. if (!point_name[0] || !event_name[0])
  243. return false;
  244. std::string event_value;
  245. base::StringAppendF(&event_value, "%s%s", point_name, event_name);
  246. return store->ClearProductEvent(product, event_value.c_str());
  247. }
  248. // RLZ storage functions.
  249. bool GetAccessPointRlz(AccessPoint point, char* rlz, size_t rlz_size) {
  250. if (!rlz || rlz_size <= 0) {
  251. ASSERT_STRING("GetAccessPointRlz: Invalid buffer");
  252. return false;
  253. }
  254. rlz[0] = 0;
  255. ScopedRlzValueStoreLock lock;
  256. RlzValueStore* store = lock.GetStore();
  257. if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
  258. return false;
  259. if (!IsAccessPointSupported(point))
  260. return false;
  261. return store->ReadAccessPointRlz(point, rlz, rlz_size);
  262. }
  263. bool SetAccessPointRlz(AccessPoint point, const char* new_rlz) {
  264. ScopedRlzValueStoreLock lock;
  265. RlzValueStore* store = lock.GetStore();
  266. if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
  267. return false;
  268. if (!new_rlz) {
  269. ASSERT_STRING("SetAccessPointRlz: Invalid buffer");
  270. return false;
  271. }
  272. // Return false if the access point is not set to Google.
  273. if (!IsAccessPointSupported(point)) {
  274. ASSERT_STRING(("SetAccessPointRlz: "
  275. "Cannot set RLZ for unsupported access point."));
  276. return false;
  277. }
  278. // Verify the RLZ length.
  279. size_t rlz_length = strlen(new_rlz);
  280. if (rlz_length > kMaxRlzLength) {
  281. ASSERT_STRING("SetAccessPointRlz: RLZ length is exceeds max allowed.");
  282. return false;
  283. }
  284. char normalized_rlz[kMaxRlzLength + 1];
  285. NormalizeRlz(new_rlz, normalized_rlz);
  286. VERIFY(strlen(new_rlz) == rlz_length);
  287. // Setting RLZ to empty == clearing.
  288. if (normalized_rlz[0] == 0)
  289. return store->ClearAccessPointRlz(point);
  290. return store->WriteAccessPointRlz(point, normalized_rlz);
  291. }
  292. bool UpdateExistingAccessPointRlz(const std::string& brand) {
  293. ScopedRlzValueStoreLock lock;
  294. RlzValueStore* store = lock.GetStore();
  295. if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
  296. return false;
  297. return store->UpdateExistingAccessPointRlz(brand);
  298. }
  299. // Financial Server pinging functions.
  300. bool FormFinancialPingRequest(Product product, const AccessPoint* access_points,
  301. const char* product_signature,
  302. const char* product_brand,
  303. const char* product_id,
  304. const char* product_lang,
  305. bool exclude_machine_id,
  306. char* request, size_t request_buffer_size) {
  307. if (!request || request_buffer_size == 0)
  308. return false;
  309. request[0] = 0;
  310. std::string request_string;
  311. if (!FinancialPing::FormRequest(product, access_points, product_signature,
  312. product_brand, product_id, product_lang,
  313. exclude_machine_id, &request_string))
  314. return false;
  315. if (request_string.size() >= request_buffer_size)
  316. return false;
  317. strncpy(request, request_string.c_str(), request_buffer_size);
  318. request[request_buffer_size - 1] = 0;
  319. return true;
  320. }
  321. // Complex helpers built on top of other functions.
  322. bool ParseFinancialPingResponse(Product product, const char* response) {
  323. // Update the last ping time irrespective of success.
  324. FinancialPing::UpdateLastPingTime(product);
  325. // Parse the ping response - update RLZs, clear events.
  326. return ParsePingResponse(product, response);
  327. }
  328. bool SendFinancialPing(Product product,
  329. const AccessPoint* access_points,
  330. const char* product_signature,
  331. const char* product_brand,
  332. const char* product_id,
  333. const char* product_lang,
  334. bool exclude_machine_id) {
  335. return SendFinancialPing(product, access_points, product_signature,
  336. product_brand, product_id, product_lang,
  337. exclude_machine_id, false);
  338. }
  339. bool SendFinancialPing(Product product,
  340. const AccessPoint* access_points,
  341. const char* product_signature,
  342. const char* product_brand,
  343. const char* product_id,
  344. const char* product_lang,
  345. bool exclude_machine_id,
  346. const bool skip_time_check) {
  347. // Create the financial ping request. To support ChromeOS retries, the
  348. // same request needs to be sent out each time in order to preserve the
  349. // machine id. Not doing so could cause the RLZ server to over count
  350. // ChromeOS machines under some network error conditions (for example,
  351. // the request is properly received but the response it not).
  352. std::string request;
  353. if (!FinancialPing::FormRequest(product, access_points, product_signature,
  354. product_brand, product_id, product_lang,
  355. exclude_machine_id, &request))
  356. return false;
  357. // Check if the time is right to ping.
  358. if (!FinancialPing::IsPingTime(product, skip_time_check))
  359. return false;
  360. // Send out the ping, update the last ping time irrespective of success.
  361. FinancialPing::UpdateLastPingTime(product);
  362. std::string response;
  363. #if BUILDFLAG(IS_CHROMEOS_ASH)
  364. const net::BackoffEntry::Policy policy = {
  365. 0, // Number of initial errors to ignore.
  366. static_cast<int>(base::Seconds(5).InMilliseconds()), // Initial delay.
  367. 2, // Factor to increase delay.
  368. 0.1, // Delay fuzzing.
  369. static_cast<int>(base::Minutes(5).InMilliseconds()), // Maximum delay.
  370. -1, // Time to keep entries. -1 == never discard.
  371. };
  372. net::BackoffEntry backoff(&policy);
  373. const int kMaxRetryCount = 3;
  374. FinancialPing::PingResponse res = FinancialPing::PING_FAILURE;
  375. while (backoff.failure_count() < kMaxRetryCount) {
  376. // Write to syslog that an RLZ ping is being attempted. This is
  377. // purposefully done via syslog so that admin and/or end users can monitor
  378. // RLZ activity from this machine. If RLZ is turned off in crosh, these
  379. // messages will be absent.
  380. SYSLOG(INFO) << "Attempting to send RLZ ping brand=" << product_brand;
  381. res = FinancialPing::PingServer(request.c_str(), &response);
  382. if (res != FinancialPing::PING_FAILURE)
  383. break;
  384. backoff.InformOfRequest(false);
  385. if (backoff.ShouldRejectRequest()) {
  386. SYSLOG(INFO) << "Failed sending RLZ ping - retrying in "
  387. << backoff.GetTimeUntilRelease().InSeconds() << " seconds";
  388. }
  389. base::PlatformThread::Sleep(backoff.GetTimeUntilRelease());
  390. }
  391. if (res != FinancialPing::PING_SUCCESSFUL) {
  392. if (res == FinancialPing::PING_FAILURE) {
  393. SYSLOG(INFO) << "Failed sending RLZ ping after " << kMaxRetryCount
  394. << " tries";
  395. } else { // res == FinancialPing::PING_SHUTDOWN
  396. SYSLOG(INFO) << "Failed sending RLZ ping due to chrome shutdown";
  397. }
  398. return false;
  399. }
  400. SYSLOG(INFO) << "Succeeded in sending RLZ ping";
  401. #else
  402. FinancialPing::PingResponse res =
  403. FinancialPing::PingServer(request.c_str(), &response);
  404. if (res != FinancialPing::PING_SUCCESSFUL)
  405. return false;
  406. #endif
  407. return ParsePingResponse(product, response.c_str());
  408. }
  409. // TODO: Use something like RSA to make sure the response is
  410. // from a Google server.
  411. bool ParsePingResponse(Product product, const char* response) {
  412. rlz_lib::ScopedRlzValueStoreLock lock;
  413. rlz_lib::RlzValueStore* store = lock.GetStore();
  414. if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
  415. return false;
  416. std::string response_string(response);
  417. int response_length = -1;
  418. if (!IsPingResponseValid(response, &response_length))
  419. return false;
  420. if (0 == response_length)
  421. return true; // Empty response - no parsing.
  422. std::string events_variable;
  423. std::string stateful_events_variable;
  424. base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable);
  425. base::SStringPrintf(&stateful_events_variable, "%s: ",
  426. kStatefulEventsCgiVariable);
  427. int rlz_cgi_length = strlen(kRlzCgiVariable);
  428. // Split response lines. Expected response format is lines of the form:
  429. // rlzW1: 1R1_____en__252
  430. int line_end_index = -1;
  431. do {
  432. int line_begin = line_end_index + 1;
  433. line_end_index = response_string.find("\n", line_begin);
  434. int line_end = line_end_index;
  435. if (line_end < 0)
  436. line_end = response_length;
  437. if (line_end <= line_begin)
  438. continue; // Empty line.
  439. std::string response_line;
  440. response_line = response_string.substr(line_begin, line_end - line_begin);
  441. if (base::StartsWith(response_line, kRlzCgiVariable,
  442. base::CompareCase::SENSITIVE)) { // An RLZ.
  443. int separator_index = -1;
  444. if ((separator_index = response_line.find(": ")) < 0)
  445. continue; // Not a valid key-value pair.
  446. // Get the access point.
  447. std::string point_name =
  448. response_line.substr(3, separator_index - rlz_cgi_length);
  449. AccessPoint point = NO_ACCESS_POINT;
  450. if (!GetAccessPointFromName(point_name.c_str(), &point) ||
  451. point == NO_ACCESS_POINT)
  452. continue; // Not a valid access point.
  453. // Get the new RLZ.
  454. std::string rlz_value(response_line.substr(separator_index + 2));
  455. base::TrimWhitespaceASCII(rlz_value, base::TRIM_LEADING, &rlz_value);
  456. size_t rlz_length = rlz_value.find_first_of("\r\n ");
  457. if (rlz_length == std::string::npos)
  458. rlz_length = rlz_value.size();
  459. if (rlz_length > kMaxRlzLength)
  460. continue; // Too long.
  461. if (IsAccessPointSupported(point))
  462. SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str());
  463. } else if (base::StartsWith(response_line, events_variable,
  464. base::CompareCase::SENSITIVE)) {
  465. // Clear events which server parsed.
  466. std::vector<ReturnedEvent> event_array;
  467. GetEventsFromResponseString(response_line, events_variable, &event_array);
  468. for (size_t i = 0; i < event_array.size(); ++i) {
  469. ClearProductEvent(product, event_array[i].access_point,
  470. event_array[i].event_type);
  471. }
  472. } else if (base::StartsWith(response_line, stateful_events_variable,
  473. base::CompareCase::SENSITIVE)) {
  474. // Record any stateful events the server send over.
  475. std::vector<ReturnedEvent> event_array;
  476. GetEventsFromResponseString(response_line, stateful_events_variable,
  477. &event_array);
  478. for (size_t i = 0; i < event_array.size(); ++i) {
  479. RecordStatefulEvent(product, event_array[i].access_point,
  480. event_array[i].event_type);
  481. }
  482. }
  483. } while (line_end_index >= 0);
  484. #if BUILDFLAG(IS_WIN)
  485. // Update the DCC in registry if needed.
  486. SetMachineDealCodeFromPingResponse(response);
  487. #endif
  488. return true;
  489. }
  490. bool GetPingParams(Product product, const AccessPoint* access_points,
  491. char* cgi, size_t cgi_size) {
  492. if (!cgi || cgi_size <= 0) {
  493. ASSERT_STRING("GetPingParams: Invalid buffer");
  494. return false;
  495. }
  496. cgi[0] = 0;
  497. if (!access_points) {
  498. ASSERT_STRING("GetPingParams: access_points is NULL");
  499. return false;
  500. }
  501. // Add the RLZ Exchange Protocol version.
  502. std::string cgi_string(kProtocolCgiArgument);
  503. // Copy the &rlz= over.
  504. base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable);
  505. {
  506. // Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz()
  507. // calls below.
  508. ScopedRlzValueStoreLock lock;
  509. RlzValueStore* store = lock.GetStore();
  510. if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
  511. return false;
  512. bool first_rlz = true; // comma before every RLZ but the first.
  513. for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
  514. char rlz[kMaxRlzLength + 1];
  515. if (GetAccessPointRlz(access_points[i], rlz, std::size(rlz))) {
  516. const char* access_point = GetAccessPointName(access_points[i]);
  517. if (!access_point)
  518. continue;
  519. base::StringAppendF(&cgi_string, "%s%s%s%s",
  520. first_rlz ? "" : kRlzCgiSeparator,
  521. access_point, kRlzCgiIndicator, rlz);
  522. first_rlz = false;
  523. }
  524. }
  525. #if BUILDFLAG(IS_WIN)
  526. // Report the DCC too if not empty. DCCs are windows-only.
  527. char dcc[kMaxDccLength + 1];
  528. dcc[0] = 0;
  529. if (GetMachineDealCode(dcc, std::size(dcc)) && dcc[0])
  530. base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc);
  531. #endif
  532. }
  533. if (cgi_string.size() >= cgi_size)
  534. return false;
  535. strncpy(cgi, cgi_string.c_str(), cgi_size);
  536. cgi[cgi_size - 1] = 0;
  537. return true;
  538. }
  539. } // namespace rlz_lib