| 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 |