Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 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 #include "chrome/browser/enumerate_modules_model_win.h" | |
| 6 | |
| 7 #include <Tlhelp32.h> | |
| 8 #include <wintrust.h> | |
| 9 | |
| 10 #include "app/l10n_util.h" | |
| 11 #include "app/win_util.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/environment.h" | |
| 14 #include "base/file_path.h" | |
| 15 #include "base/file_version_info_win.h" | |
| 16 #include "base/scoped_handle.h" | |
| 17 #include "base/sha2.h" | |
| 18 #include "base/string_number_conversions.h" | |
| 19 #include "base/string_util.h" | |
| 20 #include "base/utf_string_conversions.h" | |
| 21 #include "base/values.h" | |
| 22 #include "base/version.h" | |
| 23 #include "chrome/browser/net/service_providers_win.h" | |
| 24 #include "chrome/common/chrome_constants.h" | |
| 25 #include "chrome/common/chrome_switches.h" | |
| 26 #include "chrome/common/notification_service.h" | |
| 27 #include "grit/generated_resources.h" | |
| 28 | |
| 29 // The period of time (in milliseconds) to wait until checking to see if any | |
| 30 // incompatible modules exist. | |
| 31 static const int kModuleCheckDelayMs = 60 * 1000; | |
| 32 | |
| 33 // A sort method that sorts by ModuleType ordinal (loaded module at the top), | |
| 34 // then by full name (including path). | |
| 35 static bool ModuleSort(const ModuleEnumerator::Module& a, | |
| 36 const ModuleEnumerator::Module& b) { | |
| 37 if (a.type != b.type) | |
| 38 return a.type < b.type; | |
| 39 if (a.location == b.location) | |
| 40 return a.name < b.name; | |
| 41 | |
| 42 return a.location < b.location; | |
| 43 } | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 47 // Used to protect the LoadedModuleVector which is accessed | |
| 48 // from both the UI thread and the FILE thread. | |
| 49 Lock* lock = NULL; | |
| 50 | |
| 51 } | |
| 52 | |
| 53 // The browser process module blacklist. This lists all modules that are known | |
| 54 // to cause compatibility issues within the browser process. When adding to this | |
| 55 // list, make sure that all paths are lower-case, in long pathname form, end | |
| 56 // with a slash and use environments variables (or just look at one of the | |
| 57 // comments below and keep it consistent with that). When adding an entry with | |
| 58 // an environment variable not currently used in the list below, make sure to | |
| 59 // update the list in PreparePathMappings. Filename, Description/Signer, and | |
| 60 // Location must be entered as hashes (see GenerateHash). Filename is mandatory. | |
| 61 // Entries without any Description, Signer info, or Location will never be | |
| 62 // marked as confirmed bad (only as suspicious). | |
| 63 const ModuleEnumerator::BlacklistEntry ModuleEnumerator::kModuleBlacklist[] = { | |
| 64 // Test DLLs, to demonstrate the feature. Will be removed soon. | |
| 65 { // apphelp.dll, "%systemroot%\\system32\\" | |
| 66 "f5fda581", "23d01d5b", "", "", "", NONE | |
| 67 }, { // rsaenh.dll, "%systemroot%\\system32\\", "Microsoft Windows" | |
| 68 "6af212cb", "23d01d5b", "7b47bf79", "", "", | |
| 69 static_cast<RecommendedAction>(UPDATE | DISABLE | SEE_LINK) | |
| 70 }, | |
| 71 | |
| 72 // NOTE: Please keep this list sorted by dll name, then location. | |
| 73 | |
| 74 // foldersizecolumn.dll. | |
| 75 {"5ec91bd7", "", "", "", "", NONE}, | |
| 76 | |
| 77 // idmmbc.dll, "%programfiles%\\internet download manager\\", "Tonec Inc.". | |
| 78 // See: http://crbug.com/26892/. | |
| 79 {"b8dce5c3", "94541bf5", "d33ad640", "", "", NONE}, | |
| 80 | |
| 81 // imon.dll. See: http://crbug.com/21715. | |
| 82 {"8f42f22e", "", "", "", "", NONE}, | |
| 83 | |
| 84 // is3lsp.dll. See: http://crbug.com/26892. | |
| 85 {"7ffbdce9", "", "", "", "", NONE}, | |
| 86 | |
| 87 // nvlsp.dll. See: http://crbug.com/22083. | |
| 88 {"37f907e2", "", "", "", "", NONE}, | |
| 89 | |
| 90 // nvshell.dll. See: http://crbug.com/3269. | |
| 91 {"9290318f", "", "", "", "", NONE}, | |
| 92 | |
| 93 // securenet.dll. See: http://crbug.com/5165. | |
| 94 {"9b266e1c", "", "", "", "", NONE}, | |
| 95 | |
| 96 // sgprxy.dll. | |
| 97 {"005965ea", "", "", "", "", NONE}, | |
| 98 | |
| 99 // vaproxyd.dll. See: http://crbug.com/42445. | |
| 100 {"0a1c7f81", "", "", "", "", NONE}, | |
| 101 | |
| 102 // vlsp.dll. See: http://crbug.com/22826. | |
| 103 {"2e4eb93d", "", "", "", "", NONE}, | |
| 104 }; | |
| 105 | |
| 106 // Generates an 8 digit hash from the input given. | |
| 107 static void GenerateHash(const std::string& input, std::string* output) { | |
| 108 if (input.empty()) { | |
| 109 *output = ""; | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 uint8 hash[4]; | |
| 114 base::SHA256HashString(input, hash, sizeof(hash)); | |
| 115 *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); | |
| 116 } | |
| 117 | |
| 118 // ----------------------------------------------------------------------------- | |
| 119 | |
| 120 // static | |
| 121 void ModuleEnumerator::NormalizeModule(Module* module) { | |
| 122 string16 path = module->location; | |
| 123 if (!win_util::ConvertToLongPath(path, &module->location)) | |
| 124 module->location = path; | |
| 125 | |
| 126 module->location = l10n_util::ToLower(module->location); | |
| 127 | |
| 128 // Location contains the filename, so the last slash is where the path | |
| 129 // ends. | |
| 130 size_t last_slash = module->location.find_last_of(L"\\"); | |
| 131 if (last_slash != string16::npos) { | |
| 132 module->name = module->location.substr(last_slash + 1); | |
| 133 module->location = module->location.substr(0, last_slash + 1); | |
| 134 } else { | |
| 135 module->name = module->location; | |
| 136 module->location.clear(); | |
| 137 } | |
| 138 | |
| 139 // Some version strings have things like (win7_rtm.090713-1255) appended | |
| 140 // to them. Remove that. | |
| 141 size_t first_space = module->version.find_first_of(L" "); | |
| 142 if (first_space != string16::npos) | |
| 143 module->version = module->version.substr(0, first_space); | |
| 144 | |
| 145 module->normalized = true; | |
| 146 } | |
| 147 | |
| 148 // static | |
| 149 ModuleEnumerator::ModuleStatus ModuleEnumerator::Match( | |
| 150 const ModuleEnumerator::Module& module, | |
| 151 const ModuleEnumerator::BlacklistEntry& blacklisted) { | |
| 152 // All modules must be normalized before matching against blacklist. | |
| 153 DCHECK(module.normalized); | |
| 154 // Filename is mandatory and version should not contain spaces. | |
| 155 DCHECK(strlen(blacklisted.filename) > 0); | |
| 156 DCHECK(!strstr(blacklisted.version_from, " ")); | |
| 157 DCHECK(!strstr(blacklisted.version_to, " ")); | |
| 158 | |
| 159 std::string filename_hash, location_hash; | |
| 160 GenerateHash(WideToUTF8(module.name), &filename_hash); | |
| 161 GenerateHash(WideToUTF8(module.location), &location_hash); | |
| 162 | |
| 163 // Filenames are mandatory. Location is mandatory if given. | |
| 164 if (filename_hash == blacklisted.filename && | |
| 165 (std::string(blacklisted.location).empty() || | |
| 166 location_hash == blacklisted.location)) { | |
| 167 // We have a name match against the blacklist (and possibly location match | |
| 168 // also), so check version. | |
| 169 scoped_ptr<Version> module_version( | |
| 170 Version::GetVersionFromString(module.version)); | |
| 171 scoped_ptr<Version> version_min( | |
| 172 Version::GetVersionFromString(blacklisted.version_from)); | |
| 173 scoped_ptr<Version> version_max( | |
| 174 Version::GetVersionFromString(blacklisted.version_to)); | |
| 175 bool version_ok = !version_min.get() && !version_max.get(); | |
| 176 if (!version_ok) { | |
| 177 bool too_low = version_min.get() && | |
| 178 (!module_version.get() || | |
| 179 module_version->CompareTo(*version_min.get()) < 0); | |
| 180 bool too_high = version_max.get() && | |
| 181 (!module_version.get() || | |
| 182 module_version->CompareTo(*version_max.get()) > 0); | |
| 183 version_ok = !too_low && !too_high; | |
| 184 } | |
| 185 | |
| 186 if (version_ok) { | |
| 187 // At this point, the names match and there is no version specified | |
| 188 // or the versions also match. | |
| 189 | |
| 190 std::string desc_or_signer(blacklisted.desc_or_signer); | |
| 191 std::string signer_hash, description_hash; | |
| 192 GenerateHash(WideToUTF8(module.digital_signer), &signer_hash); | |
| 193 GenerateHash(WideToUTF8(module.description), &description_hash); | |
| 194 | |
| 195 // If signatures match, we have a winner. | |
| 196 if (!desc_or_signer.empty() && signer_hash == desc_or_signer) | |
| 197 return CONFIRMED_BAD; | |
| 198 | |
| 199 // If description matches and location, then we also have a match. | |
| 200 if (!desc_or_signer.empty() && description_hash == desc_or_signer && | |
| 201 !location_hash.empty() && location_hash == blacklisted.location) { | |
| 202 return CONFIRMED_BAD; | |
| 203 } | |
| 204 | |
| 205 // We are not sure, but it is likely bad. | |
| 206 return SUSPECTED_BAD; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 return NOT_MATCHED; | |
| 211 } | |
| 212 | |
| 213 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) | |
| 214 : observer_(observer) { | |
|
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
| |
| 215 CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_)); | |
| 216 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 217 } | |
| 218 | |
| 219 ModuleEnumerator::~ModuleEnumerator() { | |
| 220 } | |
| 221 | |
| 222 void ModuleEnumerator::ScanNow(ModulesVector* list) { | |
| 223 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 224 enumerated_modules_ = list; | |
| 225 BrowserThread::PostTask( | |
| 226 BrowserThread::FILE, FROM_HERE, | |
| 227 NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread)); | |
| 228 } | |
| 229 | |
| 230 void ModuleEnumerator::ScanOnFileThread() { | |
| 231 enumerated_modules_->clear(); | |
| 232 | |
| 233 // Make sure the path mapping vector is setup so we can collapse paths. | |
| 234 PreparePathMappings(); | |
| 235 | |
| 236 // Get all modules in the current process. | |
| 237 ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | |
| 238 ::GetCurrentProcessId())); | |
| 239 if (!snap.Get()) | |
| 240 return; | |
| 241 | |
| 242 // Walk the module list. | |
| 243 MODULEENTRY32 module = { sizeof(module) }; | |
| 244 if (!::Module32First(snap.Get(), &module)) | |
| 245 return; | |
| 246 | |
| 247 do { | |
| 248 // It would be weird to present chrome.exe as a loaded module. | |
| 249 if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) | |
| 250 continue; | |
| 251 | |
| 252 Module entry; | |
| 253 entry.type = LOADED_MODULE; | |
| 254 entry.status = NOT_MATCHED; | |
| 255 entry.normalized = false; | |
| 256 entry.location = module.szExePath; | |
| 257 entry.digital_signer = | |
| 258 GetSubjectNameFromDigitalSignature(FilePath(entry.location)); | |
| 259 entry.recommended_action = NONE; | |
| 260 scoped_ptr<FileVersionInfo> version_info( | |
| 261 FileVersionInfo::CreateFileVersionInfo(FilePath(entry.location))); | |
| 262 if (version_info.get()) { | |
| 263 FileVersionInfoWin* version_info_win = | |
| 264 static_cast<FileVersionInfoWin*>(version_info.get()); | |
| 265 | |
| 266 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); | |
| 267 if (fixed_file_info) { | |
| 268 entry.description = version_info_win->file_description(); | |
| 269 entry.version = version_info_win->file_version(); | |
| 270 entry.product_name = version_info_win->product_name(); | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 NormalizeModule(&entry); | |
| 275 CollapsePath(&entry); | |
| 276 enumerated_modules_->push_back(entry); | |
| 277 } while (::Module32Next(snap.Get(), &module)); | |
| 278 | |
| 279 // Add to this list the Winsock LSP DLLs. | |
| 280 WinsockLayeredServiceProviderList layered_providers; | |
| 281 GetWinsockLayeredServiceProviders(&layered_providers); | |
| 282 for (size_t i = 0; i < layered_providers.size(); ++i) { | |
| 283 Module entry; | |
| 284 entry.type = WINSOCK_MODULE_REGISTRATION; | |
| 285 entry.status = NOT_MATCHED; | |
| 286 entry.normalized = false; | |
| 287 entry.location = layered_providers[i].path; | |
| 288 entry.description = layered_providers[i].name; | |
| 289 entry.recommended_action = NONE; | |
| 290 | |
| 291 wchar_t expanded[MAX_PATH]; | |
| 292 DWORD size = ExpandEnvironmentStrings( | |
| 293 entry.location.c_str(), expanded, MAX_PATH); | |
| 294 if (size != 0 && size <= MAX_PATH) { | |
| 295 entry.digital_signer = | |
| 296 GetSubjectNameFromDigitalSignature(FilePath(expanded)); | |
| 297 } | |
| 298 entry.version = base::IntToString16(layered_providers[i].version); | |
| 299 | |
| 300 // Paths have already been collapsed. | |
| 301 NormalizeModule(&entry); | |
| 302 enumerated_modules_->push_back(entry); | |
| 303 } | |
| 304 | |
| 305 MatchAgainstBlacklist(); | |
| 306 | |
| 307 std::sort(enumerated_modules_->begin(), | |
| 308 enumerated_modules_->end(), ModuleSort); | |
| 309 | |
| 310 // Send a reply back on the UI thread. | |
| 311 BrowserThread::PostTask( | |
| 312 callback_thread_id_, FROM_HERE, | |
| 313 NewRunnableMethod(this, &ModuleEnumerator::ReportBack)); | |
| 314 } | |
| 315 | |
| 316 void ModuleEnumerator::PreparePathMappings() { | |
| 317 path_mapping_.clear(); | |
| 318 | |
| 319 scoped_ptr<base::Environment> environment(base::Environment::Create()); | |
| 320 std::vector<string16> env_vars; | |
| 321 env_vars.push_back(L"LOCALAPPDATA"); | |
| 322 env_vars.push_back(L"ProgramFiles"); | |
| 323 env_vars.push_back(L"USERPROFILE"); | |
| 324 env_vars.push_back(L"SystemRoot"); | |
| 325 env_vars.push_back(L"TEMP"); | |
| 326 for (std::vector<string16>::const_iterator variable = env_vars.begin(); | |
| 327 variable != env_vars.end(); ++variable) { | |
| 328 std::string path; | |
| 329 if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { | |
| 330 path_mapping_.push_back( | |
| 331 std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", | |
| 332 L"%" + l10n_util::ToLower(*variable) + L"%")); | |
| 333 } | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 void ModuleEnumerator::CollapsePath(Module* entry) { | |
| 338 // Take the path and see if we can use any of the substitution values | |
| 339 // from the vector constructed above to replace c:\windows with, for | |
| 340 // example, %systemroot%. | |
| 341 for (PathMapping::const_iterator mapping = path_mapping_.begin(); | |
| 342 mapping != path_mapping_.end(); ++mapping) { | |
| 343 string16 prefix = mapping->first; | |
| 344 if (StartsWith(entry->location, prefix, false)) { | |
| 345 entry->location = mapping->second + | |
| 346 entry->location.substr(prefix.length() - 1); | |
| 347 return; | |
| 348 } | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 void ModuleEnumerator::MatchAgainstBlacklist() { | |
| 353 for (size_t m = 0; m < enumerated_modules_->size(); ++m) { | |
| 354 // Match this module against the blacklist. | |
| 355 Module* module = &(*enumerated_modules_)[m]; | |
| 356 module->status = GOOD; // We change this below potentially. | |
| 357 for (size_t i = 0; i < arraysize(kModuleBlacklist); ++i) { | |
| 358 #if !defined(NDEBUG) | |
| 359 // This saves time when constructing the blacklist. | |
| 360 std::string hashes(kModuleBlacklist[i].filename); | |
| 361 std::string hash1, hash2, hash3; | |
| 362 GenerateHash(kModuleBlacklist[i].filename, &hash1); | |
| 363 hashes += " - " + hash1; | |
| 364 GenerateHash(kModuleBlacklist[i].location, &hash2); | |
| 365 hashes += " - " + hash2; | |
| 366 GenerateHash(kModuleBlacklist[i].desc_or_signer, &hash3); | |
| 367 hashes += " - " + hash3; | |
| 368 #endif | |
| 369 | |
| 370 ModuleStatus status = Match(*module, kModuleBlacklist[i]); | |
| 371 if (status != NOT_MATCHED) { | |
| 372 // We have a match against the blacklist. Mark it as such. | |
| 373 module->status = status; | |
| 374 module->recommended_action = kModuleBlacklist[i].help_tip; | |
| 375 break; | |
| 376 } | |
| 377 } | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 void ModuleEnumerator::ReportBack() { | |
| 382 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_)); | |
| 383 observer_->DoneScanning(); | |
| 384 } | |
| 385 | |
| 386 string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature( | |
| 387 const FilePath& filename) { | |
| 388 HCERTSTORE store = NULL; | |
| 389 HCRYPTMSG message = NULL; | |
| 390 | |
| 391 // Find the crypto message for this filename. | |
| 392 bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE, | |
| 393 filename.value().c_str(), | |
| 394 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, | |
| 395 CERT_QUERY_FORMAT_FLAG_BINARY, | |
| 396 0, | |
| 397 NULL, | |
| 398 NULL, | |
| 399 NULL, | |
| 400 &store, | |
| 401 &message, | |
| 402 NULL); | |
| 403 if (!result) | |
| 404 return string16(); | |
| 405 | |
| 406 // Determine the size of the signer info data. | |
| 407 DWORD signer_info_size = 0; | |
| 408 result = !!CryptMsgGetParam(message, | |
| 409 CMSG_SIGNER_INFO_PARAM, | |
| 410 0, | |
| 411 NULL, | |
| 412 &signer_info_size); | |
| 413 if (!result) | |
| 414 return string16(); | |
| 415 | |
| 416 // Allocate enough space to hold the signer info. | |
| 417 scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]); | |
| 418 CMSG_SIGNER_INFO* signer_info = | |
| 419 reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get()); | |
| 420 | |
| 421 // Obtain the signer info. | |
| 422 result = !!CryptMsgGetParam(message, | |
| 423 CMSG_SIGNER_INFO_PARAM, | |
| 424 0, | |
| 425 signer_info, | |
| 426 &signer_info_size); | |
| 427 if (!result) | |
| 428 return string16(); | |
| 429 | |
| 430 // Search for the signer certificate. | |
| 431 CERT_INFO CertInfo = {0}; | |
| 432 PCCERT_CONTEXT cert_context = NULL; | |
| 433 CertInfo.Issuer = signer_info->Issuer; | |
| 434 CertInfo.SerialNumber = signer_info->SerialNumber; | |
| 435 | |
| 436 cert_context = CertFindCertificateInStore( | |
| 437 store, | |
| 438 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, | |
| 439 0, | |
| 440 CERT_FIND_SUBJECT_CERT, | |
| 441 &CertInfo, | |
| 442 NULL); | |
| 443 if (!cert_context) | |
| 444 return string16(); | |
| 445 | |
| 446 // Determine the size of the Subject name. | |
| 447 DWORD subject_name_size = 0; | |
| 448 if (!(subject_name_size = CertGetNameString(cert_context, | |
| 449 CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| 450 0, | |
| 451 NULL, | |
| 452 NULL, | |
| 453 0))) { | |
| 454 return string16(); | |
| 455 } | |
| 456 | |
| 457 string16 subject_name; | |
| 458 subject_name.resize(subject_name_size); | |
| 459 | |
| 460 // Get subject name. | |
| 461 if (!(CertGetNameString(cert_context, | |
| 462 CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| 463 0, | |
| 464 NULL, | |
| 465 const_cast<LPWSTR>(subject_name.c_str()), | |
| 466 subject_name_size))) { | |
| 467 return string16(); | |
| 468 } | |
| 469 | |
| 470 return subject_name; | |
| 471 } | |
| 472 | |
| 473 // ---------------------------------------------------------------------------- | |
| 474 | |
| 475 void EnumerateModulesModel::ScanNow() { | |
| 476 if (scanning_) | |
| 477 return; // A scan is already in progress. | |
| 478 | |
| 479 lock->Acquire(); // Balanced in DoneScanning(); | |
| 480 | |
| 481 scanning_ = true; | |
| 482 | |
| 483 // Instruct the ModuleEnumerator class to load this on the File thread. | |
| 484 // ScanNow does not block. | |
| 485 if (!module_enumerator_) | |
| 486 module_enumerator_ = new ModuleEnumerator(this); | |
| 487 module_enumerator_->ScanNow(&enumerated_modules_); | |
| 488 } | |
| 489 | |
| 490 ListValue* EnumerateModulesModel::GetModuleList() { | |
| 491 if (scanning_) | |
| 492 return NULL; | |
| 493 | |
| 494 lock->Acquire(); | |
| 495 | |
| 496 if (enumerated_modules_.empty()) { | |
| 497 lock->Release(); | |
| 498 return NULL; | |
| 499 } | |
| 500 | |
| 501 ListValue* list = new ListValue(); | |
| 502 | |
| 503 for (ModuleEnumerator::ModulesVector::const_iterator module = | |
| 504 enumerated_modules_.begin(); | |
| 505 module != enumerated_modules_.end(); ++module) { | |
| 506 DictionaryValue* data = new DictionaryValue(); | |
| 507 data->SetInteger("type", module->type); | |
| 508 data->SetString("type_description", | |
| 509 (module->type == ModuleEnumerator::WINSOCK_MODULE_REGISTRATION) ? | |
| 510 ASCIIToWide("Winsock") : ASCIIToWide("")); | |
| 511 data->SetInteger("status", module->status); | |
| 512 data->SetString("location", module->location); | |
| 513 data->SetString("name", module->name); | |
| 514 data->SetString("product_name", module->product_name); | |
| 515 data->SetString("description", module->description); | |
| 516 data->SetString("version", module->version.empty() ? ASCIIToWide("") : | |
| 517 l10n_util::GetStringF(IDS_CONFLICTS_CHECK_VERSION_STRING, | |
| 518 module->version)); | |
| 519 data->SetString("digital_signer", module->digital_signer); | |
| 520 | |
| 521 // Figure out the possible resolution help string. | |
| 522 string16 actions; | |
| 523 string16 separator = ASCIIToWide(" ") + l10n_util::GetStringUTF16( | |
| 524 IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR) + | |
| 525 ASCIIToWide(" "); | |
| 526 | |
| 527 if (module->recommended_action & ModuleEnumerator::NONE) { | |
| 528 actions = l10n_util::GetStringUTF16( | |
| 529 IDS_CONFLICTS_CHECK_INVESTIGATING); | |
| 530 } | |
| 531 if (module->recommended_action & ModuleEnumerator::UNINSTALL) { | |
| 532 if (!actions.empty()) | |
| 533 actions += separator; | |
| 534 actions = l10n_util::GetStringUTF16( | |
| 535 IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL); | |
| 536 } | |
| 537 if (module->recommended_action & ModuleEnumerator::UPDATE) { | |
| 538 if (!actions.empty()) | |
| 539 actions += separator; | |
| 540 actions += l10n_util::GetStringUTF16( | |
| 541 IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE); | |
| 542 } | |
| 543 if (module->recommended_action & ModuleEnumerator::DISABLE) { | |
| 544 if (!actions.empty()) | |
| 545 actions += separator; | |
| 546 actions += l10n_util::GetStringUTF16( | |
| 547 IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE); | |
| 548 } | |
| 549 string16 possible_resolution = actions.empty() ? ASCIIToWide("") : | |
| 550 l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS) + | |
| 551 ASCIIToWide(" ") + | |
| 552 actions; | |
| 553 data->SetString("possibleResolution", possible_resolution); | |
| 554 data->SetString("help_url", ConstructHelpCenterUrl(*module).spec().c_str()); | |
| 555 | |
| 556 list->Append(data); | |
| 557 } | |
| 558 | |
| 559 lock->Release(); | |
| 560 return list; | |
| 561 } | |
| 562 | |
| 563 EnumerateModulesModel::EnumerateModulesModel() | |
| 564 : scanning_(false), | |
|
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
| |
| 565 confirmed_bad_modules_detected_(0), | |
|
tfarina
2010/11/08 14:57:47
mit: align with |scanning_|.
| |
| 566 suspected_bad_modules_detected_(0) { | |
| 567 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); | |
| 568 if (cmd_line.HasSwitch(switches::kConflictingModulesCheck)) { | |
| 569 check_modules_timer_.Start( | |
| 570 base::TimeDelta::FromMilliseconds(kModuleCheckDelayMs), | |
|
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
| |
| 571 this, &EnumerateModulesModel::ScanNow); | |
| 572 } | |
| 573 | |
| 574 lock = new Lock(); | |
| 575 } | |
| 576 | |
| 577 EnumerateModulesModel::~EnumerateModulesModel() { | |
| 578 delete lock; | |
| 579 } | |
| 580 | |
| 581 void EnumerateModulesModel::DoneScanning() { | |
| 582 confirmed_bad_modules_detected_ = 0; | |
| 583 suspected_bad_modules_detected_ = 0; | |
| 584 for (ModuleEnumerator::ModulesVector::const_iterator module = | |
| 585 enumerated_modules_.begin(); | |
| 586 module != enumerated_modules_.end(); ++module) { | |
| 587 if (module->status == ModuleEnumerator::CONFIRMED_BAD) | |
| 588 ++confirmed_bad_modules_detected_; | |
| 589 if (module->status == ModuleEnumerator::SUSPECTED_BAD) | |
| 590 ++suspected_bad_modules_detected_; | |
| 591 } | |
| 592 | |
| 593 scanning_ = false; | |
| 594 lock->Release(); | |
| 595 | |
| 596 NotificationService::current()->Notify( | |
| 597 NotificationType::MODULE_LIST_ENUMERATED, | |
| 598 Source<EnumerateModulesModel>(this), | |
| 599 NotificationService::NoDetails()); | |
| 600 | |
| 601 if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) { | |
| 602 bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0; | |
| 603 NotificationService::current()->Notify( | |
| 604 NotificationType::MODULE_INCOMPATIBILITY_DETECTED, | |
| 605 Source<EnumerateModulesModel>(this), | |
| 606 Details<bool>(&found_confirmed_bad_modules)); | |
| 607 } | |
| 608 } | |
| 609 | |
| 610 GURL EnumerateModulesModel::ConstructHelpCenterUrl( | |
| 611 const ModuleEnumerator::Module& module) { | |
| 612 if (!(module.recommended_action & ModuleEnumerator::SEE_LINK)) | |
| 613 return GURL(); | |
| 614 | |
| 615 // Construct the needed hashes. | |
| 616 std::string filename, location, description, signer; | |
| 617 GenerateHash(WideToUTF8(module.name), &filename); | |
| 618 GenerateHash(WideToUTF8(module.location), &location); | |
| 619 GenerateHash(WideToUTF8(module.description), &description); | |
| 620 GenerateHash(WideToUTF8(module.digital_signer), &signer); | |
| 621 | |
| 622 string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS, | |
| 623 ASCIIToWide(filename), ASCIIToWide(location), | |
| 624 ASCIIToWide(description), ASCIIToWide(signer)); | |
| 625 return GURL(WideToUTF8(url)); | |
| 626 } | |
| OLD | NEW |