PageRenderTime 28ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/omaha/base/path.cc

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