Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
9.9 KiB

  1. #include "precomp.hxx"
  2. #pragma hdrstop
  3. #include <shguidp.h> // CLSID_MyDocuments, CLSID_ShellFSFolder
  4. #include <shlobjp.h> // SHFlushSFCache()
  5. #include "util.h"
  6. #include "dll.h"
  7. #include "resource.h"
  8. #include "sddl.h"
  9. HRESULT GetFolderDisplayName(UINT csidl, LPTSTR pszPath, UINT cch)
  10. {
  11. *pszPath = 0;
  12. LPITEMIDLIST pidl;
  13. if (SUCCEEDED(SHGetFolderLocation(NULL, csidl | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &pidl)))
  14. {
  15. SHGetNameAndFlags(pidl, SHGDN_NORMAL, pszPath, cch, NULL);
  16. ILFree(pidl);
  17. }
  18. return *pszPath ? S_OK : E_FAIL;
  19. }
  20. #define MYDOCS_CLSID TEXT("{450d8fba-ad25-11d0-98a8-0800361b1103}") // CLSID_MyDocuments
  21. // Create/Updates file in SendTo directory to have current display name
  22. void UpdateSendToFile()
  23. {
  24. TCHAR szSendToDir[MAX_PATH];
  25. if (S_OK == SHGetFolderPath(NULL, CSIDL_SENDTO, NULL, SHGFP_TYPE_CURRENT, szSendToDir))
  26. {
  27. // Create c:\winnt\profile\chrisg\sendto\<display name>.mydocs
  28. BOOL bDeleteOnly = FALSE;
  29. TCHAR szNewFile[MAX_PATH];
  30. TCHAR szName[MAX_PATH];
  31. if (SUCCEEDED(GetFolderDisplayName(CSIDL_PERSONAL, szName, ARRAYSIZE(szName))))
  32. {
  33. PathCleanupSpec(NULL, szName); // map any illegal chars to file sys chars
  34. PathRemoveBlanks(szName);
  35. PathCombine(szNewFile, szSendToDir, szName);
  36. lstrcat(szNewFile, TEXT(".mydocs"));
  37. }
  38. else
  39. {
  40. // we can't create a new file, because we don't have a name
  41. bDeleteOnly = TRUE;
  42. }
  43. TCHAR szFile[MAX_PATH];
  44. WIN32_FIND_DATA fd;
  45. // delete c:\winnt\profile\chrisg\sendto\*.mydocs
  46. PathCombine(szFile, szSendToDir, TEXT("*.mydocs"));
  47. HANDLE hFind = FindFirstFile(szFile, &fd);
  48. if (hFind != INVALID_HANDLE_VALUE)
  49. {
  50. do
  51. {
  52. PathCombine(szFile, szSendToDir, fd.cFileName);
  53. if (0 == lstrcmp(szFile, szNewFile))
  54. {
  55. // The file that we needed to create already exists,
  56. // just leave it in place instead of deleting it and
  57. // then creating it again below (this fixes
  58. // app compat problems - see NT bug 246932)
  59. bDeleteOnly = TRUE;
  60. // file now has the exact display name, MUI adjusts the return from GetFolderDisplayName and
  61. // since we run this every time we dont have to worry about localizing the sendto target.
  62. }
  63. else
  64. {
  65. DeleteFile(szFile);
  66. }
  67. } while (FindNextFile(hFind, &fd));
  68. FindClose(hFind);
  69. }
  70. if (!bDeleteOnly)
  71. {
  72. hFind = CreateFile(szNewFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
  73. if (hFind != INVALID_HANDLE_VALUE)
  74. {
  75. CloseHandle(hFind);
  76. // file now has the exact display name, MUI adjusts the return from GetFolderDisplayName and
  77. // since we run this every time we dont have to worry about localizing the sendto target.
  78. }
  79. else
  80. {
  81. // might be illegal chars in the file name, fall back to the default MyDocs name here
  82. }
  83. }
  84. }
  85. }
  86. // test pszChild against pszParent to see if
  87. // pszChild is equal (PATH_IS_EQUAL) or
  88. // a DIRECT child (PATH_IS_CHILD)
  89. DWORD ComparePaths(LPCTSTR pszChild, LPCTSTR pszParent)
  90. {
  91. DWORD dwRet = PATH_IS_DIFFERENT;
  92. TCHAR szParent[MAX_PATH];
  93. StrCpyN(szParent, pszParent, ARRAYSIZE(szParent));
  94. if (PathIsRoot(szParent) && (-1 != PathGetDriveNumber(szParent)))
  95. {
  96. szParent[2] = 0; // trip D:\ -> D: to make code below work
  97. }
  98. INT cchParent = lstrlen(szParent);
  99. INT cchChild = lstrlen(pszChild);
  100. if (cchParent <= cchChild)
  101. {
  102. TCHAR szChild[MAX_PATH];
  103. lstrcpyn(szChild, pszChild, ARRAYSIZE(szChild));
  104. LPTSTR pszChildSlice = szChild + cchParent;
  105. if (TEXT('\\') == *pszChildSlice)
  106. {
  107. *pszChildSlice = 0;
  108. }
  109. if (lstrcmpi(szChild, szParent) == 0)
  110. {
  111. if (cchParent < cchChild)
  112. {
  113. LPTSTR pTmp = pszChildSlice + 1;
  114. while (*pTmp && *pTmp != TEXT('\\'))
  115. {
  116. pTmp++; // find second level path segments
  117. }
  118. if (!(*pTmp))
  119. {
  120. dwRet = PATH_IS_CHILD; // direct child
  121. }
  122. }
  123. else
  124. {
  125. dwRet = PATH_IS_EQUAL;
  126. }
  127. }
  128. }
  129. return dwRet;
  130. }
  131. // Checks the path to see if it is marked as system or read only and
  132. // then check desktop.ini for CLSID or CLSID2 entry...
  133. BOOL IsPathAlreadyShellFolder(LPCTSTR pszPath, DWORD dwAttrib)
  134. {
  135. BOOL bIsShellFolder = FALSE;
  136. if (PathIsSystemFolder(pszPath, dwAttrib))
  137. {
  138. TCHAR szDesktopIni[MAX_PATH];
  139. PathCombine(szDesktopIni, pszPath, TEXT("desktop.ini"));
  140. // Check for CLSID entry...
  141. TCHAR szBuffer[MAX_PATH];
  142. GetPrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID"), TEXT("foo"), szBuffer, ARRAYSIZE(szBuffer), szDesktopIni);
  143. if ((lstrcmpi(szBuffer, TEXT("foo")) !=0) &&
  144. (lstrcmpi(szBuffer, MYDOCS_CLSID) !=0))
  145. {
  146. bIsShellFolder = TRUE;
  147. }
  148. // Check for CLSID2 entry...
  149. GetPrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), TEXT("foo"), szBuffer, ARRAYSIZE(szBuffer), szDesktopIni);
  150. if ((lstrcmpi(szBuffer, TEXT("foo")) != 0) &&
  151. (lstrcmpi(szBuffer, MYDOCS_CLSID) != 0))
  152. {
  153. bIsShellFolder = TRUE;
  154. }
  155. }
  156. return bIsShellFolder;
  157. }
  158. const struct
  159. {
  160. DWORD dwDir;
  161. DWORD dwFlags;
  162. DWORD dwRet;
  163. }
  164. _adirs[] =
  165. {
  166. { CSIDL_DESKTOP, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_DESKTOP },
  167. { CSIDL_PERSONAL, PATH_IS_EQUAL , PATH_IS_MYDOCS },
  168. { CSIDL_SENDTO, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_SENDTO },
  169. { CSIDL_RECENT, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_RECENT },
  170. { CSIDL_HISTORY, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_HISTORY },
  171. { CSIDL_COOKIES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_COOKIES },
  172. { CSIDL_PRINTHOOD, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_PRINTHOOD },
  173. { CSIDL_NETHOOD, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_NETHOOD },
  174. { CSIDL_STARTMENU, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_STARTMENU },
  175. { CSIDL_TEMPLATES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_TEMPLATES },
  176. { CSIDL_FAVORITES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_FAVORITES },
  177. { CSIDL_FONTS, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_FONTS },
  178. { CSIDL_APPDATA, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_APPDATA },
  179. { CSIDL_INTERNET_CACHE, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_TEMP_INET },
  180. { CSIDL_COMMON_STARTMENU, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_STARTMENU },
  181. { CSIDL_COMMON_DESKTOPDIRECTORY, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_DESKTOP },
  182. { CSIDL_WINDOWS, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_WINDOWS },
  183. { CSIDL_SYSTEM, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_SYSTEM },
  184. { CSIDL_PROFILE, PATH_IS_EQUAL , PATH_IS_PROFILE },
  185. };
  186. BOOL PathEndsInDot(LPCTSTR pszPath)
  187. {
  188. // CreateDirectory("c:\foo.") or CreateDirectory("c:\foo.....")
  189. // will succeed but create a directory named "c:\foo", which isn't
  190. // what the user asked for. So we use this function to guard
  191. // against those cases.
  192. //
  193. // Note that this simple test also picks off "c:\foo\." -- ok for
  194. // our purposes.
  195. UINT cLen = lstrlen(pszPath);
  196. return (cLen >= 1) && (pszPath[cLen - 1] == TEXT('.'));
  197. }
  198. //
  199. // Checks the path to see if it is okay as a MyDocs path
  200. //
  201. DWORD IsPathGoodMyDocsPath(HWND hwnd, LPCTSTR pszPath)
  202. {
  203. if (NULL == pszPath)
  204. {
  205. return PATH_IS_ERROR;
  206. }
  207. TCHAR szRootPath[MAX_PATH];
  208. lstrcpyn(szRootPath, pszPath, ARRAYSIZE(szRootPath));
  209. if (!PathStripToRoot(szRootPath))
  210. {
  211. return PATH_IS_ERROR;
  212. }
  213. if (PathEndsInDot(pszPath))
  214. {
  215. return PATH_IS_ERROR;
  216. }
  217. DWORD dwRes, dwAttr = GetFileAttributes(pszPath);
  218. if (dwAttr == 0xFFFFFFFF)
  219. {
  220. if (0xFFFFFFFF == GetFileAttributes(szRootPath))
  221. {
  222. // If the root path doesn't exist, then we're not going
  223. // to be able to create a path:
  224. return PATH_IS_ERROR;
  225. }
  226. else
  227. {
  228. return PATH_IS_NONEXISTENT;
  229. }
  230. }
  231. if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
  232. {
  233. return PATH_IS_NONDIR;
  234. }
  235. for (int i = 0; i < ARRAYSIZE(_adirs); i++)
  236. {
  237. TCHAR szPathToCheck[MAX_PATH];
  238. //
  239. // Check for various special shell folders
  240. //
  241. if (S_OK == SHGetFolderPath(hwnd, _adirs[i].dwDir | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPathToCheck))
  242. {
  243. dwRes = ComparePaths(pszPath, szPathToCheck);
  244. if (dwRes & _adirs[i].dwFlags)
  245. {
  246. //
  247. // The inevitable exceptions
  248. //
  249. switch (_adirs[i].dwDir)
  250. {
  251. case CSIDL_DESKTOP:
  252. if (PATH_IS_CHILD == dwRes)
  253. {
  254. continue; // allowing subfolder of CSIDL_DESKTOP
  255. }
  256. break;
  257. default:
  258. break;
  259. } // switch
  260. return _adirs[i].dwRet;
  261. }
  262. }
  263. }
  264. //
  265. // Make sure path isn't set as a system or some other kind of
  266. // folder that already has a CLSID or CLSID2 entry...
  267. //
  268. if (IsPathAlreadyShellFolder(pszPath, dwAttr))
  269. {
  270. return PATH_IS_SHELLFOLDER;
  271. }
  272. return PATH_IS_GOOD;
  273. }