Roll harfbuzz-ng to 1.0.2
[chromium-blink-merge.git] / rlz / lib / rlz_lib.cc
blobc5403f6e4b564753dcd5990d5f84d0cba0d525a4
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.
8 #include "rlz/lib/rlz_lib.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "rlz/lib/assert.h"
13 #include "rlz/lib/crc32.h"
14 #include "rlz/lib/financial_ping.h"
15 #include "rlz/lib/lib_values.h"
16 #include "rlz/lib/rlz_value_store.h"
17 #include "rlz/lib/string_utils.h"
19 namespace {
21 // Event information returned from ping response.
22 struct ReturnedEvent {
23 rlz_lib::AccessPoint access_point;
24 rlz_lib::Event event_type;
27 // Helper functions
29 bool IsAccessPointSupported(rlz_lib::AccessPoint point) {
30 switch (point) {
31 case rlz_lib::NO_ACCESS_POINT:
32 case rlz_lib::LAST_ACCESS_POINT:
34 case rlz_lib::MOBILE_IDLE_SCREEN_BLACKBERRY:
35 case rlz_lib::MOBILE_IDLE_SCREEN_WINMOB:
36 case rlz_lib::MOBILE_IDLE_SCREEN_SYMBIAN:
37 // These AP's are never available on Windows PCs.
38 return false;
40 case rlz_lib::IE_DEFAULT_SEARCH:
41 case rlz_lib::IE_HOME_PAGE:
42 case rlz_lib::IETB_SEARCH_BOX:
43 case rlz_lib::QUICK_SEARCH_BOX:
44 case rlz_lib::GD_DESKBAND:
45 case rlz_lib::GD_SEARCH_GADGET:
46 case rlz_lib::GD_WEB_SERVER:
47 case rlz_lib::GD_OUTLOOK:
48 case rlz_lib::CHROME_OMNIBOX:
49 case rlz_lib::CHROME_HOME_PAGE:
50 // TODO: Figure out when these settings are set to Google.
52 default:
53 return true;
57 // Current RLZ can only use [a-zA-Z0-9_\-]
58 // We will be more liberal and allow some additional chars, but not url meta
59 // chars.
60 bool IsGoodRlzChar(const char ch) {
61 if (base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch))
62 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;
80 return false;
83 // This function will remove bad rlz chars and also limit the max rlz to some
84 // reasonable size. It also assumes that normalized_rlz is at least
85 // kMaxRlzLength+1 long.
86 void NormalizeRlz(const char* raw_rlz, char* normalized_rlz) {
87 size_t index = 0;
88 for (; raw_rlz[index] != 0 && index < rlz_lib::kMaxRlzLength; ++index) {
89 char current = raw_rlz[index];
90 if (IsGoodRlzChar(current)) {
91 normalized_rlz[index] = current;
92 } else {
93 normalized_rlz[index] = '.';
97 normalized_rlz[index] = 0;
100 void GetEventsFromResponseString(
101 const std::string& response_line,
102 const std::string& field_header,
103 std::vector<ReturnedEvent>* event_array) {
104 // Get the string of events.
105 std::string events = response_line.substr(field_header.size());
106 base::TrimWhitespaceASCII(events, base::TRIM_LEADING, &events);
108 int events_length = events.find_first_of("\r\n ");
109 if (events_length < 0)
110 events_length = events.size();
111 events = events.substr(0, events_length);
113 // Break this up into individual events
114 int event_end_index = -1;
115 do {
116 int event_begin = event_end_index + 1;
117 event_end_index = events.find(rlz_lib::kEventsCgiSeparator, event_begin);
118 int event_end = event_end_index;
119 if (event_end < 0)
120 event_end = events_length;
122 std::string event_string = events.substr(event_begin,
123 event_end - event_begin);
124 if (event_string.size() != 3) // 3 = 2(AP) + 1(E)
125 continue;
127 rlz_lib::AccessPoint point = rlz_lib::NO_ACCESS_POINT;
128 rlz_lib::Event event = rlz_lib::INVALID_EVENT;
129 if (!GetAccessPointFromName(event_string.substr(0, 2).c_str(), &point) ||
130 point == rlz_lib::NO_ACCESS_POINT) {
131 continue;
134 if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(),
135 &event) || event == rlz_lib::INVALID_EVENT) {
136 continue;
139 ReturnedEvent current_event = {point, event};
140 event_array->push_back(current_event);
141 } while (event_end_index >= 0);
144 // Event storage functions.
145 bool RecordStatefulEvent(rlz_lib::Product product, rlz_lib::AccessPoint point,
146 rlz_lib::Event event) {
147 rlz_lib::ScopedRlzValueStoreLock lock;
148 rlz_lib::RlzValueStore* store = lock.GetStore();
149 if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
150 return false;
152 // Write the new event to the value store.
153 const char* point_name = GetAccessPointName(point);
154 const char* event_name = GetEventName(event);
155 if (!point_name || !event_name)
156 return false;
158 if (!point_name[0] || !event_name[0])
159 return false;
161 std::string new_event_value;
162 base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
163 return store->AddStatefulEvent(product, new_event_value.c_str());
166 bool GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi,
167 size_t cgi_size,
168 rlz_lib::RlzValueStore* store) {
169 // Prepend the CGI param key to the buffer.
170 std::string cgi_arg;
171 base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable);
172 if (cgi_size <= cgi_arg.size())
173 return false;
175 size_t index;
176 for (index = 0; index < cgi_arg.size(); ++index)
177 cgi[index] = cgi_arg[index];
179 // Read stored events.
180 std::vector<std::string> events;
181 if (!store->ReadProductEvents(product, &events))
182 return false;
184 // Append the events to the buffer.
185 size_t num_values = 0;
187 for (num_values = 0; num_values < events.size(); ++num_values) {
188 cgi[index] = '\0';
190 int divider = num_values > 0 ? 1 : 0;
191 int size = cgi_size - (index + divider);
192 if (size <= 0)
193 return cgi_size >= (rlz_lib::kMaxCgiLength + 1);
195 strncpy(cgi + index + divider, events[num_values].c_str(), size);
196 if (divider)
197 cgi[index] = rlz_lib::kEventsCgiSeparator;
199 index += std::min((int)events[num_values].length(), size) + divider;
202 cgi[index] = '\0';
204 return num_values > 0;
207 } // namespace
209 namespace rlz_lib {
211 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
212 bool SetURLRequestContext(net::URLRequestContextGetter* context) {
213 return FinancialPing::SetURLRequestContext(context);
215 #endif
217 bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) {
218 if (!cgi || cgi_size <= 0) {
219 ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer");
220 return false;
223 cgi[0] = 0;
225 ScopedRlzValueStoreLock lock;
226 RlzValueStore* store = lock.GetStore();
227 if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
228 return false;
230 size_t size_local = std::min(
231 static_cast<size_t>(kMaxCgiLength + 1), cgi_size);
232 bool result = GetProductEventsAsCgiHelper(product, cgi, size_local, store);
234 if (!result) {
235 ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size");
236 cgi[0] = 0;
237 return false;
240 return true;
243 bool RecordProductEvent(Product product, AccessPoint point, Event event) {
244 ScopedRlzValueStoreLock lock;
245 RlzValueStore* store = lock.GetStore();
246 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
247 return false;
249 // Get this event's value.
250 const char* point_name = GetAccessPointName(point);
251 const char* event_name = GetEventName(event);
252 if (!point_name || !event_name)
253 return false;
255 if (!point_name[0] || !event_name[0])
256 return false;
258 std::string new_event_value;
259 base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
261 // Check whether this event is a stateful event. If so, don't record it.
262 if (store->IsStatefulEvent(product, new_event_value.c_str())) {
263 // For a stateful event we skip recording, this function is also
264 // considered successful.
265 return true;
268 // Write the new event to the value store.
269 return store->AddProductEvent(product, new_event_value.c_str());
272 bool ClearProductEvent(Product product, AccessPoint point, Event event) {
273 ScopedRlzValueStoreLock lock;
274 RlzValueStore* store = lock.GetStore();
275 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
276 return false;
278 // Get the event's value store value and delete it.
279 const char* point_name = GetAccessPointName(point);
280 const char* event_name = GetEventName(event);
281 if (!point_name || !event_name)
282 return false;
284 if (!point_name[0] || !event_name[0])
285 return false;
287 std::string event_value;
288 base::StringAppendF(&event_value, "%s%s", point_name, event_name);
289 return store->ClearProductEvent(product, event_value.c_str());
292 // RLZ storage functions.
294 bool GetAccessPointRlz(AccessPoint point, char* rlz, size_t rlz_size) {
295 if (!rlz || rlz_size <= 0) {
296 ASSERT_STRING("GetAccessPointRlz: Invalid buffer");
297 return false;
300 rlz[0] = 0;
302 ScopedRlzValueStoreLock lock;
303 RlzValueStore* store = lock.GetStore();
304 if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
305 return false;
307 if (!IsAccessPointSupported(point))
308 return false;
310 return store->ReadAccessPointRlz(point, rlz, rlz_size);
313 bool SetAccessPointRlz(AccessPoint point, const char* new_rlz) {
314 ScopedRlzValueStoreLock lock;
315 RlzValueStore* store = lock.GetStore();
316 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
317 return false;
319 if (!new_rlz) {
320 ASSERT_STRING("SetAccessPointRlz: Invalid buffer");
321 return false;
324 // Return false if the access point is not set to Google.
325 if (!IsAccessPointSupported(point)) {
326 ASSERT_STRING(("SetAccessPointRlz: "
327 "Cannot set RLZ for unsupported access point."));
328 return false;
331 // Verify the RLZ length.
332 size_t rlz_length = strlen(new_rlz);
333 if (rlz_length > kMaxRlzLength) {
334 ASSERT_STRING("SetAccessPointRlz: RLZ length is exceeds max allowed.");
335 return false;
338 char normalized_rlz[kMaxRlzLength + 1];
339 NormalizeRlz(new_rlz, normalized_rlz);
340 VERIFY(strlen(new_rlz) == rlz_length);
342 // Setting RLZ to empty == clearing.
343 if (normalized_rlz[0] == 0)
344 return store->ClearAccessPointRlz(point);
345 return store->WriteAccessPointRlz(point, normalized_rlz);
348 // Financial Server pinging functions.
350 bool FormFinancialPingRequest(Product product, const AccessPoint* access_points,
351 const char* product_signature,
352 const char* product_brand,
353 const char* product_id,
354 const char* product_lang,
355 bool exclude_machine_id,
356 char* request, size_t request_buffer_size) {
357 if (!request || request_buffer_size == 0)
358 return false;
360 request[0] = 0;
362 std::string request_string;
363 if (!FinancialPing::FormRequest(product, access_points, product_signature,
364 product_brand, product_id, product_lang,
365 exclude_machine_id, &request_string))
366 return false;
368 if (request_string.size() >= request_buffer_size)
369 return false;
371 strncpy(request, request_string.c_str(), request_buffer_size);
372 request[request_buffer_size - 1] = 0;
373 return true;
376 bool PingFinancialServer(Product product, const char* request, char* response,
377 size_t response_buffer_size) {
378 if (!response || response_buffer_size == 0)
379 return false;
380 response[0] = 0;
382 // Check if the time is right to ping.
383 if (!FinancialPing::IsPingTime(product, false))
384 return false;
386 // Send out the ping.
387 std::string response_string;
388 if (!FinancialPing::PingServer(request, &response_string))
389 return false;
391 if (response_string.size() >= response_buffer_size)
392 return false;
394 strncpy(response, response_string.c_str(), response_buffer_size);
395 response[response_buffer_size - 1] = 0;
396 return true;
399 bool IsPingResponseValid(const char* response, int* checksum_idx) {
400 if (!response || !response[0])
401 return false;
403 if (checksum_idx)
404 *checksum_idx = -1;
406 if (strlen(response) > kMaxPingResponseLength) {
407 ASSERT_STRING("IsPingResponseValid: response is too long to parse.");
408 return false;
411 // Find the checksum line.
412 std::string response_string(response);
414 std::string checksum_param("\ncrc32: ");
415 int calculated_crc;
416 int checksum_index = response_string.find(checksum_param);
417 if (checksum_index >= 0) {
418 // Calculate checksum of message preceeding checksum line.
419 // (+ 1 to include the \n)
420 std::string message(response_string.substr(0, checksum_index + 1));
421 if (!Crc32(message.c_str(), &calculated_crc))
422 return false;
423 } else {
424 checksum_param = "crc32: "; // Empty response case.
425 if (!base::StartsWith(response_string, checksum_param,
426 base::CompareCase::SENSITIVE))
427 return false;
429 checksum_index = 0;
430 if (!Crc32("", &calculated_crc))
431 return false;
434 // Find the checksum value on the response.
435 int checksum_end = response_string.find("\n", checksum_index + 1);
436 if (checksum_end < 0)
437 checksum_end = response_string.size();
439 int checksum_begin = checksum_index + checksum_param.size();
440 std::string checksum = response_string.substr(checksum_begin,
441 checksum_end - checksum_begin + 1);
442 base::TrimWhitespaceASCII(checksum, base::TRIM_ALL, &checksum);
444 if (checksum_idx)
445 *checksum_idx = checksum_index;
447 return calculated_crc == HexStringToInteger(checksum.c_str());
450 // Complex helpers built on top of other functions.
452 bool ParseFinancialPingResponse(Product product, const char* response) {
453 // Update the last ping time irrespective of success.
454 FinancialPing::UpdateLastPingTime(product);
455 // Parse the ping response - update RLZs, clear events.
456 return ParsePingResponse(product, response);
459 bool SendFinancialPing(Product product, const AccessPoint* access_points,
460 const char* product_signature,
461 const char* product_brand,
462 const char* product_id, const char* product_lang,
463 bool exclude_machine_id) {
464 return SendFinancialPing(product, access_points, product_signature,
465 product_brand, product_id, product_lang,
466 exclude_machine_id, false);
470 bool SendFinancialPing(Product product, const AccessPoint* access_points,
471 const char* product_signature,
472 const char* product_brand,
473 const char* product_id, const char* product_lang,
474 bool exclude_machine_id,
475 const bool skip_time_check) {
476 // Create the financial ping request.
477 std::string request;
478 if (!FinancialPing::FormRequest(product, access_points, product_signature,
479 product_brand, product_id, product_lang,
480 exclude_machine_id, &request))
481 return false;
483 // Check if the time is right to ping.
484 if (!FinancialPing::IsPingTime(product, skip_time_check))
485 return false;
487 // Send out the ping, update the last ping time irrespective of success.
488 FinancialPing::UpdateLastPingTime(product);
489 std::string response;
490 if (!FinancialPing::PingServer(request.c_str(), &response))
491 return false;
493 // Parse the ping response - update RLZs, clear events.
494 return ParsePingResponse(product, response.c_str());
497 // TODO: Use something like RSA to make sure the response is
498 // from a Google server.
499 bool ParsePingResponse(Product product, const char* response) {
500 rlz_lib::ScopedRlzValueStoreLock lock;
501 rlz_lib::RlzValueStore* store = lock.GetStore();
502 if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
503 return false;
505 std::string response_string(response);
506 int response_length = -1;
507 if (!IsPingResponseValid(response, &response_length))
508 return false;
510 if (0 == response_length)
511 return true; // Empty response - no parsing.
513 std::string events_variable;
514 std::string stateful_events_variable;
515 base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable);
516 base::SStringPrintf(&stateful_events_variable, "%s: ",
517 kStatefulEventsCgiVariable);
519 int rlz_cgi_length = strlen(kRlzCgiVariable);
521 // Split response lines. Expected response format is lines of the form:
522 // rlzW1: 1R1_____en__252
523 int line_end_index = -1;
524 do {
525 int line_begin = line_end_index + 1;
526 line_end_index = response_string.find("\n", line_begin);
528 int line_end = line_end_index;
529 if (line_end < 0)
530 line_end = response_length;
532 if (line_end <= line_begin)
533 continue; // Empty line.
535 std::string response_line;
536 response_line = response_string.substr(line_begin, line_end - line_begin);
538 if (base::StartsWith(response_line, kRlzCgiVariable,
539 base::CompareCase::SENSITIVE)) { // An RLZ.
540 int separator_index = -1;
541 if ((separator_index = response_line.find(": ")) < 0)
542 continue; // Not a valid key-value pair.
544 // Get the access point.
545 std::string point_name =
546 response_line.substr(3, separator_index - rlz_cgi_length);
547 AccessPoint point = NO_ACCESS_POINT;
548 if (!GetAccessPointFromName(point_name.c_str(), &point) ||
549 point == NO_ACCESS_POINT)
550 continue; // Not a valid access point.
552 // Get the new RLZ.
553 std::string rlz_value(response_line.substr(separator_index + 2));
554 base::TrimWhitespaceASCII(rlz_value, base::TRIM_LEADING, &rlz_value);
556 size_t rlz_length = rlz_value.find_first_of("\r\n ");
557 if (rlz_length == std::string::npos)
558 rlz_length = rlz_value.size();
560 if (rlz_length > kMaxRlzLength)
561 continue; // Too long.
563 if (IsAccessPointSupported(point))
564 SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str());
565 } else if (base::StartsWith(response_line, events_variable,
566 base::CompareCase::SENSITIVE)) {
567 // Clear events which server parsed.
568 std::vector<ReturnedEvent> event_array;
569 GetEventsFromResponseString(response_line, events_variable, &event_array);
570 for (size_t i = 0; i < event_array.size(); ++i) {
571 ClearProductEvent(product, event_array[i].access_point,
572 event_array[i].event_type);
574 } else if (base::StartsWith(response_line, stateful_events_variable,
575 base::CompareCase::SENSITIVE)) {
576 // Record any stateful events the server send over.
577 std::vector<ReturnedEvent> event_array;
578 GetEventsFromResponseString(response_line, stateful_events_variable,
579 &event_array);
580 for (size_t i = 0; i < event_array.size(); ++i) {
581 RecordStatefulEvent(product, event_array[i].access_point,
582 event_array[i].event_type);
585 } while (line_end_index >= 0);
587 #if defined(OS_WIN)
588 // Update the DCC in registry if needed.
589 SetMachineDealCodeFromPingResponse(response);
590 #endif
592 return true;
595 bool GetPingParams(Product product, const AccessPoint* access_points,
596 char* cgi, size_t cgi_size) {
597 if (!cgi || cgi_size <= 0) {
598 ASSERT_STRING("GetPingParams: Invalid buffer");
599 return false;
602 cgi[0] = 0;
604 if (!access_points) {
605 ASSERT_STRING("GetPingParams: access_points is NULL");
606 return false;
609 // Add the RLZ Exchange Protocol version.
610 std::string cgi_string(kProtocolCgiArgument);
612 // Copy the &rlz= over.
613 base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable);
616 // Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz()
617 // calls below.
618 ScopedRlzValueStoreLock lock;
619 RlzValueStore* store = lock.GetStore();
620 if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
621 return false;
622 bool first_rlz = true; // comma before every RLZ but the first.
623 for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
624 char rlz[kMaxRlzLength + 1];
625 if (GetAccessPointRlz(access_points[i], rlz, arraysize(rlz))) {
626 const char* access_point = GetAccessPointName(access_points[i]);
627 if (!access_point)
628 continue;
630 base::StringAppendF(&cgi_string, "%s%s%s%s",
631 first_rlz ? "" : kRlzCgiSeparator,
632 access_point, kRlzCgiIndicator, rlz);
633 first_rlz = false;
637 #if defined(OS_WIN)
638 // Report the DCC too if not empty. DCCs are windows-only.
639 char dcc[kMaxDccLength + 1];
640 dcc[0] = 0;
641 if (GetMachineDealCode(dcc, arraysize(dcc)) && dcc[0])
642 base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc);
643 #endif
646 if (cgi_string.size() >= cgi_size)
647 return false;
649 strncpy(cgi, cgi_string.c_str(), cgi_size);
650 cgi[cgi_size - 1] = 0;
652 return true;
655 } // namespace rlz_lib