Leaked source code of windows server 2003
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.

379 lines
14 KiB

  1. #include <cdlpch.h>
  2. #pragma hdrstop
  3. #include <winnls.h>
  4. #include <shlobj.h>
  5. CMutexSem g_mxsCDLGetLongPathNameGlobals;
  6. // stolen from shellp.h
  7. #define ILCreateFromPathORD 157
  8. #define ILFreeORD 155
  9. // stolen from shsemip.h
  10. typedef LPITEMIDLIST (WINAPI *ILCreateFromPathPtr)(LPCWSTR pszPath);
  11. typedef void (WINAPI *ILFreePtr)(LPITEMIDLIST pidl);
  12. // We'll use this if we're running on Memphis or NT5
  13. #ifdef UNICODE
  14. #define STR_GETLONGPATHNAMEW "GetLongPathNameW"
  15. typedef DWORD (WINAPI *GetLongPathNameWPtr)( LPCWSTR lpszShortPath,
  16. LPWSTR lpszLongPath,
  17. DWORD cchBuffer
  18. );
  19. #else
  20. #define STR_GETLONGPATHNAMEA "GetLongPathNameA"
  21. typedef DWORD (WINAPI *GetLongPathNameAPtr)( LPCSTR lpszShortPath,
  22. LPSTR lpszLongPath,
  23. DWORD cchBuffer
  24. );
  25. #endif
  26. STATIC ILCreateFromPathPtr s_pfnILCreate;
  27. STATIC ILFreePtr s_pfnILFree;
  28. STATIC GetLongPathNameAPtr s_pfnGetLongPathNameA;
  29. #define cKnownDirs 5
  30. STATIC struct KnownDirsMap {
  31. BOOL m_bInited;
  32. LPTSTR m_aszCaches[cKnownDirs];
  33. struct _tagKDMap {
  34. TCHAR szShort[MAX_PATH];
  35. int cchShort;
  36. TCHAR szCanonical[MAX_PATH];
  37. int cchCanonical;
  38. } m_aKDMap[cKnownDirs];
  39. int IndexKnownDirs( LPTSTR szName )
  40. {
  41. int i;
  42. for ( i = 0; i < cKnownDirs; i++ ) {
  43. // we only want to compare out through the cache folder itself
  44. BOOL fMatch = (m_aKDMap[i].cchShort != 0 &&
  45. CompareString( LOCALE_SYSTEM_DEFAULT,
  46. NORM_IGNORECASE,
  47. m_aKDMap[i].szShort,
  48. m_aKDMap[i].cchShort,
  49. szName,
  50. m_aKDMap[i].cchShort ) == 2)
  51. ||
  52. (m_aKDMap[i].cchCanonical != 0 &&
  53. CompareString( LOCALE_SYSTEM_DEFAULT,
  54. NORM_IGNORECASE,
  55. m_aKDMap[i].szCanonical,
  56. m_aKDMap[i].cchCanonical,
  57. szName,
  58. m_aKDMap[i].cchCanonical ) == 2);
  59. if ( fMatch )
  60. break;
  61. }
  62. if ( i >= cKnownDirs )
  63. i = -1; // signal a miss
  64. return i;
  65. };
  66. } s_kdMap = {
  67. FALSE,
  68. {
  69. "\\Occache\\",
  70. "\\OC Cache\\",
  71. "\\Downloaded ActiveX Controls\\",
  72. "\\Downloaded Components\\",
  73. "\\Downloaded Program Files\\"
  74. },
  75. {
  76. { 0, 0, 0, 0 },
  77. { 0, 0, 0, 0 },
  78. { 0, 0, 0, 0 }
  79. }
  80. };
  81. DWORD GetFullPathNameA_Wrap(
  82. LPCSTR lpFileName, // file name
  83. DWORD nBufferLength, // size of path buffer
  84. LPSTR lpBuffer, // path buffer
  85. LPSTR *lpFilePart // address of file name in path
  86. )
  87. {
  88. DWORD dwRet = GetFullPathName(lpFileName, nBufferLength, lpBuffer, lpFilePart);
  89. if (dwRet && (dwRet<nBufferLength))
  90. {
  91. if (! *lpFilePart)
  92. {
  93. // to prevent crashes where lpFileName == directory name terminating with '\'.
  94. // See IE6 bug 19001
  95. *lpFilePart = lpBuffer+dwRet; //make it point to NULL char.
  96. }
  97. }
  98. else
  99. {
  100. dwRet = 0;
  101. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  102. }
  103. return dwRet;
  104. }
  105. STATIC BOOL IsCanonicalName( LPCTSTR szName )
  106. {
  107. // simple test - if there's a ~ in it, it has a contraction in it
  108. // and is therefore non-canonical
  109. for ( ; *szName != '\0' && *szName != '~'; szName++ );
  110. return *szName != '~';
  111. }
  112. STATIC DWORD s_CDLGetLongPathName( LPTSTR szLong, LPCTSTR szShort, DWORD cchBuffer)
  113. {
  114. HRESULT hr = E_FAIL;
  115. HMODULE hmodS32;
  116. HMODULE hmodK32;
  117. DWORD cchLong = 0;
  118. // Don't jump through all these hoops if there aren't any contractions in it.
  119. if ( IsCanonicalName( szShort ) ) {
  120. lstrcpyn( szLong, szShort, cchBuffer );
  121. return lstrlen( szLong );
  122. }
  123. hmodS32 = LoadLibrary( "SHELL32.DLL" );
  124. hmodK32 = LoadLibrary( "KERNEL32.DLL" );
  125. // Set up our globals with short and long versions of the base cache path
  126. if ( hmodS32 && hmodK32 ) {
  127. #ifdef UNICODE
  128. s_pfnGetLongPathNameW = (GetLongPathNameWPtr)GetProcAddress(hmodK32, (LPCSTR)STR_GETLONGPATHNAMEW );
  129. #else
  130. s_pfnGetLongPathNameA = (GetLongPathNameAPtr)GetProcAddress(hmodK32, (LPCSTR)STR_GETLONGPATHNAMEA );
  131. #endif
  132. s_pfnILCreate = (ILCreateFromPathPtr)GetProcAddress(hmodS32, (LPCSTR)ILCreateFromPathORD );
  133. s_pfnILFree = (ILFreePtr)GetProcAddress(hmodS32, (LPCSTR)ILFreeORD );
  134. if ( s_pfnILCreate != NULL && s_pfnILFree != NULL ) {
  135. // We need to initialize our static know directories table.
  136. // To be safe, we'll grab a mutex while we do the dirty deed.
  137. { // constrain the scope of the mutex
  138. CLock lck(g_mxsCDLGetLongPathNameGlobals);
  139. if ( !s_kdMap.m_bInited ) {
  140. TCHAR szWinDir[MAX_PATH];
  141. int i;
  142. GetWindowsDirectory( szWinDir, MAX_PATH );
  143. for ( i = 0; i < cKnownDirs; i++ ) {
  144. lstrcpy( s_kdMap.m_aKDMap[i].szCanonical, szWinDir );
  145. StrCatBuff( s_kdMap.m_aKDMap[i].szCanonical, s_kdMap.m_aszCaches[i], MAX_PATH);
  146. s_kdMap.m_aKDMap[i].cchCanonical = lstrlen( s_kdMap.m_aKDMap[i].szCanonical );
  147. GetShortPathName( s_kdMap.m_aKDMap[i].szCanonical,
  148. s_kdMap.m_aKDMap[i].szShort, MAX_PATH );
  149. s_kdMap.m_aKDMap[i].cchShort = lstrlen( s_kdMap.m_aKDMap[i].szShort );
  150. }
  151. s_kdMap.m_bInited = TRUE;
  152. }
  153. }
  154. TCHAR *pch;
  155. TCHAR *szT = new CHAR[MAX_PATH];
  156. if ( szShort != NULL && szT != NULL && s_kdMap.m_bInited ) {
  157. LPITEMIDLIST pidl = NULL;
  158. // Okay, kids, now this gets fun.
  159. // If we're on Memphis or NT5, we can simply call GetLongPathName
  160. // to get the canonical format.
  161. // If not, we've a bit more work ahead of us.
  162. // If the path is not down into one of the dowload cache folders,
  163. // we can use shell32 functions to create the long path. This involves
  164. // generating a pidl for the file, then converting the pidl back into a path.
  165. // If the path goes down into one of the cache directories, we're in
  166. // a bit of trouble, because OC cache does not implement
  167. // IShellFolder::ParseDisplayName, so we cannot generate a proper pidl.
  168. // In that case, we use prefab paths to these known directories
  169. // and tack on the long name of the file.
  170. #ifdef UNICODE
  171. // NOTE: the prototypes in Winbase.h are deceiving - look at the argument types,
  172. // NOT at the argument names, the output parameter is second!
  173. if ( s_pfnGetLongPathNameW ) {
  174. hr = (s_pfnGetLongPathNameW( szShort, szLong, MAX_PATH ) != 0)? S_OK : E_FAIL;
  175. #else
  176. if ( s_pfnGetLongPathNameA ) {
  177. hr = (s_pfnGetLongPathNameA( szShort, szLong, MAX_PATH ) != 0)? S_OK : E_FAIL;
  178. #endif
  179. } else {
  180. LPTSTR szFileName;
  181. if ( GetFullPathNameA_Wrap( szShort, MAX_PATH, szT, &szFileName ) ) {
  182. int iKnownDir = s_kdMap.IndexKnownDirs( szT );
  183. if ( iKnownDir >= 0 ) {
  184. WIN32_FIND_DATA wfd;
  185. int cchBase = lstrlen(szT) - lstrlen(szFileName);
  186. // okay, it's in one of our caches
  187. if ( !IsCanonicalName( szFileName ) ) {
  188. HANDLE hfind = FindFirstFile( szShort, &wfd );
  189. if ( hfind != INVALID_HANDLE_VALUE ) {
  190. szFileName = wfd.cFileName;
  191. FindClose(hfind);
  192. } else
  193. szFileName = NULL;
  194. }
  195. if ( szFileName != NULL ) {
  196. StrNCpy( szLong, s_kdMap.m_aKDMap[iKnownDir].szCanonical, cchBuffer);
  197. // chop off szT right before the file name.
  198. // if this is longer than the known dir name, we have a
  199. // conflict.* subdirectory and must add this before the file
  200. if ( cchBase != s_kdMap.m_aKDMap[iKnownDir].cchShort &&
  201. cchBase != s_kdMap.m_aKDMap[iKnownDir].cchCanonical ) {
  202. LPTSTR szConflict;
  203. CHAR chT = szT[cchBase];
  204. szT[cchBase] = '\0';
  205. // search back from before the file name.
  206. for ( szConflict = &szT[cchBase - 2];
  207. *szConflict != '\\'; szConflict-- );
  208. szConflict++; // we already have a '\'
  209. StrCatBuff( szLong, szConflict, cchBuffer);
  210. szT[cchBase] = chT;
  211. }
  212. StrCatBuff( szLong, szFileName, cchBuffer);
  213. hr = S_OK;
  214. }
  215. } else {
  216. #ifndef UNICODE
  217. WCHAR *szwT = new WCHAR[MAX_PATH];
  218. if ( szwT &&
  219. MultiByteToWideChar( CP_ACP, 0, szShort, -1, szwT, MAX_PATH ) )
  220. pidl = s_pfnILCreate( szwT );
  221. delete szwT;
  222. #else
  223. pidl = s_pfnILCreate( szShort );
  224. #endif
  225. if ( pidl != NULL ) {
  226. // Now we get the shell to turn the item id list back into
  227. // a path rich in long file names, which is our canonical form
  228. if ( SHGetPathFromIDList( pidl, szT ) ) {
  229. if ( szLong != NULL ) {
  230. #ifdef UNICODE
  231. LPWSTR szLongW;
  232. hr = Ansi2Unicode( szT, &szLongW );
  233. if ( SUCCEEDED(hr) ) {
  234. StrCpyNW( szLong, szLongW, cchBuffer );
  235. delete szLongW;
  236. }
  237. #else
  238. //BUFFER OVERRUN lstrcpyA( szLong, szT );
  239. StrCpyN( szLong, szT, cchBuffer );
  240. hr = S_OK;
  241. #endif
  242. }
  243. }
  244. s_pfnILFree( pidl );
  245. } // if we got the pidl
  246. } // else we're getting shell32 to do the dirty-work
  247. } // if we got the full path/file name
  248. } // else we can't use GetLongPathName
  249. delete szT;
  250. } // if we can get out temp string
  251. }
  252. FreeLibrary( hmodS32 );
  253. FreeLibrary( hmodK32 );
  254. }
  255. return ((hr==S_OK)? lstrlen(szLong) : 0);
  256. }
  257. STDAPI_(DWORD) CDLGetLongPathNameA( LPSTR szLong, LPCSTR szShort, DWORD cchBuffer)
  258. {
  259. DEBUG_ENTER((DBG_DOWNLOAD,
  260. Dword,
  261. "CDLGetLongPathNameA",
  262. "%.80q, %.80q, %#x",
  263. szLong, szShort, cchBuffer
  264. ));
  265. HRESULT hr;
  266. if (!szShort || !szLong)
  267. {
  268. hr = E_FAIL;
  269. DEBUG_LEAVE(hr);
  270. return hr;
  271. }
  272. #ifndef UNICODE
  273. hr = s_CDLGetLongPathName( szLong, szShort, cchBuffer );
  274. DEBUG_LEAVE(hr);
  275. return hr;
  276. #else
  277. LPWSTR szShortW;
  278. TCHAR szLongT[MAX_PATH];
  279. hr = Ansi2Unicode( szShort, &szShortW );
  280. if ( SUCCEEDED(hr) ) {
  281. hr = s_CDLGetLongPathName( szLongT, szShortW, MAX_PATH );
  282. if ( SUCCEEDED(hr) ) {
  283. LPSTR szLongA;
  284. hr = Unicode2Ansi( szLongT, &szLongA );
  285. if ( SUCCEEDED(hr) ) {
  286. lstrcpynA( szLong, szLongA, cchBuffer );
  287. delete szLongA;
  288. }
  289. }
  290. delete szShortW;
  291. }
  292. DEBUG_LEAVE(hr);
  293. return hr;
  294. #endif
  295. }
  296. STDAPI_(DWORD) CDLGetLongPathNameW( LPWSTR szLong, LPCWSTR szShort, DWORD cchBuffer)
  297. {
  298. DEBUG_ENTER((DBG_DOWNLOAD,
  299. Dword,
  300. "CDLGetLongPathNameW",
  301. "%.80q, %.80q, %#x",
  302. szLong, szShort, cchBuffer
  303. ));
  304. HRESULT hr;
  305. if (!szShort || !szLong)
  306. {
  307. hr = E_FAIL;
  308. DEBUG_LEAVE(hr);
  309. return hr;
  310. }
  311. #ifdef UNICODE
  312. hr = s_CDLGetLongPathName( szLong, szShort, cchBuffer );
  313. DEBUG_LEAVE(hr);
  314. return hr;
  315. #else
  316. LPSTR szShortA;
  317. TCHAR szLongT[MAX_PATH];
  318. hr = Unicode2Ansi( szShort, &szShortA );
  319. if ( SUCCEEDED(hr) ) {
  320. hr = s_CDLGetLongPathName( szLongT, szShortA, cchBuffer );
  321. if ( SUCCEEDED(hr) ) {
  322. LPWSTR szLongW;
  323. hr = Ansi2Unicode( szLongT, &szLongW );
  324. if ( SUCCEEDED(hr) ) {
  325. StrCpyNW( szLong, szLongW, cchBuffer );
  326. delete szLongW;
  327. }
  328. }
  329. delete szShortA;
  330. }
  331. DEBUG_LEAVE(hr);
  332. return hr;
  333. #endif
  334. }