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.

3032 lines
100 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: main.cpp
  4. //
  5. // Module: CMDIAL32.DLL
  6. //
  7. // Synopsis: The main module implementing interfaces to external (RAS,
  8. // InetDialHandler and internal modules (CmCustomDialDlg).
  9. //
  10. // Copyright (c) 1996-1999 Microsoft Corporation
  11. //
  12. // Author: Dondu Created 96'
  13. //
  14. // History:
  15. // 5/05/97 Modified byao
  16. // added 'InetDialHandler()' for IE4
  17. // 1/26/98 Modified quintinb
  18. // Added RasCustomDialDlg, RasCustomEntryDlg,
  19. // RasCustomHangUp, and RasCustomDial stubs.
  20. // 02/10/98 Modified Heavily refvised for 1.2 architectural shift
  21. //
  22. //+----------------------------------------------------------------------------
  23. #include <windows.h>
  24. //#ifdef WIN32_LEAN_AND_MEAN
  25. //#include <shellapi.h>
  26. //#endif
  27. #include <ras.h>
  28. #include <raserror.h>
  29. #include <rasdlg.h>
  30. #include "cmmaster.h"
  31. #include <wininet.h>
  32. #include "cmtiming.h"
  33. #include "DynamicLib.h"
  34. #include "shelldll.cpp"
  35. //
  36. // Globals
  37. //
  38. HINSTANCE g_hInst;
  39. const TCHAR* const c_pszCmmgrExe = TEXT("CMMGR32.EXE");
  40. const TCHAR* const c_pszExplorerExe = TEXT("EXPLORER.EXE");
  41. const TCHAR* const c_pszCmstpExe = TEXT("CMSTP.EXE");
  42. const TCHAR* const c_pszRunDll32Exe = TEXT("RUNDLL32.EXE");
  43. const TCHAR* const c_pszRasAutoUExe = TEXT("RASAUTOU.EXE");
  44. const TCHAR* const c_pszConnectMutex = TEXT("Connection Manager Connect - "); // Root only
  45. #define RASDDFLAG_Reserved1 0x10000000
  46. //+---------------------------------------------------------------------------
  47. //
  48. // struct CmRasDialDlg
  49. //
  50. // Description: Append CM reconnect infor mation to the end of RASDIALDLG
  51. //
  52. // History: fengsun Created 11/14/97
  53. //
  54. //----------------------------------------------------------------------------
  55. struct CmRasDialDlg
  56. {
  57. RASDIALDLG RasDialDlgInfo;
  58. struct CmRasDialDlg* pSelf; // point to itself, used to verify CmReConnect
  59. DWORD dwSignature; // verify CmReConnect
  60. CMDIALINFO CmInfo; // Reconnect information
  61. enum {CM_RECONNECT_SIGNATURE = 0xC6687DB5}; // To verify dwSignature
  62. };
  63. //+----------------------------------------------------------------------------
  64. //
  65. // Function: StripTunnelSuffixW
  66. //
  67. // Synopsis: Determines if a connection name string contains a suffix of
  68. // " (for advanced use only)" and removes it if found.
  69. //
  70. // Arguments: LPWSTR pszwConnectionName - The string (connectoid name).
  71. //
  72. // Returns: Nothing
  73. //
  74. // History: nickball created 1/11/00
  75. //
  76. //+----------------------------------------------------------------------------
  77. void StripTunnelSuffixW(LPWSTR pszwConnectionName)
  78. {
  79. MYDBGASSERT(pszwConnectionName);
  80. if (NULL == pszwConnectionName)
  81. {
  82. return;
  83. }
  84. //
  85. // On 9X we have to resolve the tunnel connectoid name to the
  86. // service name by removing " (for advanced use only)" if it.
  87. // exists.
  88. //
  89. if (OS_W9X)
  90. {
  91. LPWSTR pszwSuffix = GetTunnelSuffix();
  92. //
  93. // Search for suffix and truncate as necessary.
  94. // Note: Assumes that the service name does not match our suffix.
  95. //
  96. if (pszwSuffix)
  97. {
  98. LPWSTR pszwTmp = CmStrStrW(pszwConnectionName, pszwSuffix);
  99. if (pszwTmp)
  100. {
  101. CMTRACE1(TEXT("StripTunnelSuffixW - found suffix of %s"), pszwTmp);
  102. *pszwTmp = L'\0';
  103. CMTRACE1(TEXT("StripTunnelSuffixW - long service name is %s"), pszwConnectionName);
  104. }
  105. }
  106. CmFree(pszwSuffix);
  107. }
  108. }
  109. //+----------------------------------------------------------------------------
  110. //
  111. // Function: IsStringWithInBuffer
  112. //
  113. // Synopsis: Verify whether a string go beyond the buffer.
  114. //
  115. //
  116. // Arguments: const TCHAR* pszStr - the string to be tested
  117. // DWORD dwSize - the size of the buffer
  118. //
  119. // Returns: BOOL - TRUE if is
  120. //
  121. // History: fengsun Created Header 5/22/98
  122. //
  123. //+----------------------------------------------------------------------------
  124. BOOL IsStringWithInBuffer(const TCHAR* pszStr, DWORD dwSize)
  125. {
  126. MYDBGASSERT(pszStr);
  127. //
  128. // Can not do strlen here. If the data in pszStr is garbage
  129. // strlen can cause access voilation
  130. //
  131. for (DWORD i =0; i<dwSize; i++)
  132. {
  133. if (pszStr[i] == TEXT('\0'))
  134. {
  135. return TRUE;
  136. }
  137. }
  138. return FALSE;
  139. }
  140. //+----------------------------------------------------------------------------
  141. //
  142. // Function: IsCmReconnectRequest
  143. //
  144. // Synopsis: Check whether CM reconnect information is appended to RASDIALDLG
  145. //
  146. // Arguments: const RASDIALDLG* lpInfo - the structure to check
  147. //
  148. // Returns: BOOL - TRUE if is
  149. //
  150. // History: fengsun Created Header 5/22/98
  151. //
  152. //+----------------------------------------------------------------------------
  153. BOOL IsCmReconnectRequest(const RASDIALDLG* lpInfo)
  154. {
  155. MYDBGASSERT(lpInfo);
  156. if (NULL == lpInfo)
  157. {
  158. return FALSE;
  159. }
  160. CMTRACE1(TEXT("IsCmReconnectRequest - RASDIALDLG.dwFlags is 0x%x"), ((LPRASDIALDLG)lpInfo)->dwFlags);
  161. //
  162. // See its our reconnect case by examining the dwFlags of lpInfo
  163. //
  164. if (((LPRASDIALDLG)lpInfo)->dwFlags & RASDDFLAG_Reserved1)
  165. {
  166. //
  167. // Test whether we can read beyond RASDIALDLG to avoid access violation
  168. //
  169. if (!IsBadReadPtr(lpInfo, sizeof(CmRasDialDlg)) )
  170. {
  171. CmRasDialDlg* const pCmDlgInfo = (CmRasDialDlg* const)lpInfo;
  172. //
  173. // Test whether it has the flag we added
  174. //
  175. if (pCmDlgInfo->pSelf == pCmDlgInfo &&
  176. pCmDlgInfo->dwSignature == CmRasDialDlg::CM_RECONNECT_SIGNATURE)
  177. {
  178. //
  179. // Whether the reconnect information is valid
  180. //
  181. //
  182. // Does password seem ok? Whether the password go beyond CmIndo.szPassword
  183. // We can not do strlen here. If we have some garbage here, strlen can
  184. // cauce access violation.
  185. //
  186. //
  187. // We no longer support reconnect due to security issues. The password is
  188. // not being kept in memory any longer.
  189. //
  190. }
  191. }
  192. }
  193. return FALSE;
  194. }
  195. //+----------------------------------------------------------------------------
  196. //
  197. // Function: CmReConnect
  198. //
  199. // Synopsis: Used specificly for CMMON to call upon reconnect
  200. // This function is added to fix bug 169128: RasCustomHangup not called
  201. // when hangup reconnected connection.
  202. // In order for RAS to call RasCustomHangup, we have to call RasDialDlg.
  203. // CMMON calls CmReConnect with reconnect information. CmReconnect append
  204. // CM specific information to the RASDIALDLG structure then calls RasDialDlg.
  205. // RasCustomHangup then figures out it is a reconnect request.
  206. //
  207. // Arguments: LPTSTR lpszPhonebook - Ptr to the full path and filename of the phonebook.
  208. // LPTSTR lpszEntry - Ptr to the name of the phone-book entry to dial.
  209. // LPCMDIALINFO lpCmInfo - The reconnect information
  210. //
  211. // Returns: DWORD WINAPI - Return code
  212. //
  213. //+----------------------------------------------------------------------------
  214. extern "C"
  215. BOOL CmReConnect(LPTSTR lpszPhonebook,
  216. LPTSTR lpszEntry,
  217. LPCMDIALINFO lpCmInfo)
  218. {
  219. CMTRACE(TEXT("CmReconnect"));
  220. if (OS_NT5)
  221. {
  222. //
  223. // Call RasDialDlg for NT5
  224. //
  225. CmRasDialDlg CmDlgInfo;
  226. ZeroMemory(&CmDlgInfo, sizeof(CmDlgInfo));
  227. CmDlgInfo.RasDialDlgInfo.dwSize = sizeof(CmDlgInfo.RasDialDlgInfo);
  228. CmDlgInfo.RasDialDlgInfo.dwFlags = RASDDFLAG_Reserved1;
  229. CmDlgInfo.CmInfo = *lpCmInfo;
  230. CmDlgInfo.pSelf = &CmDlgInfo;
  231. CmDlgInfo.dwSignature = (DWORD)CmRasDialDlg::CM_RECONNECT_SIGNATURE;
  232. //
  233. // Load rasdlg.dll
  234. //
  235. CDynamicLibrary libRasDlg(TEXT("rasdlg.dll"));
  236. MYDBGASSERT(libRasDlg.IsLoaded());
  237. typedef BOOL (WINAPI* fnRasDialDlgTYPE)(
  238. LPTSTR lpszPhonebook, LPTSTR lpszEntry, LPTSTR lpszPhoneNumber,
  239. LPRASDIALDLG lpInfo );
  240. #ifndef UNICODE
  241. LPSTR pszRasDialDlgText = {"RasDialDlgA"};
  242. #else
  243. LPSTR pszRasDialDlgText = {"RasDialDlgW"};
  244. #endif
  245. fnRasDialDlgTYPE fnRasDialDlg = (fnRasDialDlgTYPE)libRasDlg.GetProcAddress(pszRasDialDlgText);
  246. if (fnRasDialDlg)
  247. {
  248. //
  249. // We base on the assumption that RasDialDlg passes the same pointer to RasCustomDialDlg.
  250. //
  251. if (lpszPhonebook != NULL && lpszPhonebook[0] == TEXT('\0'))
  252. {
  253. return fnRasDialDlg(NULL, lpszEntry, NULL, (RASDIALDLG*)&CmDlgInfo);
  254. }
  255. return fnRasDialDlg(lpszPhonebook, lpszEntry, NULL, (RASDIALDLG*)&CmDlgInfo);
  256. }
  257. return FALSE;
  258. }
  259. else
  260. {
  261. //
  262. // For non-NT5 platform, call CmCustomDialDlg directly
  263. //
  264. return CmCustomDialDlg(NULL, // hwndParent
  265. RCD_AllUsers, // dwFlags
  266. lpszPhonebook,
  267. lpszEntry,
  268. NULL, // lpszPhoneNumber
  269. NULL, // lpRasDialDlg,
  270. NULL, // lpRasEntryDlg,
  271. lpCmInfo);
  272. }
  273. }
  274. //+----------------------------------------------------------------------------
  275. //
  276. // Function: WhoIsCaller
  277. //
  278. // Synopsis: Helper function to determine if we were called manually from the
  279. // desktop or programmatically.
  280. //
  281. // Arguments: dwCaller - which desktop caller.
  282. //
  283. // Returns: BOOL - TRUE if the caller matches one of those specified in dwCaller.
  284. //
  285. // History: nickball Created Header 3/17/98
  286. //
  287. //+----------------------------------------------------------------------------
  288. BOOL WhoIsCaller(DWORD dwCaller)
  289. {
  290. BOOL bRet = FALSE;
  291. TCHAR szTmp[MAX_PATH + 1];
  292. ZeroMemory(szTmp, sizeof(szTmp));
  293. //
  294. // Get the path of the calling process
  295. //
  296. MYVERIFY(GetModuleFileNameU(GetModuleHandleA(NULL), szTmp, MAX_PATH));
  297. CMTRACE1(TEXT("WhoIsCaller() - Calling process is %s"), szTmp);
  298. //
  299. // Locate the filename part
  300. //
  301. LPTSTR pszName = StripPath(szTmp);
  302. MYDBGASSERT(pszName);
  303. if (pszName)
  304. {
  305. //
  306. // Compare against CM and Shell
  307. //
  308. if (dwCaller & DT_CMMGR)
  309. {
  310. bRet = (lstrcmpiU(pszName, c_pszCmmgrExe) == 0);
  311. }
  312. if (!bRet && dwCaller & DT_CMMON)
  313. {
  314. bRet |= (lstrcmpiU(pszName, c_pszCmMonExeName) == 0);
  315. }
  316. if (!bRet && dwCaller & DT_EXPLORER)
  317. {
  318. bRet |= (lstrcmpiU(pszName, c_pszExplorerExe) == 0);
  319. }
  320. if (!bRet && dwCaller & DT_CMSTP)
  321. {
  322. bRet |= (lstrcmpiU(pszName, c_pszCmstpExe) == 0);
  323. }
  324. if (!bRet && dwCaller & DT_RUNDLL32)
  325. {
  326. bRet |= (lstrcmpiU(pszName, c_pszRunDll32Exe) == 0);
  327. }
  328. if (!bRet && dwCaller & DT_RASAUTOU)
  329. {
  330. bRet |= (lstrcmpiU(pszName, c_pszRasAutoUExe) == 0);
  331. }
  332. CmFree(pszName);
  333. }
  334. return bRet;
  335. }
  336. //+----------------------------------------------------------------------------
  337. //
  338. // Function: HandleCustomConnectRequest
  339. //
  340. // Synopsis: Attempts to resolve a connect request for the specified entry by
  341. // examining the current state if any of that connection.
  342. //
  343. // Arguments: HWND hwndParent - HWND of parent for user notification messages
  344. // CConnectionTable *pConnTable - Ptr to the connection table - assumed open
  345. // LPCTSTR pszEntry - The name of the service entry
  346. // DWORD dwFlags - The application flags FL_...
  347. // LPBOOL pfSuccess - Ptr to flag indicating that the request was
  348. // both 1) resolved and 2) already connected
  349. //
  350. // Returns: BOOL - TRUE if the request was resolved against the existing table data
  351. //
  352. // History: nickball Created 3/18/98
  353. //
  354. //+----------------------------------------------------------------------------
  355. BOOL HandleCustomConnectRequest(
  356. HWND hwndParent,
  357. CConnectionTable *pConnTable,
  358. LPCTSTR pszEntry,
  359. DWORD dwFlags,
  360. LPBOOL pfSuccess)
  361. {
  362. BOOL fResolvedInTable = FALSE;
  363. CM_CONNECTION Connection;
  364. ZeroMemory(&Connection, sizeof(CM_CONNECTION));
  365. //
  366. // Only if there is an existing entry do we have any work here
  367. //
  368. if (SUCCEEDED(pConnTable->GetEntry(pszEntry, &Connection)))
  369. {
  370. *pfSuccess = TRUE; // assume the best
  371. //
  372. // There is a connection entry for this service, examine state.
  373. //
  374. if (CM_RECONNECTPROMPT != Connection.CmState)
  375. {
  376. fResolvedInTable = TRUE; // we can handle it here
  377. //
  378. // The entry is connecting, connected, or disconnecting. If its a manual
  379. // connection just notify the user, otherwise check the exact state.
  380. //
  381. if (dwFlags & FL_DESKTOP) // Set in CMMGR
  382. {
  383. if (!(dwFlags & FL_UNATTENDED))
  384. {
  385. NotifyUserOfExistingConnection(hwndParent, &Connection, FALSE);
  386. }
  387. }
  388. else
  389. {
  390. //
  391. // Only if we are actually connected can we safely succeed.
  392. //
  393. if (CM_CONNECTED != Connection.CmState)
  394. {
  395. *pfSuccess = FALSE;
  396. }
  397. else
  398. {
  399. MYVERIFY(SUCCEEDED(pConnTable->AddEntry(Connection.szEntry, Connection.fAllUser))); // just bump ref
  400. }
  401. }
  402. }
  403. else
  404. {
  405. //
  406. // We must be in RECONNECT mode, if this connect request is
  407. // from another source, tell CMMON to stop its monitoring.
  408. //
  409. if (!(dwFlags & FL_RECONNECT))
  410. {
  411. //
  412. // Its not a reconnect, so notify CMMON
  413. //
  414. HangupNotifyCmMon(pConnTable, Connection.szEntry);
  415. }
  416. }
  417. }
  418. return fResolvedInTable;
  419. }
  420. //+---------------------------------------------------------------------------
  421. //
  422. // Function: InetDialHandler
  423. //
  424. // Synopsis: Ansi and only form of the AutoDial handler .
  425. //
  426. // Arguments: hwndParent[IN] Handle to parent window. No longer ignored.
  427. // pszConnectoid[IN] Connectoid name
  428. // dwFlags[IN] Custom dial handler execution flags
  429. // Current the following flags are supported
  430. // INTERNET_CUSTOMDIAL_CONNECT
  431. // INTERNET_CUSTOMDIAL_UNATTENDED
  432. // INTERNET_CUSTOMDIAL_DISCONNECT
  433. // These flags will be passed from WININET
  434. // lpdwRasError[OUT] RasError code returned from ICM
  435. //
  436. // Returns: The return type is different than one defined in wininet.h
  437. // TRUE: This handler handled the dial request (connected or not)
  438. // FALSE: This handler didn't handle the dial request
  439. //
  440. // When returning TRUE, lpdwRasError is set to:
  441. // ERROR_SUCCESS: Call completed
  442. // ERROR_USER_DISCONNECTION: User cancelled dial request
  443. // <other ras error> Dial attempt failed.
  444. //
  445. // This is a synchronous call. It should not return until the operation is complete.
  446. //
  447. // Note: We do not provide a wide form of this API as it is stored in
  448. // the szAutoDialfunc member of the RASENTRY downlevel. If the
  449. // wide form were avialable, RASAUTOU.EXE would call the function
  450. // (it appends A or W to the name that it finds in AutoDialFunc),
  451. // which would be inappropriate because the semantics of the
  452. // parameters differ even though the function prototypes match.
  453. //
  454. // History: byao Created - 05/05/97
  455. // quintinb Rewrote to use InetDialHandlerW - 03/09/99
  456. // nickball Removed InetDialHandlerW as it confuses RasAuto on NT4 - 07/28/99
  457. // quintinb Always return true if connect request handled #390890 - 08/19/99
  458. //
  459. //----------------------------------------------------------------------------
  460. extern "C" DWORD WINAPI InetDialHandler(HWND hwndParent,
  461. LPCSTR pszConnectoid,
  462. DWORD dwFlags,
  463. LPDWORD lpdwRasError)
  464. {
  465. MYDBGASSERT(pszConnectoid);
  466. MYDBGASSERT(lpdwRasError);
  467. CMTRACE(TEXT("InetDialHandler"));
  468. TCHAR szProfilePath[MAX_PATH+1];
  469. LPWSTR pszwConnectionName = NULL;
  470. LPTSTR pszRasPhoneBook = NULL;
  471. LPCMDIALINFO lpCmInfo = NULL;
  472. BOOL bRet = TRUE; // Read all comments before modifying this init value.
  473. BOOL bAllUser;
  474. //
  475. // Check whether the parameters are valid
  476. //
  477. if (lpdwRasError)
  478. {
  479. if (! ((INTERNET_CUSTOMDIAL_CONNECT == dwFlags) ||
  480. (INTERNET_CUSTOMDIAL_UNATTENDED == dwFlags) ||
  481. (INTERNET_CUSTOMDIAL_DISCONNECT == dwFlags) ||
  482. (INTERNET_CUSTOMDIAL_SHOWOFFLINE== dwFlags) ))
  483. {
  484. CMASSERTMSG(FALSE, TEXT("InetDialHandler called with invalid flag"));
  485. *lpdwRasError = ERROR_INVALID_PARAMETER;
  486. return FALSE;
  487. }
  488. if (!pszConnectoid || TEXT('\0') == pszConnectoid[0])
  489. {
  490. *lpdwRasError = ERROR_INVALID_PARAMETER;
  491. return FALSE;
  492. }
  493. }
  494. else
  495. {
  496. return FALSE;
  497. }
  498. //
  499. // Make a wide copy of the connectoid name. We also want a copy so
  500. // that we can modify if necessary. On 9x we will resolve tunnel
  501. // entry names down to the base connectoid/service name.
  502. //
  503. pszwConnectionName = SzToWzWithAlloc(pszConnectoid);
  504. MYDBGASSERT(pszwConnectionName);
  505. if (!pszwConnectionName)
  506. {
  507. *lpdwRasError = GetLastError();
  508. bRet = FALSE;
  509. goto InetDialHandlerExit;
  510. }
  511. StripTunnelSuffixW(pszwConnectionName);
  512. //
  513. // Handle the Hangup case
  514. //
  515. if (INTERNET_CUSTOMDIAL_DISCONNECT == dwFlags)
  516. {
  517. *lpdwRasError = CmCustomHangUp(NULL, pszwConnectionName, TRUE, FALSE);
  518. bRet = (ERROR_SUCCESS == *lpdwRasError);
  519. goto InetDialHandlerExit;
  520. }
  521. //
  522. // Its a connect request, setup CmInfo flags and call
  523. //
  524. lpCmInfo = (LPCMDIALINFO) CmMalloc(sizeof(CMDIALINFO));
  525. if (NULL == lpCmInfo)
  526. {
  527. *lpdwRasError = ERROR_NOT_ENOUGH_MEMORY;
  528. bRet = FALSE;
  529. goto InetDialHandlerExit;
  530. }
  531. if (INTERNET_CUSTOMDIAL_UNATTENDED == dwFlags )
  532. {
  533. //
  534. // Unattended dialing mode has been requested
  535. //
  536. lpCmInfo->dwCmFlags |= FL_UNATTENDED;
  537. }
  538. //
  539. // Note: Treat INTERNET_CUSTOMDIAL_SHOWOFFLINE the same as INTERNET_CUSTOMDIAL_CONNECT
  540. //
  541. bAllUser = ReadMapping(pszwConnectionName, szProfilePath, (sizeof(szProfilePath)/sizeof(TCHAR)), TRUE, TRUE); // TRUE == fAllUser, TRUE == bExpandEnvStrings
  542. if (FALSE == bAllUser)
  543. {
  544. if (FALSE == ReadMapping(pszwConnectionName, szProfilePath, (sizeof(szProfilePath)/sizeof(TCHAR)), FALSE, TRUE)) // FALSE == fAllUser, TRUE == bExpandEnvStrings
  545. {
  546. //
  547. // No mapping, no connection
  548. //
  549. *lpdwRasError = ERROR_INVALID_PARAMETER;
  550. bRet = FALSE;
  551. goto InetDialHandlerExit;
  552. }
  553. //
  554. // We have a single user profile path. If this is NT5, build a phonebook path
  555. //
  556. MYDBGASSERT(OS_NT5);
  557. if (OS_NT5)
  558. {
  559. pszRasPhoneBook = GetRasPbkFromNT5ProfilePath(szProfilePath);
  560. MYDBGASSERT(pszRasPhoneBook);
  561. }
  562. }
  563. //
  564. // InetDialHandler is usually an auto-dial case.
  565. // Exceptions are:
  566. // 1) When called from WinLogon.exe on NT4. NT #370311
  567. // 2) When called from Rundll32.exe.on any platform. 9x #127217
  568. //
  569. if ((FALSE == IsLogonAsSystem()) && (FALSE == WhoIsCaller(DT_RUNDLL32)))
  570. {
  571. lpCmInfo->dwCmFlags |= FL_AUTODIAL;
  572. }
  573. //
  574. // We set the error code based on whether or not we connected. However,
  575. // we should always return TRUE to indicate to WININET, etc. that we
  576. // handled the connection request (if we did actually handle it).
  577. // Otherwise the caller (eg.IE) will try to dial its own dialer. #390890
  578. //
  579. if (CmCustomDialDlg(hwndParent,
  580. bAllUser ? RCD_AllUsers : RCD_SingleUser,
  581. pszRasPhoneBook,
  582. pszwConnectionName,
  583. NULL,
  584. NULL,
  585. NULL,
  586. lpCmInfo))
  587. {
  588. *lpdwRasError = ERROR_SUCCESS;
  589. }
  590. else
  591. {
  592. *lpdwRasError = ERROR_USER_DISCONNECTION;
  593. }
  594. InetDialHandlerExit:
  595. CmFree(pszRasPhoneBook);
  596. CmFree(lpCmInfo);
  597. CmFree(pszwConnectionName);
  598. CMTRACE2(TEXT("InetDialHandler returns %u with *lpdwRasError %u"), bRet, *lpdwRasError);
  599. return bRet;
  600. }
  601. //+----------------------------------------------------------------------------
  602. //
  603. // Function: AutoDialFunc
  604. //
  605. // Synopsis: The original AutoDial callback function, provided for backward
  606. // compatibility.
  607. //
  608. // Arguments: HWND hwndParent - The hwnd of the caller.
  609. // LPCTSTR pszEntry - The name of the connection to be dialed
  610. // DWORD dwFlags - Specific behaviour for the dial session.
  611. // LPDWORD pdwRetCode - Buffer for return code.
  612. //
  613. // Returns: BOOL WINAPI - TRUE on success
  614. //
  615. // History: nickball Created Header 2/5/98
  616. //
  617. // Note: This is used by RAS on NT4 SP6
  618. //
  619. //+----------------------------------------------------------------------------
  620. extern "C" BOOL WINAPI AutoDialFunc(HWND hwndParent,
  621. LPCSTR pszEntry,
  622. DWORD dwFlags,
  623. LPDWORD pdwRetCode)
  624. {
  625. CMTRACE(TEXT("AutoDialFunc()"));
  626. MYDBGASSERT(OS_NT4);
  627. //
  628. // InetDialHandler always returns TRUE, thus we must determine success or
  629. // failure from the pdwRetCode. If this is ERROR_SUCCESS then we should
  630. // return TRUE, otherwise FALSE.
  631. //
  632. InetDialHandler(hwndParent, pszEntry, dwFlags, pdwRetCode);
  633. BOOL bRet = (ERROR_SUCCESS == *pdwRetCode);
  634. //
  635. // Always override pdwRetCode to ERROR_SUCCESS or RAS will throw an
  636. // unpleasant error. RAS is only interested in success or failure.
  637. //
  638. *pdwRetCode = ERROR_SUCCESS;
  639. CMTRACE2(TEXT("AutoDialFunc returns %u with *pdwRetCode %u"), bRet, *pdwRetCode);
  640. return bRet;
  641. }
  642. //+----------------------------------------------------------------------------
  643. //
  644. // Function: CopyRasInput
  645. //
  646. // Synopsis: Simple wrapper function to make copies of the parameters we
  647. // receive from RAS.
  648. //
  649. // Arguments: LPTSTR* ppszOurCopy - pointer to the string pointer to hold the return string
  650. // LPWSTR pszwStringFromRas - String from RAS
  651. //
  652. // Returns: BOOL - returns TRUE on Success, FALSE otherwise
  653. //
  654. // History: quintinb Created 4/13/99
  655. //
  656. //+----------------------------------------------------------------------------
  657. BOOL CopyRasInput(LPTSTR* ppszOurCopy, LPWSTR pszwStringFromRas)
  658. {
  659. if (pszwStringFromRas)
  660. {
  661. #ifndef _UNICODE
  662. *ppszOurCopy = WzToSzWithAlloc(pszwStringFromRas);
  663. #else
  664. *ppszOurCopy = CmStrCpyAllocW (pszwStringFromRas);
  665. #endif
  666. return (NULL != *ppszOurCopy);
  667. }
  668. return TRUE;
  669. }
  670. //+----------------------------------------------------------------------------
  671. //
  672. // Function: RasCustomDialDlg
  673. //
  674. // Synopsis: Our implementation of RasCustomDialDlg extension, analogous to
  675. // RasDialDlg, but providing custom functionality.
  676. //
  677. // Arguments: HINSTANCE hInstDll - The HINSTANCE of the caller.
  678. // DWORD dwFlags - Dial flags
  679. // LPTSTR lpszPhonebook - Ptr to the full path and filename of the phonebook.
  680. // LPTSTR lpszEntry - Ptr to the name of the phone-book entry to dial.
  681. // LPTSTR lpszPhoneNumber - Ptr toi replacement phone number
  682. // LPRASDIALDLG lpInfo - Ptr to structure for additional parameters
  683. //
  684. // Returns: BOOL WINAPI - TRUE on success
  685. //
  686. // History: nickball Created Header 2/5/98
  687. //
  688. //+----------------------------------------------------------------------------
  689. extern "C" BOOL WINAPI RasCustomDialDlg(HINSTANCE hInstDll,
  690. DWORD dwFlags,
  691. LPWSTR lpszwPhonebook,
  692. LPWSTR lpszwEntry,
  693. LPWSTR lpszwPhoneNumber,
  694. LPRASDIALDLG lpInfo,
  695. PVOID pVoid)
  696. {
  697. MYDBGASSERT(lpszwEntry);
  698. MYDBGASSERT(lpszwEntry[0]);
  699. MYDBGASSERT(lpInfo);
  700. CMTRACE1(TEXT("RasCustomDialDlg() - dwFlags = 0x%x"), dwFlags);
  701. if (lpInfo)
  702. {
  703. CMTRACE1(TEXT("RasCustomDialDlg() - (RASDIALDLG)lpInfo->dwFlags = 0x%x"), lpInfo->dwFlags);
  704. }
  705. if (NULL == lpszwEntry || 0 == lpszwEntry[0] || NULL == lpInfo)
  706. {
  707. return FALSE;
  708. }
  709. //
  710. // We have the minimum requirement of an entry name, get to work
  711. //
  712. BOOL fSuccess = TRUE;
  713. LPTSTR pszEntry = NULL;
  714. LPTSTR pszPhonebook = NULL;
  715. //
  716. // If we have a Phonebook name make a copy
  717. //
  718. fSuccess = CopyRasInput(&pszPhonebook, lpszwPhonebook);
  719. if (fSuccess)
  720. {
  721. //
  722. // If we have an entry name (always do), make a copy to work with
  723. //
  724. fSuccess = CopyRasInput(&pszEntry, lpszwEntry);
  725. if (fSuccess)
  726. {
  727. //
  728. // Its always a simple connect request, no flags, no caller ids
  729. //
  730. CMDIALINFO CmInfo;
  731. ZeroMemory(&CmInfo, sizeof(CMDIALINFO));
  732. //
  733. // If this is a reconnect request from CMMON, copy the information
  734. //
  735. if (lpInfo && IsCmReconnectRequest(lpInfo))
  736. {
  737. CmInfo = ((CmRasDialDlg* )lpInfo)->CmInfo;
  738. }
  739. else
  740. {
  741. //
  742. // If running under the system account its never an autodial
  743. //
  744. if (FALSE == IsLogonAsSystem())
  745. {
  746. //
  747. // See where the call originated. If not a desktop scenario
  748. // then set the AUTODIAL flag so that we do the right thing
  749. // down the line. This is ugly, but we have no other way of
  750. // making the determination. Note: That this entry point
  751. // only exists on NT5 and is only called by RAS so the perf
  752. // hit is contained and CMMGR is not a valid desktop scenario
  753. // so we don't have to check for it.
  754. //
  755. // DT_RASAUTOU - When ICS is enabled, rasauto starts the
  756. // rasautou.exe process to dial a connection. CM used to add
  757. // rasautou in the process watch list. The issue was that
  758. // it goes away after connecting, so cmmon32 thought it needed
  759. // to disconnected. Now, RASAUTOU is not a watched process
  760. // and cmmon32 does not disconnect.
  761. //
  762. if (FALSE == WhoIsCaller(DT_EXPLORER | DT_CMSTP | DT_RASAUTOU))
  763. {
  764. CmInfo.dwCmFlags |= FL_AUTODIAL;
  765. }
  766. }
  767. //
  768. // Note that we want to set the Unattended flag if RASDDFLAG_NoPrompt is set
  769. //
  770. if (lpInfo && (lpInfo->dwFlags & RASDDFLAG_NoPrompt))
  771. {
  772. CmInfo.dwCmFlags |= FL_UNATTENDED;
  773. CMTRACE(TEXT("RasCustomDialDlg - Setting CmInfo.dwCmFlags |= FL_UNATTENDED"));
  774. }
  775. }
  776. //
  777. // If we have a RASNOUSER struct, make sure to encode the password
  778. //
  779. LPRASNOUSER lpRasNoUser = NULL;
  780. CSecurePassword secureRasNoUserPW;
  781. if (NULL != pVoid)
  782. {
  783. if (0 == (dwFlags & RCD_Eap))
  784. {
  785. lpRasNoUser = (LPRASNOUSER) pVoid;
  786. //
  787. // Need to securely store the password from RASNOUSER structure so that
  788. // we don't leave it just encoded and sitting around for the duration of CM.
  789. // InitCredentials() will end up clearing it out of this structure after it
  790. // puts it into pArgs. Before CM exits, we need to copy the password back because it's
  791. // not our memory.
  792. //
  793. (VOID)secureRasNoUserPW.SetPassword(lpRasNoUser->szPassword);
  794. CmEncodePassword(lpRasNoUser->szPassword);
  795. }
  796. }
  797. fSuccess = CmCustomDialDlg(lpInfo ? lpInfo->hwndOwner : NULL,
  798. dwFlags,
  799. pszPhonebook,
  800. pszEntry,
  801. NULL,
  802. lpInfo,
  803. NULL,
  804. &CmInfo,
  805. pVoid);
  806. //
  807. // If we have a RASNOUSER struct, decode the password to make it plain text again
  808. //
  809. if (NULL != lpRasNoUser)
  810. {
  811. //
  812. // This should have already been wiped by InitCredentials()
  813. //
  814. CMASSERTMSG(0 == lstrlenU(lpRasNoUser->szPassword), TEXT("RasCustomDialDlg - RASNOUSER->szPassword isn't blank."));
  815. //
  816. // Now we need to copy the password back into RASNOUSER structure
  817. // because it was wiped by InitCredentials()
  818. //
  819. LPTSTR pszClearPassword = NULL;
  820. DWORD cbClearPassword = 0;
  821. BOOL fRetPassword = FALSE;
  822. fRetPassword = secureRasNoUserPW.GetPasswordWithAlloc(&pszClearPassword, &cbClearPassword);
  823. if (fRetPassword && pszClearPassword)
  824. {
  825. lstrcpynU(lpRasNoUser->szPassword, pszClearPassword, CELEMS(lpRasNoUser->szPassword));
  826. secureRasNoUserPW.ClearAndFree(&pszClearPassword, cbClearPassword);
  827. }
  828. }
  829. }
  830. }
  831. //
  832. // Cleanup and go home
  833. //
  834. CmFree(pszPhonebook);
  835. CmFree(pszEntry);
  836. CMTRACE1(TEXT("RasCustomDialDlg returning %d"), fSuccess);
  837. return fSuccess;
  838. }
  839. //+----------------------------------------------------------------------------
  840. //
  841. // Function: RasCustomEntryDlg
  842. //
  843. // Synopsis: Our implementation of RasCustomEntryDlg extension, analogous to
  844. // RasEntryDlg, but providing custom functionality.
  845. //
  846. // Arguments: HINSTANCE hInstDll - The HINSTANCE of the caller.
  847. // LPTSTR lpszPhonebook - Ptr to the full path and name of the phonebook to be edited.
  848. // LPTSTR lpszEntry - Ptr to the name of the entry to be edited.
  849. // LPRASENTRYDLG lpInfo - Ptr to structure containing additional parameters.
  850. //
  851. // Returns: BOOL WINAPI - TRUE on success
  852. //
  853. // History: nickball 2/5/98 Created Header
  854. // nickball 1/11/00 Now used on 9x, added use of function
  855. // StripTunnelSuffixW() to resolve 9x tunnel
  856. // connectoid names.
  857. //
  858. //+----------------------------------------------------------------------------
  859. extern "C" BOOL WINAPI RasCustomEntryDlg(HINSTANCE hInstDll,
  860. LPWSTR lpszwPhonebook,
  861. LPWSTR lpszwEntry,
  862. LPRASENTRYDLG lpInfo,
  863. DWORD dwFlags)
  864. {
  865. MYDBGASSERT(lpszwEntry);
  866. MYDBGASSERT(lpszwEntry[0]);
  867. CMTRACE1(TEXT("RasCustomEntryDlg() - dwFlags = 0x%x"), dwFlags);
  868. if (NULL == lpszwEntry || 0 == lpszwEntry[0])
  869. {
  870. return FALSE;
  871. }
  872. //
  873. // We have the minimum requirement of an entry name, get to work
  874. //
  875. BOOL fSuccess = TRUE;
  876. LPTSTR pszEntry = NULL;
  877. LPTSTR pszPhonebook = NULL;
  878. //
  879. // If we have a Phonebook name, make a copy to work with
  880. //
  881. fSuccess = CopyRasInput(&pszPhonebook, lpszwPhonebook);
  882. if (fSuccess)
  883. {
  884. //
  885. // If we have an entry name (always do), make a copy to work with
  886. //
  887. fSuccess = CopyRasInput(&pszEntry, lpszwEntry);
  888. if (fSuccess)
  889. {
  890. StripTunnelSuffixW(pszEntry); // Assumes we'll always compile Unicode
  891. //
  892. // Its always a properties request, set the flag and dial
  893. //
  894. LPCMDIALINFO lpCmInfo = (LPCMDIALINFO) CmMalloc(sizeof(CMDIALINFO));
  895. if (lpCmInfo)
  896. {
  897. lpCmInfo->dwCmFlags |= FL_PROPERTIES;
  898. fSuccess = CmCustomDialDlg(lpInfo ? lpInfo->hwndOwner : NULL,
  899. dwFlags,
  900. pszPhonebook,
  901. pszEntry,
  902. NULL,
  903. NULL,
  904. lpInfo,
  905. lpCmInfo);
  906. }
  907. else
  908. {
  909. fSuccess = FALSE;
  910. }
  911. CmFree(lpCmInfo);
  912. }
  913. }
  914. //
  915. // Cleanup and go home
  916. //
  917. CmFree(pszPhonebook);
  918. CmFree(pszEntry);
  919. return fSuccess;
  920. }
  921. //+----------------------------------------------------------------------------
  922. //
  923. // Function: RasCustomHangUp
  924. //
  925. // Synopsis: Our implementation of the RasCustomHangUp extension, analogous to
  926. // RasHangup, but providing custom functionality. This function is
  927. // only called on NT5
  928. //
  929. // Arguments: HRASCONN hRasConn - The handle of the connection to be terminated.
  930. //
  931. // Returns: DWORD WINAPI - Return code
  932. //
  933. // History: nickball Created Header 2/5/98
  934. //
  935. //+----------------------------------------------------------------------------
  936. extern "C" DWORD WINAPI RasCustomHangUp(HRASCONN hRasConn)
  937. {
  938. //
  939. // If someone is calling this function on a system other then NT5, assert.
  940. //
  941. MYDBGASSERT(OS_NT5);
  942. MYDBGASSERT(hRasConn);
  943. CMTRACE(TEXT("RasCustomHangup()"));
  944. DWORD dwRes = ERROR_SUCCESS;
  945. //
  946. // First try to open the table, if none found then succeed.
  947. //
  948. CConnectionTable ConnTable;
  949. if (FAILED(ConnTable.Open()))
  950. {
  951. CMTRACE(TEXT("RasCustomHangup() - ConnTable.Open() Failed."));
  952. return dwRes;
  953. }
  954. //
  955. // If we have an entry, do the Disconnect
  956. //
  957. CM_CONNECTION Connection;
  958. ZeroMemory(&Connection, sizeof(CM_CONNECTION));
  959. if (SUCCEEDED(ConnTable.GetEntry(hRasConn, &Connection)))
  960. {
  961. MYDBGASSERT(hRasConn == Connection.hDial || hRasConn == Connection.hTunnel);
  962. //
  963. // Check connect state of entry.
  964. // If we are already in the DISCONNECTING state, perform a simple hangup.
  965. //
  966. if (CM_DISCONNECTING == Connection.CmState)
  967. {
  968. //
  969. // Set up RAS linkage
  970. //
  971. RasLinkageStruct rlsRasLink;
  972. ZeroMemory(&rlsRasLink, sizeof(RasLinkageStruct));
  973. if (TRUE == LinkToRas(&rlsRasLink) && rlsRasLink.pfnHangUp)
  974. {
  975. //
  976. // Linkage is good, make the hangup call
  977. //
  978. dwRes = DoRasHangup(&rlsRasLink, hRasConn);
  979. }
  980. else
  981. {
  982. MYDBGASSERT(FALSE);
  983. dwRes = ERROR_NOT_READY;
  984. }
  985. //
  986. // Cleanup
  987. //
  988. UnlinkFromRas(&rlsRasLink);
  989. }
  990. else
  991. {
  992. //
  993. // If we're still here then we are not in the middle of an existing
  994. // disconnect, handle disconnect as we otherwise would.
  995. //
  996. dwRes = Disconnect(&ConnTable, &Connection, FALSE, FALSE);
  997. }
  998. }
  999. else
  1000. {
  1001. dwRes = ERROR_NOT_FOUND;
  1002. }
  1003. //
  1004. // We are done with the table, close it now.
  1005. //
  1006. MYVERIFY(SUCCEEDED(ConnTable.Close()));
  1007. return dwRes;
  1008. }
  1009. //+----------------------------------------------------------------------------
  1010. //
  1011. // Function: RasCustomDial
  1012. //
  1013. // Synopsis: Our implementation of RasCustomDial which we don't support.
  1014. // Provided so that we can return E_NOTIMPL to indicate our lack of
  1015. // support for this extension.
  1016. //
  1017. // Arguments: N/A
  1018. //
  1019. // Returns: DWORD WINAPI - E_NOTIMPL
  1020. //
  1021. // History: nickball Created Header 2/5/98
  1022. //
  1023. //+----------------------------------------------------------------------------
  1024. extern "C" DWORD WINAPI RasCustomDial(
  1025. HINSTANCE hInstDll,
  1026. LPRASDIALEXTENSIONS lpRasDialExtensions,
  1027. LPWSTR lpszPhonebook,
  1028. LPRASDIALPARAMSW lpRasDialParams,
  1029. DWORD dwNotifierType,
  1030. LPVOID lpvNotifier,
  1031. LPHRASCONN lphRasConn,
  1032. DWORD dwFlags)
  1033. {
  1034. return E_NOTIMPL;
  1035. }
  1036. //+----------------------------------------------------------------------------
  1037. //
  1038. // Function: RasCustomDeleteEntryNotify
  1039. //
  1040. // Synopsis: Our implementation of RasCustomDeleteEntry.
  1041. //
  1042. // Arguments:
  1043. //
  1044. // Returns: DWORD WINAPI -
  1045. //
  1046. // History: quintinb Created Header 2/5/98
  1047. //
  1048. //+----------------------------------------------------------------------------
  1049. extern "C" DWORD WINAPI RasCustomDeleteEntryNotify(LPWSTR pszPhonebook, LPWSTR pszEntry, DWORD dwFlags)
  1050. {
  1051. CDynamicLibrary UserEnv(L"userenv.dll");
  1052. CDynamicLibrary Advapi32(L"advapi32.dll");
  1053. DWORD dwReturn = ERROR_INVALID_PARAMETER;
  1054. HANDLE hImpersonationToken = NULL; // The token of the thread
  1055. HANDLE hPrimaryToken = NULL; // The primary token for the new process
  1056. LPWSTR pszShortServiceName = NULL;
  1057. LPWSTR pszCmDirpath = NULL;
  1058. PROCESS_INFORMATION ProcessInfo = {0};
  1059. STARTUPINFO StartupInfo = {0};
  1060. WCHAR szCmpPath[MAX_PATH+1] = {0};
  1061. WCHAR szInfPath[MAX_PATH+1];
  1062. WCHAR szParams[2*MAX_PATH+1];
  1063. WCHAR szExactCmstpLocation[MAX_PATH + 10 + 1]; // 10 = length of "\cmstp.exe"
  1064. typedef BOOL (WINAPI* pfnCreateEnvironmentBlockSpec)(LPVOID*, HANDLE, BOOL);
  1065. typedef BOOL (WINAPI* pfnDestroyEnvironmentBlockSpec)(LPVOID);
  1066. typedef BOOL (WINAPI* pfnDuplicateTokenExSpec)(HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE);
  1067. pfnCreateEnvironmentBlockSpec pfnCreateEnvironmentBlock = NULL;
  1068. pfnDestroyEnvironmentBlockSpec pfnDestroyEnvironmentBlock = NULL;
  1069. pfnDuplicateTokenExSpec pfnDuplicateTokenEx = NULL;
  1070. //
  1071. // Are we deleting an All User or a Single User Connection
  1072. //
  1073. BOOL bAllUser = (RCD_AllUsers & dwFlags);
  1074. //
  1075. // Assume we are impersonating until we know otherwise. Profiles deleted from the
  1076. // IE Connections Tab will not be impersonating, whereas delete requests from the
  1077. // folder go through Netman.dll in svchost.exe and are thus impersonating.
  1078. //
  1079. BOOL bImpersonatingProfile = TRUE;
  1080. //
  1081. // Check the params, note that pszPhoneBook could be NULL
  1082. //
  1083. if ((NULL == pszEntry) || (L'\0' == pszEntry[0]) ||
  1084. ((NULL != pszPhonebook) && (L'\0' == pszPhonebook[0])))
  1085. {
  1086. goto exit;
  1087. }
  1088. //
  1089. // Next lets setup the impersonation Token
  1090. //
  1091. if (OpenThreadToken(GetCurrentThread(),
  1092. TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
  1093. TRUE, &hImpersonationToken))
  1094. {
  1095. //
  1096. // Okay, we have an impersonation token. Lets get it, duplicate it and then
  1097. // we can use it to call CreateProcessAsUser
  1098. //
  1099. pfnDuplicateTokenEx = (pfnDuplicateTokenExSpec)Advapi32.GetProcAddress("DuplicateTokenEx");
  1100. if (NULL == pfnDuplicateTokenEx)
  1101. {
  1102. dwReturn = GetLastError();
  1103. CMTRACE1(TEXT("RasCustomDeleteEntry -- Unable get proc address for DuplicateTokenEx, GLE %d"), GetLastError());
  1104. goto exit;
  1105. }
  1106. if (!pfnDuplicateTokenEx(hImpersonationToken,
  1107. TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
  1108. NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
  1109. {
  1110. dwReturn = GetLastError();
  1111. CMTRACE1(TEXT("RasCustomDeleteEntry -- DuplicateTokenEx Failed, GLE %d"), GetLastError());
  1112. goto exit;
  1113. }
  1114. }
  1115. else
  1116. {
  1117. bImpersonatingProfile = FALSE;
  1118. }
  1119. //
  1120. // First let's read the Mappings Key, note that we don't expand the environment strings
  1121. // if it is an impersonating profile. ExpandEnvironmentStrings doesn't have the correct
  1122. // environment loaded sometimes when we are impersonating. We are launching cmstp.exe
  1123. // with a full environment block via CreateProcessAsUser, this will take care of the
  1124. // expansion so there is no need.
  1125. //
  1126. if (FALSE == ReadMapping(pszEntry, szCmpPath, MAX_PATH, bAllUser, !bImpersonatingProfile)) // !bImpersonatingProfile == bExpandEnvStrings
  1127. {
  1128. //
  1129. // No mappings key, return failure
  1130. //
  1131. CMASSERTMSG(FALSE, TEXT("RasCustomDeleteEntry -- ReadMapping returned FALSE, unable to find the profile."));
  1132. dwReturn = ERROR_FILE_NOT_FOUND;
  1133. goto exit;
  1134. }
  1135. //
  1136. // At this point we should have a mappings value. We need to convert that into the INF
  1137. // path. CM 1.0/1.1 profiles stored their INF files in the system(32) dir.
  1138. // CM 1.2 Profiles store this file in the Profile directory. Since
  1139. // a user could install an old profile we must try the Legacy location
  1140. // if the current location fails.
  1141. //
  1142. pszShortServiceName = CmStripPathAndExt(szCmpPath);
  1143. pszCmDirpath = CmStripFileName(szCmpPath, TRUE); // bKeepSlash == TRUE
  1144. if (pszShortServiceName && pszCmDirpath)
  1145. {
  1146. //
  1147. // Build the new inf location
  1148. //
  1149. wsprintfW(szInfPath, L"%s%s\\%s.inf", pszCmDirpath, pszShortServiceName, pszShortServiceName);
  1150. if (!FileExists(szInfPath) && bAllUser) // if the doesn't file exists and we are all user then try the sys dir
  1151. {
  1152. //
  1153. // Looks like this is an old style profile with the inf in the system directory.
  1154. // Now build the old style path and see if it exists. Note that 1.0 profiles were All User only
  1155. //
  1156. if (0 != GetSystemDirectoryU(szInfPath, MAX_PATH))
  1157. {
  1158. lstrcatU(szInfPath, L"\\");
  1159. lstrcatU(szInfPath, pszShortServiceName);
  1160. lstrcatU(szInfPath, L".inf");
  1161. if (!FileExists(szInfPath))
  1162. {
  1163. CMASSERTMSG(FALSE, TEXT("RasCustomDeleteEntry -- Unable to locate profile inf."));
  1164. dwReturn = ERROR_FILE_NOT_FOUND;
  1165. goto exit;
  1166. }
  1167. }
  1168. else
  1169. {
  1170. dwReturn = GetLastError();
  1171. goto exit;
  1172. }
  1173. }
  1174. }
  1175. else
  1176. {
  1177. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  1178. goto exit;
  1179. }
  1180. (void) GetSystemDirectory(szExactCmstpLocation, MAX_PATH);
  1181. lstrcatU(szExactCmstpLocation, TEXT("\\cmstp.exe"));
  1182. lstrcpyU(szParams, L"cmstp.exe /u /s \"");
  1183. lstrcatU(szParams, szInfPath);
  1184. lstrcatU(szParams, L"\"");
  1185. if (bImpersonatingProfile)
  1186. {
  1187. //
  1188. // Fill in the environment block
  1189. //
  1190. WCHAR* pszEnvBlock;
  1191. pfnCreateEnvironmentBlock = (pfnCreateEnvironmentBlockSpec)UserEnv.GetProcAddress("CreateEnvironmentBlock");
  1192. pfnDestroyEnvironmentBlock = (pfnDestroyEnvironmentBlockSpec)UserEnv.GetProcAddress("DestroyEnvironmentBlock");
  1193. if ((NULL == pfnCreateEnvironmentBlock) || (NULL == pfnDestroyEnvironmentBlock))
  1194. {
  1195. dwReturn = ERROR_PROC_NOT_FOUND;
  1196. CMTRACE(TEXT("RasCustomDeleteEntry -- Unable to load pfnCreateEnvironmentBlock Or pfnDestroyEnvironmentBlock."));
  1197. goto exit;
  1198. }
  1199. if (pfnCreateEnvironmentBlock((void**)&pszEnvBlock, hPrimaryToken, TRUE))
  1200. {
  1201. if (CreateProcessAsUser(hPrimaryToken,
  1202. szExactCmstpLocation, // lpApplicationName
  1203. szParams, // lpCommandLine
  1204. NULL, // pProcessAttributes
  1205. NULL, // lpThreadAttributes
  1206. FALSE, // bInheritHandles
  1207. CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
  1208. pszEnvBlock, // lpEnvironment
  1209. NULL, // lpCurrentDirectory
  1210. &StartupInfo,
  1211. &ProcessInfo))
  1212. {
  1213. CloseHandle(ProcessInfo.hProcess);
  1214. CloseHandle(ProcessInfo.hThread);
  1215. dwReturn = ERROR_SUCCESS;
  1216. }
  1217. else
  1218. {
  1219. dwReturn = GetLastError();
  1220. CMTRACE1(TEXT("RasCustomDeleteEntry -- CreateProcessAsUser Failed, GLE %d"), GetLastError());
  1221. }
  1222. pfnDestroyEnvironmentBlock(pszEnvBlock);
  1223. }
  1224. else
  1225. {
  1226. CMTRACE1(L"Unable to Create the Environment block, GLE %d", GetLastError());
  1227. }
  1228. }
  1229. else
  1230. {
  1231. //
  1232. // We aren't impersonating just use regular CreateProcess, we could use CreateProcessU here but it isn't necessary as
  1233. // this only runs on win2k and CreateProcessW exists (although isn't implemented) all the way back to win95.
  1234. //
  1235. if (CreateProcess(NULL, // lpApplicationName
  1236. szParams, // lpCommandLine
  1237. NULL, // pProcessAttributes
  1238. NULL, // lpThreadAttributes
  1239. FALSE, // bInheritHandles
  1240. CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
  1241. NULL, // lpEnvironment
  1242. NULL, // lpCurrentDirectory
  1243. &StartupInfo,
  1244. &ProcessInfo))
  1245. {
  1246. CloseHandle(ProcessInfo.hProcess);
  1247. CloseHandle(ProcessInfo.hThread);
  1248. dwReturn = ERROR_SUCCESS;
  1249. }
  1250. else
  1251. {
  1252. dwReturn = GetLastError();
  1253. CMTRACE1(TEXT("RasCustomDeleteEntry -- CreateProcessAsUser Failed, GLE %d"), GetLastError());
  1254. }
  1255. }
  1256. exit:
  1257. if (hImpersonationToken)
  1258. {
  1259. CloseHandle(hImpersonationToken);
  1260. }
  1261. if (hPrimaryToken)
  1262. {
  1263. CloseHandle(hPrimaryToken);
  1264. }
  1265. CmFree(pszCmDirpath);
  1266. CmFree(pszShortServiceName);
  1267. return dwReturn;
  1268. }
  1269. //+----------------------------------------------------------------------------
  1270. //
  1271. // Function: DllMain
  1272. //
  1273. // Synopsis: Main entry point into the DLL.
  1274. //
  1275. // Arguments: HINSTANCE hinstDLL - Our HINSTANCE
  1276. // DWORD fdwReason - The reason we are being called.
  1277. // LPVOID lpvReserved - Reserved
  1278. //
  1279. // Returns: BOOL WINAPI - TRUE - always
  1280. //
  1281. // History: nickball Created Header 2/5/98
  1282. //
  1283. //+----------------------------------------------------------------------------
  1284. extern "C" BOOL WINAPI DllMain(HINSTANCE hInstDLL,
  1285. DWORD fdwReason,
  1286. LPVOID lpvReserved)
  1287. {
  1288. switch (fdwReason)
  1289. {
  1290. case DLL_PROCESS_ATTACH:
  1291. if (!InitUnicodeAPI())
  1292. {
  1293. //
  1294. // Without our U api's we are going no where. Bail.
  1295. //
  1296. CMTRACE(TEXT("Cmdial32.dll Initialization Error: Unable to initialize Unicode to ANSI conversion layer, exiting."));
  1297. return FALSE;
  1298. }
  1299. CMTRACE(TEXT("====================================================="));
  1300. CMTRACE1(TEXT(" CMDIAL32.DLL - LOADING - Process ID is 0x%x "), GetCurrentProcessId());
  1301. CMTRACE(TEXT("====================================================="));
  1302. #ifdef DEBUG
  1303. TCHAR szTmp[MAX_PATH];
  1304. MYVERIFY(GetModuleFileNameU (GetModuleHandleA(NULL), szTmp, MAX_PATH));
  1305. CMTRACE1(TEXT("Calling process is %s"), szTmp);
  1306. #endif
  1307. //
  1308. // Setup global instance data
  1309. //
  1310. g_hInst = hInstDLL;
  1311. //
  1312. // Disable thread attach notification
  1313. //
  1314. MYVERIFY(DisableThreadLibraryCalls(hInstDLL));
  1315. break;
  1316. case DLL_PROCESS_DETACH:
  1317. CMTRACE(TEXT("====================================================="));
  1318. CMTRACE1(TEXT(" CMDIAL32.DLL - UNLOADING - Process ID is 0x%x "), GetCurrentProcessId());
  1319. CMTRACE(TEXT("====================================================="));
  1320. if (!UnInitUnicodeAPI())
  1321. {
  1322. CMASSERTMSG(FALSE, TEXT("cmdial32 dllmain UnInitUnicodeAPI failed - we are probably leaking a handle"));
  1323. }
  1324. //
  1325. // Unlike Windows 95, on Windows NT, Windows classes
  1326. // that a DLL registers are NOT unregistered when the DLL is unloaded.
  1327. // Bug 168251:First launch of profile from connections UI causes access violation,
  1328. // after copy new CM bits
  1329. //
  1330. if (OS_NT)
  1331. {
  1332. //
  1333. // Unregister the bitmap class. The new CM bits will re-register the class with
  1334. // correct wnd proc address.
  1335. //
  1336. UnregisterClassU(ICONNMGR_BMP_CLASS, g_hInst);
  1337. UnRegisterWindowClass(g_hInst);
  1338. }
  1339. break;
  1340. }
  1341. return TRUE;
  1342. }
  1343. //+----------------------------------------------------------------------------
  1344. //
  1345. // Function: CmCustomDialDlg
  1346. //
  1347. // Synopsis: Our CM specific variation on RasCustomDialDlg.
  1348. //
  1349. // Arguments: HWND hwndParent - The HWND of the parent if deemed necessary by the caller
  1350. // DWORD dwFlags - Dial flags
  1351. // LPTSTR lpszPhonebook - Ptr to the full path and filename of the phonebook.
  1352. // NULL = RAS system phone book
  1353. // "something" = user-defined RAS phonebook
  1354. // "" = has not been determined yet
  1355. // LPTSTR lpszEntry - Ptr to the name of the phone-book entry to dial.
  1356. // LPTSTR lpszPhoneNumber - Ptr to replacement phone number [IGNORED]
  1357. // LPRASDIALDLG lpRasDialDlg - Ptr to RASDIALDLG struct
  1358. // LPRASENTRYDLG lpRasEntryDlg - Ptr to RASENTRYDLG struct
  1359. // LPCMDIALINFO lpCmInfo - Ptr to CMDIALINFO struct containing CM dial info such as flags.
  1360. //
  1361. // Returns: BOOL WINAPI - TRUE on success
  1362. //
  1363. // History: nickball Created Header 2/5/98
  1364. //
  1365. //+----------------------------------------------------------------------------
  1366. extern "C" BOOL WINAPI CmCustomDialDlg(HWND hwndParent,
  1367. DWORD dwFlags,
  1368. LPTSTR lpszPhonebook,
  1369. LPCTSTR lpszEntry,
  1370. LPTSTR, // lpszPhoneNumber
  1371. LPRASDIALDLG lpRasDialDlg,
  1372. LPRASENTRYDLG lpRasEntryDlg,
  1373. LPCMDIALINFO lpCmInfo,
  1374. PVOID pvLogonBlob)
  1375. {
  1376. MYDBGASSERT(lpCmInfo);
  1377. MYDBGASSERT(lpszEntry);
  1378. MYDBGASSERT(lpszEntry[0]);
  1379. //DebugBreak();
  1380. CMTRACE1(TEXT("CmCustomDialDlg() - dwFlags = 0x%x"), dwFlags);
  1381. CMTRACE1(TEXT("CmCustomDialDlg() - lpszPhonebook = %s"), MYDBGSTR(lpszPhonebook));
  1382. CMTRACE1(TEXT("CmCustomDialDlg() - lpszEntry = %s"), MYDBGSTR(lpszEntry));
  1383. //
  1384. // lpszPhonebook can be NULL, because we are called by our own modules, CMMGR, CMMON, etc.
  1385. //
  1386. if (NULL == lpszEntry || NULL == lpszEntry[0] || NULL == lpCmInfo)
  1387. {
  1388. return FALSE;
  1389. }
  1390. CM_SET_TIMING_INTERVAL("CmCustomDialDlg - Begin");
  1391. #ifdef DEBUG
  1392. //
  1393. // Here we don't care whether we are in FUS, but need to know how many TS sessions are on the machine
  1394. // currently. This is for debugging issues only to make sure ICS works and there is always at least one session.
  1395. //
  1396. DWORD dwSessionCount = 0;
  1397. (VOID)InFastUserSwitch(&dwSessionCount);
  1398. CMTRACE1(TEXT("CmCustomDialDlg() - TS Session Count = %d"), dwSessionCount);
  1399. #endif
  1400. CNamedMutex ConnectMutex;
  1401. CConnectionTable ConnTable;
  1402. BOOL fConnTableExists = FALSE;
  1403. BOOL fMultiInst = FALSE;
  1404. if (!(lpCmInfo->dwCmFlags & FL_PROPERTIES))
  1405. {
  1406. //
  1407. // Try to acquire connect mutex
  1408. //
  1409. LPTSTR pszTmp = CmStrCpyAlloc(c_pszConnectMutex);
  1410. pszTmp = CmStrCatAlloc(&pszTmp, lpszEntry);
  1411. if (FALSE == ConnectMutex.Lock(pszTmp, FALSE))
  1412. {
  1413. if (FALSE == IsLogonAsSystem())
  1414. {
  1415. //
  1416. // Another connect instance exists, try to front it
  1417. //
  1418. FrontExistingUI(NULL, lpszEntry, TRUE);
  1419. //
  1420. // Now wait for Mutex to be released.
  1421. //
  1422. ConnectMutex.Lock(pszTmp, TRUE, INFINITE, TRUE);
  1423. //
  1424. // Mutex was released by the other instance, we'll handle the connect
  1425. // request in the main path. If there is no table, we know that the
  1426. // instance which previously owned the mutex terminated without
  1427. // connecting and we follow suit by returning failure. Otherwise, we
  1428. // have to take a closer look.
  1429. //
  1430. fMultiInst = TRUE;
  1431. }
  1432. else
  1433. {
  1434. //
  1435. // No one is logged on and we don't need to be waiting for the mutex.
  1436. //
  1437. CmFree(pszTmp);
  1438. return FALSE;
  1439. }
  1440. }
  1441. CmFree(pszTmp);
  1442. }
  1443. CMTRACE(TEXT("CmCustomDialDlg - Connect mutex acquired. Examining connection table."));
  1444. fConnTableExists = SUCCEEDED(ConnTable.Open());
  1445. if ((!fConnTableExists) && fMultiInst)
  1446. {
  1447. //
  1448. // If we're a secondary thread that was released from the mutex and
  1449. // there is no connection table, then the user canceled, so bail.
  1450. //
  1451. CMTRACE(TEXT("CmCustomDialDlg - returning connect failure post mutex wait"));
  1452. return FALSE;
  1453. }
  1454. //
  1455. // If this is a connect request, see if connection exists.
  1456. //
  1457. if (!(lpCmInfo->dwCmFlags & FL_PROPERTIES))
  1458. {
  1459. if (fConnTableExists)
  1460. {
  1461. //
  1462. // Examine the connection table and try to resolve the connect request
  1463. //
  1464. BOOL fSuccess = FALSE;
  1465. BOOL fDone = HandleCustomConnectRequest(NULL,
  1466. &ConnTable,
  1467. lpszEntry,
  1468. lpCmInfo->dwCmFlags,
  1469. &fSuccess);
  1470. //
  1471. // If we resolved the request, or we're in fMultiInst mode
  1472. // then we can we can bail with the given success code. If
  1473. // fMultInst, we know we can bail because there is no entry
  1474. // in the table, from which we infer that the previous owner
  1475. // of the mutex failed and we return this out to our caller.
  1476. //
  1477. // NOTE: There is a theoretical corner case here in the multi-inst
  1478. // request case. If a succesful connection was established by the
  1479. // first thread, there is a window between the moment that the mutex
  1480. // lock is cleared above (releasing the waiting thread), and when
  1481. // the newly release thread reaches here. The logic is fouled if,
  1482. // and only if, the connection were dropped and entered the
  1483. // reconnect prompt state during this window. This is because the
  1484. // second thread would dismiss the CMMON reconnect prompt UI during
  1485. // the call to HandleCustomConnectRequest, but would then return
  1486. // despite fDone being FALSE. The correct behavior would be to
  1487. // continue, and honor the connect request. This state could be
  1488. // identified by an fDone of FALSE coupled with an fSuccess of TRUE.
  1489. //
  1490. if (fDone || fMultiInst)
  1491. {
  1492. MYVERIFY(SUCCEEDED(ConnTable.Close()));
  1493. return fSuccess;
  1494. }
  1495. }
  1496. }
  1497. else
  1498. {
  1499. //
  1500. // Its a properties request, front any UI that might exist
  1501. //
  1502. if (TRUE == FrontExistingUI(fConnTableExists? &ConnTable : NULL, lpszEntry, FALSE))
  1503. {
  1504. if (fConnTableExists)
  1505. {
  1506. MYVERIFY(SUCCEEDED(ConnTable.Close()));
  1507. }
  1508. return TRUE;
  1509. }
  1510. }
  1511. if (fConnTableExists)
  1512. {
  1513. MYVERIFY(SUCCEEDED(ConnTable.Close()));
  1514. }
  1515. //
  1516. // Make a connection attempt
  1517. //
  1518. HRESULT hrRes = Connect(hwndParent,
  1519. lpszEntry,
  1520. lpszPhonebook,
  1521. lpRasDialDlg,
  1522. lpRasEntryDlg,
  1523. lpCmInfo,
  1524. OS_NT5 ? dwFlags : RCD_AllUsers, // Always AllUser downlevel
  1525. pvLogonBlob);
  1526. //
  1527. // Make sure we push error codes back out
  1528. //
  1529. BOOL bReturn = SUCCEEDED(hrRes);
  1530. DWORD dwError = 0;
  1531. if (lpRasDialDlg)
  1532. {
  1533. if (ERROR_CANCELLED == HRESULT_CODE(hrRes))
  1534. {
  1535. //
  1536. // If the user canceled then RasDialDlg returns a false to indicate failure
  1537. // but sets the dwError value to 0. In order to match the way RAS does
  1538. // things we need to do this too.
  1539. //
  1540. lpRasDialDlg->dwError = 0;
  1541. }
  1542. else
  1543. {
  1544. //
  1545. // If the user entered the wrong PIN, we pass the error up to RAS unchanged
  1546. // so that RAS knows to take down its 'choose connectoid' dialog and drop
  1547. // the user back to winlogon
  1548. //
  1549. lpRasDialDlg->dwError = (BAD_SCARD_PIN(hrRes) ? hrRes : HRESULT_CODE(hrRes));
  1550. }
  1551. dwError = lpRasDialDlg->dwError;
  1552. }
  1553. if (lpRasEntryDlg)
  1554. {
  1555. if (ERROR_CANCELLED == HRESULT_CODE(hrRes))
  1556. {
  1557. //
  1558. // If the user canceled then RasEntryDlg returns a false to indicate failure
  1559. // but sets the dwError value to 0. In order to match the way RAS does
  1560. // things we need to do this too.
  1561. //
  1562. lpRasEntryDlg->dwError = 0;
  1563. }
  1564. else
  1565. {
  1566. lpRasEntryDlg->dwError = HRESULT_CODE(hrRes);
  1567. }
  1568. dwError = lpRasEntryDlg->dwError;
  1569. }
  1570. //
  1571. // Let go of the connect mutex and go home to papa.
  1572. //
  1573. CMTRACE(TEXT("CmCustomDialDlg - Releasing mutex"));
  1574. ConnectMutex.Unlock();
  1575. CMTRACE2(TEXT("CmCustomDialDlg() returning with bReturn = %d, dwError = 0x%x"), bReturn, dwError);
  1576. return bReturn;
  1577. }
  1578. //+----------------------------------------------------------------------------
  1579. //
  1580. // Function: CmCustomHangUp
  1581. //
  1582. // Synopsis: Our CM specific variation on RasCustomHangUp. Optionally, the entry
  1583. // name may be given instead of the RAS handle.
  1584. //
  1585. // Arguments: HRASCONN hRasConn - The handle of the connection to be terminated.
  1586. // LPCTSTR pszEntry - Ptr to the name of the entry to be terminated.
  1587. // BOLL fPersist - Preserve the entry and its usage count.
  1588. //
  1589. // Returns: DWORD WINAPI - Return code
  1590. //
  1591. //+----------------------------------------------------------------------------
  1592. extern "C" DWORD WINAPI CmCustomHangUp(HRASCONN hRasConn,
  1593. LPCTSTR pszEntry,
  1594. BOOL fIgnoreRefCount,
  1595. BOOL fPersist)
  1596. {
  1597. CMTRACE(TEXT("CmCustomHangUp"));
  1598. MYDBGASSERT(hRasConn || (pszEntry && pszEntry[0]));
  1599. DWORD dwRes = ERROR_SUCCESS;
  1600. //
  1601. // Must have a handle or an entry name
  1602. //
  1603. if (NULL == hRasConn && (NULL == pszEntry || 0 == pszEntry[0]))
  1604. {
  1605. return ERROR_INVALID_PARAMETER;
  1606. }
  1607. //
  1608. // First try to open the table, if none found then succeed.
  1609. //
  1610. CConnectionTable ConnTable;
  1611. if (FAILED(ConnTable.Open()))
  1612. {
  1613. return ERROR_NOT_FOUND;
  1614. }
  1615. //
  1616. // Look up the specified entry
  1617. //
  1618. HRESULT hrRes;
  1619. CM_CONNECTION Connection;
  1620. ZeroMemory(&Connection, sizeof(CM_CONNECTION));
  1621. if (hRasConn)
  1622. {
  1623. hrRes = ConnTable.GetEntry(hRasConn, &Connection);
  1624. }
  1625. else
  1626. {
  1627. hrRes = ConnTable.GetEntry(pszEntry, &Connection);
  1628. }
  1629. //
  1630. // We have an entry, do the Disconnect
  1631. //
  1632. if (SUCCEEDED(hrRes))
  1633. {
  1634. if (CM_CONNECTING == Connection.CmState)
  1635. {
  1636. dwRes = ERROR_NOT_FOUND;
  1637. }
  1638. else
  1639. {
  1640. //
  1641. // If the persist flag is not set and the caller is from the desktop
  1642. // then ignore the ref count and do a complete hangup.
  1643. //
  1644. // BOOL fIgnoreRefCount = ((!fPersist) && WhoIsCaller(DT_CMMON | DT_EXPLORER));
  1645. dwRes = Disconnect(&ConnTable, &Connection, fIgnoreRefCount, fPersist);
  1646. }
  1647. }
  1648. MYVERIFY(SUCCEEDED(ConnTable.Close()));
  1649. return dwRes;
  1650. }
  1651. BOOL IsCustomPropertyEnabled();
  1652. BOOL GetCMPFile(LPCWSTR pszEntryName, LPWSTR pszCMP);
  1653. BOOL GetCMPFileFromMappingsKey(HKEY hBaseKey, LPCWSTR szEntryName, HANDLE hPrimaryToken, LPWSTR pszCmpFile);
  1654. BOOL GetCMSFile(LPWSTR pszCmpFile, LPWSTR pszCmsFile);
  1655. DWORD GetCMProperty(LPWSTR pszCmpFile, LPWSTR pszCMSFile, LPWSTR szSection, LPCWSTR pszProperty, PBYTE *ppbValue, DWORD *pdwValueLen, BOOL fAlloc);
  1656. DWORD GetMenuItems(LPWSTR pszCmpFile, LPWSTR pszCmsFile, CON_TRAY_MENU_DATA** ppMenuData);
  1657. BOOL GetShortName(LPWSTR pszCmpFile, LPWSTR pszSvcName);
  1658. BOOL GetProfileDir(LPWSTR pszCmpFile, LPWSTR pszProfDir);
  1659. HRESULT HrGetMenuNameAndCmdLine(PWSTR pszString,
  1660. PWSTR szName, UINT uNameLen,
  1661. PWSTR szProgram, UINT uProgramLen,
  1662. PWSTR szParams, UINT uParamsLen);
  1663. HRESULT HrFillInConTrayMenuEntry (LPTSTR pszCmpFile,
  1664. PCWSTR szName,
  1665. PCWSTR szCmdLine,
  1666. PCWSTR szParams,
  1667. CON_TRAY_MENU_ENTRY* pMenuEntry);
  1668. HRESULT HrCoTaskMemAlloc(ULONG cb, VOID **ppv);
  1669. //+---------------------------------------------------------------------------
  1670. //
  1671. // Function: GetCustomProperty
  1672. //
  1673. // Purpose: This function returns custom properties (Icon and Tray Menu).
  1674. // It is used by the NetCon folder so that we abstract our design
  1675. // from their code.
  1676. //
  1677. // Arguments: pszRasPhoneBook - Ras phone book for this entry. Not used currently,
  1678. // but it might be in the future.
  1679. // pszEntryName - Profile name
  1680. // pszProperty - property to retrieve
  1681. // ppbValue - pointer to a buffer
  1682. // cbValue - if 0, we must allocate memory otherwise the caller
  1683. // supplied a buffer of this size.
  1684. //
  1685. // Returns: TRUE or FALSE
  1686. //
  1687. //+---------------------------------------------------------------------------
  1688. extern "C" BOOL WINAPI GetCustomProperty(LPCWSTR pszRasPhoneBook, LPCWSTR pszEntryName, LPWSTR pszProperty, PBYTE *ppbValue, DWORD *cbValue)
  1689. {
  1690. BOOL fRetVal = FALSE;
  1691. BOOL fAlloc = FALSE;
  1692. WCHAR szCMP[(MAX_PATH*2)+1]={0};
  1693. WCHAR szCMS[(MAX_PATH*2)+1]={0};
  1694. DWORD dwRes = ERROR_SUCCESS;
  1695. CMTRACE(TEXT("GetCustomProperty - BEGIN ----------------"));
  1696. //
  1697. // Make sure we have all the params that we need
  1698. //
  1699. if (!ppbValue || !pszProperty || !cbValue || !pszEntryName)
  1700. {
  1701. return FALSE;
  1702. }
  1703. if (!OS_NT5)
  1704. {
  1705. return FALSE;
  1706. }
  1707. //
  1708. // Check if the reg key is set in the registry.
  1709. // If it's not set or doesn't exist, then we can proceed and the
  1710. // function returns TRUE.
  1711. //
  1712. if (FALSE == IsCustomPropertyEnabled())
  1713. {
  1714. CMTRACE(TEXT("GetCustomProperty - IsCustomPropertyEnabled() is returning FALSE. Exiting"));
  1715. return FALSE;
  1716. }
  1717. CMTRACE2(TEXT("GetCustomProperty - Entry Name: %s Property: %s"), pszEntryName, pszProperty);
  1718. //
  1719. // Should we alloc? If a buffer has size already, then don't alloc,
  1720. // because the caller supplied their own buffer. (As in the current case of the NetCon folders - for Icons only)
  1721. // Menu we have to allocate
  1722. //
  1723. fAlloc = (((BOOL)*cbValue)? FALSE : TRUE);
  1724. //
  1725. // First Get the CMP filename
  1726. //
  1727. if (GetCMPFile((LPCTSTR)pszEntryName, szCMP))
  1728. {
  1729. //CMTRACE(TEXT("GetCustomProperty - After GetCMPFile"));
  1730. //
  1731. // Get the CMS filename
  1732. //
  1733. if (GetCMSFile(szCMP, szCMS))
  1734. {
  1735. //CMTRACE(TEXT("GetCustomProperty - After GetCMSFile"));
  1736. //
  1737. // Figure out which property the caller wants
  1738. //
  1739. if ((0 == lstrcmpiU(L"Icon", pszProperty)) ||
  1740. (0 == lstrcmpiU(L"HideTrayIcon", pszProperty)) ||
  1741. (0 == lstrcmpiU(L"TrayIcon", pszProperty)))
  1742. {
  1743. //CMTRACE(TEXT("GetCustomProperty - ICONS"));
  1744. PBYTE pbValue = NULL;
  1745. DWORD dwValueLen = 0;
  1746. if (FALSE == fAlloc)
  1747. {
  1748. dwValueLen = *cbValue; // Pass down the buffer size
  1749. dwRes = GetCMProperty(szCMP, szCMS, TEXT("Connection Manager"), pszProperty, ppbValue, &dwValueLen, fAlloc);
  1750. if (ERROR_SUCCESS == dwRes)
  1751. {
  1752. *cbValue = dwValueLen;
  1753. fRetVal = TRUE;
  1754. }
  1755. }
  1756. else
  1757. {
  1758. dwRes = GetCMProperty(szCMP, szCMS, TEXT("Connection Manager"), pszProperty, &pbValue, &dwValueLen, fAlloc);
  1759. if (ERROR_SUCCESS == dwRes)
  1760. {
  1761. *ppbValue = pbValue;
  1762. *cbValue = dwValueLen;
  1763. fRetVal = TRUE;
  1764. }
  1765. }
  1766. }
  1767. else if (0 == lstrcmpiU(L"Menu Items", pszProperty))
  1768. {
  1769. //CMTRACE(TEXT("GetCustomProperty - MENUS"));
  1770. CON_TRAY_MENU_DATA *pConTrayMenuData = NULL;
  1771. //
  1772. // Currently we don't suppport if users want to allocate their buffers themselves
  1773. // for Tray Menu Items
  1774. //
  1775. if (fAlloc)
  1776. {
  1777. dwRes = GetMenuItems(szCMP, szCMS, &pConTrayMenuData);
  1778. if (ERROR_SUCCESS == dwRes)
  1779. {
  1780. *ppbValue = (PBYTE)pConTrayMenuData;
  1781. fRetVal = TRUE;
  1782. }
  1783. }
  1784. }
  1785. }
  1786. }
  1787. CMTRACE(TEXT("GetCustomProperty - END ------------------"));
  1788. return fRetVal;
  1789. }
  1790. //+---------------------------------------------------------------------------
  1791. //
  1792. // Function: GetCMPFile
  1793. //
  1794. // Purpose: This function returns the CMP file path.
  1795. // Need to take a guess and look into the HKLM (Mappings key) first.
  1796. // If the call returns FALSE, we try HKCU to get the CMP path. Since
  1797. // We are being called from a system process, we need to impersonate
  1798. // the user in order to open the HKCU key.
  1799. //
  1800. // Arguments: szEntryName - profile entry name
  1801. // pszCMP - [OUT] .CMP file
  1802. //
  1803. // Returns: TRUE or FALSE
  1804. //
  1805. //+---------------------------------------------------------------------------
  1806. BOOL GetCMPFile(LPCWSTR pszEntryName, LPWSTR pszCMP)
  1807. {
  1808. BOOL fRetCode = FALSE;
  1809. LPWSTR pszCmpFile = NULL;
  1810. BOOL fIsAllUser = FALSE;
  1811. HANDLE hImpersonationToken = NULL; // The token of the thread
  1812. HANDLE hPrimaryToken = NULL; // The primary token for the new process
  1813. DWORD dwSize = MAX_PATH;
  1814. HKEY hKey;
  1815. HANDLE hBaseKey = NULL;
  1816. HANDLE hFile;
  1817. HRESULT hr = E_FAIL;
  1818. HRESULT hrImpersonate = E_FAIL;
  1819. DWORD dwRes = 0;
  1820. HMODULE hNTDll = NULL;
  1821. HMODULE hAdvapi32 = NULL;
  1822. HMODULE hOle32Dll = NULL;
  1823. if (!OS_NT5)
  1824. {
  1825. return FALSE;
  1826. }
  1827. if (!pszCMP)
  1828. {
  1829. return FALSE;
  1830. }
  1831. typedef HRESULT (WINAPI* pfnCoImpersonateClientSpec)();
  1832. pfnCoImpersonateClientSpec pfnCoImpersonateClient = NULL;
  1833. typedef HRESULT (WINAPI* pfnCoRevertToSelfSpec)();
  1834. pfnCoRevertToSelfSpec pfnCoRevertToSelf = NULL;
  1835. //
  1836. // Check the HKLM key to see if this profile is All-User
  1837. // If not, we'll need to impersonate the logged on user and use HKCU since we are being called
  1838. // from a system account.
  1839. //
  1840. fIsAllUser = GetCMPFileFromMappingsKey(HKEY_LOCAL_MACHINE, pszEntryName, NULL, pszCMP);
  1841. if (FALSE == fIsAllUser)
  1842. {
  1843. //
  1844. // Then we have a private profile. Since netman runs as a system account, and we were called by netman
  1845. // we must impersonate the client and then make an RTL call to get
  1846. // the current users HKCU hive before querying the registry for the
  1847. // cmp path. We also need to get the user token so that we can expand the
  1848. // cmp string in the single user case.
  1849. //
  1850. hOle32Dll = LoadLibrary(TEXT("ole32.dll"));
  1851. if (hOle32Dll)
  1852. {
  1853. pfnCoRevertToSelf = (pfnCoRevertToSelfSpec)GetProcAddress(hOle32Dll, "CoRevertToSelf");
  1854. pfnCoImpersonateClient = (pfnCoImpersonateClientSpec)GetProcAddress(hOle32Dll, "CoImpersonateClient");
  1855. MYDBGASSERT(pfnCoRevertToSelf);
  1856. MYDBGASSERT(pfnCoImpersonateClient);
  1857. if (pfnCoImpersonateClient && pfnCoRevertToSelf)
  1858. {
  1859. hrImpersonate = pfnCoImpersonateClient();
  1860. if (SUCCEEDED(hrImpersonate))
  1861. {
  1862. //
  1863. // load ntdll.dll
  1864. //
  1865. hNTDll = LoadLibrary(TEXT("ntdll.dll"));
  1866. hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
  1867. MYDBGASSERT(hNTDll);
  1868. MYDBGASSERT(hAdvapi32);
  1869. if (hNTDll && hAdvapi32)
  1870. {
  1871. typedef NTSTATUS (WINAPI* pfnRtlOpenCurrentUserSpec)(ULONG, PHANDLE);
  1872. pfnRtlOpenCurrentUserSpec pfnRtlOpenCurrentUser = NULL;
  1873. pfnRtlOpenCurrentUser = (pfnRtlOpenCurrentUserSpec)GetProcAddress(hNTDll, "RtlOpenCurrentUser");
  1874. typedef BOOL (WINAPI* pfnDuplicateTokenExSpec)(HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE);
  1875. pfnDuplicateTokenExSpec pfnDuplicateTokenEx = NULL;
  1876. pfnDuplicateTokenEx = (pfnDuplicateTokenExSpec)GetProcAddress(hAdvapi32, "DuplicateTokenEx");
  1877. typedef BOOL (WINAPI* pfnOpenThreadTokenSpec)(HANDLE, DWORD, BOOL, PHANDLE);
  1878. pfnOpenThreadTokenSpec pfnOpenThreadToken = NULL;
  1879. pfnOpenThreadToken = (pfnOpenThreadTokenSpec)GetProcAddress(hAdvapi32, "OpenThreadToken");
  1880. MYDBGASSERT(pfnRtlOpenCurrentUser);
  1881. MYDBGASSERT(pfnDuplicateTokenEx);
  1882. MYDBGASSERT(pfnOpenThreadToken);
  1883. if (pfnRtlOpenCurrentUser && pfnDuplicateTokenEx && pfnOpenThreadToken)
  1884. {
  1885. NTSTATUS ntstat = pfnRtlOpenCurrentUser(KEY_READ | KEY_WRITE, &hBaseKey);
  1886. hr = HRESULT_FROM_NT(ntstat);
  1887. if (SUCCEEDED(hr))
  1888. {
  1889. //
  1890. // Create a primary token
  1891. //
  1892. if (!pfnOpenThreadToken(
  1893. GetCurrentThread(),
  1894. TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
  1895. TRUE,
  1896. &hImpersonationToken))
  1897. {
  1898. hr = HRESULT_FROM_WIN32(GetLastError());
  1899. }
  1900. else
  1901. {
  1902. if(!pfnDuplicateTokenEx(hImpersonationToken,
  1903. TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
  1904. NULL,
  1905. SecurityImpersonation,
  1906. TokenPrimary,
  1907. &hPrimaryToken
  1908. ))
  1909. {
  1910. hr = HRESULT_FROM_WIN32(GetLastError());
  1911. }
  1912. }
  1913. }
  1914. }
  1915. }
  1916. }
  1917. else
  1918. {
  1919. hr = hrImpersonate;
  1920. }
  1921. }
  1922. }
  1923. //
  1924. // Now Open the mappings key and get the cmp file path for a Single User Profile
  1925. //
  1926. if (SUCCEEDED(hr) && hBaseKey)
  1927. {
  1928. fRetCode = GetCMPFileFromMappingsKey((HKEY)hBaseKey, pszEntryName, hPrimaryToken, pszCMP);
  1929. }
  1930. }
  1931. if (fIsAllUser)
  1932. {
  1933. fRetCode = TRUE;
  1934. }
  1935. if (SUCCEEDED(hrImpersonate) && pfnCoRevertToSelf)
  1936. {
  1937. hr = pfnCoRevertToSelf();
  1938. }
  1939. if (FALSE == fIsAllUser)
  1940. {
  1941. if (hImpersonationToken)
  1942. {
  1943. CloseHandle(hImpersonationToken);
  1944. hImpersonationToken = NULL;
  1945. }
  1946. if (hPrimaryToken)
  1947. {
  1948. CloseHandle(hPrimaryToken);
  1949. hPrimaryToken = NULL;
  1950. }
  1951. // If it's a valid key that is not NULL then we can close it because we opened it.
  1952. if (hBaseKey && hNTDll)
  1953. {
  1954. typedef NTSTATUS (WINAPI* pfnNtCloseFunc)(HANDLE);
  1955. pfnNtCloseFunc pfnNtClose = NULL;
  1956. pfnNtClose = (pfnNtCloseFunc)GetProcAddress(hNTDll, "NtClose");
  1957. if (pfnNtClose)
  1958. {
  1959. (VOID)pfnNtClose(hBaseKey);
  1960. hBaseKey = NULL;
  1961. }
  1962. }
  1963. if (hNTDll)
  1964. {
  1965. FreeLibrary(hNTDll);
  1966. hNTDll = NULL;
  1967. }
  1968. if (hAdvapi32)
  1969. {
  1970. FreeLibrary(hAdvapi32);
  1971. hAdvapi32 = NULL;
  1972. }
  1973. if (hOle32Dll)
  1974. {
  1975. FreeLibrary(hOle32Dll);
  1976. hOle32Dll = NULL;
  1977. }
  1978. }
  1979. return fRetCode;
  1980. }
  1981. //+---------------------------------------------------------------------------
  1982. //
  1983. // Function: GetCMProperty
  1984. //
  1985. // Purpose: This function retrieves a property from the .cms file
  1986. //
  1987. // Arguments: pszCmpFile - .cmp filepath
  1988. // pszCMSFile - .cms filepath
  1989. // szSection - section
  1990. // pszProperty - property to get
  1991. // ppbValue - [IN/OUT] buffer
  1992. // pdwValueLen [IN/OUT] size of buffer
  1993. //
  1994. // Returns: ERROR_SUCCESS or an error code
  1995. //
  1996. //+---------------------------------------------------------------------------
  1997. DWORD GetCMProperty(LPWSTR pszCmpFile, LPWSTR pszCMSFile, LPWSTR szSection, LPCWSTR pszProperty, PBYTE *ppbValue, DWORD *pdwValueLen, BOOL fAlloc)
  1998. {
  1999. DWORD dwRet = ERROR_NOT_FOUND;
  2000. WCHAR szIconPath[(2*MAX_PATH) + 1]={0};
  2001. PBYTE pbBuf = NULL;
  2002. if (!pszCmpFile || !pszCMSFile || !szSection || !pszProperty || !ppbValue || !pdwValueLen)
  2003. {
  2004. return ERROR_INVALID_PARAMETER;
  2005. }
  2006. if (fAlloc)
  2007. {
  2008. //
  2009. // Only want to clear the incoming variables if we are going to allocate memory,
  2010. // because *pdwValueLen is used to make sure the buffer is big enough if the
  2011. // caller has allocated it himself
  2012. //
  2013. *ppbValue = NULL;
  2014. *pdwValueLen = 0;
  2015. }
  2016. dwRet = GetPrivateProfileStringU(szSection, pszProperty, TEXT(""), szIconPath, 2*MAX_PATH, pszCMSFile);
  2017. if (0 == dwRet)
  2018. {
  2019. //
  2020. // Failed - This happens when it can't find that property in the section.
  2021. // Since we don't have a default, the caller needs to handle the error.
  2022. //
  2023. dwRet = ERROR_NOT_FOUND;
  2024. }
  2025. else if (((2*MAX_PATH) - 1) == dwRet)
  2026. {
  2027. //
  2028. // Buffer Too small - for the icons we are using hardcoded buffers from the netcon folder
  2029. // so this shouldn't occur.
  2030. //
  2031. dwRet = ERROR_INSUFFICIENT_BUFFER;
  2032. }
  2033. else
  2034. {
  2035. //
  2036. // Get the CM Dir and append the icon dir
  2037. //
  2038. WCHAR szwDrive[MAX_PATH+1];
  2039. WCHAR szwDir[MAX_PATH+1];
  2040. WCHAR szwFileName[MAX_PATH+1];
  2041. WCHAR szwExtension[MAX_PATH+1];
  2042. _wsplitpath(pszCmpFile, szwDrive, szwDir, szwFileName, szwExtension);
  2043. //
  2044. // Can't use CmStrCpyAlloc(szwDrive), because this memory is freed by the caller
  2045. // after this DLL get unloaded. CmStrCpyAlloc uses CmMalloc internally and in debug mode
  2046. // CmMalloc checks for memory leaks by increasing an internal counter.
  2047. //
  2048. DWORD dwDriveLen = lstrlenU(szwDrive) + lstrlenU(szwDir) + lstrlenU(szIconPath);
  2049. if (fAlloc)
  2050. {
  2051. // Alloc
  2052. HRESULT hr = HrCoTaskMemAlloc((dwDriveLen + 1)*sizeof(WCHAR), (LPVOID*)&pbBuf);
  2053. if (pbBuf)
  2054. {
  2055. lstrcpyU((LPTSTR)pbBuf, szwDrive);
  2056. lstrcatU((LPTSTR)pbBuf, szwDir);
  2057. lstrcatU((LPTSTR)pbBuf, szIconPath);
  2058. *ppbValue = pbBuf;
  2059. *pdwValueLen = (dwDriveLen + 1)* sizeof(WCHAR);
  2060. dwRet = ERROR_SUCCESS;
  2061. }
  2062. }
  2063. else
  2064. {
  2065. LPTSTR pszBuf = (LPTSTR)ppbValue;
  2066. if (dwDriveLen < *pdwValueLen)
  2067. {
  2068. UINT nNumChars = wsprintfW(pszBuf, L"%s%s%s", szwDrive, szwDir, szIconPath);
  2069. *pdwValueLen = (dwDriveLen+1) * sizeof(WCHAR);
  2070. dwRet = ERROR_SUCCESS;
  2071. }
  2072. }
  2073. }
  2074. return dwRet;
  2075. }
  2076. BOOL GetCMSFile(LPWSTR pszCmpFile, LPWSTR pszCmsFile)
  2077. {
  2078. WCHAR szwDrive[MAX_PATH+1]={0};
  2079. WCHAR szwDir[MAX_PATH+1]={0};
  2080. WCHAR szwFileName[MAX_PATH+1]={0};
  2081. WCHAR szwExtension[MAX_PATH+1]={0};
  2082. WCHAR szwCmDir[MAX_PATH+1]={0};
  2083. WCHAR szCmsPathFromCmp[MAX_PATH+1]={0};
  2084. LPWSTR pszActualCmsPath = NULL;
  2085. if (!pszCmpFile || !pszCmsFile)
  2086. {
  2087. return FALSE;
  2088. }
  2089. // Now split the path
  2090. //
  2091. _wsplitpath(pszCmpFile, szwDrive, szwDir, szwFileName, szwExtension);
  2092. DWORD dwRet = GetPrivateProfileStringU(L"Connection Manager", L"CMSFile", L"", szCmsPathFromCmp, MAX_PATH, pszCmpFile);
  2093. if (dwRet)
  2094. {
  2095. if ((lstrlenU(szwDrive) + lstrlenU(szwDir) + lstrlenU(szCmsPathFromCmp)) < MAX_PATH)
  2096. {
  2097. UINT nNumChars = wsprintfW(pszCmsFile, L"%s%s%s", szwDrive, szwDir, szCmsPathFromCmp);
  2098. return TRUE;
  2099. }
  2100. }
  2101. return FALSE;
  2102. }
  2103. BOOL GetShortName(LPWSTR pszCmpFile, LPWSTR pszSvcName)
  2104. {
  2105. LPTSTR pszShortName = NULL;
  2106. if (!pszCmpFile || !pszSvcName)
  2107. {
  2108. return FALSE;
  2109. }
  2110. WCHAR szwDrive[MAX_PATH+1] = {0};
  2111. WCHAR szwDir[MAX_PATH+1] = {0};
  2112. WCHAR szwFileName[MAX_PATH+1] = {0};
  2113. WCHAR szwExtension[MAX_PATH+1] = {0};
  2114. // Now split the path
  2115. //
  2116. _wsplitpath(pszCmpFile, szwDrive, szwDir, szwFileName, szwExtension);
  2117. if (lstrlenU(szwFileName) < MAX_PATH)
  2118. {
  2119. lstrcpyU(pszSvcName, szwFileName);
  2120. return TRUE;
  2121. }
  2122. return FALSE;
  2123. }
  2124. BOOL GetProfileDir(LPWSTR pszCmpFile, LPWSTR pszProfDir)
  2125. {
  2126. LPWSTR pszProfileDir = NULL;
  2127. WCHAR szProfDir[(2*MAX_PATH)+1] = {0};
  2128. if (!pszCmpFile || !pszProfDir)
  2129. {
  2130. return FALSE;
  2131. }
  2132. WCHAR szwDrive[MAX_PATH+1];
  2133. WCHAR szwDir[MAX_PATH+1];
  2134. WCHAR szwFileName[MAX_PATH+1];
  2135. WCHAR szwExtension[MAX_PATH+1];
  2136. // Now split the path
  2137. //
  2138. _wsplitpath(pszCmpFile, szwDrive, szwDir, szwFileName, szwExtension);
  2139. if ((lstrlenU(szwDrive) + lstrlenU(szwDir) + lstrlenU(szwFileName)) < MAX_PATH)
  2140. {
  2141. UINT nNumChars = wsprintfW(pszProfDir, L"%s%s%s\\", szwDrive, szwDir, szwFileName);
  2142. return TRUE;
  2143. }
  2144. return FALSE;
  2145. }
  2146. BOOL IsCustomPropertyEnabled()
  2147. {
  2148. BOOL fRetVal = TRUE; // By default always return TRUE unless the key is set
  2149. HKEY hKey = NULL;
  2150. LONG lRes = 0;
  2151. lRes = RegOpenKeyExU(HKEY_LOCAL_MACHINE, c_pszRegCmRoot, 0, KEY_READ, &hKey);
  2152. if (ERROR_SUCCESS == lRes)
  2153. {
  2154. DWORD dwType = REG_DWORD;
  2155. DWORD dwValue = 0;
  2156. DWORD dwSize = sizeof(dwValue);
  2157. lRes = RegQueryValueExU(hKey, L"DisableCustomProperty", 0, &dwType, (LPBYTE)&dwValue, &dwSize);
  2158. if (ERROR_SUCCESS == lRes)
  2159. {
  2160. if (dwValue)
  2161. {
  2162. fRetVal = FALSE;
  2163. }
  2164. }
  2165. RegCloseKey(hKey);
  2166. }
  2167. return fRetVal;
  2168. }
  2169. //+---------------------------------------------------------------------------
  2170. //
  2171. // Function: GetCMPFileFromMappingsKey
  2172. //
  2173. // Purpose: This function returns the CMP file path from the Mappings key.
  2174. //
  2175. // Arguments: hBaseKey - determines whether it's HKLM or HKCU
  2176. // szEntryName - profile entry name
  2177. // hPrimaryToken - in case of single user profile we use environment
  2178. // variables, this is used to expand those.
  2179. // pszCmpFile - [OUT] .CMP file
  2180. //
  2181. // Returns: ERROR_SUCCESS or an error code
  2182. //
  2183. //+---------------------------------------------------------------------------
  2184. BOOL GetCMPFileFromMappingsKey(HKEY hBaseKey, LPCWSTR szEntryName, HANDLE hPrimaryToken, LPWSTR pszCmpFile)
  2185. {
  2186. HKEY hKey = NULL;
  2187. BOOL fRetVal = FALSE;
  2188. LONG lRes = 0;
  2189. if (!szEntryName || !pszCmpFile)
  2190. {
  2191. return FALSE;
  2192. }
  2193. //
  2194. // Now Open the mappings key and see if this entry name is under HKLM
  2195. // If yes, then get the cmp file path as well.
  2196. //
  2197. lRes = RegOpenKeyExU(hBaseKey, c_pszRegCmMappings, 0, KEY_READ, &hKey);
  2198. if (ERROR_SUCCESS == lRes)
  2199. {
  2200. DWORD dwSize = MAX_PATH;
  2201. WCHAR szTemp[(2*MAX_PATH)+1] = {0};
  2202. WCHAR szExpandedTemp[(2*MAX_PATH)+1] = {0};
  2203. DWORD dwType = REG_SZ;
  2204. lRes = RegQueryValueExU(hKey, szEntryName, 0, &dwType, (LPBYTE)&szTemp[0], &dwSize);
  2205. if (ERROR_SUCCESS == lRes)
  2206. {
  2207. HMODULE hUserEnvDll = LoadLibrary(L"userenv.dll");
  2208. if (hUserEnvDll)
  2209. {
  2210. BOOL fRetCode = FALSE;
  2211. typedef BOOL (WINAPI* pfnExpandEnvironmentStringsForUserFunc)(HANDLE, LPCTSTR, LPTSTR, DWORD);
  2212. pfnExpandEnvironmentStringsForUserFunc pfnExpandEnvironmentStringsForUser = NULL;
  2213. #ifdef UNICODE
  2214. pfnExpandEnvironmentStringsForUser = (pfnExpandEnvironmentStringsForUserFunc)GetProcAddress(hUserEnvDll, "ExpandEnvironmentStringsForUserW");
  2215. #else
  2216. pfnExpandEnvironmentStringsForUser = (pfnExpandEnvironmentStringsForUserFunc)GetProcAddress(hUserEnvDll, "ExpandEnvironmentStringsForUserA");
  2217. #endif
  2218. if (pfnExpandEnvironmentStringsForUser)
  2219. {
  2220. fRetCode = pfnExpandEnvironmentStringsForUser(hPrimaryToken, szTemp, szExpandedTemp, MAX_PATH);
  2221. if (fRetCode)
  2222. {
  2223. lstrcpyU(pszCmpFile, szExpandedTemp);
  2224. fRetVal = TRUE;
  2225. }
  2226. }
  2227. FreeLibrary(hUserEnvDll);
  2228. hUserEnvDll = NULL;
  2229. }
  2230. if (FALSE == fRetVal)
  2231. {
  2232. //
  2233. // Let's just try copying the actual value we got from the registry. In most cases (All-User)
  2234. // profiles it shouldn't contain an environment string anyway.
  2235. //
  2236. lstrcpyU(pszCmpFile, szTemp);
  2237. fRetVal = TRUE;
  2238. }
  2239. }
  2240. RegCloseKey(hKey);
  2241. }
  2242. return fRetVal;
  2243. }
  2244. //+---------------------------------------------------------------------------
  2245. //
  2246. // Function: GetPrivateProfileSectionWithAlloc
  2247. //
  2248. // Purpose: This function gets the 'Menu Options' from the .CMS file
  2249. //
  2250. // Arguments: pszCmsFile - .CMS file path
  2251. // pszSection - [OUT] returns the 'Menu Options' section
  2252. // pnSize - [OUT] the size of the returned buffer
  2253. //
  2254. // Returns: ERROR_SUCCESS or an error code
  2255. //
  2256. //+---------------------------------------------------------------------------
  2257. DWORD GetPrivateProfileSectionWithAlloc(LPWSTR pszCmsFile, WCHAR **pszSection, int *pnSize)
  2258. {
  2259. DWORD dwRetVal = ERROR_SUCCESS;
  2260. if (!pszSection || !pnSize || !pszCmsFile)
  2261. {
  2262. return ERROR_INVALID_PARAMETER;
  2263. }
  2264. if (!OS_NT5)
  2265. {
  2266. return ERROR_INVALID_PARAMETER;
  2267. }
  2268. const int c_64K= 64*1024;
  2269. int nAllocated = 1024;
  2270. *pnSize = nAllocated - 2;
  2271. while ((nAllocated <= c_64K) && ((*pnSize) == (nAllocated - 2)))
  2272. {
  2273. // Should never need more than the 4-5 lines we already allocated
  2274. // but someone might want lots of menu options.
  2275. //
  2276. if (NULL != *pszSection)
  2277. {
  2278. delete (*pszSection);
  2279. }
  2280. *pszSection = new WCHAR[nAllocated];
  2281. if (*pszSection)
  2282. {
  2283. //
  2284. // This will only be executed on Win2K + so we shouldn't need a 'U' function
  2285. //
  2286. *pnSize = GetPrivateProfileSection(L"Menu Options", *pszSection, nAllocated, pszCmsFile);
  2287. }
  2288. else
  2289. {
  2290. dwRetVal = E_OUTOFMEMORY;
  2291. break;
  2292. }
  2293. nAllocated = 2*nAllocated;
  2294. }
  2295. if (nAllocated > c_64K)
  2296. {
  2297. dwRetVal = E_UNEXPECTED;
  2298. }
  2299. if (nAllocated > c_64K || 0 == *pnSize)
  2300. {
  2301. // We need to free this in both cases, because if the size is 0, then the callers don't free this.
  2302. delete *pszSection;
  2303. }
  2304. return dwRetVal;
  2305. }
  2306. //+---------------------------------------------------------------------------
  2307. //
  2308. // Function: GetMenuItems
  2309. //
  2310. // Purpose: This function does the work of reading in the menu items and
  2311. // allocating the structures apporpiately.
  2312. //
  2313. // Arguments: pszCmpFile - .CMP file path
  2314. // pszCmsFile - .CMS file path
  2315. // ppMenuData - [OUT] pointer to contain the menu items
  2316. //
  2317. // Returns: ERROR_SUCCESS or an error code
  2318. //
  2319. //+---------------------------------------------------------------------------
  2320. DWORD GetMenuItems(LPWSTR pszCmpFile, LPWSTR pszCmsFile, CON_TRAY_MENU_DATA** ppMenuData)
  2321. {
  2322. HRESULT hr = S_OK;
  2323. DWORD dwRetVal = ERROR_SUCCESS;
  2324. if (!ppMenuData || !pszCmpFile || !pszCmsFile)
  2325. {
  2326. return ERROR_INVALID_PARAMETER;
  2327. }
  2328. *ppMenuData = NULL;
  2329. CON_TRAY_MENU_DATA * pMenuData = NULL;
  2330. //
  2331. // Get the menu item section
  2332. //
  2333. WCHAR* pszMenuItemsSection = NULL;
  2334. int nSize;
  2335. hr = GetPrivateProfileSectionWithAlloc(pszCmsFile, &pszMenuItemsSection, &nSize);
  2336. // Process the menu items
  2337. //
  2338. if (SUCCEEDED(hr) && (nSize>0))
  2339. {
  2340. // We have menu items to process. First make a copy of the data
  2341. // and figure out a line count.
  2342. //
  2343. hr = HrCoTaskMemAlloc(sizeof(CON_TRAY_MENU_DATA), (LPVOID*)&pMenuData);
  2344. if (SUCCEEDED(hr))
  2345. {
  2346. DWORD dwCount = 0;
  2347. WCHAR *pszLine = NULL;
  2348. WCHAR szName[MAX_PATH+1]={0};
  2349. WCHAR szCmdLine[MAX_PATH+1]={0};
  2350. WCHAR szParams[MAX_PATH+1]={0};
  2351. pszLine = pszMenuItemsSection;
  2352. while ((NULL != pszLine) && (0 != *pszLine))
  2353. {
  2354. if (SUCCEEDED(HrGetMenuNameAndCmdLine(pszLine, szName, CELEMS(szName),
  2355. szCmdLine, CELEMS(szCmdLine), szParams, CELEMS(szParams))))
  2356. {
  2357. dwCount++;
  2358. }
  2359. pszLine = pszLine + lstrlenW(pszLine) + 1;
  2360. }
  2361. // Now that we have an accurate count, lets
  2362. // allocate the memory for the marshalling and
  2363. // reparse the items.
  2364. //
  2365. hr = HrCoTaskMemAlloc(dwCount*sizeof(CON_TRAY_MENU_ENTRY),
  2366. (LPVOID*)&pMenuData->pctme);
  2367. if (SUCCEEDED(hr))
  2368. {
  2369. pMenuData->dwCount = dwCount;
  2370. DWORD dwNumAdded = 0;
  2371. pszLine = pszMenuItemsSection;
  2372. while ((NULL != pszLine) && (0 != *pszLine) && SUCCEEDED(hr))
  2373. {
  2374. if (SUCCEEDED(HrGetMenuNameAndCmdLine(pszLine, szName, CELEMS(szName),
  2375. szCmdLine, CELEMS(szCmdLine),
  2376. szParams, CELEMS(szParams))) &&
  2377. (dwNumAdded <= dwCount))
  2378. {
  2379. hr = HrFillInConTrayMenuEntry(pszCmpFile, szName, szCmdLine, szParams,
  2380. &(pMenuData->pctme[dwNumAdded]));
  2381. if (FAILED(hr))
  2382. {
  2383. CoTaskMemFree(&pMenuData->pctme);
  2384. }
  2385. dwNumAdded++;
  2386. }
  2387. pszLine = pszLine + lstrlenW(pszLine) + 1;
  2388. }
  2389. }
  2390. else
  2391. {
  2392. delete pMenuData;
  2393. }
  2394. }
  2395. delete (pszMenuItemsSection);
  2396. }
  2397. // Fill in the out param struct if we succeeded, otherwise leave it alone so it will still
  2398. // marshall.
  2399. //
  2400. if (SUCCEEDED(hr))
  2401. {
  2402. *ppMenuData = pMenuData;
  2403. }
  2404. return dwRetVal;
  2405. }
  2406. //+---------------------------------------------------------------------------
  2407. //
  2408. // Function: HrGetMenuNameAndCmdLine
  2409. //
  2410. // Purpose: Given a menu item line from a CMS file parses out the Menu item name,
  2411. // Menu executable, and Menu item parameters.
  2412. //
  2413. // Arguments: pMenuData -- Pointer to a Tray Menu Data struct
  2414. //
  2415. // Returns: S_OK or an error code
  2416. //
  2417. //+---------------------------------------------------------------------------
  2418. HRESULT HrGetMenuNameAndCmdLine(PWSTR pszString,
  2419. PWSTR szName, UINT uMaxNameLen,
  2420. PWSTR szProgram, UINT uMaxProgramLen,
  2421. PWSTR szParams, UINT uMaxParamsLen)
  2422. {
  2423. WCHAR* pszPtr1 = NULL;
  2424. WCHAR* pszPtr2 = NULL;
  2425. WCHAR szLine[MAX_PATH+1]={0};
  2426. BOOL fLong = FALSE;
  2427. HRESULT hr;
  2428. if (!pszString || !szName || !szProgram || !szParams || !uMaxNameLen || !uMaxProgramLen || !uMaxParamsLen)
  2429. {
  2430. return E_INVALIDARG;
  2431. }
  2432. ZeroMemory(szName, uMaxNameLen * sizeof(WCHAR));
  2433. ZeroMemory(szProgram, uMaxProgramLen * sizeof(WCHAR));
  2434. ZeroMemory(szParams, uMaxParamsLen * sizeof(WCHAR));
  2435. lstrcpynW(szLine, pszString, CELEMS(szLine));
  2436. // Process the first portion, the "Name=" part
  2437. //
  2438. pszPtr1 = wcsstr(szLine, L"=");
  2439. if (pszPtr1)
  2440. {
  2441. *pszPtr1 = 0;
  2442. lstrcpynW(szName, szLine, uMaxNameLen);
  2443. // Process next portion, the program name
  2444. //
  2445. pszPtr1++;
  2446. if (pszPtr1)
  2447. {
  2448. // Look for "+" or " " marking end of program portion
  2449. //
  2450. if (*pszPtr1 == L'+')
  2451. {
  2452. pszPtr1++;
  2453. pszPtr2 = wcsstr(pszPtr1, L"+");
  2454. fLong = TRUE;
  2455. }
  2456. else
  2457. {
  2458. // If not a long filename then we have two choices,
  2459. // either a short program name and params or just a
  2460. // short program name.
  2461. //
  2462. pszPtr2 = wcsstr(pszPtr1, L" ");
  2463. fLong = FALSE;
  2464. }
  2465. // Terminate program name and copy
  2466. //
  2467. if (pszPtr2)
  2468. {
  2469. if (*pszPtr2 != 0)
  2470. {
  2471. *pszPtr2 = 0;
  2472. pszPtr2++;
  2473. }
  2474. lstrcpynW(szProgram, pszPtr1, uMaxProgramLen);
  2475. // Process final portion, the params
  2476. //
  2477. if (fLong)
  2478. {
  2479. pszPtr2++; // skip blank
  2480. }
  2481. // Now we are have the param string
  2482. //
  2483. if (pszPtr2)
  2484. {
  2485. lstrcpynW(szParams, pszPtr2, uMaxParamsLen);
  2486. }
  2487. }
  2488. else
  2489. {
  2490. // Just a program with no params and no space seperator
  2491. // (this happens on memphis)
  2492. //
  2493. lstrcpynW(szProgram, pszPtr1, uMaxProgramLen);
  2494. }
  2495. }
  2496. hr = S_OK;
  2497. }
  2498. else
  2499. {
  2500. // No entries
  2501. //
  2502. hr = E_UNEXPECTED;
  2503. }
  2504. return hr;
  2505. }
  2506. //+---------------------------------------------------------------------------
  2507. //
  2508. // Member: HrFillInConTrayMenuEntry
  2509. //
  2510. // Purpose: Given the elements of a ConTrayMenuEntry struct, the function
  2511. // allocs the memory necessary and copies of the given elements.
  2512. //
  2513. // Arguments: szwName - Display name of the command to show in the tray context menu
  2514. // szwCmdLine - actual command to run for this menu entry
  2515. // szwParams - command params for this command
  2516. // pMenuEntry - pointer to the struct to fill in and execute
  2517. //
  2518. // Returns: S_OK or an error code
  2519. //
  2520. //+---------------------------------------------------------------------------
  2521. HRESULT HrFillInConTrayMenuEntry (LPWSTR pszCmpFile,
  2522. PCWSTR szName,
  2523. PCWSTR szCmdLine,
  2524. PCWSTR szParams,
  2525. CON_TRAY_MENU_ENTRY* pMenuEntry)
  2526. {
  2527. HRESULT hr;
  2528. WCHAR szProfileDir[(2*MAX_PATH)+1]={0};
  2529. WCHAR szSvcName[(2*MAX_PATH)+1]={0};
  2530. ZeroMemory(pMenuEntry, sizeof(CON_TRAY_MENU_ENTRY));
  2531. (VOID)GetShortName(pszCmpFile, szSvcName);
  2532. (VOID)GetProfileDir(pszCmpFile, szProfileDir);
  2533. hr = HrCoTaskMemAlloc ((lstrlenW(szName)+1)*sizeof(WCHAR),
  2534. (LPVOID*)&(pMenuEntry->szwMenuText));
  2535. if (SUCCEEDED(hr))
  2536. {
  2537. lstrcpyW(pMenuEntry->szwMenuText, szName);
  2538. hr = HrCoTaskMemAlloc ((lstrlenW(szParams)+1)*sizeof(WCHAR),
  2539. (LPVOID*)&(pMenuEntry->szwMenuParams));
  2540. if (S_OK == hr)
  2541. {
  2542. lstrcpyW(pMenuEntry->szwMenuParams, szParams);
  2543. if (0 == wcsncmp(szSvcName, szCmdLine,
  2544. lstrlenW(szSvcName)))
  2545. {
  2546. //
  2547. // Then we have an included file. Add the profile dir path
  2548. //
  2549. // Take out the "short service name" because it's already included in the path
  2550. PCWSTR pszFileName = szCmdLine + lstrlenW(szSvcName) + 1;
  2551. hr = HrCoTaskMemAlloc ((lstrlenW(pszFileName)+lstrlenW(szProfileDir)+1)*sizeof(WCHAR),
  2552. (LPVOID*)&(pMenuEntry->szwMenuCmdLine));
  2553. if (S_OK == hr)
  2554. {
  2555. lstrcpyW(pMenuEntry->szwMenuCmdLine, szProfileDir);
  2556. lstrcatW(pMenuEntry->szwMenuCmdLine, pszFileName);
  2557. }
  2558. }
  2559. else
  2560. {
  2561. hr = HrCoTaskMemAlloc ((lstrlenW(szCmdLine)+1)*sizeof(WCHAR),
  2562. (LPVOID*)&(pMenuEntry->szwMenuCmdLine));
  2563. if (S_OK == hr)
  2564. {
  2565. lstrcpyW(pMenuEntry->szwMenuCmdLine, szCmdLine);
  2566. }
  2567. }
  2568. }
  2569. }
  2570. if (FAILED(hr))
  2571. {
  2572. //
  2573. // We Failed so free the memory
  2574. //
  2575. CoTaskMemFree(pMenuEntry->szwMenuText);
  2576. CoTaskMemFree(pMenuEntry->szwMenuCmdLine);
  2577. CoTaskMemFree(pMenuEntry->szwMenuParams);
  2578. }
  2579. return hr;
  2580. }
  2581. //+---------------------------------------------------------------------------
  2582. //
  2583. // Function: HrCoTaskMemAlloc
  2584. //
  2585. // Purpose: Call CoTaskMemAlloc but return an HRESULT.
  2586. //
  2587. // Arguments:
  2588. // cb [in] Count of bytes to allocate.
  2589. // ppv [out] Returned pointer to bytes.
  2590. //
  2591. // Returns: S_OK or E_OUTOFMEMORY.
  2592. //
  2593. // Author: shaunco 31 May 1997
  2594. //
  2595. // Notes:
  2596. //
  2597. HRESULT
  2598. HrCoTaskMemAlloc (
  2599. ULONG cb,
  2600. VOID** ppv)
  2601. {
  2602. HRESULT hr = S_OK;
  2603. *ppv = CoTaskMemAlloc (cb);
  2604. if (!*ppv)
  2605. {
  2606. hr = E_OUTOFMEMORY;
  2607. }
  2608. return hr;
  2609. }