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.

436 lines
13 KiB

  1. // adext.cpp - Active Directory Extension class
  2. #include "stdafx.h"
  3. #include "adext.h"
  4. #include "util.h"
  5. #include "query.h"
  6. #include <shellapi.h>
  7. #include <atlgdi.h>
  8. #include <shlobj.h>
  9. #include <dsclient.h>
  10. #include <lmcons.h>
  11. #include <lmapibuf.h> // NetApiBufferFree
  12. #include <dsgetdc.h> // DsGetDCName
  13. // Proxy window class object
  14. CMsgWindowClass ADProxyWndClass(L"ADProxyClass", CActDirExtProxy::WndProc);
  15. UINT CADDataObject::m_cfDsObjects = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  16. UINT CADDataObject::m_cfDsDispSpecOptions = RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
  17. HRESULT CADDataObject::GetData(LPFORMATETC lpFormatetcIn, LPSTGMEDIUM lpMedium)
  18. {
  19. if( !lpFormatetcIn || !lpMedium ) return E_POINTER;
  20. if (lpFormatetcIn->cfFormat == m_cfDsObjects)
  21. {
  22. // Form full object path of form: LDAP://<dc name>/<obj path>
  23. tstring strFullPath = L"LDAP://";
  24. strFullPath += m_strDcName;
  25. strFullPath += L"/";
  26. strFullPath += m_strObjPath;
  27. // Get sizes of strings to be returned
  28. int cbObjPath = (strFullPath.length() + 1) * sizeof(WCHAR);
  29. int cbClass = (m_strClass.length() + 1) * sizeof(WCHAR);
  30. // Allocate global memory for object names struct plus two strings
  31. HGLOBAL hGlobal = ::GlobalAlloc(GMEM_SHARE | GMEM_FIXED, sizeof(DSOBJECTNAMES) + cbObjPath + cbClass);
  32. if (hGlobal == NULL) return STG_E_MEDIUMFULL;
  33. // Fill in object names struct
  34. LPDSOBJECTNAMES pObjNames = reinterpret_cast<LPDSOBJECTNAMES>(GlobalLock(hGlobal));
  35. if( !pObjNames ) return E_OUTOFMEMORY;
  36. memset(&pObjNames->clsidNamespace, 0, sizeof(GUID));
  37. memcpy(&pObjNames->clsidNamespace, &CLSID_MicrosoftDS, sizeof(CLSID_MicrosoftDS));
  38. pObjNames->cItems = 1;
  39. pObjNames->aObjects[0].dwFlags = DSPROVIDER_ADVANCED;
  40. pObjNames->aObjects[0].dwProviderFlags = 0;
  41. pObjNames->aObjects[0].offsetName = sizeof(DSOBJECTNAMES);
  42. pObjNames->aObjects[0].offsetClass = sizeof(DSOBJECTNAMES) + cbObjPath;
  43. // Append strings to struct
  44. memcpy((LPBYTE)pObjNames + pObjNames->aObjects[0].offsetName, strFullPath.c_str(), cbObjPath);
  45. memcpy((LPBYTE)pObjNames + pObjNames->aObjects[0].offsetClass, m_strClass.c_str(), cbClass);
  46. GlobalUnlock(hGlobal);
  47. // fill in medium struct
  48. lpMedium->tymed = TYMED_HGLOBAL;
  49. lpMedium->hGlobal = hGlobal;
  50. lpMedium->pUnkForRelease = NULL;
  51. return S_OK;
  52. }
  53. else if (lpFormatetcIn->cfFormat == m_cfDsDispSpecOptions)
  54. {
  55. static WCHAR szPrefix[] = L"admin";
  56. int cbDcName = (m_strDcName.length() + 1) * sizeof(WCHAR);
  57. // Allocate global memory for options struct plus prefix string and Dc name
  58. // BUGBUG - Due to an error in the DSPropertyPages code (dsuiext.dll), we must pass it a fixed memory block
  59. HGLOBAL hGlobal = ::GlobalAlloc(GMEM_SHARE | GMEM_FIXED, sizeof(DSDISPLAYSPECOPTIONS) + sizeof(szPrefix) + cbDcName);
  60. if (hGlobal == NULL) return STG_E_MEDIUMFULL;
  61. // Fill in struct
  62. LPDSDISPLAYSPECOPTIONS pOptions = reinterpret_cast<LPDSDISPLAYSPECOPTIONS>(GlobalLock(hGlobal));
  63. if( !pOptions ) return E_OUTOFMEMORY;
  64. pOptions->dwSize = sizeof(DSDISPLAYSPECOPTIONS);
  65. pOptions->dwFlags = DSDSOF_HASUSERANDSERVERINFO;
  66. pOptions->offsetAttribPrefix = sizeof(DSDISPLAYSPECOPTIONS);
  67. pOptions->offsetUserName = 0;
  68. pOptions->offsetPassword = 0;
  69. pOptions->offsetServer = pOptions->offsetAttribPrefix + sizeof(szPrefix);
  70. pOptions->offsetServerConfigPath = 0;
  71. // Append prefix string
  72. memcpy((LPBYTE)pOptions + pOptions->offsetAttribPrefix, szPrefix, sizeof(szPrefix));
  73. memcpy((LPBYTE)pOptions + pOptions->offsetServer, m_strDcName.c_str(), cbDcName);
  74. GlobalUnlock(hGlobal);
  75. // fill in medium struct
  76. lpMedium->tymed = TYMED_HGLOBAL;
  77. lpMedium->hGlobal = hGlobal;
  78. lpMedium->pUnkForRelease = NULL;
  79. return S_OK;
  80. }
  81. return DV_E_FORMATETC;
  82. }
  83. /////////////////////////////////////////////////////////////////////////////////////////
  84. // CActDirExt
  85. HRESULT CActDirExt::Initialize(LPCWSTR pszClass, LPCWSTR pszObjPath)
  86. {
  87. if( !pszClass || !pszObjPath ) return E_POINTER;
  88. // Escape each forward slash in object name
  89. tstring strObj;
  90. EscapeSlashes(pszObjPath, strObj);
  91. // Get DC name
  92. DOMAIN_CONTROLLER_INFO* pDcInfo = NULL;
  93. DWORD dwStat = DsGetDcName(NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED|DS_RETURN_DNS_NAME, &pDcInfo);
  94. if( dwStat != NO_ERROR || pDcInfo == NULL )
  95. return E_FAIL;
  96. // verify name begins with '\\'
  97. if( !(pDcInfo->DomainControllerName && pDcInfo->DomainControllerName[0] == _T('\\') && pDcInfo->DomainControllerName[1] == _T('\\')) )
  98. {
  99. NetApiBufferFree(pDcInfo);
  100. return E_FAIL;
  101. }
  102. // discard the leading '\\'
  103. LPCTSTR pszDcName = pDcInfo->DomainControllerName + 2;
  104. // Create a directory data object
  105. CComObject<CADDataObject>* pObj;
  106. HRESULT hr = CComObject<CADDataObject>::CreateInstance(&pObj);
  107. // Initialize it with the object path and class
  108. if( SUCCEEDED(hr) )
  109. {
  110. hr = pObj->Initialize(strObj.c_str(), pszClass, pszDcName);
  111. }
  112. NetApiBufferFree(pDcInfo);
  113. pDcInfo = NULL;
  114. // Verify that all is good now
  115. RETURN_ON_FAILURE(hr);
  116. // Hold IDataObject interface with a smart pointer
  117. IDataObjectPtr spDataObj = pObj;
  118. ASSERT(spDataObj != NULL);
  119. // Create a DsPropertyPage object (despite name it handles both context menus and property pages)
  120. hr = CoCreateInstance(CLSID_DsPropertyPages, NULL, CLSCTX_INPROC_SERVER, IID_IShellExtInit, (LPVOID*)&m_spExtInit);
  121. RETURN_ON_FAILURE(hr)
  122. // Intialize the object with our data object
  123. hr = m_spExtInit->Initialize(NULL, spDataObj, NULL);
  124. if (FAILED(hr))
  125. m_spExtInit.Release();
  126. return hr;
  127. }
  128. HRESULT CActDirExt::Initialize(LPCWSTR pszClass)
  129. {
  130. // Find an object of the specified class
  131. tstring strObjPath;
  132. HRESULT hr = FindClassObject( pszClass, strObjPath );
  133. RETURN_ON_FAILURE(hr)
  134. // Now do normal initialization
  135. return Initialize(pszClass, strObjPath.c_str());
  136. }
  137. HRESULT CActDirExt::GetMenuItems(menu_vector& vMenuNames)
  138. {
  139. if( !m_spExtInit ) return E_FAIL;
  140. // Get context menu interface
  141. CComQIPtr<IContextMenu> spCtxMenu = m_spExtInit;
  142. if( !spCtxMenu ) return E_NOINTERFACE;
  143. // Start with clean menu
  144. m_menu.DestroyMenu();
  145. if( !m_menu.CreatePopupMenu() ) return E_FAIL;
  146. if( !m_menu.m_hMenu ) return E_FAIL;
  147. // Call extension to add menu commands
  148. HRESULT hr = spCtxMenu->QueryContextMenu(m_menu, 0, MENU_CMD_MIN, MENU_CMD_MAX, CMF_NORMAL);
  149. RETURN_ON_FAILURE(hr);
  150. // Copy each menu name to the output string vector
  151. WCHAR wszCmdName[1024];
  152. WCHAR wszCmdIndName[1024];
  153. UINT nItems = m_menu.GetMenuItemCount();
  154. for( UINT i = 0; i < nItems; i++ )
  155. {
  156. UINT uID = m_menu.GetMenuItemID(i);
  157. if (uID >= MENU_CMD_MIN)
  158. {
  159. BOMMENU bmenu;
  160. int nFullSize = m_menu.GetMenuString(i, wszCmdName, lengthof(wszCmdName), MF_BYPOSITION);
  161. if( (nFullSize == 0) || (nFullSize >= lengthof(wszCmdName)) )
  162. {
  163. return E_FAIL;
  164. }
  165. bmenu.strPlain = wszCmdName;
  166. HRESULT hr2 = spCtxMenu->GetCommandString(uID - MENU_CMD_MIN, GCS_VERBW, NULL, (LPSTR)wszCmdIndName, lengthof(wszCmdIndName));
  167. if( (hr2 != NOERROR) || (wcslen( wszCmdIndName) >= lengthof(wszCmdIndName)-1) )
  168. {
  169. // Lots of Menu items (extended ones!) have no
  170. // language-independant menu identifiers
  171. bmenu.strNoLoc = _T("");
  172. }
  173. else
  174. {
  175. bmenu.strNoLoc = wszCmdIndName;
  176. }
  177. vMenuNames.push_back(bmenu);
  178. }
  179. }
  180. return hr;
  181. }
  182. HRESULT CActDirExt::Execute(BOMMENU* pbmMenu)
  183. {
  184. if( !pbmMenu ) return E_POINTER;
  185. if( !m_spExtInit || !m_menu.m_hMenu ) return E_FAIL;
  186. // Get context menu interface
  187. CComQIPtr<IContextMenu> spCtxMenu = m_spExtInit;
  188. if( !spCtxMenu ) return E_NOINTERFACE;
  189. HRESULT hr = E_FAIL;
  190. // Locate selected command by name
  191. WCHAR szCmdName[1024];
  192. WCHAR szCmdNoLocName[1024];
  193. UINT nItems = m_menu.GetMenuItemCount();
  194. for (int i=0; i<nItems; i++)
  195. {
  196. szCmdName[0] = 0;
  197. szCmdNoLocName[0] = 0;
  198. UINT uID = m_menu.GetMenuItemID(i);
  199. // Get our Unique and non-unique ID Strings
  200. int nFullSize = m_menu.GetMenuString(i, szCmdName, lengthof(szCmdName), MF_BYPOSITION);
  201. if( (nFullSize <= 0) || (nFullSize >= lengthof(szCmdName)) )
  202. {
  203. continue;
  204. }
  205. hr = spCtxMenu->GetCommandString(uID - MENU_CMD_MIN, GCS_VERBW, NULL, (LPSTR)szCmdNoLocName, lengthof(szCmdNoLocName));
  206. if( hr != NOERROR )
  207. {
  208. // We want to make sure that if there's an error getting the
  209. // language independant menu name that we don't do anything stupid.
  210. szCmdNoLocName[0] = 0;
  211. }
  212. // If we got a Unique ID String, compare that to what was passed in, otherwise
  213. // use the stored Display String
  214. // NOTE: We had to use both, because Exchange does not support the language independant
  215. // menu identifiers
  216. if( ( pbmMenu->strNoLoc.size() && _tcscmp(pbmMenu->strNoLoc.c_str(), szCmdNoLocName) == 0 ) ||
  217. ( _tcscmp(pbmMenu->strPlain.c_str(), szCmdName) == 0 ) )
  218. {
  219. CMINVOKECOMMANDINFO cmdInfo;
  220. ZeroMemory( &cmdInfo, sizeof(cmdInfo) );
  221. cmdInfo.cbSize = sizeof(cmdInfo);
  222. cmdInfo.fMask = CMIC_MASK_ASYNCOK;
  223. cmdInfo.hwnd = GetDesktopWindow();
  224. cmdInfo.lpVerb = (LPSTR)MAKEINTRESOURCE(uID - MENU_CMD_MIN);
  225. cmdInfo.nShow = SW_NORMAL;
  226. hr = spCtxMenu->InvokeCommand(&cmdInfo);
  227. break;
  228. }
  229. }
  230. ASSERT(i < nItems);
  231. return hr;
  232. }
  233. //
  234. // Add Page callback function
  235. //
  236. static BOOL CALLBACK AddPageCallback(HPROPSHEETPAGE hsheetpage, LPARAM lParam)
  237. {
  238. hpage_vector* pvhPages = reinterpret_cast<hpage_vector*>(lParam);
  239. if( !pvhPages ) return FALSE;
  240. pvhPages->push_back(hsheetpage);
  241. return TRUE;
  242. }
  243. HRESULT CActDirExt::GetPropertyPages(hpage_vector& vhPages)
  244. {
  245. if( !m_spExtInit ) return E_FAIL;
  246. // Get Property page interface
  247. CComQIPtr<IShellPropSheetExt> spPropSht = m_spExtInit;
  248. if( !spPropSht ) return E_NOINTERFACE;
  249. HRESULT hr = spPropSht->AddPages(&AddPageCallback, reinterpret_cast<LPARAM>(&vhPages));
  250. return hr;
  251. }
  252. //////////////////////////////////////////////////////////////////////////////////////////
  253. // CActDirExtProxy
  254. HWND CActDirExtProxy::m_hWndProxy = 0;
  255. LRESULT CALLBACK CActDirExtProxy::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  256. {
  257. if (uMsg >= MSG_BEGIN && uMsg < MSG_END)
  258. {
  259. if( hWnd != m_hWndProxy )
  260. {
  261. ASSERT( !_T("We have the wrong window.") );
  262. return E_FAIL;
  263. }
  264. CActDirExtProxy* pProxy = reinterpret_cast<CActDirExtProxy*>(wParam);
  265. if( !pProxy ) return E_FAIL;
  266. CActDirExt* pExt = pProxy->m_pExt;
  267. if( !pExt ) return E_FAIL;
  268. HRESULT hr = S_OK;
  269. switch (uMsg)
  270. {
  271. case MSG_INIT1:
  272. hr = pExt->Initialize(reinterpret_cast<LPCWSTR>(pProxy->m_lParam1));
  273. break;
  274. case MSG_INIT2:
  275. hr = pExt->Initialize(reinterpret_cast<LPCWSTR>(pProxy->m_lParam1),
  276. reinterpret_cast<LPCWSTR>(pProxy->m_lParam2));
  277. break;
  278. case MSG_GETMENUITEMS:
  279. hr = pExt->GetMenuItems(*reinterpret_cast<menu_vector*>(pProxy->m_lParam1));
  280. break;
  281. case MSG_GETPROPPAGES:
  282. hr = pExt->GetPropertyPages(*reinterpret_cast<hpage_vector*>(pProxy->m_lParam1));
  283. break;
  284. case MSG_EXECUTE:
  285. hr = pExt->Execute( reinterpret_cast<BOMMENU*>(pProxy->m_lParam1) );
  286. break;
  287. case MSG_DELETE:
  288. delete pExt;
  289. pProxy->m_pExt = NULL;
  290. break;
  291. }
  292. return hr;
  293. }
  294. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  295. }
  296. CActDirExtProxy::CActDirExtProxy()
  297. {
  298. m_pExt = new CActDirExt();
  299. ASSERT(m_pExt != NULL);
  300. }
  301. CActDirExtProxy::~CActDirExtProxy()
  302. {
  303. if (m_pExt != NULL)
  304. {
  305. ForwardCall(MSG_DELETE);
  306. }
  307. }
  308. void CActDirExtProxy::InitProxy()
  309. {
  310. if( !m_hWndProxy )
  311. {
  312. m_hWndProxy = ADProxyWndClass.Window();
  313. }
  314. else
  315. {
  316. ASSERT(IsWindow(m_hWndProxy));
  317. }
  318. }
  319. HRESULT CActDirExtProxy::ForwardCall(eProxyMsg eMsg, LPARAM lParam1, LPARAM lParam2)
  320. {
  321. m_lParam1 = lParam1;
  322. m_lParam2 = lParam2;
  323. if( !m_hWndProxy ) return E_FAIL;
  324. return SendMessage(m_hWndProxy, eMsg, reinterpret_cast<LPARAM>(this), NULL);
  325. }