OLD | NEW |
| (Empty) |
1 // Copyright 2007-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Path utility functions. | |
17 | |
18 #include "omaha/base/path.h" | |
19 | |
20 #include <atlbase.h> | |
21 #include <atlstr.h> | |
22 #include <atlpath.h> | |
23 #include <map> | |
24 #include <vector> | |
25 #include "omaha/base/error.h" | |
26 #include "omaha/base/file.h" | |
27 #include "omaha/base/shell.h" | |
28 #include "omaha/base/string.h" | |
29 #include "omaha/base/utils.h" | |
30 | |
31 namespace omaha { | |
32 | |
33 const TCHAR* const kRegSvr32Cmd1 = _T("regsvr32 "); | |
34 const TCHAR* const kRegSvr32Cmd2 = _T("regsvr32.exe "); | |
35 const TCHAR* const kRunDll32Cmd1 = _T("rundll32 "); | |
36 const TCHAR* const kRunDll32Cmd2 = _T("rundll32.exe "); | |
37 const TCHAR* const kMsiExecCmd1 = _T("msiexec "); | |
38 const TCHAR* const kMsiExecCmd2 = _T("msiexec.exe "); | |
39 const TCHAR* const kDotExe = _T(".exe"); | |
40 | |
41 | |
42 namespace detail { | |
43 | |
44 typedef bool (*Filter)(const WIN32_FIND_DATA&); | |
45 | |
46 bool IsFile(const WIN32_FIND_DATA& find_data) { | |
47 return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; | |
48 } | |
49 | |
50 bool IsDirectory(const WIN32_FIND_DATA& find_data) { | |
51 return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | |
52 } | |
53 | |
54 bool AllFiles(const WIN32_FIND_DATA&) { | |
55 return true; | |
56 } | |
57 | |
58 HRESULT FindFilesEx(const CString& dir, | |
59 const CString& pattern, | |
60 std::vector<CString>* files, | |
61 Filter func) { | |
62 ASSERT1(files); | |
63 | |
64 files->clear(); | |
65 if (!File::Exists(dir)) { | |
66 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); | |
67 } | |
68 | |
69 CString files_to_find = ConcatenatePath(dir, pattern); | |
70 WIN32_FIND_DATA find_data = {0}; | |
71 scoped_hfind hfind(::FindFirstFile(files_to_find, &find_data)); | |
72 if (!hfind) { | |
73 HRESULT hr = HRESULTFromLastError(); | |
74 UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr)); | |
75 return hr; | |
76 } | |
77 | |
78 do { | |
79 if (func(find_data)) { | |
80 files->push_back(find_data.cFileName); | |
81 } | |
82 } while (::FindNextFile(get(hfind), &find_data)); | |
83 | |
84 return S_OK; | |
85 } | |
86 | |
87 } // namespace detail. | |
88 | |
89 // Get the starting path from the command string | |
90 CString GetStartingPathFromString(const CString& s) { | |
91 CString path; | |
92 CString str(s); | |
93 TrimCString(str); | |
94 | |
95 int len = str.GetLength(); | |
96 if (len > 0) { | |
97 if (str[0] == _T('"')) { | |
98 // For something like: "c:\Program Files\...\" ... | |
99 int idx = String_FindChar(str.GetString() + 1, _T('"')); | |
100 if (idx != -1) | |
101 path.SetString(str.GetString() + 1, idx); | |
102 } else { | |
103 // For something like: c:\PRGRA~1\... ... | |
104 int idx = String_FindChar(str, _T(' ')); | |
105 path.SetString(str, idx == -1 ? len : idx); | |
106 } | |
107 } | |
108 | |
109 return path; | |
110 } | |
111 | |
112 // Get the trailing path from the command string | |
113 CString GetTrailingPathFromString(const CString& s) { | |
114 CString path; | |
115 CString str(s); | |
116 TrimCString(str); | |
117 | |
118 int len = str.GetLength(); | |
119 if (len > 0) { | |
120 if (str[len - 1] == _T('"')) { | |
121 // For something like: regsvr32 /u /s "c:\Program Files\..." | |
122 str.Truncate(len - 1); | |
123 int idx = String_ReverseFindChar(str, _T('"')); | |
124 if (idx != -1) | |
125 path.SetString(str.GetString() + idx + 1, len - idx - 1); | |
126 } else { | |
127 // For something like: regsvr32 /u /s c:\PRGRA~1\... | |
128 int idx = String_ReverseFindChar(str, _T(' ')); | |
129 if (idx != -1) | |
130 path.SetString(str.GetString() + idx + 1, len - idx - 1); | |
131 } | |
132 } | |
133 | |
134 return path; | |
135 } | |
136 | |
137 // Get the file from the command string | |
138 HRESULT GetFileFromCommandString(const TCHAR* s, CString* file) { | |
139 ASSERT1(file); | |
140 | |
141 if (!s || !*s) { | |
142 return E_INVALIDARG; | |
143 } | |
144 | |
145 CString str(s); | |
146 TrimCString(str); | |
147 | |
148 // Handle the string starting with quotation mark | |
149 // For example: "C:\Program Files\WinZip\WINZIP32.EXE" /uninstall | |
150 if (str[0] == _T('"')) { | |
151 int idx_quote = str.Find(_T('"'), 1); | |
152 if (idx_quote != -1) { | |
153 file->SetString(str.GetString() + 1, idx_quote - 1); | |
154 return S_OK; | |
155 } else { | |
156 return E_FAIL; | |
157 } | |
158 } | |
159 | |
160 // Handle the string starting with "regsvr32" | |
161 // For example: regsvr32 /u /s "c:\program files\google\googletoolbar3.dll" | |
162 if (String_StartsWith(str, kRegSvr32Cmd1, true) || | |
163 String_StartsWith(str, kRegSvr32Cmd2, true)) { | |
164 file->SetString(GetTrailingPathFromString(str)); | |
165 return S_OK; | |
166 } | |
167 | |
168 // Handle the string starting with "rundll32" | |
169 // For example: "rundll32.exe setupapi.dll,InstallHinfSection DefaultUninstall
132 C:\WINDOWS\INF\PCHealth.inf" // NOLINT | |
170 if (String_StartsWith(str, kRunDll32Cmd1, true) || | |
171 String_StartsWith(str, kRunDll32Cmd2, true)) { | |
172 int idx_space = str.Find(_T(' ')); | |
173 ASSERT1(idx_space != -1); | |
174 int idx_comma = str.Find(_T(','), idx_space + 1); | |
175 if (idx_comma != -1) { | |
176 file->SetString(str.GetString() + idx_space + 1, | |
177 idx_comma - idx_space - 1); | |
178 TrimCString(*file); | |
179 return S_OK; | |
180 } else { | |
181 return E_FAIL; | |
182 } | |
183 } | |
184 | |
185 // Handle the string starting with "msiexec" | |
186 // For example: MsiExec.exe /I{25A13826-8E4A-4FBF-AD2B-776447FE9646} | |
187 if (String_StartsWith(str, kMsiExecCmd1, true) || | |
188 String_StartsWith(str, kMsiExecCmd2, true)) { | |
189 return E_FAIL; | |
190 } | |
191 | |
192 // Otherwise, try to find the file till reaching ".exe" | |
193 // For example: "C:\Program Files\Google\Google Desktop Search\GoogleDesktopSe
tup.exe -uninstall" // NOLINT | |
194 for (int i = 0; i < str.GetLength(); ++i) { | |
195 if (String_StartsWith(str.GetString() + i, kDotExe, true)) { | |
196 file->SetString(str, i + _tcslen(kDotExe)); | |
197 return S_OK; | |
198 } | |
199 } | |
200 | |
201 // As last resort, return the part from the beginning to first space found. | |
202 int idx = str.Find(_T(' ')); | |
203 if (idx == -1) { | |
204 file->SetString(str); | |
205 } else { | |
206 file->SetString(str, idx); | |
207 } | |
208 | |
209 return S_OK; | |
210 } | |
211 | |
212 // Expands the string with embedded special folder variables. | |
213 // TODO(omaha): This function seems to have a very specific purpose, which | |
214 // is not used in our code base. Consider removing it. | |
215 HRESULT ExpandStringWithSpecialFolders(CString* str) { | |
216 ASSERT(str, (L"")); | |
217 | |
218 #pragma warning(push) | |
219 // construction of local static object is not thread-safe | |
220 #pragma warning(disable : 4640) | |
221 static std::map<CString, CString> g_special_folders_mapping; | |
222 #pragma warning(pop) | |
223 | |
224 if (g_special_folders_mapping.size() == 0) { | |
225 RET_IF_FAILED( | |
226 Shell::GetSpecialFolderKeywordsMapping(&g_special_folders_mapping)); | |
227 } | |
228 | |
229 CString expanded_str; | |
230 RET_IF_FAILED( | |
231 ExpandEnvLikeStrings(*str, g_special_folders_mapping, &expanded_str)); | |
232 | |
233 str->SetString(expanded_str); | |
234 | |
235 return S_OK; | |
236 } | |
237 | |
238 // Internal helper method for normalizing a path | |
239 HRESULT NormalizePathInternal(const TCHAR* path, CString* normalized_path) { | |
240 // We use '|' to separate fields | |
241 CString field; | |
242 int bar_idx = String_FindChar(path, _T('|')); | |
243 if (bar_idx == -1) | |
244 field = path; | |
245 else | |
246 field.SetString(path, bar_idx); | |
247 | |
248 if (IsRegistryPath(field)) { | |
249 CString key_name, value_name; | |
250 RET_IF_FAILED(RegSplitKeyvalueName(field, &key_name, &value_name)); | |
251 | |
252 CString reg_value; | |
253 RET_IF_FAILED(RegKey::GetValue(key_name, value_name, ®_value)); | |
254 normalized_path->Append(reg_value); | |
255 } else { | |
256 RET_IF_FAILED(ExpandStringWithSpecialFolders(&field)); | |
257 normalized_path->Append(field); | |
258 } | |
259 | |
260 if (bar_idx != -1) | |
261 return NormalizePathInternal(path + bar_idx + 1, normalized_path); | |
262 else | |
263 return S_OK; | |
264 } | |
265 | |
266 // Normalize a path | |
267 HRESULT NormalizePath(const TCHAR* path, CString* normalized_path) { | |
268 ASSERT1(normalized_path); | |
269 | |
270 normalized_path->Empty(); | |
271 | |
272 if (path) { | |
273 HRESULT hr = NormalizePathInternal(path, normalized_path); | |
274 if (FAILED(hr)) { | |
275 normalized_path->Empty(); | |
276 UTIL_LOG(LE, (_T("[NormalizePath - unable to normalize path][%s][0x%x]"), | |
277 path, hr)); | |
278 } | |
279 return hr; | |
280 } else { | |
281 return S_OK; | |
282 } | |
283 } | |
284 | |
285 CString ConcatenatePath(const CString& path1, const CString& path2) { | |
286 CString ret(path1); | |
287 | |
288 // Append the file path using the PathAppend. | |
289 VERIFY1(::PathAppend(CStrBuf(ret, MAX_PATH), path2)); | |
290 | |
291 return ret; | |
292 } | |
293 | |
294 // Get the filename from the path | |
295 // "C:\TEST\sample.txt" returns "sample.txt" | |
296 CString GetFileFromPath(const CString& path) { | |
297 CPath path1(path); | |
298 path1.StripPath(); | |
299 return static_cast<CString>(path1); | |
300 } | |
301 | |
302 // Get the directory from the path | |
303 // "C:\TEST\sample.txt" returns "C:\TEST" | |
304 // Get the directory out of the file path | |
305 CString GetDirectoryFromPath(const CString& path) { | |
306 CPath path1(path); | |
307 path1.RemoveFileSpec(); | |
308 return static_cast<CString>(path1); | |
309 } | |
310 | |
311 // Remove the extension from the path. | |
312 // "C:\TEST\sample.txt" returns "C:\TEST\sample" | |
313 CString GetPathRemoveExtension(const CString& path) { | |
314 CPath path1(path); | |
315 path1.RemoveExtension(); | |
316 return static_cast<CString>(path1); | |
317 } | |
318 | |
319 // Basically, an absolute path starts with X:\ or \\ (a UNC name) | |
320 bool IsAbsolutePath(const TCHAR* path) { | |
321 ASSERT1(path); | |
322 | |
323 int len = ::_tcslen(path); | |
324 if (len < 3) | |
325 return false; | |
326 if (*path == _T('"')) | |
327 path++; | |
328 if (String_StartsWith(path+1, _T(":\\"), false)) | |
329 return true; | |
330 if (String_StartsWith(path, _T("\\\\"), false)) | |
331 return true; | |
332 return false; | |
333 } | |
334 | |
335 void EnclosePath(CString* path) { | |
336 ASSERT1(path); | |
337 | |
338 if (path->IsEmpty()) { | |
339 return; | |
340 } | |
341 | |
342 bool starts_with_quote = (_T('"') == path->GetAt(0)); | |
343 bool ends_with_quote = (_T('"') == path->GetAt(path->GetLength() - 1)); | |
344 ASSERT(starts_with_quote == ends_with_quote, (_T("%s"), path->GetString())); | |
345 bool is_enclosed = starts_with_quote && ends_with_quote; | |
346 if (is_enclosed) { | |
347 return; | |
348 } | |
349 | |
350 path->Insert(0, _T('"')); | |
351 path->AppendChar(_T('"')); | |
352 } | |
353 | |
354 CString EnclosePathIfExe(const CString& module_path) { | |
355 if (!String_EndsWith(module_path, _T(".exe"), true)) { | |
356 return module_path; | |
357 } | |
358 | |
359 CString enclosed_path(module_path); | |
360 EnclosePath(&enclosed_path); | |
361 return enclosed_path; | |
362 } | |
363 | |
364 // remove any double quotation masks from an enclosed path | |
365 void UnenclosePath(CString* path) { | |
366 ASSERT1(path); | |
367 | |
368 if (path->GetLength() > 1 && path->GetAt(0) == _T('"')) { | |
369 bool right_quote_exists = (path->GetAt(path->GetLength() - 1) == _T('"')); | |
370 ASSERT(right_quote_exists, | |
371 (_T("[UnenclosePath - double quote mismatches]"))); | |
372 if (right_quote_exists) { | |
373 // Remove the double quotation masks | |
374 path->Delete(0); | |
375 path->Truncate(path->GetLength() - 1); | |
376 } | |
377 } | |
378 } | |
379 | |
380 void RemoveMismatchedEndQuoteInDirectoryPath(CString* directory_path) { | |
381 ASSERT1(directory_path); | |
382 | |
383 if (directory_path->GetLength() <= 1) { | |
384 return; | |
385 } | |
386 | |
387 ASSERT1(directory_path->GetAt(0) != _T('"')); | |
388 if (directory_path->GetAt(directory_path->GetLength() - 1) == _T('"')) { | |
389 directory_path->Truncate(directory_path->GetLength() - 1); | |
390 } | |
391 } | |
392 | |
393 HRESULT ShortPathToLongPath(const CString& short_path, CString* long_path) { | |
394 ASSERT1(long_path); | |
395 | |
396 TCHAR long_name[MAX_PATH] = {0}; | |
397 if (!::GetLongPathName(short_path, long_name, MAX_PATH)) { | |
398 return HRESULTFromLastError(); | |
399 } | |
400 | |
401 *long_path = long_name; | |
402 return S_OK; | |
403 } | |
404 | |
405 HRESULT FindFilesEx(const CString& dir, | |
406 const CString& pattern, | |
407 std::vector<CString>* files) { | |
408 return detail::FindFilesEx(dir, pattern, files, &detail::IsFile); | |
409 } | |
410 | |
411 HRESULT FindFiles(const CString& dir, | |
412 const CString& pattern, | |
413 std::vector<CString>* files) { | |
414 return detail::FindFilesEx(dir, pattern, files, &detail::AllFiles); | |
415 } | |
416 | |
417 HRESULT FindSubDirectories(const CString& dir, | |
418 const CString& pattern, | |
419 std::vector<CString>* files) { | |
420 return detail::FindFilesEx(dir, pattern, files, &detail::IsDirectory); | |
421 } | |
422 | |
423 HRESULT FindFileRecursive(const CString& dir, | |
424 const CString& pattern, | |
425 std::vector<CString>* files) { | |
426 ASSERT1(files); | |
427 | |
428 std::vector<CString> temp_files; | |
429 HRESULT hr = FindFilesEx(dir, pattern, &temp_files); | |
430 if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && FAILED(hr)) { | |
431 return hr; | |
432 } | |
433 | |
434 for (size_t i = 0; i < temp_files.size(); ++i) { | |
435 files->push_back(ConcatenatePath(dir, temp_files[i])); | |
436 } | |
437 | |
438 std::vector<CString> sub_dirs; | |
439 hr = FindSubDirectories(dir, _T("*"), &sub_dirs); | |
440 if (FAILED(hr)) { | |
441 return hr; | |
442 } | |
443 | |
444 for (size_t i = 0; i < sub_dirs.size(); ++i) { | |
445 const CString& sub_dir = sub_dirs[i]; | |
446 if (sub_dir == _T(".") || sub_dir == _T("..")) { | |
447 continue; | |
448 } | |
449 | |
450 CString path = ConcatenatePath(dir, sub_dir); | |
451 hr = FindFileRecursive(path, pattern, files); | |
452 if (FAILED(hr)) { | |
453 return hr; | |
454 } | |
455 } | |
456 | |
457 return S_OK; | |
458 } | |
459 | |
460 } // namespace omaha | |
461 | |
OLD | NEW |