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
1199 lines
41 KiB
/*****************************************************************************\
|
|
FILE: AutoDiscBase.cpp
|
|
|
|
DESCRIPTION:
|
|
This is the Autmation Object to AutoDiscover account information.
|
|
|
|
BryanSt 10/3/1999
|
|
Copyright (C) Microsoft Corp 1999-1999. All rights reserved.
|
|
\*****************************************************************************/
|
|
|
|
#include "priv.h"
|
|
#include <cowsite.h>
|
|
#include <atlbase.h>
|
|
#include <crypto\md5.h>
|
|
|
|
#include "AutoDiscover.h"
|
|
#include "INStoXML.h"
|
|
|
|
|
|
//#define SZ_WININET_AGENT_AUTO_DISCOVER TEXT("Microsoft(r) Windows(tm) Account AutoDiscovery Agent")
|
|
#define SZ_WININET_AGENT_AUTO_DISCOVER TEXT("Mozilla/4.0 (compatible; MSIE.5.01; Windows.NT.5.0)")
|
|
|
|
// BUGBUG: Ditch default.asp
|
|
#define SZ_ADSERVER_XMLFILE "/AutoDiscover/default.xml"
|
|
|
|
#define SZ_PATH_AUTODISCOVERY L"AutoDiscovery"
|
|
#define SZ_FILEEXTENSION L".xml"
|
|
#define SZ_TEMPEXTENSION L".tmp"
|
|
|
|
|
|
// this is how long we wait for the UI thread to create the progress hwnd before giving up
|
|
#define WAIT_AUTODISCOVERY_STARTUP_HWND 10*1000 // ten seconds
|
|
|
|
// The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601
|
|
#define SECONDS_IN_ONE_DAY (60/*seconds*/ * 60/*minutes*/ * 24/*hrs*/)
|
|
|
|
//===========================
|
|
// *** Class Internals & Helpers ***
|
|
//===========================
|
|
|
|
HRESULT GetTempPathHr(IN DWORD cchSize, IN LPTSTR pszPath)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD cchSizeNeeded = GetTempPath(cchSize, pszPath);
|
|
|
|
if ((0 == cchSizeNeeded) || (cchSizeNeeded > cchSize))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT GetTempFileNameHr(IN LPCTSTR lpPathName, IN LPCTSTR lpPrefixString, IN UINT uUnique, IN LPTSTR lpTempFileName)
|
|
{
|
|
if (0 == GetTempFileName(lpPathName, lpPrefixString, uUnique, lpTempFileName))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CreateXMLTempFile(IN BSTR bstrXML, IN LPTSTR pszPath, IN DWORD cchSize)
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
HRESULT hr = GetTempPathHr(ARRAYSIZE(szTemp), szTemp);
|
|
|
|
AssertMsg((MAX_PATH <= cchSize), "You need to be at least MAX_PATH. Required by GetTempFileName()");
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = GetTempFileNameHr(szTemp, TEXT("AD_"), 0, pszPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HANDLE hFile;
|
|
|
|
hr = CreateFileHrWrap(pszPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL, &hFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPSTR pszAnsiXML = AllocStringFromBStr(bstrXML);
|
|
if (pszAnsiXML)
|
|
{
|
|
DWORD cchWritten;
|
|
|
|
hr = WriteFileWrap(hFile, pszAnsiXML, (lstrlenA(pszAnsiXML) + 1), &cchWritten, NULL);
|
|
LocalFree(pszAnsiXML);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DeleteFile(pszPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
DESCRIPTION:
|
|
This function will see if pbstrXML is valid AutoDiscovery XML or is
|
|
in the .INS/.ISP format that can be converted to valid XML. It will then look
|
|
for a redirect URL and return on if one exists.
|
|
\*****************************************************************************/
|
|
HRESULT CAccountDiscoveryBase::_VerifyValidXMLResponse(IN BSTR * pbstrXML, IN LPWSTR pszRedirURL, IN DWORD cchSize)
|
|
{
|
|
IXMLDOMDocument * pXMLDOMDoc;
|
|
bool fConverted = false;
|
|
HRESULT hr = XMLDOMFromBStr(*pbstrXML, &pXMLDOMDoc);
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
pszRedirURL[0] = 0;
|
|
if (FAILED(hr))
|
|
{
|
|
// It may have failed if it was an .INS or .ISP formatted
|
|
// file. Since we need to be compatible with these
|
|
// file formats, check for it and convert it if it
|
|
// is in that format.
|
|
hr = CreateXMLTempFile(*pbstrXML, szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fConverted = true;
|
|
if (IsINSFile(szPath))
|
|
{
|
|
hr = ConvertINSToXML(szPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = XMLDOMFromFile(szPath, &pXMLDOMDoc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IXMLDOMElement * pXMLElementMessage = NULL;
|
|
|
|
hr = pXMLDOMDoc->get_documentElement(&pXMLElementMessage);
|
|
if (S_FALSE == hr)
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
// This is only valid XML if the root tag is "AUTODISCOVERY".
|
|
// The case is not important.
|
|
hr = XMLElem_VerifyTagName(pXMLElementMessage, SZ_XMLELEMENT_AUTODISCOVERY);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Now we are in search for a redirect URL.
|
|
IXMLDOMNode * pXMLReponse;
|
|
|
|
// Enter the <RESPONSE> tag.
|
|
if (SUCCEEDED(XMLNode_GetChildTag(pXMLElementMessage, SZ_XMLELEMENT_RESPONSE, &pXMLReponse)))
|
|
{
|
|
IXMLDOMElement * pXMLElementMessage;
|
|
|
|
if (SUCCEEDED(pXMLReponse->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pXMLElementMessage))))
|
|
{
|
|
IXMLDOMNodeList * pNodeListAccounts;
|
|
|
|
// Iterate thru the list of <ACCOUNT> tags...
|
|
if (SUCCEEDED(XMLElem_GetElementsByTagName(pXMLElementMessage, SZ_XMLELEMENT_ACCOUNT, &pNodeListAccounts)))
|
|
{
|
|
DWORD dwIndex = 0;
|
|
IXMLDOMNode * pXMLNodeAccount = NULL;
|
|
|
|
// We are going to look thru each one for one of them with <TYPE>email</TYPE>
|
|
while (S_OK == XMLNodeList_GetChild(pNodeListAccounts, dwIndex, &pXMLNodeAccount))
|
|
{
|
|
// FUTURE: We could support redirects or error messages here depending on
|
|
// <ACTION> redirect | message </ACTION>
|
|
if (XML_IsChildTagTextEqual(pXMLNodeAccount, SZ_XMLELEMENT_TYPE, SZ_XMLTEXT_EMAIL) &&
|
|
XML_IsChildTagTextEqual(pXMLNodeAccount, SZ_XMLELEMENT_ACTION, SZ_XMLTEXT_REDIRECT))
|
|
{
|
|
CComBSTR bstrRedirURL;
|
|
|
|
// This file may or may not settings to contact the server. However in either case
|
|
// it may contain an INFOURL tag. If it does, then the URL in side will point to a
|
|
// web page.
|
|
// <INFOURL> xxx </INFOURL>
|
|
if (SUCCEEDED(XMLNode_GetChildTagTextValue(pXMLNodeAccount, SZ_XMLELEMENT_REDIRURL, &bstrRedirURL)))
|
|
{
|
|
StrCpyNW(pszRedirURL, bstrRedirURL, cchSize);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No, so keep looking.
|
|
ATOMICRELEASE(pXMLNodeAccount);
|
|
dwIndex++;
|
|
}
|
|
|
|
ATOMICRELEASE(pXMLNodeAccount);
|
|
pNodeListAccounts->Release();
|
|
}
|
|
|
|
pXMLElementMessage->Release();
|
|
}
|
|
|
|
pXMLReponse->Release();
|
|
}
|
|
}
|
|
|
|
pXMLElementMessage->Release();
|
|
}
|
|
|
|
if (true == fConverted)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// It only succeeded after the conversion, so we need to move the
|
|
// XML from the temp file to pbstrXML.
|
|
SysFreeString(*pbstrXML);
|
|
*pbstrXML = NULL;
|
|
|
|
hr = XMLBStrFromDOM(pXMLDOMDoc, pbstrXML);
|
|
}
|
|
}
|
|
|
|
pXMLDOMDoc->Release();
|
|
}
|
|
|
|
if (true == fConverted)
|
|
{
|
|
DeleteFile(szPath);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
typedef HINSTANCE (STDAPICALLTYPE *PFNMLLOADLIBARY)(LPCSTR lpLibFileName, HMODULE hModule, DWORD dwCrossCodePage);
|
|
static const char c_szShlwapiDll[] = "shlwapi.dll";
|
|
static const char c_szDllGetVersion[] = "DllGetVersion";
|
|
|
|
HINSTANCE LoadLangDll(HINSTANCE hInstCaller, LPCSTR szDllName, BOOL fNT)
|
|
{
|
|
char szPath[MAX_PATH];
|
|
HINSTANCE hinstShlwapi;
|
|
PFNMLLOADLIBARY pfn;
|
|
DLLGETVERSIONPROC pfnVersion;
|
|
int iEnd;
|
|
DLLVERSIONINFO info;
|
|
HINSTANCE hInst = NULL;
|
|
|
|
hinstShlwapi = LoadLibraryA(c_szShlwapiDll);
|
|
if (hinstShlwapi != NULL)
|
|
{
|
|
pfnVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstShlwapi, c_szDllGetVersion);
|
|
if (pfnVersion != NULL)
|
|
{
|
|
info.cbSize = sizeof(DLLVERSIONINFO);
|
|
if (SUCCEEDED(pfnVersion(&info)))
|
|
{
|
|
if (info.dwMajorVersion >= 5)
|
|
{
|
|
pfn = (PFNMLLOADLIBARY)GetProcAddress(hinstShlwapi, MAKEINTRESOURCEA(377));
|
|
if (pfn != NULL)
|
|
hInst = pfn(szDllName, hInstCaller, (ML_NO_CROSSCODEPAGE));
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hinstShlwapi);
|
|
}
|
|
|
|
if ((NULL == hInst) && (GetModuleFileNameA(hInstCaller, szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
if (PathRemoveFileSpecA(szPath) && PathAppendA(szPath, szDllName))
|
|
{
|
|
hInst = LoadLibraryA(szPath);
|
|
}
|
|
}
|
|
|
|
return hInst;
|
|
}
|
|
|
|
|
|
#define SZ_DLL_OE_ACCTRES_DLL "acctres.dll"
|
|
HRESULT CAccountDiscoveryBase::_SendStatusMessage(UINT nStringID, LPCWSTR pwzArg)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_hwndAsync && IsWindow(m_hwndAsync))
|
|
{
|
|
WCHAR szMessage[MAX_URL_STRING*3];
|
|
WCHAR szTemplate[MAX_URL_STRING*3];
|
|
|
|
// Our DLL has these message.
|
|
LoadString(HINST_THISDLL, nStringID, szTemplate, ARRAYSIZE(szTemplate));
|
|
|
|
HINSTANCE hInstOE = LoadLangDll(GetModuleHandleA(NULL), SZ_DLL_OE_ACCTRES_DLL, IsOSNT());
|
|
if (hInstOE)
|
|
{
|
|
// We prefer to get the string from OE because it will be localized based on the installed
|
|
// language.
|
|
LoadString(hInstOE, nStringID, szTemplate, ARRAYSIZE(szTemplate));
|
|
FreeLibrary(hInstOE);
|
|
}
|
|
|
|
if (pwzArg)
|
|
{
|
|
wnsprintfW(szMessage, ARRAYSIZE(szMessage), szTemplate, pwzArg);
|
|
}
|
|
else
|
|
{
|
|
StrCpyN(szMessage, szTemplate, ARRAYSIZE(szMessage));
|
|
}
|
|
|
|
DWORD cchSize = (lstrlenW(szMessage) + 1);
|
|
LPWSTR pszString = (LPWSTR) LocalAlloc(LPTR, cchSize * sizeof(szMessage[0]));
|
|
if (pszString)
|
|
{
|
|
StrCpyN(pszString, szMessage, cchSize);
|
|
PostMessage(m_hwndAsync, (m_wMsgAsync + 1), (WPARAM)pszString, (LPARAM)0);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_UrlToComponents(IN LPCWSTR pszURL, IN BOOL * pfHTTPS, IN LPWSTR pszDomain, IN DWORD cchSize, IN LPSTR pszURLPath, IN DWORD cchSizeURLPath)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH];
|
|
WCHAR szURLPath[INTERNET_MAX_PATH_LENGTH];
|
|
URL_COMPONENTS urlComponents = {0};
|
|
|
|
urlComponents.dwStructSize = sizeof(urlComponents);
|
|
urlComponents.lpszScheme = szScheme;
|
|
urlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
|
|
urlComponents.lpszHostName = pszDomain;
|
|
urlComponents.dwHostNameLength = cchSize;
|
|
urlComponents.lpszUrlPath = szURLPath;
|
|
urlComponents.dwUrlPathLength = ARRAYSIZE(szURLPath);
|
|
|
|
*pfHTTPS = ((INTERNET_SCHEME_HTTPS == urlComponents.nScheme) ? TRUE : FALSE);
|
|
if (!InternetCrackUrlW(pszURL, 0, 0, &urlComponents))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
SHUnicodeToAnsi(szURLPath, pszURLPath, cchSizeURLPath);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_GetInfoFromDomain(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN LPCWSTR pwzDomain, IN BOOL fHTTPS, IN BOOL fPost, IN LPCSTR pszURLPath, OUT BSTR * pbstrXML)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
DWORD cbToSend = (lstrlenW(bstrXMLRequest));
|
|
LPSTR pszPostData = (LPSTR) LocalAlloc(LPTR, (cbToSend + 1) * sizeof(bstrXMLRequest[0]));
|
|
TCHAR szRedirectURL[MAX_URL_STRING];
|
|
|
|
szRedirectURL[0] = 0;
|
|
if (pszPostData)
|
|
{
|
|
HINTERNET hInternetHTTPConnect = NULL;
|
|
|
|
SHUnicodeToAnsi(bstrXMLRequest, pszPostData, (cbToSend + 1));
|
|
_SendStatusMessage(IDS_STATUS_CONNECTING_TO, pwzDomain);
|
|
|
|
// We may want to use INTERNET_FLAG_KEEP_CONNECTION.
|
|
hr = InternetConnectWrap(m_hInternetSession, FALSE, pwzDomain, (fHTTPS ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT),
|
|
NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL, &hInternetHTTPConnect);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HINTERNET hInternetHTTPRequest = NULL;
|
|
DWORD cbBytesRead;
|
|
|
|
// NOTE: The web server may want to redirect to an https URL for additional security.
|
|
// We need to pass the INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS to HttpOpenRequest
|
|
// or HttpSendRequest() will fail with ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
|
|
|
|
// NOTE: We may need to split the URL into lpszReferer + lpszObjectName.
|
|
hr = HttpOpenRequestWrap(hInternetHTTPConnect, (fPost ? SZ_HTTP_VERB_POST : NULL), pszURLPath, HTTP_VERSIONA,
|
|
/*pszReferer*/ NULL, NULL, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, NULL, &cbBytesRead, &hInternetHTTPRequest);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = HttpSendRequestWrap(hInternetHTTPRequest, NULL, 0, (fPost ? pszPostData : NULL), (fPost ? cbToSend : 0));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_SendStatusMessage(IDS_STATUS_DOWNLOADING, pwzDomain);
|
|
hr = InternetReadIntoBSTR(hInternetHTTPRequest, pbstrXML);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _VerifyValidXMLResponse(pbstrXML, szRedirectURL, ARRAYSIZE(szRedirectURL));
|
|
if (FAILED(hr))
|
|
{
|
|
SysFreeString(*pbstrXML);
|
|
*pbstrXML = NULL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InternetCloseHandleWrap(hInternetHTTPRequest);
|
|
}
|
|
else
|
|
{
|
|
InternetCloseHandleWrap(hInternetHTTPRequest);
|
|
}
|
|
}
|
|
|
|
InternetCloseHandleWrap(hInternetHTTPRequest);
|
|
}
|
|
|
|
InternetCloseHandleWrap(hInternetHTTPConnect);
|
|
}
|
|
|
|
LocalFree(pszPostData);
|
|
}
|
|
|
|
// Did the caller want to redirect to another server?
|
|
if (szRedirectURL[0])
|
|
{
|
|
// Yes, so do that now via recursion.
|
|
WCHAR szDomain[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
CHAR szURLPath[INTERNET_MAX_PATH_LENGTH];
|
|
|
|
SysFreeString(*pbstrXML);
|
|
*pbstrXML = NULL;
|
|
|
|
hr = _UrlToComponents(szRedirectURL, &fHTTPS, szDomain, ARRAYSIZE(szDomain), szURLPath, ARRAYSIZE(szURLPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _GetInfoFromDomain(bstrXMLRequest, bstrEmail, szDomain, fHTTPS, TRUE, szURLPath, pbstrXML);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#define SZ_XML_NOTFOUNDRESULTS L"<?xml version=\"1.0\"?><AUTODISCOVERY><NOFOUND /></AUTODISCOVERY>"
|
|
|
|
HRESULT CAccountDiscoveryBase::_GetInfoFromDomainWithSubdirAndCacheCheck(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN LPCWSTR pwzDomain, IN BSTR * pbstrXML, IN DWORD dwFlags, IN LPCSTR pszURLPath)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR wzCacheURL[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
|
|
if (dwFlags & ADDN_SKIP_CACHEDRESULTS)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = _CheckInCacheAndAddHash(pwzDomain, bstrEmail, pszURLPath, wzCacheURL, ARRAYSIZE(wzCacheURL), bstrXMLRequest, pbstrXML);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
hr = _GetInfoFromDomain(bstrXMLRequest, bstrEmail, pwzDomain, FALSE, FALSE, pszURLPath, pbstrXML);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Put the data into the cache for the next time.
|
|
_CacheResults(wzCacheURL, *pbstrXML);
|
|
}
|
|
else
|
|
{
|
|
// We want to make a blank entry so we don't keep hitting the server
|
|
_CacheResults(wzCacheURL, SZ_XML_NOTFOUNDRESULTS);
|
|
}
|
|
}
|
|
|
|
// Did we find a blank entry?
|
|
if (SUCCEEDED(hr) && pbstrXML && *pbstrXML && !StrCmpIW(*pbstrXML, SZ_XML_NOTFOUNDRESULTS))
|
|
{
|
|
// Yes, so we didn't get a successful results, so fail.
|
|
// This way we will try other sources.
|
|
hr = E_FAIL;
|
|
SysFreeString(*pbstrXML);
|
|
*pbstrXML = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL IsExpired(FILETIME ftExpireTime)
|
|
{
|
|
BOOL fIsExpired = TRUE;
|
|
SYSTEMTIME stCurrentTime;
|
|
FILETIME ftCurrentTime;
|
|
|
|
GetSystemTime(&stCurrentTime);
|
|
SystemTimeToFileTime(&stCurrentTime, &ftCurrentTime);
|
|
|
|
// It is not expired if the current time is before the expired time.
|
|
if (-1 == CompareFileTime(&ftCurrentTime, &ftExpireTime))
|
|
{
|
|
fIsExpired = FALSE;
|
|
}
|
|
|
|
return fIsExpired;
|
|
}
|
|
|
|
|
|
#define SZ_HASHSTR_HEADER L"MD5"
|
|
HRESULT GenerateHashStr(IN LPCWSTR pwzHashData, IN LPWSTR pwzHashStr, IN DWORD cchSize)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
MD5_CTX md5;
|
|
DWORD * pdwHashChunk = (DWORD *)&md5.digest;
|
|
|
|
MD5Init(&md5);
|
|
MD5Update(&md5, (const unsigned char *) pwzHashData, (lstrlenW(pwzHashData) * sizeof(OLECHAR)));
|
|
MD5Final(&md5);
|
|
|
|
StrCpyNW(pwzHashStr, SZ_HASHSTR_HEADER, cchSize);
|
|
|
|
// Break the hash into 64 bit chunks and turn them into strings.
|
|
// pwzHashStr will then contain the header and each chunk concatinated.
|
|
for (int nIndex = 0; nIndex < (sizeof(md5.digest) / sizeof(*pdwHashChunk)); nIndex++)
|
|
{
|
|
WCHAR szNumber[MAX_PATH];
|
|
|
|
wnsprintfW(szNumber, ARRAYSIZE(szNumber), L"%08lX", pdwHashChunk[nIndex]);
|
|
StrCatBuffW(pwzHashStr, szNumber, cchSize);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_CheckInCacheAndAddHash(IN LPCWSTR pwzDomain, IN BSTR bstrEmail, IN LPCSTR pszSubdir, IN LPWSTR pwzCacheURL, IN DWORD cchSize, IN BSTR bstrXMLRequest, OUT BSTR * pbstrXML)
|
|
{
|
|
WCHAR szHash[MAX_PATH];
|
|
|
|
// We add the MD5 of the XML request to the URL so that the different XML requests to the
|
|
// same server are cached separately
|
|
GenerateHashStr(bstrXMLRequest, szHash, ARRAYSIZE(szHash));
|
|
wnsprintfW(pwzCacheURL, cchSize, L"http://%ls.%ls%hs/%ls.xml", szHash, pwzDomain, pszSubdir, bstrEmail);
|
|
|
|
return _CheckInCache(pwzCacheURL, pbstrXML);
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_CheckInCache(IN LPWSTR pwzCacheURL, OUT BSTR * pbstrXML)
|
|
{
|
|
HINTERNET hOpenUrlSession;
|
|
DWORD cbSize = (sizeof(INTERNET_CACHE_ENTRY_INFO) + 4048);
|
|
LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, cbSize);
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (lpCacheEntryInfo)
|
|
{
|
|
// HACKHACK: I wish InternetOpenUrlWrap() would respect the INTERNET_FLAG_FROM_CACHE flag but
|
|
// it doesn't. Therefore I call GetUrlCacheEntryInfo() to check and check the expired
|
|
// myself.
|
|
lpCacheEntryInfo->dwStructSize = cbSize;
|
|
if (GetUrlCacheEntryInfo(pwzCacheURL, lpCacheEntryInfo, &cbSize))
|
|
{
|
|
if (!IsExpired(lpCacheEntryInfo->ExpireTime))
|
|
{
|
|
hr = InternetOpenUrlWrap(m_hInternetSession, pwzCacheURL, NULL, 0, INTERNET_FLAG_FROM_CACHE, NULL, &hOpenUrlSession);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InternetReadIntoBSTR(hOpenUrlSession, pbstrXML);
|
|
InternetCloseHandleWrap(hOpenUrlSession);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
LocalFree(lpCacheEntryInfo);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#define AUTODISC_EXPIRE_TIME 7 /*days*/
|
|
|
|
HRESULT GetModifiedAndExpiredDates(IN FILETIME * pftExpireTime, IN FILETIME * pftLastModifiedTime)
|
|
{
|
|
SYSTEMTIME stCurrentUTC;
|
|
ULARGE_INTEGER uliTimeMath;
|
|
ULARGE_INTEGER uliExpireTime;
|
|
|
|
GetSystemTime(&stCurrentUTC);
|
|
SystemTimeToFileTime(&stCurrentUTC, pftLastModifiedTime);
|
|
|
|
*pftExpireTime = *pftLastModifiedTime;
|
|
uliTimeMath.HighPart = pftExpireTime->dwHighDateTime;
|
|
uliTimeMath.LowPart = pftExpireTime->dwLowDateTime;
|
|
|
|
uliExpireTime.QuadPart = 1000000; // One Second;
|
|
uliExpireTime.QuadPart *= (SECONDS_IN_ONE_DAY * AUTODISC_EXPIRE_TIME);
|
|
|
|
uliTimeMath.QuadPart += uliExpireTime.QuadPart;
|
|
pftExpireTime->dwHighDateTime = uliTimeMath.HighPart;
|
|
pftExpireTime->dwLowDateTime = uliTimeMath.LowPart;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_CacheResults(IN LPCWSTR pwzCacheURL, IN BSTR bstrXML)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR wzPath[MAX_PATH];
|
|
|
|
hr = CreateUrlCacheEntryWrap(pwzCacheURL, (lstrlenW(bstrXML) + 1), L"xml", wzPath, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HANDLE hFile;
|
|
|
|
hr = CreateFileHrWrap(wzPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL, &hFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPSTR pszAnsiXML = AllocStringFromBStr(bstrXML);
|
|
if (pszAnsiXML)
|
|
{
|
|
DWORD cchWritten;
|
|
|
|
hr = WriteFileWrap(hFile, pszAnsiXML, (lstrlenA(pszAnsiXML) + 1), &cchWritten, NULL);
|
|
LocalFree(pszAnsiXML);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
FILETIME ftExpireTime;
|
|
FILETIME ftLastModifiedTime;
|
|
|
|
GetModifiedAndExpiredDates(&ftExpireTime, &ftLastModifiedTime);
|
|
hr = CommitUrlCacheEntryWrap(pwzCacheURL, wzPath, ftExpireTime, ftLastModifiedTime, NORMAL_CACHE_ENTRY, NULL, 0, NULL, pwzCacheURL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
LPCWSTR _GetNextDomain(IN LPCWSTR pwszDomain)
|
|
{
|
|
LPCWSTR pwzNext = NULL;
|
|
|
|
pwszDomain = StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR);
|
|
if (pwszDomain) // We did find the next one.
|
|
{
|
|
pwszDomain = CharNext(pwszDomain); // Skip past '.'
|
|
|
|
if (StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR)) // is this the primary domain "com"?
|
|
{
|
|
// No, so that's good. Because we can't search for JoeUser@com.
|
|
pwzNext = pwszDomain;
|
|
}
|
|
}
|
|
|
|
return pwzNext;
|
|
}
|
|
|
|
|
|
#define SZ_HTTP_SCHEME L"http://"
|
|
HRESULT GetDomainFromURL(IN LPCWSTR pwzURL, IN LPWSTR pwzDomain, IN int cchSize)
|
|
{
|
|
StrCpyNW(pwzDomain, pwzURL, cchSize);
|
|
|
|
if (!StrCmpNIW(SZ_HTTP_SCHEME, pwzDomain, (ARRAYSIZE(SZ_HTTP_SCHEME) - 1)))
|
|
{
|
|
StrCpyNW(pwzDomain, &pwzURL[(ARRAYSIZE(SZ_HTTP_SCHEME) - 1)], cchSize);
|
|
|
|
LPWSTR pszRemovePath = StrChrW(pwzDomain, L'/');
|
|
if (pszRemovePath)
|
|
{
|
|
pszRemovePath[0] = 0;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_UseOptimizedService(IN LPCWSTR pwzServiceURL, IN LPCWSTR pwzDomain, IN BSTR * pbstrXML, IN DWORD dwFlags)
|
|
{
|
|
WCHAR szURL[MAX_URL_STRING];
|
|
HINTERNET hOpenUrlSession;
|
|
|
|
wnsprintfW(szURL, ARRAYSIZE(szURL), L"%lsDomain=%ls", pwzServiceURL, pwzDomain);
|
|
|
|
HRESULT hr = _CheckInCache(szURL, pbstrXML);
|
|
if (FAILED(hr))
|
|
{
|
|
WCHAR szDomain[MAX_PATH];
|
|
|
|
if (SUCCEEDED(GetDomainFromURL(szURL, szDomain, ARRAYSIZE(szDomain))))
|
|
{
|
|
_SendStatusMessage(IDS_STATUS_CONNECTING_TO, szDomain);
|
|
}
|
|
|
|
// NOTE: The web server may want to redirect to an https URL for additional security.
|
|
// We need to pass the INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS to HttpOpenRequest
|
|
// or HttpSendRequest() will fail with ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
|
|
|
|
// INTERNET_FLAG_IGNORE_CERT_CN_INVALID is another option we may want to use.
|
|
hr = InternetOpenUrlWrap(m_hInternetSession, szURL, NULL, 0, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, NULL, &hOpenUrlSession);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InternetReadIntoBSTR(hOpenUrlSession, pbstrXML);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD cbSize = (sizeof(INTERNET_CACHE_ENTRY_INFO) + 4048);
|
|
LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo = (LPINTERNET_CACHE_ENTRY_INFO) LocalAlloc(LPTR, cbSize);
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (lpCacheEntryInfo)
|
|
{
|
|
lpCacheEntryInfo->dwStructSize = cbSize;
|
|
if (GetUrlCacheEntryInfo(szURL, lpCacheEntryInfo, &cbSize))
|
|
{
|
|
lpCacheEntryInfo->CacheEntryType |= CACHE_ENTRY_EXPTIME_FC;
|
|
GetModifiedAndExpiredDates(&(lpCacheEntryInfo->ExpireTime), &(lpCacheEntryInfo->LastModifiedTime));
|
|
SetUrlCacheEntryInfo(szURL, lpCacheEntryInfo, (CACHE_ENTRY_EXPTIME_FC | CACHE_ENTRY_MODTIME_FC));
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
LocalFree(lpCacheEntryInfo);
|
|
}
|
|
}
|
|
InternetCloseHandleWrap(hOpenUrlSession);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// We turn this off because JoshCo said that it would make
|
|
// it hard to turn it into an international standard.
|
|
// There are cases where [email protected] may trust
|
|
// organization.co.uk but not co.uk.
|
|
//#define FEATURE_WALK_UP_DOMAIN
|
|
|
|
HRESULT CAccountDiscoveryBase::_GetInfo(IN BSTR bstrXMLRequest, IN BSTR bstrEmail, IN BSTR * pbstrXML, IN DWORD dwFlags)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCWSTR pwszDomain = StrChrW(bstrEmail, CH_EMAIL_AT);
|
|
|
|
if (pwszDomain)
|
|
{
|
|
pwszDomain = CharNext(pwszDomain); // Skip past the '@'
|
|
IAutoDiscoveryProvider * pProviders;
|
|
|
|
hr = _getPrimaryProviders(bstrEmail, &pProviders);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
long nTotal = 0;
|
|
VARIANT varIndex;
|
|
|
|
varIndex.vt = VT_I4;
|
|
|
|
hr = pProviders->get_length(&nTotal);
|
|
hr = E_FAIL;
|
|
for (varIndex.lVal = 0; FAILED(hr) && (varIndex.lVal < nTotal); varIndex.lVal++)
|
|
{
|
|
CComBSTR bstrDomain;
|
|
|
|
hr = pProviders->get_item(varIndex, &bstrDomain);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _GetInfoFromDomainWithSubdirAndCacheCheck(bstrXMLRequest, bstrEmail, bstrDomain, pbstrXML, dwFlags, SZ_ADSERVER_XMLFILE);
|
|
}
|
|
}
|
|
|
|
pProviders->Release();
|
|
}
|
|
|
|
// Do we still need to find the settings and should we fall back
|
|
// to trying public internet servers that can try to find the email mappings?
|
|
// We also only want to try one of the public servers if the domain is not an internet
|
|
// domain because we don't want to send intranet email server names outside of
|
|
// the corp-net to public servers. We detect intranet type servers by the lack
|
|
// of a 'period' in the name. For Example: JustUser@internetemailserver vs
|
|
// [email protected].
|
|
if (FAILED(hr) && (ADDN_CONFIGURE_EMAIL_FALLBACK & dwFlags) &&
|
|
(SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_TEST_INTRANETS, FALSE, /*default:*/FALSE) ||
|
|
StrChrW(pwszDomain, CH_EMAIL_DOMAIN_SEPARATOR)))
|
|
{
|
|
hr = _getSecondaryProviders(bstrEmail, &pProviders, dwFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
long nTotal = 0;
|
|
VARIANT varIndex;
|
|
|
|
varIndex.vt = VT_I4;
|
|
|
|
hr = pProviders->get_length(&nTotal);
|
|
hr = E_FAIL;
|
|
for (varIndex.lVal = 0; FAILED(hr) && (varIndex.lVal < nTotal); varIndex.lVal++)
|
|
{
|
|
CComBSTR bstrURL;
|
|
|
|
hr = pProviders->get_item(varIndex, &bstrURL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _UseOptimizedService(bstrURL, pwszDomain, pbstrXML, dwFlags);
|
|
}
|
|
}
|
|
|
|
pProviders->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// We turn this off because JoshCo said that it would make
|
|
// it hard to turn it into an international standard.
|
|
// There are cases where [email protected] may trust
|
|
// organization.co.uk but not co.uk.
|
|
//#define FEATURE_WALK_UP_DOMAIN
|
|
|
|
HRESULT CAccountDiscoveryBase::_getPrimaryProviders(IN LPCWSTR pwzEmailAddress, OUT IAutoDiscoveryProvider ** ppProviders)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppProviders)
|
|
{
|
|
*ppProviders = NULL;
|
|
if (!m_hdpaPrimary && pwzEmailAddress)
|
|
{
|
|
LPCWSTR pwszDomain = StrChrW(pwzEmailAddress, CH_EMAIL_AT);
|
|
if (pwszDomain)
|
|
{
|
|
pwszDomain = CharNext(pwszDomain); // Skip past the "@"
|
|
if (pwszDomain[0])
|
|
{
|
|
// While we still have a domain and it's at least a second level domain...
|
|
if (pwszDomain)
|
|
{
|
|
WCHAR wzDomain[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
|
|
// First we try "AutoDiscovery.<domainname>". That way, if admins receive a large amount
|
|
// of traffic, they can change their DNS to have a "AutoDiscovery" alias that points to
|
|
// a web server of their choosing to handle this traffic.
|
|
wnsprintfW(wzDomain, ARRAYSIZE(wzDomain), L"autodiscover.%ls", pwszDomain);
|
|
if (SUCCEEDED(AddHDPA_StrDup(wzDomain, &m_hdpaPrimary)))
|
|
{
|
|
// Add ballback server here. If the administrators don't want to do all the work
|
|
// of having another machine or creating a DNS alias, we will try the main server.
|
|
AddHDPA_StrDup(pwszDomain, &m_hdpaPrimary);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_hdpaPrimary)
|
|
{
|
|
hr = CADProviders_CreateInstance(m_hdpaPrimary, SAFECAST(this, IObjectWithSite *), ppProviders);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_getSecondaryProviders(IN LPCWSTR pwzEmailAddress, OUT IAutoDiscoveryProvider ** ppProviders, IN DWORD dwFlags)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppProviders)
|
|
{
|
|
*ppProviders = NULL;
|
|
if (!m_hdpaSecondary && pwzEmailAddress)
|
|
{
|
|
LPCWSTR pwszDomain = StrChrW(pwzEmailAddress, CH_EMAIL_AT);
|
|
if (pwszDomain)
|
|
{
|
|
pwszDomain = CharNext(pwszDomain); // Skip past the "@"
|
|
if (pwszDomain[0])
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
|
|
|
BOOL fUseGlobalService = SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_SERVICES_POLICY, FALSE, /*default:*/TRUE);
|
|
if (fUseGlobalService)
|
|
{
|
|
// If this policy is set, then we only want to use the Global Service for certain (i.e. Microsoft Owned)
|
|
// domains. If people don't feel confortable with us providing settings for non-Microsoft
|
|
// email providers, then we can turn this on and only provide them for Microsoft providers.
|
|
if (SHRegGetBoolUSValue(SZ_REGKEY_AUTODISCOVERY, SZ_REGVALUE_MS_ONLY_ADDRESSES, FALSE, /*default:*/FALSE))
|
|
{
|
|
fUseGlobalService = SHRegGetBoolUSValue(SZ_REGKEY_SERVICESALLOWLIST, pwszDomain, FALSE, /*default:*/FALSE);
|
|
}
|
|
}
|
|
|
|
if (fUseGlobalService)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SZ_REGKEY_GLOBALSERVICES, 0, KEY_READ, &hKey);
|
|
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
WCHAR szServiceURL[MAX_PATH];
|
|
int nIndex = 0;
|
|
|
|
do
|
|
{
|
|
WCHAR szValue[MAX_PATH];
|
|
DWORD cchValueSize = ARRAYSIZE(szValue);
|
|
DWORD dwType = REG_SZ;
|
|
DWORD cbDataSize = sizeof(szServiceURL);
|
|
|
|
dwError = RegEnumValueW(hKey, nIndex, szValue, &cchValueSize, NULL, &dwType, (unsigned char *)szServiceURL, &cbDataSize);
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
// FEATURE_OPTIMIZED_SERVICE: We can either pass the entire XML request or just put the domain name
|
|
// in the QueryString. The QueryString is faster for the server and they can optimize by using it.
|
|
AddHDPA_StrDup(szServiceURL, &m_hdpaSecondary);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
nIndex++;
|
|
}
|
|
while (1);
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_hdpaSecondary)
|
|
{
|
|
hr = CADProviders_CreateInstance(m_hdpaSecondary, SAFECAST(this, IObjectWithSite *), ppProviders);
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_PerformAutoDiscovery(IN BSTR bstrEmailAddress, IN DWORD dwFlags, IN BSTR bstrXMLRequest, OUT IXMLDOMDocument ** ppXMLResponse)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
*ppXMLResponse = NULL;
|
|
if (bstrEmailAddress)
|
|
{
|
|
hr = InternetOpenWrap(SZ_WININET_AGENT_AUTO_DISCOVER, PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0, &m_hInternetSession);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BSTR bstrXML;
|
|
|
|
hr = _GetInfo(bstrXMLRequest, bstrEmailAddress, &bstrXML, dwFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = XMLDOMFromBStr(bstrXML, ppXMLResponse);
|
|
SysFreeString(bstrXML);
|
|
}
|
|
|
|
InternetCloseHandleWrap(m_hInternetSession);
|
|
m_hInternetSession = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_InternalDiscoverNow(IN BSTR bstrEmailAddress, IN DWORD dwFlags, IN BSTR bstrXMLRequest, OUT IXMLDOMDocument ** ppXMLResponse)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
*ppXMLResponse = NULL;
|
|
if (bstrEmailAddress)
|
|
{
|
|
// Does the caller want this done async?
|
|
if (m_hwndAsync)
|
|
{
|
|
// No, so cache the params so we can use them when async.
|
|
SysFreeString(m_bstrEmailAsync);
|
|
hr = HrSysAllocString(bstrEmailAddress, &m_bstrEmailAsync);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SysFreeString(m_bstrXMLRequest);
|
|
hr = HrSysAllocString(bstrXMLRequest, &m_bstrXMLRequest);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD idThread;
|
|
|
|
m_dwFlagsAsync = dwFlags;
|
|
|
|
AddRef();
|
|
HANDLE hThread = CreateThread(NULL, 0, CAccountDiscoveryBase::AutoDiscoveryUIThreadProc, this, 0, &idThread);
|
|
if (hThread)
|
|
{
|
|
// We wait WAIT_AUTODISCOVERY_STARTUP_HWND for the new thread to create the COM object
|
|
if (m_hCreatedBackgroundTask)
|
|
{
|
|
DWORD dwRet = WaitForSingleObject(m_hCreatedBackgroundTask, WAIT_AUTODISCOVERY_STARTUP_HWND);
|
|
ASSERT(dwRet != WAIT_TIMEOUT);
|
|
}
|
|
|
|
hr = m_hrSuccess;
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Yes.
|
|
hr = _PerformAutoDiscovery(bstrEmailAddress, dwFlags, bstrXMLRequest, ppXMLResponse);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
DWORD CAccountDiscoveryBase::_AutoDiscoveryUIThreadProc(void)
|
|
{
|
|
m_hrSuccess = CoInitialize(NULL);
|
|
|
|
// We need to make sure that the API is installed and
|
|
// accessible before we can continue.
|
|
if (SUCCEEDED(m_hrSuccess))
|
|
{
|
|
IXMLDOMDocument * pXMLResponse;
|
|
BSTR bstrXMLResponse = NULL;
|
|
|
|
// Signal the main thread that we have successfully started
|
|
if (m_hCreatedBackgroundTask)
|
|
SetEvent(m_hCreatedBackgroundTask);
|
|
|
|
// we give up the remainder of our timeslice here so that our parent thread has time to run
|
|
// and will notice that we have signaled the m_hCreatedBackgroundTask event and can therefore return
|
|
Sleep(0);
|
|
|
|
m_hrSuccess = _PerformAutoDiscovery(m_bstrEmailAsync, m_dwFlagsAsync, m_bstrXMLRequest, &pXMLResponse);
|
|
if (SUCCEEDED(m_hrSuccess))
|
|
{
|
|
m_hrSuccess = XMLBStrFromDOM(pXMLResponse, &bstrXMLResponse);
|
|
pXMLResponse->Release();
|
|
}
|
|
|
|
_AsyncParseResponse(bstrXMLResponse);
|
|
|
|
// Whether we succeeded or failed, inform the caller of our results.
|
|
if (IsWindow(m_hwndAsync))
|
|
{
|
|
PostMessage(m_hwndAsync, m_wMsgAsync, m_hrSuccess, (LPARAM)bstrXMLResponse);
|
|
}
|
|
else
|
|
{
|
|
SysFreeString(bstrXMLResponse);
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
{
|
|
// Signal the main thread that they can wake up to find that we
|
|
// failed to start the async operation.
|
|
if (m_hCreatedBackgroundTask)
|
|
SetEvent(m_hCreatedBackgroundTask);
|
|
}
|
|
|
|
Release();
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::_WorkAsync(IN HWND hwnd, IN UINT wMsg)
|
|
{
|
|
m_hwndAsync = hwnd;
|
|
m_wMsgAsync = wMsg;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================
|
|
// *** IUnknown Interface ***
|
|
//===========================
|
|
ULONG CAccountDiscoveryBase::AddRef()
|
|
{
|
|
m_cRef++;
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
ULONG CAccountDiscoveryBase::Release()
|
|
{
|
|
ASSERT(m_cRef > 0);
|
|
m_cRef--;
|
|
|
|
if (m_cRef > 0)
|
|
return m_cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT CAccountDiscoveryBase::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CAccountDiscoveryBase, IObjectWithSite),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
//===========================
|
|
// *** Class Methods ***
|
|
//===========================
|
|
CAccountDiscoveryBase::CAccountDiscoveryBase() : m_cRef(1)
|
|
{
|
|
// DllAddRef(); // Done by our inheriting class
|
|
|
|
// This needs to be allocated in Zero Inited Memory.
|
|
// Assert that all Member Variables are inited to Zero.
|
|
ASSERT(!m_hInternetSession);
|
|
ASSERT(!m_hwndAsync);
|
|
ASSERT(!m_wMsgAsync);
|
|
ASSERT(!m_dwFlagsAsync);
|
|
ASSERT(!m_bstrEmailAsync);
|
|
ASSERT(!m_bstrXMLRequest);
|
|
ASSERT(S_OK == m_hrSuccess);
|
|
ASSERT(!m_hdpaPrimary);
|
|
ASSERT(!m_hdpaSecondary);
|
|
|
|
// We use this event to signal the primary thread that the hwnd was created on the UI thread.
|
|
m_hCreatedBackgroundTask = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
}
|
|
|
|
|
|
CAccountDiscoveryBase::~CAccountDiscoveryBase()
|
|
{
|
|
SysFreeString(m_bstrEmailAsync);
|
|
SysFreeString(m_bstrXMLRequest);
|
|
|
|
if (m_hCreatedBackgroundTask)
|
|
CloseHandle(m_hCreatedBackgroundTask);
|
|
|
|
if (m_hdpaPrimary)
|
|
{
|
|
DPA_DestroyCallback(m_hdpaPrimary, DPALocalFree_Callback, NULL);
|
|
}
|
|
|
|
if (m_hdpaSecondary)
|
|
{
|
|
DPA_DestroyCallback(m_hdpaSecondary, DPALocalFree_Callback, NULL);
|
|
}
|
|
|
|
//DllRelease(); // Done by our inheriting class
|
|
}
|
|
|