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.

623 lines
15 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. LPWSTR APIENTRY
  4. SheRemoveQuotesW(
  5. LPWSTR sz)
  6. {
  7. LPWSTR lpT;
  8. if (WCHAR_QUOTE == *sz) {
  9. for (lpT = sz+1; *lpT && WCHAR_QUOTE != *lpT; lpT++) {
  10. *(lpT-1) = *lpT;
  11. }
  12. if (WCHAR_QUOTE == *lpT) {
  13. *(lpT-1) = WCHAR_NULL;
  14. }
  15. }
  16. return(sz);
  17. }
  18. LPSTR APIENTRY
  19. SheRemoveQuotesA(
  20. LPSTR sz)
  21. {
  22. LPSTR lpT;
  23. if (CHAR_QUOTE == *sz) {
  24. for (lpT = sz+1; *lpT && CHAR_QUOTE != *lpT; lpT++) {
  25. *(lpT-1) = *lpT;
  26. #if (defined(DBCS) || defined(FE_SB))
  27. if (IsDBCSLeadByte(*lpT)) {
  28. lpT++;
  29. *(lpT-1) = *lpT;
  30. }
  31. #endif
  32. }
  33. if (CHAR_QUOTE == *lpT) {
  34. *(lpT-1) = CHAR_NULL;
  35. }
  36. }
  37. return(sz);
  38. }
  39. /////////////////////////////////////////////////////////////////////
  40. //
  41. // Name: SheShortenPathA
  42. //
  43. // Synopsis: Thunk to ShortenPathW
  44. //
  45. /////////////////////////////////////////////////////////////////////
  46. BOOL APIENTRY
  47. SheShortenPathA(LPSTR pPath, BOOL bShorten)
  48. {
  49. WCHAR pPathW[MAX_PATH];
  50. BOOL bRetVal;
  51. MultiByteToWideChar(CP_ACP, 0, pPath, -1, pPathW, MAX_PATH);
  52. bRetVal = SheShortenPathW(pPathW, bShorten);
  53. WideCharToMultiByte(CP_ACP, 0, pPathW, -1, pPath, MAX_PATH,
  54. NULL, NULL);
  55. return bRetVal;
  56. }
  57. /////////////////////////////////////////////////////////////////////
  58. //
  59. // Name: SheShortenPath
  60. //
  61. // Synopsis: Takes a pathname and converts all dirs to shortnames/longnames
  62. //
  63. // INOUT: lpszPath -- Path to shorten/lengthen (May be in DQUOTES)
  64. // Must not be a commandline!
  65. //
  66. // bShorten -- T=shorten, F=Lengthen
  67. //
  68. // Return: BOOL T=Converted,
  69. // F=ran out of space, buffer left alone
  70. //
  71. //
  72. // Assumes: lpszPath takes the form {"}?:\{f\}*f{"} or {"}\\f\f\{f\}*f{"}
  73. // COUNTOF pSrc buffer >= MAXPATHELN
  74. //
  75. // Effects: Strips quotes out of pPath, if any
  76. //
  77. //
  78. // Notes:
  79. //
  80. /////////////////////////////////////////////////////////////////////
  81. BOOL APIENTRY
  82. SheShortenPathW(LPWSTR pPath, BOOL bShorten)
  83. {
  84. WCHAR szDest[MAX_PATH];
  85. LPWSTR pSrcNextSpec, pReplaceSpec;
  86. LPWSTR pDest, pNewName, p;
  87. LPWSTR pSrc;
  88. DWORD cchPathOffset;
  89. HANDLE hFind;
  90. WIN32_FIND_DATA FindData;
  91. UINT i;
  92. INT nSpaceLeft = MAX_PATH-1;
  93. pSrc = pPath;
  94. //
  95. // Eliminate d-quotes
  96. //
  97. for (p = pDest = pSrc; *p; p++, pDest++) {
  98. if (WCHAR_QUOTE == *p)
  99. p++;
  100. *pDest = *p;
  101. }
  102. *pDest = WCHAR_NULL;
  103. //
  104. // Strip out leading spaces
  105. //
  106. while (WCHAR_SPACE == *pSrc)
  107. pSrc++;
  108. //
  109. // Initialize pNewName so it is calculated once.
  110. //
  111. pNewName = bShorten ?
  112. FindData.cAlternateFileName :
  113. FindData.cFileName;
  114. //
  115. // Skip past \\foo\bar or <drive>:
  116. //
  117. pDest = szDest;
  118. pSrcNextSpec = pSrc;
  119. // reuse shell32 internal api that calculates path
  120. // offset. cchPathOffset will be the offset that when
  121. // added to the pointer will result in a pointer to the
  122. // backslash before the first part of the path
  123. cchPathOffset = SheGetPathOffsetW(pSrc);
  124. // Check to see if it's valid. If pSrc is not of the \\foo\bar
  125. // or <drive>: form we just do nothing
  126. if (0xFFFFFFFF == cchPathOffset) {
  127. return TRUE;
  128. }
  129. // cchPathOffset will then always be atleast 1 and is the
  130. // number of characters - 1 that we want to copy (that is, if 0
  131. // was permissible, it would denote 1 character).
  132. do {
  133. *pDest++ = *pSrcNextSpec++;
  134. if (!--nSpaceLeft)
  135. return FALSE;
  136. } while (cchPathOffset--);
  137. //
  138. // At this point, we have just the filenames that we can shorten:
  139. // \\foo\bar\it\is\here -> it\is\here
  140. // c:\angry\lions -> angry\lions
  141. //
  142. while(pSrcNextSpec) {
  143. //
  144. // pReplaceSpec holds the current spec we need to replace.
  145. // By default, if we can't find the altname, then just use this.
  146. //
  147. pReplaceSpec = pSrcNextSpec;
  148. //
  149. // Search for trailing "\"
  150. // pSrcNextSpec will point to the next spec to fix (*pSrcNextSpec=NULL if done)
  151. //
  152. for(;*pSrcNextSpec && WCHAR_BSLASH != *pSrcNextSpec; pSrcNextSpec++)
  153. ;
  154. if (*pSrcNextSpec) {
  155. //
  156. // If there is more, then pSrcNextSpec should point to it.
  157. // Also delimit this spec.
  158. //
  159. *pSrcNextSpec = WCHAR_NULL;
  160. } else {
  161. pSrcNextSpec = NULL;
  162. }
  163. hFind = FindFirstFile(pSrc, &FindData);
  164. //
  165. // We could exit as soon as this FindFirstFileFails,
  166. // but there's the special case of having execute
  167. // without read permission. This would fail since the lfn
  168. // is valid for lfn apps.
  169. //
  170. if (INVALID_HANDLE_VALUE != hFind) {
  171. FindClose(hFind);
  172. if (pNewName[0]) {
  173. //
  174. // We have found an altname.
  175. // Use it instead.
  176. //
  177. pReplaceSpec = pNewName;
  178. }
  179. }
  180. i = wcslen(pReplaceSpec);
  181. nSpaceLeft -= i;
  182. if (nSpaceLeft <= 0)
  183. return FALSE;
  184. if(FAILED(StringCchCopy(pDest, ARRAYSIZE(szDest) - (pDest - szDest), pReplaceSpec)))
  185. {
  186. return FALSE;
  187. }
  188. pDest+=i;
  189. //
  190. // Now replace the WCHAR_NULL with a slash if necessary
  191. //
  192. if (pSrcNextSpec) {
  193. *pSrcNextSpec++ = WCHAR_BSLASH;
  194. //
  195. // Also add backslash to dest
  196. //
  197. *pDest++ = WCHAR_BSLASH;
  198. nSpaceLeft--;
  199. }
  200. }
  201. // !! MAX_PATH assumed, the only current (04/09/02) caller is. Note: this is a public API, so it can't be changed to pass the buffer size
  202. if(FAILED(StringCchCopy(pPath, MAX_PATH, szDest)))
  203. {
  204. return FALSE;
  205. }
  206. return TRUE;
  207. }
  208. /*
  209. * Reads the list of program strings from win.ini
  210. */
  211. LPWSTR GetPrograms()
  212. {
  213. static LPWSTR lpPrograms = WCHAR_NULL;
  214. LPWSTR lpT,lpS;
  215. if (lpPrograms) {
  216. return lpPrograms;
  217. }
  218. if (!(lpPrograms = (LPWSTR)LocalAlloc(LPTR, (MAX_PATH+1) * sizeof(WCHAR)))) {
  219. return(NULL);
  220. }
  221. else
  222. {
  223. GetProfileString(L"windows",L"programs",WSTR_BLANK,lpPrograms,MAX_PATH);
  224. for (lpS = lpT = lpPrograms; *lpT; lpT++)
  225. {
  226. if (*lpT == WCHAR_SPACE) {
  227. while (*lpT == WCHAR_SPACE) {
  228. lpT++;
  229. }
  230. lpT--;
  231. *lpS++ = 0;
  232. } else {
  233. *lpS++ = *lpT;
  234. }
  235. }
  236. *lpS++ = WCHAR_NULL;
  237. *lpS++ = WCHAR_NULL;
  238. return(lpPrograms);
  239. }
  240. }
  241. /* finds a file along the path. Returns the error code or 0 if success.
  242. */
  243. static WORD
  244. _SearchForFile(
  245. LPCWSTR lpDir,
  246. LPWSTR lpFile,
  247. LPWSTR lpFullPath,
  248. DWORD cchFullPath,
  249. LPWSTR lpExt,
  250. DWORD cchExt)
  251. {
  252. LPWSTR lpT;
  253. LPWSTR lpD;
  254. LPWSTR lpExts;
  255. WCHAR szFile[MAX_PATH];
  256. DWORD cchPath;
  257. if (*lpFile == WCHAR_QUOTE) {
  258. lpFile = SheRemoveQuotes(lpFile);
  259. }
  260. if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_BSLASH))) {
  261. ++lpT;
  262. } else if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_COLON))) {
  263. ++lpT;
  264. } else {
  265. lpT = lpFile;
  266. }
  267. if (NULL != (lpT=StrRChrW(lpT, NULL, WCHAR_DOT)))
  268. {
  269. StringCchCopy(lpExt, MAX_PATH, lpT + 1); // changed the (only one I see) caller to MAX_PATH
  270. }
  271. else
  272. {
  273. *lpExt = WCHAR_NULL;
  274. }
  275. // If there's no extension then just use programs list don't
  276. // try searc hing for the app sans extension. This fixes the bogus
  277. // file.run stuff.
  278. if (!*lpExt) {
  279. goto UseDefExts;
  280. }
  281. //
  282. // NOTE: Do NOT call CharUpper for any of the strings in this routine.
  283. // It will cause problems for the Turkish locale.
  284. //
  285. cchPath = SearchPath(lpDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  286. if (!cchPath) {
  287. cchPath = SearchPath(NULL, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  288. }
  289. if (cchPath >= cchFullPath) {
  290. return(SE_ERR_OOM);
  291. }
  292. if (cchPath == 0) {
  293. return(SE_ERR_FNF);
  294. }
  295. CheckEscapes(lpFullPath, cchFullPath);
  296. return 0;
  297. UseDefExts:
  298. if(FAILED(StringCchCopy(szFile, ARRAYSIZE(szFile), lpFile)))
  299. {
  300. return SE_ERR_OOM;
  301. }
  302. lpFile = szFile;
  303. if(FAILED(StringCchCat(lpFile, ARRAYSIZE(szFile), WSTR_DOT)))
  304. {
  305. return SE_ERR_OOM;
  306. }
  307. lpD = lpFile + wcslen(lpFile);
  308. if (NULL != (lpExts = GetPrograms()))
  309. {
  310. // We want to pass through the loop twice checking whether the
  311. // file is in lpDir first, and then if it's in the sysdirs, via SearchPath(NULL, ...)
  312. // Add some state and extend the while loop
  313. LPCWSTR lpTempDir = lpDir;
  314. LPWSTR lpTempExts = lpExts;
  315. BOOL bCheckedSysDirs = FALSE;
  316. while (*lpTempExts || !bCheckedSysDirs)
  317. {
  318. // After the first pass, lpTempExts will be NULL
  319. // Reset it and loop through again with lpTempDir = NULL so that
  320. // SearchPath looks at the system dirs
  321. if (!*lpTempExts) {
  322. bCheckedSysDirs = TRUE;
  323. lpTempExts = lpExts;
  324. lpTempDir = NULL;
  325. }
  326. if(FAILED(StringCchCopy(lpD, ARRAYSIZE(szFile) - (lpD - szFile), lpTempExts)) ||
  327. FAILED(StringCchCopy(lpExt, MAX_PATH, lpTempExts)))
  328. {
  329. return SE_ERR_OOM;
  330. }
  331. cchPath = SearchPath(lpTempDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  332. if (cchPath >= cchFullPath)
  333. {
  334. return(SE_ERR_OOM);
  335. }
  336. if (cchPath != 0)
  337. {
  338. CheckEscapes(lpFullPath, cchFullPath);
  339. return 0;
  340. }
  341. lpTempExts += wcslen(lpTempExts)+1;
  342. }
  343. }
  344. return(SE_ERR_FNF);
  345. }
  346. /////////////////////////////////////////////////////////////////////
  347. //
  348. // Name: QualifyAppName
  349. //
  350. // Synopsis: Creates a fully qualified path to the app in a commandline
  351. //
  352. // INC lpCmdLine Command line to qualify
  353. // (Must have DQuotes if has spaces)
  354. // OUT lpImage Fully qualified result
  355. // OUT ppArgs Pointer to args in lpCmdLine, _incl_ leading space
  356. // OPTIONAL
  357. //
  358. // Return: DWORD length of path, 0 = fail
  359. //
  360. //
  361. // Assumes: len of executable in lpCmdLine is < MAX_PATH
  362. // len of exts are < 64
  363. //
  364. // Effects:
  365. //
  366. //
  367. // Notes:
  368. //
  369. /////////////////////////////////////////////////////////////////////
  370. DWORD
  371. QualifyAppName(
  372. IN LPCWSTR lpCmdLine,
  373. OUT LPWSTR lpImage,
  374. OPTIONAL OUT LPCWSTR* ppArgs)
  375. {
  376. LPWSTR lpAppName;
  377. BOOL bAppNameInQuotes = FALSE;
  378. DWORD cch = 0;
  379. lpAppName = lpImage;
  380. // sanity check
  381. if (!lpCmdLine) {
  382. return(0);
  383. }
  384. while (*lpCmdLine &&
  385. (*lpCmdLine != WCHAR_SPACE || bAppNameInQuotes)) {
  386. if (*lpCmdLine == WCHAR_QUOTE) {
  387. bAppNameInQuotes = !bAppNameInQuotes;
  388. lpCmdLine++;
  389. continue;
  390. }
  391. *lpAppName++ = *lpCmdLine++;
  392. cch++;
  393. }
  394. *lpAppName = WCHAR_NULL;
  395. //
  396. // Save the pointer to the argument list
  397. //
  398. if (ppArgs) {
  399. *ppArgs = lpCmdLine;
  400. }
  401. if (SheGetPathOffsetW(lpImage) == -1) {
  402. WCHAR szTemp[MAX_PATH];
  403. if(FAILED(StringCchCopy((LPWSTR)szTemp, ARRAYSIZE(szTemp), lpImage)))
  404. return 0;
  405. if (StrChrW(lpImage, WCHAR_DOT)) {
  406. LPWSTR lpFileName;
  407. return(SearchPath(NULL, szTemp, NULL, MAX_PATH, lpImage, &lpFileName));
  408. }
  409. else {
  410. WCHAR szExt[MAX_PATH];
  411. *lpImage = WCHAR_NULL;
  412. if (_SearchForFile(NULL, (LPWSTR)szTemp, lpImage, MAX_PATH, szExt, MAX_PATH)) {
  413. return(0);
  414. }
  415. return(lstrlen(lpImage));
  416. }
  417. }
  418. return(cch);
  419. }
  420. BOOL
  421. SheConvertPathW(
  422. LPWSTR lpCmdLine,
  423. LPWSTR lpFile,
  424. UINT cchCmdBuf)
  425. /*++
  426. Routine Description:
  427. Takes a command line and file and shortens both if the app in the
  428. command line is dos/wow.
  429. Returns: BOOL T=converted
  430. Arguments:
  431. INOUT lpCmdLine Command line to test
  432. exe must be in DQuotes if it has spaces,
  433. on return, will have DQuotes if necessary
  434. INOUT lpFile Fully qualified file to shorten
  435. May be in DQuotes, but on return will not
  436. have DQuotes (since single file)
  437. IN cchCmdBuf Size of buffer in characters
  438. Return Value:
  439. VOID, but lpFile shortened (in place) if lpCmdLine is dos/wow.
  440. There are pathalogoical "lfns" (Single unicode chars) that can
  441. actually get longer when they are shortened. In this case, we
  442. won't AV, but we will truncate the parms!
  443. // Qualify path assumes that the second parm is a buffer of
  444. // size atleast MAX_PATH, which is nicely equivalent to MAX_PATH
  445. // needs cleanup!
  446. --*/
  447. {
  448. LPWSTR lpszFullPath;
  449. LONG lBinaryType;
  450. BOOL bInQuote = FALSE;
  451. LPWSTR lpArgs;
  452. UINT cchNewLen;
  453. BOOL bRetVal = FALSE;
  454. lpszFullPath = (LPWSTR) LocalAlloc(LMEM_FIXED,
  455. cchCmdBuf*sizeof(*lpCmdLine));
  456. if (!lpszFullPath)
  457. return bRetVal;
  458. //
  459. // We must do the swap here since we need to copy the
  460. // parms back to lpCmdLine.
  461. //
  462. if(FAILED(StringCchCopy(lpszFullPath, cchCmdBuf, lpCmdLine)))
  463. {
  464. LocalFree(lpszFullPath);
  465. return FALSE;
  466. }
  467. if (QualifyAppName(lpszFullPath, lpCmdLine, &lpArgs)) {
  468. if (!GetBinaryType(lpCmdLine, &lBinaryType) ||
  469. lBinaryType == SCS_DOS_BINARY ||
  470. lBinaryType == SCS_WOW_BINARY) {
  471. SheShortenPath(lpCmdLine, TRUE);
  472. if (lpFile) {
  473. SheShortenPath(lpFile, TRUE);
  474. }
  475. bRetVal = TRUE;
  476. }
  477. //
  478. // Must readd quotes
  479. //
  480. CheckEscapes(lpCmdLine, cchCmdBuf);
  481. cchNewLen = lstrlen(lpCmdLine);
  482. if(FAILED(StringCchCopy(lpCmdLine + cchNewLen, cchCmdBuf - cchNewLen, lpArgs)))
  483. bRetVal = FALSE;
  484. } else
  485. {
  486. //
  487. // QualifyAppName failed, restore the command line back
  488. // to the original state.
  489. //
  490. if(FAILED(StringCchCopy(lpCmdLine, cchCmdBuf, lpszFullPath)))
  491. bRetVal = FALSE;
  492. }
  493. LocalFree((HLOCAL)lpszFullPath);
  494. return bRetVal;
  495. }