| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/installer/util/user_experiment.h" | 5 #include "chrome/installer/util/user_experiment.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <sddl.h> | 8 #include <sddl.h> |
| 9 #include <wtsapi32.h> | 9 #include <wtsapi32.h> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 const wchar_t kToastExpUninstallGroup[] = L"04"; | 43 const wchar_t kToastExpUninstallGroup[] = L"04"; |
| 44 const wchar_t kToastExpTriesOkGroup[] = L"18"; | 44 const wchar_t kToastExpTriesOkGroup[] = L"18"; |
| 45 const wchar_t kToastExpTriesErrorGroup[] = L"28"; | 45 const wchar_t kToastExpTriesErrorGroup[] = L"28"; |
| 46 const wchar_t kToastActiveGroup[] = L"40"; | 46 const wchar_t kToastActiveGroup[] = L"40"; |
| 47 const wchar_t kToastUDDirFailure[] = L"40"; | 47 const wchar_t kToastUDDirFailure[] = L"40"; |
| 48 const wchar_t kToastExpBaseGroup[] = L"80"; | 48 const wchar_t kToastExpBaseGroup[] = L"80"; |
| 49 | 49 |
| 50 // Substitute the locale parameter in uninstall URL with whatever | 50 // Substitute the locale parameter in uninstall URL with whatever |
| 51 // Google Update tells us is the locale. In case we fail to find | 51 // Google Update tells us is the locale. In case we fail to find |
| 52 // the locale, we use US English. | 52 // the locale, we use US English. |
| 53 string16 LocalizeUrl(const wchar_t* url) { | 53 base::string16 LocalizeUrl(const wchar_t* url) { |
| 54 string16 language; | 54 base::string16 language; |
| 55 if (!GoogleUpdateSettings::GetLanguage(&language)) | 55 if (!GoogleUpdateSettings::GetLanguage(&language)) |
| 56 language = L"en-US"; // Default to US English. | 56 language = L"en-US"; // Default to US English. |
| 57 return ReplaceStringPlaceholders(url, language.c_str(), NULL); | 57 return ReplaceStringPlaceholders(url, language.c_str(), NULL); |
| 58 } | 58 } |
| 59 | 59 |
| 60 string16 GetWelcomeBackUrl() { | 60 base::string16 GetWelcomeBackUrl() { |
| 61 const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/" | 61 const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/" |
| 62 L"welcomeback-new.html"; | 62 L"welcomeback-new.html"; |
| 63 return LocalizeUrl(kWelcomeUrl); | 63 return LocalizeUrl(kWelcomeUrl); |
| 64 } | 64 } |
| 65 | 65 |
| 66 // Converts FILETIME to hours. FILETIME times are absolute times in | 66 // Converts FILETIME to hours. FILETIME times are absolute times in |
| 67 // 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464. | 67 // 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464. |
| 68 int FileTimeToHours(const FILETIME& time) { | 68 int FileTimeToHours(const FILETIME& time) { |
| 69 const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60; | 69 const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60; |
| 70 ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime}; | 70 ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime}; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 DWORD len = sizeof(buff); | 152 DWORD len = sizeof(buff); |
| 153 PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff); | 153 PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff); |
| 154 if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION, | 154 if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION, |
| 155 sd, len, &len)) { | 155 sd, len, &len)) { |
| 156 return false; | 156 return false; |
| 157 } | 157 } |
| 158 wchar_t* sddl = 0; | 158 wchar_t* sddl = 0; |
| 159 if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd, | 159 if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd, |
| 160 SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL)) | 160 SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL)) |
| 161 return false; | 161 return false; |
| 162 string16 new_sddl(sddl); | 162 base::string16 new_sddl(sddl); |
| 163 ::LocalFree(sddl); | 163 ::LocalFree(sddl); |
| 164 sd = NULL; | 164 sd = NULL; |
| 165 // See MSDN for the security descriptor definition language (SDDL) syntax, | 165 // See MSDN for the security descriptor definition language (SDDL) syntax, |
| 166 // in our case we add "A;" generic read 'GR' and generic execute 'GX' for | 166 // in our case we add "A;" generic read 'GR' and generic execute 'GX' for |
| 167 // the nt\authenticated_users 'AU' group, that becomes: | 167 // the nt\authenticated_users 'AU' group, that becomes: |
| 168 const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)"; | 168 const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)"; |
| 169 // We should check that there are no special ACES for the group we | 169 // We should check that there are no special ACES for the group we |
| 170 // are interested, which is nt\authenticated_users. | 170 // are interested, which is nt\authenticated_users. |
| 171 if (string16::npos != new_sddl.find(L";AU)")) | 171 if (base::string16::npos != new_sddl.find(L";AU)")) |
| 172 return false; | 172 return false; |
| 173 // Specific ACEs (not inherited) need to go to the front. It is ok if we | 173 // Specific ACEs (not inherited) need to go to the front. It is ok if we |
| 174 // are the very first one. | 174 // are the very first one. |
| 175 size_t pos_insert = new_sddl.find(L"("); | 175 size_t pos_insert = new_sddl.find(L"("); |
| 176 if (string16::npos == pos_insert) | 176 if (base::string16::npos == pos_insert) |
| 177 return false; | 177 return false; |
| 178 // All good, time to change the dacl. | 178 // All good, time to change the dacl. |
| 179 new_sddl.insert(pos_insert, kAllowACE); | 179 new_sddl.insert(pos_insert, kAllowACE); |
| 180 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(), | 180 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(), |
| 181 SDDL_REVISION_1, &sd, NULL)) | 181 SDDL_REVISION_1, &sd, NULL)) |
| 182 return false; | 182 return false; |
| 183 bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION, | 183 bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION, |
| 184 sd) == TRUE; | 184 sd) == TRUE; |
| 185 ::LocalFree(sd); | 185 ::LocalFree(sd); |
| 186 return rv; | 186 return rv; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 237 bool launched = base::LaunchProcess(*cmd_line, options, NULL); | 237 bool launched = base::LaunchProcess(*cmd_line, options, NULL); |
| 238 ::CloseHandle(user_token); | 238 ::CloseHandle(user_token); |
| 239 VLOG(1) << __FUNCTION__ << " result: " << launched; | 239 VLOG(1) << __FUNCTION__ << " result: " << launched; |
| 240 return launched; | 240 return launched; |
| 241 } | 241 } |
| 242 | 242 |
| 243 // A helper function that writes to HKLM if the handle was passed through the | 243 // A helper function that writes to HKLM if the handle was passed through the |
| 244 // command line, but HKCU otherwise. |experiment_group| is the value to write | 244 // command line, but HKCU otherwise. |experiment_group| is the value to write |
| 245 // and |last_write| is used when writing to HKLM to determine whether to close | 245 // and |last_write| is used when writing to HKLM to determine whether to close |
| 246 // the handle when done. | 246 // the handle when done. |
| 247 void SetClient(const string16& experiment_group, bool last_write) { | 247 void SetClient(const base::string16& experiment_group, bool last_write) { |
| 248 static int reg_key_handle = -1; | 248 static int reg_key_handle = -1; |
| 249 if (reg_key_handle == -1) { | 249 if (reg_key_handle == -1) { |
| 250 // If a specific Toast Results key handle (presumably to our HKLM key) was | 250 // If a specific Toast Results key handle (presumably to our HKLM key) was |
| 251 // passed in to the command line (such as for system level installs), we use | 251 // passed in to the command line (such as for system level installs), we use |
| 252 // it. Otherwise, we write to the key under HKCU. | 252 // it. Otherwise, we write to the key under HKCU. |
| 253 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); | 253 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); |
| 254 if (cmd_line.HasSwitch(switches::kToastResultsKey)) { | 254 if (cmd_line.HasSwitch(switches::kToastResultsKey)) { |
| 255 // Get the handle to the key under HKLM. | 255 // Get the handle to the key under HKLM. |
| 256 base::StringToInt( | 256 base::StringToInt( |
| 257 cmd_line.GetSwitchValueNative(switches::kToastResultsKey), | 257 cmd_line.GetSwitchValueNative(switches::kToastResultsKey), |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 323 L"DA", // Experiment is DAxx. | 323 L"DA", // Experiment is DAxx. |
| 324 // One single flavor. | 324 // One single flavor. |
| 325 { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault }, | 325 { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault }, |
| 326 { 0, 0 }, | 326 { 0, 0 }, |
| 327 { 0, 0 }, | 327 { 0, 0 }, |
| 328 { 0, 0 } | 328 { 0, 0 } |
| 329 } | 329 } |
| 330 } | 330 } |
| 331 }; | 331 }; |
| 332 | 332 |
| 333 string16 locale; | 333 base::string16 locale; |
| 334 GoogleUpdateSettings::GetLanguage(&locale); | 334 GoogleUpdateSettings::GetLanguage(&locale); |
| 335 if (locale.empty() || (locale == ASCIIToWide("en"))) | 335 if (locale.empty() || (locale == ASCIIToWide("en"))) |
| 336 locale = ASCIIToWide("en-US"); | 336 locale = ASCIIToWide("en-US"); |
| 337 | 337 |
| 338 string16 brand; | 338 base::string16 brand; |
| 339 if (!GoogleUpdateSettings::GetBrand(&brand)) | 339 if (!GoogleUpdateSettings::GetBrand(&brand)) |
| 340 brand = ASCIIToWide(""); // Could still be viable for catch-all rules. | 340 brand = ASCIIToWide(""); // Could still be viable for catch-all rules. |
| 341 | 341 |
| 342 for (int i = 0; i < arraysize(kExperiments); ++i) { | 342 for (int i = 0; i < arraysize(kExperiments); ++i) { |
| 343 if (kExperiments[i].locale != locale && | 343 if (kExperiments[i].locale != locale && |
| 344 kExperiments[i].locale != ASCIIToWide("*")) | 344 kExperiments[i].locale != ASCIIToWide("*")) |
| 345 continue; | 345 continue; |
| 346 | 346 |
| 347 std::vector<string16> brand_codes; | 347 std::vector<base::string16> brand_codes; |
| 348 base::SplitString(kExperiments[i].brands, L',', &brand_codes); | 348 base::SplitString(kExperiments[i].brands, L',', &brand_codes); |
| 349 if (brand_codes.empty()) | 349 if (brand_codes.empty()) |
| 350 return false; | 350 return false; |
| 351 for (std::vector<string16>::iterator it = brand_codes.begin(); | 351 for (std::vector<base::string16>::iterator it = brand_codes.begin(); |
| 352 it != brand_codes.end(); ++it) { | 352 it != brand_codes.end(); ++it) { |
| 353 if (*it != brand && *it != L"*") | 353 if (*it != brand && *it != L"*") |
| 354 continue; | 354 continue; |
| 355 // We have found our match. | 355 // We have found our match. |
| 356 const UserExperimentSpecs& match = kExperiments[i]; | 356 const UserExperimentSpecs& match = kExperiments[i]; |
| 357 // Find out how many flavors we have. Zero means no experiment. | 357 // Find out how many flavors we have. Zero means no experiment. |
| 358 int num_flavors = 0; | 358 int num_flavors = 0; |
| 359 while (match.flavors[num_flavors].heading_id) { ++num_flavors; } | 359 while (match.flavors[num_flavors].heading_id) { ++num_flavors; } |
| 360 if (!num_flavors) | 360 if (!num_flavors) |
| 361 return false; | 361 return false; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 } | 402 } |
| 403 | 403 |
| 404 // The |flavor| value ends up being processed by TryChromeDialogView to show | 404 // The |flavor| value ends up being processed by TryChromeDialogView to show |
| 405 // different experiments. | 405 // different experiments. |
| 406 ExperimentDetails experiment; | 406 ExperimentDetails experiment; |
| 407 if (!CreateExperimentDetails(-1, &experiment)) { | 407 if (!CreateExperimentDetails(-1, &experiment)) { |
| 408 VLOG(1) << "Failed to get experiment details."; | 408 VLOG(1) << "Failed to get experiment details."; |
| 409 return; | 409 return; |
| 410 } | 410 } |
| 411 int flavor = experiment.flavor; | 411 int flavor = experiment.flavor; |
| 412 string16 base_group = experiment.prefix; | 412 base::string16 base_group = experiment.prefix; |
| 413 | 413 |
| 414 string16 brand; | 414 base::string16 brand; |
| 415 if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) { | 415 if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) { |
| 416 // Testing only: the user automatically qualifies for the experiment. | 416 // Testing only: the user automatically qualifies for the experiment. |
| 417 VLOG(1) << "Experiment qualification bypass"; | 417 VLOG(1) << "Experiment qualification bypass"; |
| 418 } else { | 418 } else { |
| 419 // Check that the user was not already drafted in this experiment. | 419 // Check that the user was not already drafted in this experiment. |
| 420 string16 client; | 420 base::string16 client; |
| 421 GoogleUpdateSettings::GetClient(&client); | 421 GoogleUpdateSettings::GetClient(&client); |
| 422 if (client.size() > 2) { | 422 if (client.size() > 2) { |
| 423 if (base_group == client.substr(0, 2)) { | 423 if (base_group == client.substr(0, 2)) { |
| 424 VLOG(1) << "User already participated in this experiment"; | 424 VLOG(1) << "User already participated in this experiment"; |
| 425 return; | 425 return; |
| 426 } | 426 } |
| 427 } | 427 } |
| 428 // Check browser usage inactivity by the age of the last-write time of the | 428 // Check browser usage inactivity by the age of the last-write time of the |
| 429 // most recently-used chrome user data directory. | 429 // most recently-used chrome user data directory. |
| 430 std::vector<base::FilePath> user_data_dirs; | 430 std::vector<base::FilePath> user_data_dirs; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 cmd_line.AppendSwitchASCII(switches::kInactiveUserToast, | 476 cmd_line.AppendSwitchASCII(switches::kInactiveUserToast, |
| 477 base::IntToString(flavor)); | 477 base::IntToString(flavor)); |
| 478 cmd_line.AppendSwitchASCII(switches::kExperimentGroup, | 478 cmd_line.AppendSwitchASCII(switches::kExperimentGroup, |
| 479 WideToASCII(base_group)); | 479 WideToASCII(base_group)); |
| 480 LaunchSetup(&cmd_line, system_level); | 480 LaunchSetup(&cmd_line, system_level); |
| 481 } | 481 } |
| 482 | 482 |
| 483 // User qualifies for the experiment. To test, use --try-chrome-again=|flavor| | 483 // User qualifies for the experiment. To test, use --try-chrome-again=|flavor| |
| 484 // as a parameter to chrome.exe. | 484 // as a parameter to chrome.exe. |
| 485 void InactiveUserToastExperiment(int flavor, | 485 void InactiveUserToastExperiment(int flavor, |
| 486 const string16& experiment_group, | 486 const base::string16& experiment_group, |
| 487 const Product& product, | 487 const Product& product, |
| 488 const base::FilePath& application_path) { | 488 const base::FilePath& application_path) { |
| 489 // Add the 'welcome back' url for chrome to show. | 489 // Add the 'welcome back' url for chrome to show. |
| 490 CommandLine options(CommandLine::NO_PROGRAM); | 490 CommandLine options(CommandLine::NO_PROGRAM); |
| 491 options.AppendSwitchNative(::switches::kTryChromeAgain, | 491 options.AppendSwitchNative(::switches::kTryChromeAgain, |
| 492 base::IntToString16(flavor)); | 492 base::IntToString16(flavor)); |
| 493 // Prepend the url with a space. | 493 // Prepend the url with a space. |
| 494 string16 url(GetWelcomeBackUrl()); | 494 base::string16 url(GetWelcomeBackUrl()); |
| 495 options.AppendArg("--"); | 495 options.AppendArg("--"); |
| 496 options.AppendArgNative(url); | 496 options.AppendArgNative(url); |
| 497 // The command line should now have the url added as: | 497 // The command line should now have the url added as: |
| 498 // "chrome.exe -- <url>" | 498 // "chrome.exe -- <url>" |
| 499 DCHECK_NE(string16::npos, | 499 DCHECK_NE(base::string16::npos, |
| 500 options.GetCommandLineString().find(L" -- " + url)); | 500 options.GetCommandLineString().find(L" -- " + url)); |
| 501 | 501 |
| 502 // Launch chrome now. It will show the toast UI. | 502 // Launch chrome now. It will show the toast UI. |
| 503 int32 exit_code = 0; | 503 int32 exit_code = 0; |
| 504 if (!product.LaunchChromeAndWait(application_path, options, &exit_code)) | 504 if (!product.LaunchChromeAndWait(application_path, options, &exit_code)) |
| 505 return; | 505 return; |
| 506 | 506 |
| 507 // The chrome process has exited, figure out what happened. | 507 // The chrome process has exited, figure out what happened. |
| 508 const wchar_t* outcome = NULL; | 508 const wchar_t* outcome = NULL; |
| 509 switch (exit_code) { | 509 switch (exit_code) { |
| 510 case content::RESULT_CODE_NORMAL_EXIT: | 510 case content::RESULT_CODE_NORMAL_EXIT: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 529 // running. | 529 // running. |
| 530 bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch( | 530 bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch( |
| 531 switches::kSystemLevelToast); | 531 switches::kSystemLevelToast); |
| 532 | 532 |
| 533 CommandLine cmd(InstallUtil::GetChromeUninstallCmd( | 533 CommandLine cmd(InstallUtil::GetChromeUninstallCmd( |
| 534 system_level_toast, product.distribution()->GetType())); | 534 system_level_toast, product.distribution()->GetType())); |
| 535 base::LaunchProcess(cmd, base::LaunchOptions(), NULL); | 535 base::LaunchProcess(cmd, base::LaunchOptions(), NULL); |
| 536 } | 536 } |
| 537 | 537 |
| 538 } // namespace installer | 538 } // namespace installer |
| OLD | NEW |