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.

1199 lines
41 KiB

  1. /*****************************************************************************\
  2. FILE: AutoDiscBase.cpp
  3. DESCRIPTION:
  4. This is the Autmation Object to AutoDiscover account information.
  5. BryanSt 10/3/1999
  6. Copyright (C) Microsoft Corp 1999-1999. All rights reserved.
  7. \*****************************************************************************/
  8. #include "priv.h"
  9. #include <cowsite.h>
  10. #include <atlbase.h>
  11. #include <crypto\md5.h>
  12. #include "AutoDiscover.h"
  13. #include "INStoXML.h"
  14. //#define SZ_WININET_AGENT_AUTO_DISCOVER TEXT("Microsoft(r) Windows(tm) Account AutoDiscovery Agent")
  15. #define SZ_WININET_AGENT_AUTO_DISCOVER TEXT("Mozilla/4.0 (compatible; MSIE.5.01; Windows.NT.5.0)")
  16. // BUGBUG: Ditch default.asp
  17. #define SZ_ADSERVER_XMLFILE "/AutoDiscover/default.xml"
  18. #define SZ_PATH_AUTODISCOVERY L"AutoDiscovery"
  19. #define SZ_FILEEXTENSION L".xml"
  20. #define SZ_TEMPEXTENSION L".tmp"
  21. // this is how long we wait for the UI thread to create the progress hwnd before giving up
  22. #define WAIT_AUTODISCOVERY_STARTUP_HWND 10*1000 // ten seconds
  23. // The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601
  24. #define SECONDS_IN_ONE_DAY (60/*seconds*/ * 60/*minutes*/ * 24/*hrs*/)
  25. //===========================
  26. // *** Class Internals & Helpers ***
  27. //===========================
  28. HRESULT GetTempPathHr(IN DWORD cchSize, IN LPTSTR pszPath)
  29. {
  30. HRESULT hr = S_OK;
  31. DWORD cchSizeNeeded = GetTempPath(cchSize, pszPath);
  32. if ((0 == cchSizeNeeded) || (cchSizeNeeded > cchSize))
  33. {
  34. hr = E_FAIL;
  35. }
  36. return hr;
  37. }
  38. HRESULT GetTempFileNameHr(IN LPCTSTR lpPathName, IN LPCTSTR lpPrefixString, IN UINT uUnique, IN LPTSTR lpTempFileName)
  39. {
  40. if (0 == GetTempFileName(lpPathName, lpPrefixString, uUnique, lpTempFileName))
  41. {
  42. return HRESULT_FROM_WIN32(GetLastError());
  43. }
  44. return S_OK;
  45. }
  46. HRESULT CreateXMLTempFile(IN BSTR bstrXML, IN LPTSTR pszPath, IN DWORD cchSize)
  47. {
  48. TCHAR szTemp[MAX_PATH];
  49. HRESULT hr = GetTempPathHr(ARRAYSIZE(szTemp), szTemp);
  50. AssertMsg((MAX_PATH <= cchSize), "You need to be at least MAX_PATH. Required by GetTempFileName()");
  51. if (SUCCEEDED(hr))
  52. {
  53. hr = GetTempFileNameHr(szTemp, TEXT("AD_"), 0, pszPath);
  54. if (SUCCEEDED(hr))
  55. {
  56. HANDLE hFile;
  57. hr = CreateFileHrWrap(pszPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL, &hFile);
  58. if (SUCCEEDED(hr))
  59. {
  60. LPSTR pszAnsiXML = AllocStringFromBStr(bstrXML);
  61. if (pszAnsiXML)
  62. {
  63. DWORD cchWritten;
  64. hr = WriteFileWrap(hFile, pszAnsiXML, (lstrlenA(pszAnsiXML) + 1), &cchWritten, NULL);
  65. LocalFree(pszAnsiXML);
  66. }
  67. else
  68. {
  69. hr = E_OUTOFMEMORY;
  70. }
  71. CloseHandle(hFile);
  72. }
  73. if (FAILED(hr))
  74. {
  75. DeleteFile(pszPath);
  76. }
  77. }
  78. }
  79. return hr;
  80. }
  81. /*****************************************************************************\
  82. DESCRIPTION:
  83. This function will see if pbstrXML is valid AutoDiscovery XML or is
  84. in the .INS/.ISP format that can be converted to valid XML. It will then look
  85. for a redirect URL and return on if one exists.
  86. \*****************************************************************************/
  87. HRESULT CAccountDiscoveryBase::_VerifyValidXMLResponse(IN BSTR * pbstrXML, IN LPWSTR pszRedirURL, IN DWORD cchSize)
  88. {
  89. IXMLDOMDocument * pXMLDOMDoc;
  90. bool fConverted = false;
  91. HRESULT hr = XMLDOMFromBStr(*pbstrXML, &pXMLDOMDoc);
  92. TCHAR szPath[MAX_PATH];
  93. pszRedirURL[0] = 0;
  94. if (FAILED(hr))
  95. {
  96. // It may have failed if it was an .INS or .ISP formatted
  97. // file. Since we need to be compatible with these
  98. // file formats, check for it and convert it if it
  99. // is in that format.
  100. hr = CreateXMLTempFile(*pbstrXML, szPath, ARRAYSIZE(szPath));
  101. if (SUCCEEDED(hr))
  102. {
  103. fConverted = true;
  104. if (IsINSFile(szPath))
  105. {
  106. hr = ConvertINSToXML(szPath);
  107. if (SUCCEEDED(hr))
  108. {
  109. hr = XMLDOMFromFile(szPath, &pXMLDOMDoc);
  110. }
  111. }
  112. else
  113. {
  114. hr = E_FAIL;
  115. }
  116. }
  117. }
  118. if (SUCCEEDED(hr))
  119. {
  120. IXMLDOMElement * pXMLElementMessage = NULL;
  121. hr = pXMLDOMDoc->get_documentElement(&pXMLElementMessage);
  122. if (S_FALSE == hr)
  123. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  124. else if (SUCCEEDED(hr))
  125. {
  126. // This is only valid XML if the root tag is "AUTODISCOVERY".
  127. // The case is not important.
  128. hr = XMLElem_VerifyTagName(pXMLElementMessage, SZ_XMLELEMENT_AUTODISCOVERY);
  129. if (SUCCEEDED(hr))
  130. {
  131. // Now we are in search for a redirect URL.
  132. IXMLDOMNode * pXMLReponse;
  133. // Enter the <RESPONSE> tag.
  134. if (SUCCEEDED(XMLNode_GetChildTag(pXMLElementMessage, SZ_XMLELEMENT_RESPONSE, &pXMLReponse)))
  135. {
  136. IXMLDOMElement * pXMLElementMessage;
  137. if (SUCCEEDED(pXMLReponse->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pXMLElementMessage))))
  138. {
  139. IXMLDOMNodeList * pNodeListAccounts;
  140. // Iterate thru the list of <ACCOUNT> tags...
  141. if (SUCCEEDED(XMLElem_GetElementsByTagName(pXMLElementMessage, SZ_XMLELEMENT_ACCOUNT, &pNodeListAccounts)))
  142. {
  143. DWORD dwIndex = 0;
  144. IXMLDOMNode * pXMLNodeAccount = NULL;
  145. // We are going to look thru each one for one of them with <TYPE>email</TYPE>
  146. while (S_OK == XMLNodeList_GetChild(pNodeListAccounts, dwIndex, &pXMLNodeAccount))
  147. {
  148. // FUTURE: We could support redirects or error messages here depending on
  149. // <ACTION> redirect | message </ACTION>
  150. if (XML_IsChildTagTextEqual(pXMLNodeAccount, SZ_XMLELEMENT_TYPE, SZ_XMLTEXT_EMAIL) &&
  151. XML_IsChildTagTextEqual(pXMLNodeAccount, SZ_XMLELEMENT_ACTION, SZ_XMLTEXT_REDIRECT))
  152. {
  153. CComBSTR bstrRedirURL;
  154. // This file may or may not settings to contact the server. However in either case
  155. // it may contain an INFOURL tag. If it does, then the URL in side will point to a
  156. // web page.
  157. // <INFOURL> xxx </INFOURL>
  158. if (SUCCEEDED(XMLNode_GetChildTagTextValue(pXMLNodeAccount, SZ_XMLELEMENT_REDIRURL, &bstrRedirURL)))
  159. {
  160. StrCpyNW(pszRedirURL, bstrRedirURL, cchSize);
  161. break;
  162. }
  163. }
  164. // No, so keep looking.
  165. ATOMICRELEASE(pXMLNodeAccount);
  166. dwIndex++;
  167. }
  168. ATOMICRELEASE(pXMLNodeAccount);
  169. pNodeListAccounts->Release();
  170. }
  171. pXMLElementMessage->Release();
  172. }
  173. pXMLReponse->Release();
  174. }
  175. }
  176. pXMLElementMessage->Release();
  177. }
  178. if (true == fConverted)
  179. {
  180. if (SUCCEEDED(hr))
  181. {
  182. // It only succeeded after the conversion, so we need to move the
  183. // XML from the temp file to pbstrXML.
  184. SysFreeString(*pbstrXML);
  185. *pbstrXML = NULL;
  186. hr = XMLBStrFromDOM(pXMLDOMDoc, pbstrXML);
  187. }
  188. }
  189. pXMLDOMDoc->Release();
  190. }
  191. if (true == fConverted)
  192. {
  193. DeleteFile(szPath);
  194. }
  195. return hr;
  196. }
  197. typedef HINSTANCE (STDAPICALLTYPE *PFNMLLOADLIBARY)(LPCSTR lpLibFileName, HMODULE hModule, DWORD dwCrossCodePage);
  198. static const char c_szShlwapiDll[] = "shlwapi.dll";
  199. static const char c_szDllGetVersion[] = "DllGetVersion";
  200. HINSTANCE LoadLangDll(HINSTANCE hInstCaller, LPCSTR szDllName, BOOL fNT)
  201. {
  202. char szPath[MAX_PATH];
  203. HINSTANCE hinstShlwapi;
  204. PFNMLLOADLIBARY pfn;
  205. DLLGETVERSIONPROC pfnVersion;
  206. int iEnd;
  207. DLLVERSIONINFO info;
  208. HINSTANCE hInst = NULL;
  209. hinstShlwapi = LoadLibraryA(c_szShlwapiDll);
  210. if (hinstShlwapi != NULL)
  211. {
  212. pfnVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstShlwapi, c_szDllGetVersion);
  213. if (pfnVersion != NULL)
  214. {
  215. info.cbSize = sizeof(DLLVERSIONINFO);
  216. if (SUCCEEDED(pfnVersion(&info)))
  217. {
  218. if (info.dwMajorVersion >= 5)
  219. {
  220. pfn = (PFNMLLOADLIBARY)GetProcAddress(hinstShlwapi, MAKEINTRESOURCEA(377));
  221. if (pfn != NULL)
  222. hInst = pfn(szDllName, hInstCaller, (ML_NO_CROSSCODEPAGE));
  223. }
  224. }
  225. }
  226. FreeLibrary(hinstShlwapi);
  227. }
  228. if ((NULL == hInst) && (GetModuleFileNameA(hInstCaller, szPath, ARRAYSIZE(szPath))))
  229. {
  230. if (PathRemoveFileSpecA(szPath) && PathAppendA(szPath, szDllName))
  231. {
  232. hInst = LoadLibraryA(szPath);
  233. }
  234. }
  235. return hInst;
  236. }
  237. #define SZ_DLL_OE_ACCTRES_DLL "acctres.dll"
  238. HRESULT CAccountDiscoveryBase::_SendStatusMessage(UINT nStringID, LPCWSTR pwzArg)
  239. {
  240. HRESULT hr = S_OK;
  241. if (m_hwndAsync && IsWindow(m_hwndAsync))
  242. {
  243. WCHAR szMessage[MAX_URL_STRING*3];
  244. WCHAR szTemplate[MAX_URL_STRING*3];
  245. // Our DLL has these message.
  246. LoadString(HINST_THISDLL, nStringID, szTemplate, ARRAYSIZE(szTemplate));
  247. HINSTANCE hInstOE = LoadLangDll(GetModuleHandleA(NULL), SZ_DLL_OE_ACCTRES_DLL, IsOSNT());
  248. if (hInstOE)
  249. {
  250. // We prefer to get the string from OE because it will be localized based on the installed
  251. // language.
  252. LoadString(hInstOE, nStringID, szTemplate, ARRAYSIZE(szTemplate));
  253. FreeLibrary(hInstOE);
  254. }
  255. if (pwzArg)
  256. {
  257. wnsprintfW(szMessage, ARRAYSIZE(szMessage), szTemplate, pwzArg);
  258. }
  259. else
  260. {
  261. StrCpyN(szMessage, szTemplate, ARRAYSIZE(szMessage));
  262. }
  263. DWORD cchSize = (lstrlenW(szMessage) + 1);
  264. LPWSTR pszString = (LPWSTR) LocalAlloc(LPTR, cchSize * sizeof(szMessage[0]));
  265. if (pszString)
  266. {
  267. StrCpyN(pszString, szMessage, cchSize);
  268. PostMessage(m_hwndAsync, (m_wMsgAsync + 1), (WPARAM)pszString, (LPARAM)0);
  269. }
  270. else
  271. {
  272. hr = E_OUTOFMEMORY;
  273. }
  274. }
  275. return hr;
  276. }
  277. HRESULT CAccountDiscoveryBase::_UrlToComponents(IN LPCWSTR pszURL, IN BOOL * pfHTTPS, IN LPWSTR pszDomain, IN DWORD cchSize, IN LPSTR pszURLPath, IN DWORD cchSizeURLPath)
  278. {
  279. HRESULT hr = S_OK;
  280. WCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH];
  281. WCHAR szURLPath[INTERNET_MAX_PATH_LENGTH];
  282. URL_COMPONENTS urlComponents = {0};
  283. urlComponents.dwStructSize = sizeof(urlComponents);
  284. urlComponents.lpszScheme = szScheme;
  285. urlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
  286. urlComponents.lpszHostName = pszDomain;
  287. urlComponents.dwHostNameLength = cchSize;
  288. urlComponents.lpszUrlPath = szURLPath;
  289. urlComponents.dwUrlPathLength = ARRAYSIZE(szURLPath);
  290. *pfHTTPS = ((INTERNET_SCHEME_HTTPS == urlComponents.nScheme) ? TRUE : FALSE);
  291. if (!InternetCrackUrlW(pszURL, 0, 0, &urlComponents))
  292. {
  293. hr = HRESULT_FROM_WIN32(GetLastError());
  294. }
  295. else
  296. {
  297. SHUnicodeToAnsi(szURLPath, pszURLPath, cchSizeURLPath);
  298. }
  299. return hr;
  300. }
  301. HRESULT CAccountDiscoveryBase::_GetInfoFromDomain(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN LPCWSTR pwzDomain, IN BOOL fHTTPS, IN BOOL fPost, IN LPCSTR pszURLPath, OUT BSTR * pbstrXML)
  302. {
  303. HRESULT hr = E_OUTOFMEMORY;
  304. DWORD cbToSend = (lstrlenW(bstrXMLRequest));
  305. LPSTR pszPostData = (LPSTR) LocalAlloc(LPTR, (cbToSend + 1) * sizeof(bstrXMLRequest[0]));
  306. TCHAR szRedirectURL[MAX_URL_STRING];
  307. szRedirectURL[0] = 0;
  308. if (pszPostData)
  309. {
  310. HINTERNET hInternetHTTPConnect = NULL;
  311. SHUnicodeToAnsi(bstrXMLRequest, pszPostData, (cbToSend + 1));
  312. _SendStatusMessage(IDS_STATUS_CONNECTING_TO, pwzDomain);
  313. // We may want to use INTERNET_FLAG_KEEP_CONNECTION.
  314. hr = InternetConnectWrap(m_hInternetSession, FALSE, pwzDomain, (fHTTPS ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT),
  315. NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL, &hInternetHTTPConnect);
  316. if (SUCCEEDED(hr))
  317. {
  318. HINTERNET hInternetHTTPRequest = NULL;
  319. DWORD cbBytesRead;
  320. // NOTE: The web server may want to redirect to an https URL for additional security.
  321. // We need to pass the INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS to HttpOpenRequest
  322. // or HttpSendRequest() will fail with ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
  323. // NOTE: We may need to split the URL into lpszReferer + lpszObjectName.
  324. hr = HttpOpenRequestWrap(hInternetHTTPConnect, (fPost ? SZ_HTTP_VERB_POST : NULL), pszURLPath, HTTP_VERSIONA,
  325. /*pszReferer*/ NULL, NULL, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, NULL, &cbBytesRead, &hInternetHTTPRequest);
  326. if (SUCCEEDED(hr))
  327. {
  328. hr = HttpSendRequestWrap(hInternetHTTPRequest, NULL, 0, (fPost ? pszPostData : NULL), (fPost ? cbToSend : 0));
  329. if (SUCCEEDED(hr))
  330. {
  331. _SendStatusMessage(IDS_STATUS_DOWNLOADING, pwzDomain);
  332. hr = InternetReadIntoBSTR(hInternetHTTPRequest, pbstrXML);
  333. if (SUCCEEDED(hr))
  334. {
  335. hr = _VerifyValidXMLResponse(pbstrXML, szRedirectURL, ARRAYSIZE(szRedirectURL));
  336. if (FAILED(hr))
  337. {
  338. SysFreeString(*pbstrXML);
  339. *pbstrXML = NULL;
  340. }
  341. }
  342. if (SUCCEEDED(hr))
  343. {
  344. hr = InternetCloseHandleWrap(hInternetHTTPRequest);
  345. }
  346. else
  347. {
  348. InternetCloseHandleWrap(hInternetHTTPRequest);
  349. }
  350. }
  351. InternetCloseHandleWrap(hInternetHTTPRequest);
  352. }
  353. InternetCloseHandleWrap(hInternetHTTPConnect);
  354. }
  355. LocalFree(pszPostData);
  356. }
  357. // Did the caller want to redirect to another server?
  358. if (szRedirectURL[0])
  359. {
  360. // Yes, so do that now via recursion.
  361. WCHAR szDomain[INTERNET_MAX_HOST_NAME_LENGTH];
  362. CHAR szURLPath[INTERNET_MAX_PATH_LENGTH];
  363. SysFreeString(*pbstrXML);
  364. *pbstrXML = NULL;
  365. hr = _UrlToComponents(szRedirectURL, &fHTTPS, szDomain, ARRAYSIZE(szDomain), szURLPath, ARRAYSIZE(szURLPath));
  366. if (SUCCEEDED(hr))
  367. {
  368. hr = _GetInfoFromDomain(bstrXMLRequest, bstrEmail, szDomain, fHTTPS, TRUE, szURLPath, pbstrXML);
  369. }
  370. }
  371. return hr;
  372. }
  373. #define SZ_XML_NOTFOUNDRESULTS L"<?xml version=\"1.0\"?><AUTODISCOVERY><NOFOUND /></AUTODISCOVERY>"
  374. HRESULT CAccountDiscoveryBase::_GetInfoFromDomainWithSubdirAndCacheCheck(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN LPCWSTR pwzDomain, IN BSTR * pbstrXML, IN DWORD dwFlags, IN LPCSTR pszURLPath)
  375. {
  376. HRESULT hr;
  377. WCHAR wzCacheURL[INTERNET_MAX_HOST_NAME_LENGTH];
  378. if (dwFlags & ADDN_SKIP_CACHEDRESULTS)
  379. {
  380. hr = E_FAIL;
  381. }
  382. else
  383. {
  384. hr = _CheckInCacheAndAddHash(pwzDomain, bstrEmail, pszURLPath, wzCacheURL, ARRAYSIZE(wzCacheURL), bstrXMLRequest, pbstrXML);
  385. }
  386. if (FAILED(hr))
  387. {
  388. hr = _GetInfoFromDomain(bstrXMLRequest, bstrEmail, pwzDomain, FALSE, FALSE, pszURLPath, pbstrXML);
  389. if (SUCCEEDED(hr))
  390. {
  391. // Put the data into the cache for the next time.
  392. _CacheResults(wzCacheURL, *pbstrXML);
  393. }
  394. else
  395. {
  396. // We want to make a blank entry so we don't keep hitting the server
  397. _CacheResults(wzCacheURL, SZ_XML_NOTFOUNDRESULTS);
  398. }
  399. }
  400. // Did we find a blank entry?
  401. if (SUCCEEDED(hr) && pbstrXML && *pbstrXML && !StrCmpIW(*pbstrXML, SZ_XML_NOTFOUNDRESULTS))
  402. {
  403. // Yes, so we didn't get a successful results, so fail.
  404. // This way we will try other sources.
  405. hr = E_FAIL;
  406. SysFreeString(*pbstrXML);
  407. *pbstrXML = NULL;
  408. }
  409. return hr;
  410. }
  411. BOOL IsExpired(FILETIME ftExpireTime)
  412. {
  413. BOOL fIsExpired = TRUE;
  414. SYSTEMTIME stCurrentTime;
  415. FILETIME ftCurrentTime;
  416. GetSystemTime(&stCurrentTime);
  417. SystemTimeToFileTime(&stCurrentTime, &ftCurrentTime);
  418. // It is not expired if the current time is before the expired time.
  419. if (-1 == CompareFileTime(&ftCurrentTime, &ftExpireTime))
  420. {
  421. fIsExpired = FALSE;
  422. }
  423. return fIsExpired;
  424. }
  425. #define SZ_HASHSTR_HEADER L"MD5"
  426. HRESULT GenerateHashStr(IN LPCWSTR pwzHashData, IN LPWSTR pwzHashStr, IN DWORD cchSize)
  427. {
  428. HRESULT hr = E_FAIL;
  429. MD5_CTX md5;
  430. DWORD * pdwHashChunk = (DWORD *)&md5.digest;
  431. MD5Init(&md5);
  432. MD5Update(&md5, (const unsigned char *) pwzHashData, (lstrlenW(pwzHashData) * sizeof(OLECHAR)));
  433. MD5Final(&md5);
  434. StrCpyNW(pwzHashStr, SZ_HASHSTR_HEADER, cchSize);
  435. // Break the hash into 64 bit chunks and turn them into strings.
  436. // pwzHashStr will then contain the header and each chunk concatinated.
  437. for (int nIndex = 0; nIndex < (sizeof(md5.digest) / sizeof(*pdwHashChunk)); nIndex++)
  438. {
  439. WCHAR szNumber[MAX_PATH];
  440. wnsprintfW(szNumber, ARRAYSIZE(szNumber), L"%08lX", pdwHashChunk[nIndex]);
  441. StrCatBuffW(pwzHashStr, szNumber, cchSize);
  442. }
  443. return hr;
  444. }
  445. HRESULT CAccountDiscoveryBase::_CheckInCacheAndAddHash(IN LPCWSTR pwzDomain, IN BSTR bstrEmail, IN LPCSTR pszSubdir, IN LPWSTR pwzCacheURL, IN DWORD cchSize, IN BSTR bstrXMLRequest, OUT BSTR * pbstrXML)
  446. {
  447. WCHAR szHash[MAX_PATH];
  448. // We add the MD5 of the XML request to the URL so that the different XML requests to the
  449. // same server are cached separately
  450. GenerateHashStr(bstrXMLRequest, szHash, ARRAYSIZE(szHash));
  451. wnsprintfW(pwzCacheURL, cchSize, L"http://%ls.%ls%hs/%ls.xml", szHash, pwzDomain, pszSubdir, bstrEmail);
  452. return _CheckInCache(pwzCacheURL, pbstrXML);
  453. }
  454. HRESULT CAccountDiscoveryBase::_CheckInCache(IN LPWSTR pwzCacheURL, OUT BSTR * pbstrXML)
  455. {
  456. HINTERNET hOpenUrlSession;
  457. DWORD cbSize = (sizeof(INTERNET_CACHE_ENTRY_INFO) + 4048);
  458. LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, cbSize);
  459. HRESULT hr = E_FAIL;
  460. if (lpCacheEntryInfo)
  461. {
  462. // HACKHACK: I wish InternetOpenUrlWrap() would respect the INTERNET_FLAG_FROM_CACHE flag but
  463. // it doesn't. Therefore I call GetUrlCacheEntryInfo() to check and check the expired
  464. // myself.
  465. lpCacheEntryInfo->dwStructSize = cbSize;
  466. if (GetUrlCacheEntryInfo(pwzCacheURL, lpCacheEntryInfo, &cbSize))
  467. {
  468. if (!IsExpired(lpCacheEntryInfo->ExpireTime))
  469. {
  470. hr = InternetOpenUrlWrap(m_hInternetSession, pwzCacheURL, NULL, 0, INTERNET_FLAG_FROM_CACHE, NULL, &hOpenUrlSession);
  471. if (SUCCEEDED(hr))
  472. {
  473. hr = InternetReadIntoBSTR(hOpenUrlSession, pbstrXML);
  474. InternetCloseHandleWrap(hOpenUrlSession);
  475. }
  476. }
  477. }
  478. else
  479. {
  480. hr = HRESULT_FROM_WIN32(GetLastError());
  481. }
  482. LocalFree(lpCacheEntryInfo);
  483. }
  484. return hr;
  485. }
  486. #define AUTODISC_EXPIRE_TIME 7 /*days*/
  487. HRESULT GetModifiedAndExpiredDates(IN FILETIME * pftExpireTime, IN FILETIME * pftLastModifiedTime)
  488. {
  489. SYSTEMTIME stCurrentUTC;
  490. ULARGE_INTEGER uliTimeMath;
  491. ULARGE_INTEGER uliExpireTime;
  492. GetSystemTime(&stCurrentUTC);
  493. SystemTimeToFileTime(&stCurrentUTC, pftLastModifiedTime);
  494. *pftExpireTime = *pftLastModifiedTime;
  495. uliTimeMath.HighPart = pftExpireTime->dwHighDateTime;
  496. uliTimeMath.LowPart = pftExpireTime->dwLowDateTime;
  497. uliExpireTime.QuadPart = 1000000; // One Second;
  498. uliExpireTime.QuadPart *= (SECONDS_IN_ONE_DAY * AUTODISC_EXPIRE_TIME);
  499. uliTimeMath.QuadPart += uliExpireTime.QuadPart;
  500. pftExpireTime->dwHighDateTime = uliTimeMath.HighPart;
  501. pftExpireTime->dwLowDateTime = uliTimeMath.LowPart;
  502. return S_OK;
  503. }
  504. HRESULT CAccountDiscoveryBase::_CacheResults(IN LPCWSTR pwzCacheURL, IN BSTR bstrXML)
  505. {
  506. HRESULT hr = S_OK;
  507. WCHAR wzPath[MAX_PATH];
  508. hr = CreateUrlCacheEntryWrap(pwzCacheURL, (lstrlenW(bstrXML) + 1), L"xml", wzPath, 0);
  509. if (SUCCEEDED(hr))
  510. {
  511. HANDLE hFile;
  512. hr = CreateFileHrWrap(wzPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL, &hFile);
  513. if (SUCCEEDED(hr))
  514. {
  515. LPSTR pszAnsiXML = AllocStringFromBStr(bstrXML);
  516. if (pszAnsiXML)
  517. {
  518. DWORD cchWritten;
  519. hr = WriteFileWrap(hFile, pszAnsiXML, (lstrlenA(pszAnsiXML) + 1), &cchWritten, NULL);
  520. LocalFree(pszAnsiXML);
  521. }
  522. else
  523. {
  524. hr = E_OUTOFMEMORY;
  525. }
  526. CloseHandle(hFile);
  527. if (SUCCEEDED(hr))
  528. {
  529. FILETIME ftExpireTime;
  530. FILETIME ftLastModifiedTime;
  531. GetModifiedAndExpiredDates(&ftExpireTime, &ftLastModifiedTime);
  532. hr = CommitUrlCacheEntryWrap(pwzCacheURL, wzPath, ftExpireTime, ftLastModifiedTime, NORMAL_CACHE_ENTRY, NULL, 0, NULL, pwzCacheURL);
  533. }
  534. }
  535. }
  536. return hr;
  537. }
  538. LPCWSTR _GetNextDomain(IN LPCWSTR pwszDomain)
  539. {
  540. LPCWSTR pwzNext = NULL;
  541. pwszDomain = StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR);
  542. if (pwszDomain) // We did find the next one.
  543. {
  544. pwszDomain = CharNext(pwszDomain); // Skip past '.'
  545. if (StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR)) // is this the primary domain "com"?
  546. {
  547. // No, so that's good. Because we can't search for JoeUser@com.
  548. pwzNext = pwszDomain;
  549. }
  550. }
  551. return pwzNext;
  552. }
  553. #define SZ_HTTP_SCHEME L"http://"
  554. HRESULT GetDomainFromURL(IN LPCWSTR pwzURL, IN LPWSTR pwzDomain, IN int cchSize)
  555. {
  556. StrCpyNW(pwzDomain, pwzURL, cchSize);
  557. if (!StrCmpNIW(SZ_HTTP_SCHEME, pwzDomain, (ARRAYSIZE(SZ_HTTP_SCHEME) - 1)))
  558. {
  559. StrCpyNW(pwzDomain, &pwzURL[(ARRAYSIZE(SZ_HTTP_SCHEME) - 1)], cchSize);
  560. LPWSTR pszRemovePath = StrChrW(pwzDomain, L'/');
  561. if (pszRemovePath)
  562. {
  563. pszRemovePath[0] = 0;
  564. }
  565. }
  566. return S_OK;
  567. }
  568. HRESULT CAccountDiscoveryBase::_UseOptimizedService(IN LPCWSTR pwzServiceURL, IN LPCWSTR pwzDomain, IN BSTR * pbstrXML, IN DWORD dwFlags)
  569. {
  570. WCHAR szURL[MAX_URL_STRING];
  571. HINTERNET hOpenUrlSession;
  572. wnsprintfW(szURL, ARRAYSIZE(szURL), L"%lsDomain=%ls", pwzServiceURL, pwzDomain);
  573. HRESULT hr = _CheckInCache(szURL, pbstrXML);
  574. if (FAILED(hr))
  575. {
  576. WCHAR szDomain[MAX_PATH];
  577. if (SUCCEEDED(GetDomainFromURL(szURL, szDomain, ARRAYSIZE(szDomain))))
  578. {
  579. _SendStatusMessage(IDS_STATUS_CONNECTING_TO, szDomain);
  580. }
  581. // NOTE: The web server may want to redirect to an https URL for additional security.
  582. // We need to pass the INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS to HttpOpenRequest
  583. // or HttpSendRequest() will fail with ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
  584. // INTERNET_FLAG_IGNORE_CERT_CN_INVALID is another option we may want to use.
  585. hr = InternetOpenUrlWrap(m_hInternetSession, szURL, NULL, 0, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, NULL, &hOpenUrlSession);
  586. if (SUCCEEDED(hr))
  587. {
  588. hr = InternetReadIntoBSTR(hOpenUrlSession, pbstrXML);
  589. if (SUCCEEDED(hr))
  590. {
  591. DWORD cbSize = (sizeof(INTERNET_CACHE_ENTRY_INFO) + 4048);
  592. LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, cbSize);
  593. HRESULT hr = E_FAIL;
  594. if (lpCacheEntryInfo)
  595. {
  596. lpCacheEntryInfo->dwStructSize = cbSize;
  597. if (GetUrlCacheEntryInfo(szURL, lpCacheEntryInfo, &cbSize))
  598. {
  599. lpCacheEntryInfo->CacheEntryType |= CACHE_ENTRY_EXPTIME_FC;
  600. GetModifiedAndExpiredDates(&(lpCacheEntryInfo->ExpireTime), &(lpCacheEntryInfo->LastModifiedTime));
  601. SetUrlCacheEntryInfo(szURL, lpCacheEntryInfo, (CACHE_ENTRY_EXPTIME_FC | CACHE_ENTRY_MODTIME_FC));
  602. }
  603. else
  604. {
  605. hr = HRESULT_FROM_WIN32(GetLastError());
  606. }
  607. LocalFree(lpCacheEntryInfo);
  608. }
  609. }
  610. InternetCloseHandleWrap(hOpenUrlSession);
  611. }
  612. }
  613. return hr;
  614. }
  615. // We turn this off because JoshCo said that it would make
  616. // it hard to turn it into an international standard.
  617. // There are cases where [email protected] may trust
  618. // organization.co.uk but not co.uk.
  619. //#define FEATURE_WALK_UP_DOMAIN
  620. HRESULT CAccountDiscoveryBase::_GetInfo(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN BSTR * pbstrXML, IN DWORD dwFlags)
  621. {
  622. HRESULT hr = E_INVALIDARG;
  623. LPCWSTR pwszDomain = StrChrW(bstrEmail, CH_EMAIL_AT);
  624. if (pwszDomain)
  625. {
  626. pwszDomain = CharNext(pwszDomain); // Skip past the '@'
  627. IAutoDiscoveryProvider * pProviders;
  628. hr = _getPrimaryProviders(bstrEmail, &pProviders);
  629. if (SUCCEEDED(hr))
  630. {
  631. long nTotal = 0;
  632. VARIANT varIndex;
  633. varIndex.vt = VT_I4;
  634. hr = pProviders->get_length(&nTotal);
  635. hr = E_FAIL;
  636. for (varIndex.lVal = 0; FAILED(hr) && (varIndex.lVal < nTotal); varIndex.lVal++)
  637. {
  638. CComBSTR bstrDomain;
  639. hr = pProviders->get_item(varIndex, &bstrDomain);
  640. if (SUCCEEDED(hr))
  641. {
  642. hr = _GetInfoFromDomainWithSubdirAndCacheCheck(bstrXMLRequest, bstrEmail, bstrDomain, pbstrXML, dwFlags, SZ_ADSERVER_XMLFILE);
  643. }
  644. }
  645. pProviders->Release();
  646. }
  647. // Do we still need to find the settings and should we fall back
  648. // to trying public internet servers that can try to find the email mappings?
  649. // We also only want to try one of the public servers if the domain is not an internet
  650. // domain because we don't want to send intranet email server names outside of
  651. // the corp-net to public servers. We detect intranet type servers by the lack
  652. // of a 'period' in the name. For Example: JustUser@internetemailserver vs
  653. // [email protected].
  654. if (FAILED(hr) && (ADDN_CONFIGURE_EMAIL_FALLBACK & dwFlags) &&
  655. (SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_TEST_INTRANETS, FALSE, /*default:*/FALSE) ||
  656. StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR)))
  657. {
  658. hr = _getSecondaryProviders(bstrEmail, &pProviders, dwFlags);
  659. if (SUCCEEDED(hr))
  660. {
  661. long nTotal = 0;
  662. VARIANT varIndex;
  663. varIndex.vt = VT_I4;
  664. hr = pProviders->get_length(&nTotal);
  665. hr = E_FAIL;
  666. for (varIndex.lVal = 0; FAILED(hr) && (varIndex.lVal < nTotal); varIndex.lVal++)
  667. {
  668. CComBSTR bstrURL;
  669. hr = pProviders->get_item(varIndex, &bstrURL);
  670. if (SUCCEEDED(hr))
  671. {
  672. hr = _UseOptimizedService(bstrURL, pwszDomain, pbstrXML, dwFlags);
  673. }
  674. }
  675. pProviders->Release();
  676. }
  677. }
  678. }
  679. return hr;
  680. }
  681. // We turn this off because JoshCo said that it would make
  682. // it hard to turn it into an international standard.
  683. // There are cases where [email protected] may trust
  684. // organization.co.uk but not co.uk.
  685. //#define FEATURE_WALK_UP_DOMAIN
  686. HRESULT CAccountDiscoveryBase::_getPrimaryProviders(IN LPCWSTR pwzEmailAddress, OUT IAutoDiscoveryProvider ** ppProviders)
  687. {
  688. HRESULT hr = E_INVALIDARG;
  689. if (ppProviders)
  690. {
  691. *ppProviders = NULL;
  692. if (!m_hdpaPrimary && pwzEmailAddress)
  693. {
  694. LPCWSTR pwszDomain = StrChrW(pwzEmailAddress, CH_EMAIL_AT);
  695. if (pwszDomain)
  696. {
  697. pwszDomain = CharNext(pwszDomain); // Skip past the "@"
  698. if (pwszDomain[0])
  699. {
  700. // While we still have a domain and it's at least a second level domain...
  701. if (pwszDomain)
  702. {
  703. WCHAR wzDomain[INTERNET_MAX_HOST_NAME_LENGTH];
  704. // First we try "AutoDiscovery.<domainname>". That way, if admins receive a large amount
  705. // of traffic, they can change their DNS to have a "AutoDiscovery" alias that points to
  706. // a web server of their choosing to handle this traffic.
  707. wnsprintfW(wzDomain, ARRAYSIZE(wzDomain), L"autodiscover.%ls", pwszDomain);
  708. if (SUCCEEDED(AddHDPA_StrDup(wzDomain, &m_hdpaPrimary)))
  709. {
  710. // Add ballback server here. If the administrators don't want to do all the work
  711. // of having another machine or creating a DNS alias, we will try the main server.
  712. AddHDPA_StrDup(pwszDomain, &m_hdpaPrimary);
  713. }
  714. }
  715. }
  716. }
  717. }
  718. if (m_hdpaPrimary)
  719. {
  720. hr = CADProviders_CreateInstance(m_hdpaPrimary, SAFECAST(this, IObjectWithSite *), ppProviders);
  721. }
  722. }
  723. return hr;
  724. }
  725. HRESULT CAccountDiscoveryBase::_getSecondaryProviders(IN LPCWSTR pwzEmailAddress, OUT IAutoDiscoveryProvider ** ppProviders, IN DWORD dwFlags)
  726. {
  727. HRESULT hr = E_INVALIDARG;
  728. if (ppProviders)
  729. {
  730. *ppProviders = NULL;
  731. if (!m_hdpaSecondary && pwzEmailAddress)
  732. {
  733. LPCWSTR pwszDomain = StrChrW(pwzEmailAddress, CH_EMAIL_AT);
  734. if (pwszDomain)
  735. {
  736. pwszDomain = CharNext(pwszDomain); // Skip past the "@"
  737. if (pwszDomain[0])
  738. {
  739. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  740. BOOL fUseGlobalService = SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_SERVICES_POLICY, FALSE, /*default:*/TRUE);
  741. if (fUseGlobalService)
  742. {
  743. // If this policy is set, then we only want to use the Global Service for certain (i.e. Microsoft Owned)
  744. // domains. If people don't feel confortable with us providing settings for non-Microsoft
  745. // email providers, then we can turn this on and only provide them for Microsoft providers.
  746. if (SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_MS_ONLY_ADDRESSES, FALSE, /*default:*/FALSE))
  747. {
  748. fUseGlobalService = SHRegGetBoolUSValue(SZ_REGKEY_SERVICESALLOWLIST, pwszDomain, FALSE, /*default:*/FALSE);
  749. }
  750. }
  751. if (fUseGlobalService)
  752. {
  753. HKEY hKey;
  754. DWORD dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SZ_REGKEY_GLOBALSERVICES, 0, KEY_READ, &hKey);
  755. if (ERROR_SUCCESS == dwError)
  756. {
  757. WCHAR szServiceURL[MAX_PATH];
  758. int nIndex = 0;
  759. do
  760. {
  761. WCHAR szValue[MAX_PATH];
  762. DWORD cchValueSize = ARRAYSIZE(szValue);
  763. DWORD dwType = REG_SZ;
  764. DWORD cbDataSize = sizeof(szServiceURL);
  765. dwError = RegEnumValueW(hKey, nIndex, szValue, &cchValueSize, NULL, &dwType, (unsigned char *)szServiceURL, &cbDataSize);
  766. if (ERROR_SUCCESS == dwError)
  767. {
  768. // FEATURE_OPTIMIZED_SERVICE: We can either pass the entire XML request or just put the domain name
  769. // in the QueryString. The QueryString is faster for the server and they can optimize by using it.
  770. AddHDPA_StrDup(szServiceURL, &m_hdpaSecondary);
  771. }
  772. else
  773. {
  774. break;
  775. }
  776. nIndex++;
  777. }
  778. while (1);
  779. RegCloseKey(hKey);
  780. }
  781. }
  782. }
  783. }
  784. }
  785. if (m_hdpaSecondary)
  786. {
  787. hr = CADProviders_CreateInstance(m_hdpaSecondary, SAFECAST(this, IObjectWithSite *), ppProviders);
  788. }
  789. }
  790. return hr;
  791. }
  792. HRESULT CAccountDiscoveryBase::_PerformAutoDiscovery(IN BSTR bstrEmailAddress, IN DWORD dwFlags, IN BSTR bstrXMLRequest, OUT IXMLDOMDocument ** ppXMLResponse)
  793. {
  794. HRESULT hr = E_INVALIDARG;
  795. *ppXMLResponse = NULL;
  796. if (bstrEmailAddress)
  797. {
  798. hr = InternetOpenWrap(SZ_WININET_AGENT_AUTO_DISCOVER, PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0, &m_hInternetSession);
  799. if (SUCCEEDED(hr))
  800. {
  801. BSTR bstrXML;
  802. hr = _GetInfo(bstrXMLRequest, bstrEmailAddress, &bstrXML, dwFlags);
  803. if (SUCCEEDED(hr))
  804. {
  805. hr = XMLDOMFromBStr(bstrXML, ppXMLResponse);
  806. SysFreeString(bstrXML);
  807. }
  808. InternetCloseHandleWrap(m_hInternetSession);
  809. m_hInternetSession = NULL;
  810. }
  811. }
  812. return hr;
  813. }
  814. HRESULT CAccountDiscoveryBase::_InternalDiscoverNow(IN BSTR bstrEmailAddress, IN DWORD dwFlags, IN BSTR bstrXMLRequest, OUT IXMLDOMDocument ** ppXMLResponse)
  815. {
  816. HRESULT hr = E_INVALIDARG;
  817. *ppXMLResponse = NULL;
  818. if (bstrEmailAddress)
  819. {
  820. // Does the caller want this done async?
  821. if (m_hwndAsync)
  822. {
  823. // No, so cache the params so we can use them when async.
  824. SysFreeString(m_bstrEmailAsync);
  825. hr = HrSysAllocString(bstrEmailAddress, &m_bstrEmailAsync);
  826. if (SUCCEEDED(hr))
  827. {
  828. SysFreeString(m_bstrXMLRequest);
  829. hr = HrSysAllocString(bstrXMLRequest, &m_bstrXMLRequest);
  830. if (SUCCEEDED(hr))
  831. {
  832. DWORD idThread;
  833. m_dwFlagsAsync = dwFlags;
  834. AddRef();
  835. HANDLE hThread = CreateThread(NULL, 0, CAccountDiscoveryBase::AutoDiscoveryUIThreadProc, this, 0, &idThread);
  836. if (hThread)
  837. {
  838. // We wait WAIT_AUTODISCOVERY_STARTUP_HWND for the new thread to create the COM object
  839. if (m_hCreatedBackgroundTask)
  840. {
  841. DWORD dwRet = WaitForSingleObject(m_hCreatedBackgroundTask, WAIT_AUTODISCOVERY_STARTUP_HWND);
  842. ASSERT(dwRet != WAIT_TIMEOUT);
  843. }
  844. hr = m_hrSuccess;
  845. CloseHandle(hThread);
  846. }
  847. else
  848. {
  849. Release();
  850. }
  851. }
  852. }
  853. }
  854. else
  855. {
  856. // Yes.
  857. hr = _PerformAutoDiscovery(bstrEmailAddress, dwFlags, bstrXMLRequest, ppXMLResponse);
  858. }
  859. }
  860. return hr;
  861. }
  862. DWORD CAccountDiscoveryBase::_AutoDiscoveryUIThreadProc(void)
  863. {
  864. m_hrSuccess = CoInitialize(NULL);
  865. // We need to make sure that the API is installed and
  866. // accessible before we can continue.
  867. if (SUCCEEDED(m_hrSuccess))
  868. {
  869. IXMLDOMDocument * pXMLResponse;
  870. BSTR bstrXMLResponse = NULL;
  871. // Signal the main thread that we have successfully started
  872. if (m_hCreatedBackgroundTask)
  873. SetEvent(m_hCreatedBackgroundTask);
  874. // we give up the remainder of our timeslice here so that our parent thread has time to run
  875. // and will notice that we have signaled the m_hCreatedBackgroundTask event and can therefore return
  876. Sleep(0);
  877. m_hrSuccess = _PerformAutoDiscovery(m_bstrEmailAsync, m_dwFlagsAsync, m_bstrXMLRequest, &pXMLResponse);
  878. if (SUCCEEDED(m_hrSuccess))
  879. {
  880. m_hrSuccess = XMLBStrFromDOM(pXMLResponse, &bstrXMLResponse);
  881. pXMLResponse->Release();
  882. }
  883. _AsyncParseResponse(bstrXMLResponse);
  884. // Whether we succeeded or failed, inform the caller of our results.
  885. if (IsWindow(m_hwndAsync))
  886. {
  887. PostMessage(m_hwndAsync, m_wMsgAsync, m_hrSuccess, (LPARAM)bstrXMLResponse);
  888. }
  889. else
  890. {
  891. SysFreeString(bstrXMLResponse);
  892. }
  893. CoUninitialize();
  894. }
  895. else
  896. {
  897. // Signal the main thread that they can wake up to find that we
  898. // failed to start the async operation.
  899. if (m_hCreatedBackgroundTask)
  900. SetEvent(m_hCreatedBackgroundTask);
  901. }
  902. Release();
  903. return 0;
  904. }
  905. HRESULT CAccountDiscoveryBase::_WorkAsync(IN HWND hwnd, IN UINT wMsg)
  906. {
  907. m_hwndAsync = hwnd;
  908. m_wMsgAsync = wMsg;
  909. return S_OK;
  910. }
  911. //===========================
  912. // *** IUnknown Interface ***
  913. //===========================
  914. ULONG CAccountDiscoveryBase::AddRef()
  915. {
  916. m_cRef++;
  917. return m_cRef;
  918. }
  919. ULONG CAccountDiscoveryBase::Release()
  920. {
  921. ASSERT(m_cRef > 0);
  922. m_cRef--;
  923. if (m_cRef > 0)
  924. return m_cRef;
  925. delete this;
  926. return 0;
  927. }
  928. HRESULT CAccountDiscoveryBase::QueryInterface(REFIID riid, void **ppvObj)
  929. {
  930. static const QITAB qit[] = {
  931. QITABENT(CAccountDiscoveryBase, IObjectWithSite),
  932. { 0 },
  933. };
  934. return QISearch(this, qit, riid, ppvObj);
  935. }
  936. //===========================
  937. // *** Class Methods ***
  938. //===========================
  939. CAccountDiscoveryBase::CAccountDiscoveryBase() : m_cRef(1)
  940. {
  941. // DllAddRef(); // Done by our inheriting class
  942. // This needs to be allocated in Zero Inited Memory.
  943. // Assert that all Member Variables are inited to Zero.
  944. ASSERT(!m_hInternetSession);
  945. ASSERT(!m_hwndAsync);
  946. ASSERT(!m_wMsgAsync);
  947. ASSERT(!m_dwFlagsAsync);
  948. ASSERT(!m_bstrEmailAsync);
  949. ASSERT(!m_bstrXMLRequest);
  950. ASSERT(S_OK == m_hrSuccess);
  951. ASSERT(!m_hdpaPrimary);
  952. ASSERT(!m_hdpaSecondary);
  953. // We use this event to signal the primary thread that the hwnd was created on the UI thread.
  954. m_hCreatedBackgroundTask = CreateEvent(NULL, FALSE, FALSE, NULL);
  955. }
  956. CAccountDiscoveryBase::~CAccountDiscoveryBase()
  957. {
  958. SysFreeString(m_bstrEmailAsync);
  959. SysFreeString(m_bstrXMLRequest);
  960. if (m_hCreatedBackgroundTask)
  961. CloseHandle(m_hCreatedBackgroundTask);
  962. if (m_hdpaPrimary)
  963. {
  964. DPA_DestroyCallback(m_hdpaPrimary, DPALocalFree_Callback, NULL);
  965. }
  966. if (m_hdpaSecondary)
  967. {
  968. DPA_DestroyCallback(m_hdpaSecondary, DPALocalFree_Callback, NULL);
  969. }
  970. //DllRelease(); // Done by our inheriting class
  971. }