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 8 #include "rlz/lib/rlz_lib.h" 9 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" 18 19 namespace { 20 21 // Event information returned from ping response. 22 struct ReturnedEvent { 23 rlz_lib::AccessPoint access_point; 24 rlz_lib::Event event_type; 25 }; 26 27 // Helper functions 28 29 bool IsAccessPointSupported(rlz_lib::AccessPoint point) { 30 switch (point) { 31 case rlz_lib::NO_ACCESS_POINT: 32 case rlz_lib::LAST_ACCESS_POINT: 33 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; 39 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. 51 52 default: 53 return true; 54 } 55 } 56 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 (IsAsciiAlpha(ch) || IsAsciiDigit(ch)) 62 return true; 63 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 80 return false; 81 } 82 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] = '.'; 94 } 95 } 96 97 normalized_rlz[index] = 0; 98 } 99 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); 107 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); 112 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; 121 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; 126 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; 132 } 133 134 if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(), 135 &event) || event == rlz_lib::INVALID_EVENT) { 136 continue; 137 } 138 139 ReturnedEvent current_event = {point, event}; 140 event_array->push_back(current_event); 141 } while (event_end_index >= 0); 142 } 143 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; 151 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; 157 158 if (!point_name[0] || !event_name[0]) 159 return false; 160 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()); 164 } 165 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; 174 175 size_t index; 176 for (index = 0; index < cgi_arg.size(); ++index) 177 cgi[index] = cgi_arg[index]; 178 179 // Read stored events. 180 std::vector<std::string> events; 181 if (!store->ReadProductEvents(product, &events)) 182 return false; 183 184 // Append the events to the buffer. 185 size_t num_values = 0; 186 187 for (num_values = 0; num_values < events.size(); ++num_values) { 188 cgi[index] = '\0'; 189 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); 194 195 strncpy(cgi + index + divider, events[num_values].c_str(), size); 196 if (divider) 197 cgi[index] = rlz_lib::kEventsCgiSeparator; 198 199 index += std::min((int)events[num_values].length(), size) + divider; 200 } 201 202 cgi[index] = '\0'; 203 204 return num_values > 0; 205 } 206 207 } // namespace 208 209 namespace rlz_lib { 210 211 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 212 bool SetURLRequestContext(net::URLRequestContextGetter* context) { 213 return FinancialPing::SetURLRequestContext(context); 214 } 215 #endif 216 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; 221 } 222 223 cgi[0] = 0; 224 225 ScopedRlzValueStoreLock lock; 226 RlzValueStore* store = lock.GetStore(); 227 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 228 return false; 229 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); 233 234 if (!result) { 235 ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size"); 236 cgi[0] = 0; 237 return false; 238 } 239 240 return true; 241 } 242 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; 248 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; 254 255 if (!point_name[0] || !event_name[0]) 256 return false; 257 258 std::string new_event_value; 259 base::StringAppendF(&new_event_value, "%s%s", point_name, event_name); 260 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; 266 } 267 268 // Write the new event to the value store. 269 return store->AddProductEvent(product, new_event_value.c_str()); 270 } 271 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; 277 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; 283 284 if (!point_name[0] || !event_name[0]) 285 return false; 286 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()); 290 } 291 292 // RLZ storage functions. 293 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; 298 } 299 300 rlz[0] = 0; 301 302 ScopedRlzValueStoreLock lock; 303 RlzValueStore* store = lock.GetStore(); 304 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 305 return false; 306 307 if (!IsAccessPointSupported(point)) 308 return false; 309 310 return store->ReadAccessPointRlz(point, rlz, rlz_size); 311 } 312 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; 318 319 if (!new_rlz) { 320 ASSERT_STRING("SetAccessPointRlz: Invalid buffer"); 321 return false; 322 } 323 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; 329 } 330 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; 336 } 337 338 char normalized_rlz[kMaxRlzLength + 1]; 339 NormalizeRlz(new_rlz, normalized_rlz); 340 VERIFY(strlen(new_rlz) == rlz_length); 341 342 // Setting RLZ to empty == clearing. 343 if (normalized_rlz[0] == 0) 344 return store->ClearAccessPointRlz(point); 345 return store->WriteAccessPointRlz(point, normalized_rlz); 346 } 347 348 // Financial Server pinging functions. 349 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; 359 360 request[0] = 0; 361 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; 367 368 if (request_string.size() >= request_buffer_size) 369 return false; 370 371 strncpy(request, request_string.c_str(), request_buffer_size); 372 request[request_buffer_size - 1] = 0; 373 return true; 374 } 375 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; 381 382 // Check if the time is right to ping. 383 if (!FinancialPing::IsPingTime(product, false)) 384 return false; 385 386 // Send out the ping. 387 std::string response_string; 388 if (!FinancialPing::PingServer(request, &response_string)) 389 return false; 390 391 if (response_string.size() >= response_buffer_size) 392 return false; 393 394 strncpy(response, response_string.c_str(), response_buffer_size); 395 response[response_buffer_size - 1] = 0; 396 return true; 397 } 398 399 bool IsPingResponseValid(const char* response, int* checksum_idx) { 400 if (!response || !response[0]) 401 return false; 402 403 if (checksum_idx) 404 *checksum_idx = -1; 405 406 if (strlen(response) > kMaxPingResponseLength) { 407 ASSERT_STRING("IsPingResponseValid: response is too long to parse."); 408 return false; 409 } 410 411 // Find the checksum line. 412 std::string response_string(response); 413 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 (!StartsWithASCII(response_string, checksum_param, true)) 426 return false; 427 428 checksum_index = 0; 429 if (!Crc32("", &calculated_crc)) 430 return false; 431 } 432 433 // Find the checksum value on the response. 434 int checksum_end = response_string.find("\n", checksum_index + 1); 435 if (checksum_end < 0) 436 checksum_end = response_string.size(); 437 438 int checksum_begin = checksum_index + checksum_param.size(); 439 std::string checksum = response_string.substr(checksum_begin, 440 checksum_end - checksum_begin + 1); 441 base::TrimWhitespaceASCII(checksum, base::TRIM_ALL, &checksum); 442 443 if (checksum_idx) 444 *checksum_idx = checksum_index; 445 446 return calculated_crc == HexStringToInteger(checksum.c_str()); 447 } 448 449 // Complex helpers built on top of other functions. 450 451 bool ParseFinancialPingResponse(Product product, const char* response) { 452 // Update the last ping time irrespective of success. 453 FinancialPing::UpdateLastPingTime(product); 454 // Parse the ping response - update RLZs, clear events. 455 return ParsePingResponse(product, response); 456 } 457 458 bool SendFinancialPing(Product product, const AccessPoint* access_points, 459 const char* product_signature, 460 const char* product_brand, 461 const char* product_id, const char* product_lang, 462 bool exclude_machine_id) { 463 return SendFinancialPing(product, access_points, product_signature, 464 product_brand, product_id, product_lang, 465 exclude_machine_id, false); 466 } 467 468 469 bool SendFinancialPing(Product product, const AccessPoint* access_points, 470 const char* product_signature, 471 const char* product_brand, 472 const char* product_id, const char* product_lang, 473 bool exclude_machine_id, 474 const bool skip_time_check) { 475 // Create the financial ping request. 476 std::string request; 477 if (!FinancialPing::FormRequest(product, access_points, product_signature, 478 product_brand, product_id, product_lang, 479 exclude_machine_id, &request)) 480 return false; 481 482 // Check if the time is right to ping. 483 if (!FinancialPing::IsPingTime(product, skip_time_check)) 484 return false; 485 486 // Send out the ping, update the last ping time irrespective of success. 487 FinancialPing::UpdateLastPingTime(product); 488 std::string response; 489 if (!FinancialPing::PingServer(request.c_str(), &response)) 490 return false; 491 492 // Parse the ping response - update RLZs, clear events. 493 return ParsePingResponse(product, response.c_str()); 494 } 495 496 // TODO: Use something like RSA to make sure the response is 497 // from a Google server. 498 bool ParsePingResponse(Product product, const char* response) { 499 rlz_lib::ScopedRlzValueStoreLock lock; 500 rlz_lib::RlzValueStore* store = lock.GetStore(); 501 if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)) 502 return false; 503 504 std::string response_string(response); 505 int response_length = -1; 506 if (!IsPingResponseValid(response, &response_length)) 507 return false; 508 509 if (0 == response_length) 510 return true; // Empty response - no parsing. 511 512 std::string events_variable; 513 std::string stateful_events_variable; 514 base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable); 515 base::SStringPrintf(&stateful_events_variable, "%s: ", 516 kStatefulEventsCgiVariable); 517 518 int rlz_cgi_length = strlen(kRlzCgiVariable); 519 520 // Split response lines. Expected response format is lines of the form: 521 // rlzW1: 1R1_____en__252 522 int line_end_index = -1; 523 do { 524 int line_begin = line_end_index + 1; 525 line_end_index = response_string.find("\n", line_begin); 526 527 int line_end = line_end_index; 528 if (line_end < 0) 529 line_end = response_length; 530 531 if (line_end <= line_begin) 532 continue; // Empty line. 533 534 std::string response_line; 535 response_line = response_string.substr(line_begin, line_end - line_begin); 536 537 if (StartsWithASCII(response_line, kRlzCgiVariable, true)) { // An RLZ. 538 int separator_index = -1; 539 if ((separator_index = response_line.find(": ")) < 0) 540 continue; // Not a valid key-value pair. 541 542 // Get the access point. 543 std::string point_name = 544 response_line.substr(3, separator_index - rlz_cgi_length); 545 AccessPoint point = NO_ACCESS_POINT; 546 if (!GetAccessPointFromName(point_name.c_str(), &point) || 547 point == NO_ACCESS_POINT) 548 continue; // Not a valid access point. 549 550 // Get the new RLZ. 551 std::string rlz_value(response_line.substr(separator_index + 2)); 552 base::TrimWhitespaceASCII(rlz_value, base::TRIM_LEADING, &rlz_value); 553 554 size_t rlz_length = rlz_value.find_first_of("\r\n "); 555 if (rlz_length == std::string::npos) 556 rlz_length = rlz_value.size(); 557 558 if (rlz_length > kMaxRlzLength) 559 continue; // Too long. 560 561 if (IsAccessPointSupported(point)) 562 SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str()); 563 } else if (StartsWithASCII(response_line, events_variable, true)) { 564 // Clear events which server parsed. 565 std::vector<ReturnedEvent> event_array; 566 GetEventsFromResponseString(response_line, events_variable, &event_array); 567 for (size_t i = 0; i < event_array.size(); ++i) { 568 ClearProductEvent(product, event_array[i].access_point, 569 event_array[i].event_type); 570 } 571 } else if (StartsWithASCII(response_line, stateful_events_variable, true)) { 572 // Record any stateful events the server send over. 573 std::vector<ReturnedEvent> event_array; 574 GetEventsFromResponseString(response_line, stateful_events_variable, 575 &event_array); 576 for (size_t i = 0; i < event_array.size(); ++i) { 577 RecordStatefulEvent(product, event_array[i].access_point, 578 event_array[i].event_type); 579 } 580 } 581 } while (line_end_index >= 0); 582 583 #if defined(OS_WIN) 584 // Update the DCC in registry if needed. 585 SetMachineDealCodeFromPingResponse(response); 586 #endif 587 588 return true; 589 } 590 591 bool GetPingParams(Product product, const AccessPoint* access_points, 592 char* cgi, size_t cgi_size) { 593 if (!cgi || cgi_size <= 0) { 594 ASSERT_STRING("GetPingParams: Invalid buffer"); 595 return false; 596 } 597 598 cgi[0] = 0; 599 600 if (!access_points) { 601 ASSERT_STRING("GetPingParams: access_points is NULL"); 602 return false; 603 } 604 605 // Add the RLZ Exchange Protocol version. 606 std::string cgi_string(kProtocolCgiArgument); 607 608 // Copy the &rlz= over. 609 base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable); 610 611 { 612 // Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz() 613 // calls below. 614 ScopedRlzValueStoreLock lock; 615 RlzValueStore* store = lock.GetStore(); 616 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 617 return false; 618 bool first_rlz = true; // comma before every RLZ but the first. 619 for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) { 620 char rlz[kMaxRlzLength + 1]; 621 if (GetAccessPointRlz(access_points[i], rlz, arraysize(rlz))) { 622 const char* access_point = GetAccessPointName(access_points[i]); 623 if (!access_point) 624 continue; 625 626 base::StringAppendF(&cgi_string, "%s%s%s%s", 627 first_rlz ? "" : kRlzCgiSeparator, 628 access_point, kRlzCgiIndicator, rlz); 629 first_rlz = false; 630 } 631 } 632 633 #if defined(OS_WIN) 634 // Report the DCC too if not empty. DCCs are windows-only. 635 char dcc[kMaxDccLength + 1]; 636 dcc[0] = 0; 637 if (GetMachineDealCode(dcc, arraysize(dcc)) && dcc[0]) 638 base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc); 639 #endif 640 } 641 642 if (cgi_string.size() >= cgi_size) 643 return false; 644 645 strncpy(cgi, cgi_string.c_str(), cgi_size); 646 cgi[cgi_size - 1] = 0; 647 648 return true; 649 } 650 651 } // namespace rlz_lib 652