OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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/conflicts/installed_programs_win.h" | |
6 | |
7 #include <algorithm> | |
8 #include <utility> | |
9 | |
10 #include "base/callback.h" | |
11 #include "base/memory/ptr_util.h" | |
12 #include "base/strings/string_util.h" | |
13 #include "base/strings/stringprintf.h" | |
14 #include "base/task_scheduler/post_task.h" | |
15 #include "base/win/registry.h" | |
16 #include "chrome/browser/conflicts/msi_util_win.h" | |
17 | |
18 namespace { | |
19 | |
20 // Fetches a string |value| out of |key|. Return false if a non-empty value | |
21 // could not be retrived. | |
chrisha
2017/05/02 21:28:24
retrieved*
Patrick Monette
2017/05/29 22:04:54
Done.
| |
22 bool GetRegistryKeyValue(const base::win::RegKey& key, | |
23 const wchar_t* value, | |
24 base::string16* result) { | |
25 return key.ReadValue(value, result) == ERROR_SUCCESS && !result->empty(); | |
26 } | |
27 | |
28 // Returns true if |candidate| is registered as a system component. | |
29 bool IsSystemComponent(const base::win::RegKey& candidate) { | |
30 DWORD system_component = 0; | |
31 return candidate.ReadValueDW(L"SystemComponent", &system_component) == | |
32 ERROR_SUCCESS && | |
33 system_component == 1; | |
34 } | |
35 | |
36 // Fetches |value| out of |key|. Return false if a non-empty value could not | |
37 // be retrived. | |
chrisha
2017/05/02 21:28:25
retrieved*
Patrick Monette
2017/05/29 22:04:54
Done.
| |
38 bool GetValue(const base::win::RegKey& key, | |
39 const wchar_t* value, | |
40 base::string16* result) { | |
41 return key.ReadValue(value, result) == ERROR_SUCCESS && !result->empty(); | |
42 } | |
43 | |
44 // Try to get the |install_path| from |candidate| using the InstallLocation | |
45 // value. Return true on success. | |
46 bool GetInstallPathUsingInstallLocation(const base::win::RegKey& candidate, | |
47 base::FilePath* install_path) { | |
48 base::string16 install_location; | |
49 if (GetValue(candidate, L"InstallLocation", &install_location)) { | |
50 *install_path = base::FilePath(std::move(install_location)); | |
51 return true; | |
52 } | |
53 return false; | |
54 } | |
55 | |
56 // Returns true if the |component_path| points to a registry key. Registry key | |
57 // paths are characterized by a number instead of a drive letter. | |
58 // See the documentation for ::MsiGetComponentPath(): | |
59 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370112(v=vs.85).as px | |
60 bool IsRegistryComponentPath(const base::string16& component_path) { | |
61 base::string16 drive_letter = | |
62 component_path.substr(0, component_path.find(':')); | |
63 | |
64 for (const auto& registry_drive_letter : | |
65 {L"00", L"01", L"02", L"03", L"20", L"21", L"22", L"23"}) { | |
66 if (drive_letter == registry_drive_letter) | |
67 return true; | |
68 } | |
69 | |
70 return false; | |
71 } | |
72 | |
73 // Returns all the dlls installed by the product identified by |product_guid|. | |
74 // Returns true on success. | |
75 bool GetInstalledDllsUsingMsiGuid(const base::string16& product_guid, | |
76 std::vector<base::FilePath>* dlls) { | |
77 // An invalid product guid may have been passed to this function. In those | |
chrisha
2017/05/02 21:28:24
In this*
Patrick Monette
2017/05/29 22:04:55
Done.
| |
78 // case, GetMsiComponentPaths() will return false so it is not necessary to | |
79 // specifically filter those out. | |
80 std::vector<base::string16> component_paths; | |
81 if (!GetMsiComponentPaths(product_guid, &component_paths)) | |
82 return false; | |
83 | |
84 for (const auto& component_path : component_paths) { | |
85 // Exclude registry component paths. | |
86 if (!IsRegistryComponentPath(component_path)) { | |
chrisha
2017/05/02 21:28:25
I have a very minor preference:
if (...)
contin
Patrick Monette
2017/05/29 22:04:55
Done.
| |
87 base::FilePath file_path(std::move(component_path)); | |
88 if (base::EqualsCaseInsensitiveASCII(file_path.Extension(), L".dll")) | |
chrisha
2017/05/02 21:28:24
This might be an artificial constraint. Loadable m
Patrick Monette
2017/05/29 22:04:54
This was mainly done so that searching for the ass
| |
89 dlls->push_back(std::move(file_path)); | |
90 } | |
91 } | |
92 | |
93 return true; | |
94 } | |
95 | |
96 // Checks if the registry key that the first 3 parameters refers to references | |
97 // an installed program in the Apps & Features settings page. If so, populates | |
98 // |internal_data| with the program name and its related file paths. | |
99 void CheckRegistryKeyForInstalledProgram( | |
100 HKEY hkey, | |
101 const base::string16& key_path, | |
102 const base::string16& key_name, | |
103 InstalledPrograms::InternalData* internal_data) { | |
104 base::win::RegKey candidate( | |
105 hkey, | |
106 base::StringPrintf(L"%ls\\%ls", key_path.c_str(), key_name.c_str()) | |
107 .c_str(), | |
108 KEY_READ); | |
109 | |
110 if (!candidate.Valid()) | |
111 return; | |
112 | |
113 // System components are not displayed in the Add or remove programs list. | |
114 if (IsSystemComponent(candidate)) | |
115 return; | |
116 | |
117 // If there is no UninstallString, the Uninstall button is grayed out. | |
118 base::string16 uninstall_string; | |
119 if (!GetValue(candidate, L"UninstallString", &uninstall_string)) | |
120 return; | |
121 | |
122 // Ignore Microsoft programs. | |
123 base::string16 publisher; | |
124 if (GetValue(candidate, L"Publisher", &publisher) && | |
125 publisher == L"Microsoft Corporation") { | |
126 return; | |
127 } | |
128 | |
129 // Candidates with no display names are ignored. | |
130 // Because this class is used to display warning to the user, not having the | |
chrisha
2017/05/02 21:28:25
s/the//
Patrick Monette
2017/05/29 22:04:54
Done.
| |
131 // a display name renders the warning somewhat useless. | |
132 base::string16 display_name; | |
133 if (!GetValue(candidate, L"DisplayName", &display_name)) | |
chrisha
2017/05/02 21:28:25
In this case could we directly check the image fil
Patrick Monette
2017/05/29 22:04:54
It would be possible. But remember that having no
| |
134 return; | |
135 | |
136 base::FilePath install_path; | |
137 if (GetInstallPathUsingInstallLocation(candidate, &install_path)) { | |
138 internal_data->program_names.push_back(std::move(display_name)); | |
139 internal_data->install_locations.push_back(std::make_pair( | |
140 std::move(install_path), internal_data->program_names.size() - 1)); | |
141 return; | |
chrisha
2017/05/02 21:28:25
So if we find an InstallPath then we don't want to
Patrick Monette
2017/05/29 22:04:54
If the InstallPath is good, it's faster to check v
| |
142 } | |
143 | |
144 std::vector<base::FilePath> dlls; | |
145 if (GetInstalledDllsUsingMsiGuid(key_name, &dlls)) { | |
146 internal_data->program_names.push_back(std::move(display_name)); | |
147 for (size_t i = 0; i < dlls.size(); i++) { | |
148 internal_data->dll_map.push_back(std::make_pair( | |
149 std::move(dlls[i]), internal_data->program_names.size() - 1)); | |
150 } | |
151 } | |
152 } | |
153 | |
154 // Returns the index of the matching parent directory of |file| in |container|. | |
155 // Returns false if no matches were found. | |
156 bool BinarySearch( | |
chrisha
2017/05/02 21:28:25
Yay, you can write a binary search! You're hired :
Patrick Monette
2017/05/29 22:04:55
Yay!
| |
157 const std::vector<std::pair<base::FilePath, size_t>>& container, | |
158 const base::FilePath& file, | |
159 size_t* result) { | |
160 int min = 0; | |
161 int max = container.size() - 1; | |
162 while (min <= max) { | |
163 int mid_index = (max + min) / 2; | |
164 const auto& mid = container[mid_index]; | |
165 if (mid.first.IsParent(file)) { | |
166 *result = mid.second; | |
167 return true; | |
168 } | |
169 | |
170 if (base::FilePath::CompareLessIgnoreCase(file.value(), mid.first.value())) | |
171 max = mid_index - 1; | |
172 else | |
173 min = mid_index + 1; | |
174 } | |
175 | |
176 return false; | |
177 } | |
178 | |
179 } // namespace | |
180 | |
181 InstalledPrograms::InstalledPrograms() | |
182 : initialized_(false), weak_ptr_factory_(this) {} | |
183 | |
184 InstalledPrograms::~InstalledPrograms() = default; | |
185 | |
186 bool InstalledPrograms::GetInstalledProgramName(const base::FilePath& file, | |
187 base::string16* program_name) { | |
188 DCHECK(initialized_); | |
189 | |
190 // First check if the exact file path can be found. | |
191 auto iter = dll_map_.find(file); | |
192 if (iter != dll_map_.end()) { | |
193 *program_name = program_names_[iter->second]; | |
194 return true; | |
195 } | |
196 | |
197 // Then check if one of the install locations matches the file. | |
198 if (install_locations_.empty()) | |
199 return false; | |
200 | |
201 size_t index; | |
202 if (!BinarySearch(install_locations_, file, &index)) | |
203 return false; | |
204 | |
205 *program_name = program_names_[index]; | |
206 return true; | |
207 } | |
208 | |
209 // static | |
210 std::unique_ptr<InstalledPrograms::InternalData> | |
211 InstalledPrograms::GetInternalData() { | |
212 auto internal_data = base::MakeUnique<InternalData>(); | |
213 | |
214 const wchar_t* kUninstallKeyPaths[] = { | |
215 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", | |
216 L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall", | |
217 }; | |
218 | |
219 for (const auto& uninstall_key_path : kUninstallKeyPaths) { | |
220 for (const auto& hkey : {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}) { | |
221 for (base::win::RegistryKeyIterator i(hkey, uninstall_key_path); | |
222 i.Valid(); ++i) { | |
223 CheckRegistryKeyForInstalledProgram(hkey, uninstall_key_path, i.Name(), | |
224 internal_data.get()); | |
225 } | |
226 } | |
227 } | |
228 | |
229 return internal_data; | |
230 } | |
231 | |
232 void InstalledPrograms::OnInternalDataReceived( | |
233 const base::Closure& on_initialized_callback, | |
234 std::unique_ptr<InternalData> internal_data) { | |
235 program_names_ = std::move(internal_data->program_names); | |
236 base::flat_map<base::FilePath, size_t, FilePathLess> flat_map( | |
237 internal_data->dll_map, base::KEEP_FIRST_OF_DUPES); | |
238 dll_map_ = std::move(flat_map); | |
239 install_locations_ = std::move(internal_data->install_locations); | |
chrisha
2017/05/02 21:28:25
Just std::move the entire InternalData object?
Patrick Monette
2017/05/29 22:04:54
Done.
| |
240 | |
241 // Sort |install_locations_| so that it is possible to use binary search. | |
242 std::sort(install_locations_.begin(), install_locations_.end(), | |
243 [](const auto& lhs, const auto& rhs) { | |
244 return base::FilePath::CompareLessIgnoreCase(lhs.first.value(), | |
245 rhs.first.value()); | |
246 }); | |
247 | |
248 initialized_ = true; | |
249 if (on_initialized_callback) | |
250 on_initialized_callback.Run(); | |
251 } | |
252 | |
253 void InstalledPrograms::Initialize( | |
254 const base::Closure& on_initialized_callback) { | |
255 DCHECK(!initialized_); | |
256 base::PostTaskWithTraitsAndReplyWithResult( | |
257 FROM_HERE, | |
258 base::TaskTraits() | |
259 .MayBlock() | |
260 .WithPriority(base::TaskPriority::BACKGROUND) | |
261 .WithShutdownBehavior( | |
262 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN), | |
263 base::Bind(&InstalledPrograms::GetInternalData), | |
264 base::Bind(&InstalledPrograms::OnInternalDataReceived, | |
265 weak_ptr_factory_.GetWeakPtr(), on_initialized_callback)); | |
266 } | |
267 bool InstalledPrograms::FilePathLess::operator()( | |
268 const base::FilePath& lhs, | |
269 const base::FilePath& rhs) const { | |
270 return base::FilePath::CompareLessIgnoreCase(lhs.value(), rhs.value()); | |
271 } | |
OLD | NEW |