#include "private.h" #include "subsmgrp.h" #include "subitem.h" // Contains implementations of IEnumSubscription and ISubscriptionMgr2 HRESULT SubscriptionItemFromCookie(BOOL fCreateNew, const SUBSCRIPTIONCOOKIE UNALIGNED *pCookie, ISubscriptionItem **ppSubscriptionItem); HRESULT DoGetItemFromURL(LPCTSTR pszURL, ISubscriptionItem **ppSubscriptionItem) { HRESULT hr; SUBSCRIPTIONCOOKIE cookie; if ((NULL == pszURL) || (NULL == ppSubscriptionItem)) { return E_INVALIDARG; } hr = ReadCookieFromInetDB(pszURL, &cookie); if (SUCCEEDED(hr)) { hr = SubscriptionItemFromCookie(FALSE, &cookie, ppSubscriptionItem); } return hr; } HRESULT DoGetItemFromURLW(LPCWSTR pwszURL, ISubscriptionItem **ppSubscriptionItem) { TCHAR szURL[INTERNET_MAX_URL_LENGTH]; if ((NULL == pwszURL) || (NULL == ppSubscriptionItem)) { return E_INVALIDARG; } StrCpyN(szURL, pwszURL, ARRAYSIZE(szURL)); return DoGetItemFromURL(szURL, ppSubscriptionItem); } HRESULT DoAbortItems( /* [in] */ DWORD dwNumCookies, /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies) { HRESULT hr; if ((0 == dwNumCookies) || (NULL == pCookies)) { return E_INVALIDARG; } ISubscriptionThrottler *pst; hr = CoCreateInstance(CLSID_SubscriptionThrottler, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_ISubscriptionThrottler, (void **)&pst); if (SUCCEEDED(hr)) { hr = pst->AbortItems(dwNumCookies, pCookies); pst->Release(); } else { hr = S_FALSE; } return hr; } HRESULT DoCreateSubscriptionItem( /* [in] */ const SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo, /* [out] */ SUBSCRIPTIONCOOKIE *pNewCookie, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { HRESULT hr; ISubscriptionItem *psi; if ((NULL == pSubscriptionItemInfo) || (NULL == pNewCookie) || (NULL == ppSubscriptionItem)) { return E_INVALIDARG; } *ppSubscriptionItem = NULL; CreateCookie(pNewCookie); hr = SubscriptionItemFromCookie(TRUE, pNewCookie, &psi); if (SUCCEEDED(hr)) { ASSERT(NULL != psi); hr = psi->SetSubscriptionItemInfo(pSubscriptionItemInfo); if (SUCCEEDED(hr)) { *ppSubscriptionItem = psi; } else { // Don't leak or leave slop hanging around psi->Release(); DoDeleteSubscriptionItem(pNewCookie, FALSE); } } return hr; } HRESULT DoCloneSubscriptionItem( /* [in] */ ISubscriptionItem *pSubscriptionItem, /* [out] */ SUBSCRIPTIONCOOKIE *pNewCookie, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { HRESULT hr; SUBSCRIPTIONCOOKIE NewCookie; if ((NULL == pSubscriptionItem) || (NULL == ppSubscriptionItem)) { return E_INVALIDARG; } IEnumItemProperties *peip; SUBSCRIPTIONITEMINFO sii; *ppSubscriptionItem = NULL; // First get existing subscription details sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO); hr = pSubscriptionItem->GetSubscriptionItemInfo(&sii); if (SUCCEEDED(hr)) { ISubscriptionItem *psi; // Mark as temp and create a new subscription item sii.dwFlags |= SI_TEMPORARY; hr = DoCreateSubscriptionItem(&sii, &NewCookie, &psi); if (SUCCEEDED(hr)) { if (pNewCookie) *pNewCookie = NewCookie; // Get properties from existing item hr = pSubscriptionItem->EnumProperties(&peip); if (SUCCEEDED(hr)) { ULONG count; ASSERT(NULL != peip); hr = peip->GetCount(&count); if (SUCCEEDED(hr)) { ITEMPROP *pProps = new ITEMPROP[count]; LPWSTR *pNames = new LPWSTR[count]; VARIANT *pVars = new VARIANT[count]; if ((NULL != pProps) && (NULL != pNames) && (NULL != pVars)) { hr = peip->Next(count, pProps, &count); if (SUCCEEDED(hr)) { ULONG i; for (i = 0; i < count; i++) { pNames[i] = pProps[i].pwszName; pVars[i] = pProps[i].variantValue; } hr = psi->WriteProperties(count, pNames, pVars); // clean up from enum for (i = 0; i < count; i++) { if (pProps[i].pwszName) { CoTaskMemFree(pProps[i].pwszName); } VariantClear(&pProps[i].variantValue); } } } else { hr = E_OUTOFMEMORY; } SAFEDELETE(pProps); SAFEDELETE(pNames); SAFEDELETE(pVars); } peip->Release(); } if (SUCCEEDED(hr)) { *ppSubscriptionItem = psi; } else { psi->Release(); } } } return hr; } HRESULT DoDeleteSubscriptionItem( /* [in] */ const SUBSCRIPTIONCOOKIE UNALIGNED *pCookie_ua, /* [in] */ BOOL fAbortItem) { HRESULT hr; TCHAR szKey[MAX_PATH]; SUBSCRIPTIONCOOKIE cookie_buf; SUBSCRIPTIONCOOKIE *pCookie; // // Make an aligned copy of pCookie_ua and set a pointer to it. // if (pCookie_ua != NULL) { cookie_buf = *pCookie_ua; pCookie = &cookie_buf; } else { pCookie = NULL; } if (NULL == pCookie) { return E_INVALIDARG; } if (fAbortItem) { DoAbortItems(1, pCookie); } if (ItemKeyNameFromCookie(pCookie, szKey, ARRAYSIZE(szKey))) { // Notify the agent that it is about to get deleted. ISubscriptionItem *pItem=NULL; if (SUCCEEDED(SubscriptionItemFromCookie(FALSE, pCookie, &pItem))) { SUBSCRIPTIONITEMINFO sii = { sizeof(SUBSCRIPTIONITEMINFO) }; if (SUCCEEDED(pItem->GetSubscriptionItemInfo(&sii))) { ASSERT(!(sii.dwFlags & SI_TEMPORARY)); ISubscriptionAgentControl *psac=NULL; if (SUCCEEDED( CoCreateInstance(sii.clsidAgent, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionAgentControl, (void**)&psac))) { psac->SubscriptionControl(pItem, SUBSCRIPTION_AGENT_DELETE); psac->Release(); } FireSubscriptionEvent(SUBSNOTF_DELETE, pCookie); if (GUID_NULL != sii.ScheduleGroup) { ISyncScheduleMgr *pSyncScheduleMgr; hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL, IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr); if (SUCCEEDED(hr)) { pSyncScheduleMgr->RemoveSchedule(&sii.ScheduleGroup); pSyncScheduleMgr->Release(); } } } pItem->Release(); } hr = (SHDeleteKey(HKEY_CURRENT_USER, szKey) == ERROR_SUCCESS) ? S_OK : E_FAIL; } else { TraceMsg(TF_ALWAYS, "Failed to delete subscription item."); hr = E_FAIL; } return hr; } HRESULT AddUpdateSubscription(SUBSCRIPTIONCOOKIE UNALIGNED *pCookie_ua, SUBSCRIPTIONITEMINFO *psii, LPCWSTR pwszURL, ULONG nProps, const LPWSTR rgwszName[], VARIANT rgValue[]) { HRESULT hr = S_OK; SUBSCRIPTIONCOOKIE cookie_buf; SUBSCRIPTIONCOOKIE *pCookie; // // Make an aligned copy of pCookie_ua and set a pointer to it. // cookie_buf = *pCookie_ua; pCookie = &cookie_buf; ASSERT((0 == nProps) || ((NULL != rgwszName) && (NULL != rgValue))); TCHAR szURL[INTERNET_MAX_URL_LENGTH]; ISubscriptionItem *psi = NULL; SUBSCRIPTIONCOOKIE cookie; StrCpyNW(szURL, pwszURL, ARRAYSIZE(szURL)); // Try and get the cookie from the inet db otherwise // create a new one. if (*pCookie == CLSID_NULL) { CreateCookie(&cookie); } else { cookie = *pCookie; } // Update the inet db WriteCookieToInetDB(szURL, &cookie, FALSE); hr = SubscriptionItemFromCookie(TRUE, &cookie, &psi); if (SUCCEEDED(hr)) { hr = psi->SetSubscriptionItemInfo(psii); if (SUCCEEDED(hr) && (nProps > 0)) { ASSERT(NULL != psi); hr = psi->WriteProperties(nProps, rgwszName, rgValue); } psi->Release(); if (FAILED(hr)) { DoDeleteSubscriptionItem(&cookie, TRUE); } } *pCookie_ua = cookie_buf; return hr; } HRESULT SubscriptionItemFromCookie(BOOL fCreateNew, const SUBSCRIPTIONCOOKIE UNALIGNED *pCookie_ua, ISubscriptionItem **ppSubscriptionItem) { HRESULT hr; SUBSCRIPTIONCOOKIE cookie_buf; SUBSCRIPTIONCOOKIE *pCookie; // // Make an aligned copy of pCookie_ua and set a pointer to it. // if (pCookie_ua != NULL) { cookie_buf = *pCookie_ua; pCookie = &cookie_buf; } else { pCookie = NULL; } ASSERT((NULL != pCookie) && (NULL != ppSubscriptionItem)); HKEY hkey; if (OpenItemKey(pCookie, fCreateNew ? TRUE : FALSE, KEY_READ | KEY_WRITE, &hkey)) { *ppSubscriptionItem = new CSubscriptionItem(pCookie, hkey); if (NULL != *ppSubscriptionItem) { hr = S_OK; } else { hr = E_OUTOFMEMORY; } RegCloseKey(hkey); } else { *ppSubscriptionItem = NULL; hr = E_FAIL; } return hr; } BOOL ItemKeyNameFromCookie(const SUBSCRIPTIONCOOKIE *pCookie, TCHAR *pszKeyName, DWORD cchKeyName) { WCHAR wszGuid[GUIDSTR_MAX]; ASSERT((NULL != pCookie) && (NULL != pszKeyName) && (cchKeyName >= ARRAYSIZE(WEBCHECK_REGKEY_STORE) + GUIDSTR_MAX)); if (StringFromGUID2(*pCookie, wszGuid, ARRAYSIZE(wszGuid))) { wnsprintfW(pszKeyName, cchKeyName, L"%s\\%s", c_szRegKeyStore, wszGuid); return TRUE; } return FALSE; } BOOL OpenItemKey(const SUBSCRIPTIONCOOKIE *pCookie, BOOL fCreateNew, REGSAM samDesired, HKEY *phkey) { TCHAR szKeyName[MAX_PATH]; ASSERT((NULL != pCookie) && (NULL != phkey)); if (ItemKeyNameFromCookie(pCookie, szKeyName, ARRAYSIZE(szKeyName))) { if (fCreateNew) { DWORD dwDisposition; return RegCreateKeyEx(HKEY_CURRENT_USER, szKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, phkey, &dwDisposition) == ERROR_SUCCESS; } else { return RegOpenKeyEx(HKEY_CURRENT_USER, szKeyName, 0, samDesired, phkey) == ERROR_SUCCESS; } } return FALSE; } // ISubscriptionMgr2 members STDMETHODIMP CSubscriptionMgr::GetItemFromURL( /* [in] */ LPCWSTR pwszURL, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { return DoGetItemFromURLW(pwszURL, ppSubscriptionItem); } STDMETHODIMP CSubscriptionMgr::GetItemFromCookie( /* [in] */ const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { if ((NULL == pSubscriptionCookie) || (NULL == ppSubscriptionItem)) { return E_INVALIDARG; } return SubscriptionItemFromCookie(FALSE, pSubscriptionCookie, ppSubscriptionItem); } STDMETHODIMP CSubscriptionMgr::GetSubscriptionRunState( /* [in] */ DWORD dwNumCookies, /* [in] */ const SUBSCRIPTIONCOOKIE *pSubscriptionCookies, /* [out] */ DWORD *pdwRunState) { HRESULT hr; if ((0 == dwNumCookies) || (NULL == pSubscriptionCookies) || (NULL == pdwRunState)) { return E_INVALIDARG; } ISubscriptionThrottler *pst; hr = CoCreateInstance(CLSID_SubscriptionThrottler, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_ISubscriptionThrottler, (void **)&pst); if (SUCCEEDED(hr)) { hr = pst->GetSubscriptionRunState(dwNumCookies, pSubscriptionCookies, pdwRunState); pst->Release(); } else { // Couldn't connect to a running throttler so assume nothing // is running. for (DWORD i = 0; i < dwNumCookies; i++) { *pdwRunState++ = 0; } hr = S_FALSE; } return hr; } STDMETHODIMP CSubscriptionMgr::EnumSubscriptions( /* [in] */ DWORD dwFlags, /* [out] */ IEnumSubscription **ppEnumSubscriptions) { HRESULT hr; if ((dwFlags & ~SUBSMGRENUM_MASK) || (NULL == ppEnumSubscriptions)) { return E_INVALIDARG; } CEnumSubscription *pes = new CEnumSubscription; *ppEnumSubscriptions = NULL; if (NULL != pes) { hr = pes->Initialize(dwFlags); if (SUCCEEDED(hr)) { hr = pes->QueryInterface(IID_IEnumSubscription, (void **)ppEnumSubscriptions); } pes->Release(); } else { hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CSubscriptionMgr::UpdateItems( /* [in] */ DWORD dwFlags, /* [in] */ DWORD dwNumCookies, /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies) { HRESULT hr; if ((dwFlags & ~SUBSMGRUPDATE_MASK) || (0 == dwNumCookies) || (NULL == pCookies)) { return E_INVALIDARG; } // // Fail if restrictions are in place. // FEATURE: Should we have a flag parameter to override this? // if (SHRestricted2W(REST_NoManualUpdates, NULL, 0)) { SGMessageBox(NULL, IDS_RESTRICTED, MB_OK); return E_ACCESSDENIED; } ISyncMgrSynchronizeInvoke *pSyncMgrInvoke; hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL, IID_ISyncMgrSynchronizeInvoke, (void **)&pSyncMgrInvoke); if (SUCCEEDED(hr)) { DWORD dwInvokeFlags = SYNCMGRINVOKE_STARTSYNC; if (dwFlags & SUBSMGRUPDATE_MINIMIZE) { dwInvokeFlags |= SYNCMGRINVOKE_MINIMIZED; } hr = pSyncMgrInvoke->UpdateItems(dwInvokeFlags, CLSID_WebCheckOfflineSync, dwNumCookies * sizeof(SUBSCRIPTIONCOOKIE), (const BYTE *)pCookies); pSyncMgrInvoke->Release(); } return hr; } STDMETHODIMP CSubscriptionMgr::AbortItems( /* [in] */ DWORD dwNumCookies, /* [size_is][in] */ const SUBSCRIPTIONCOOKIE *pCookies) { return DoAbortItems(dwNumCookies, pCookies); } STDMETHODIMP CSubscriptionMgr::AbortAll() { HRESULT hr; ISubscriptionThrottler *pst; hr = CoCreateInstance(CLSID_SubscriptionThrottler, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_ISubscriptionThrottler, (void **)&pst); if (SUCCEEDED(hr)) { hr = pst->AbortAll(); pst->Release(); } else { hr = S_FALSE; } return hr; } // ISubscriptionMgrPriv STDMETHODIMP CSubscriptionMgr::CreateSubscriptionItem( /* [in] */ const SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo, /* [out] */ SUBSCRIPTIONCOOKIE *pNewCookie, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { return DoCreateSubscriptionItem(pSubscriptionItemInfo, pNewCookie, ppSubscriptionItem); } STDMETHODIMP CSubscriptionMgr::CloneSubscriptionItem( /* [in] */ ISubscriptionItem *pSubscriptionItem, /* [out] */ SUBSCRIPTIONCOOKIE *pNewCookie, /* [out] */ ISubscriptionItem **ppSubscriptionItem) { return DoCloneSubscriptionItem(pSubscriptionItem, pNewCookie, ppSubscriptionItem); } STDMETHODIMP CSubscriptionMgr::DeleteSubscriptionItem( /* [in] */ const SUBSCRIPTIONCOOKIE *pCookie) { return DoDeleteSubscriptionItem(pCookie, TRUE); } // ** CEnumSubscription ** CEnumSubscription::CEnumSubscription() { ASSERT(NULL == m_pCookies); ASSERT(0 == m_nCurrent); ASSERT(0 == m_nCount); m_cRef = 1; DllAddRef(); } CEnumSubscription::~CEnumSubscription() { if (NULL != m_pCookies) { delete [] m_pCookies; } DllRelease(); } HRESULT CEnumSubscription::Initialize(DWORD dwFlags) { HRESULT hr = E_FAIL; HKEY hkey = NULL; DWORD dwDisposition; ASSERT(0 == m_nCount); if (RegCreateKeyEx(HKEY_CURRENT_USER, c_szRegKeyStore, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkey, &dwDisposition) == ERROR_SUCCESS) { DWORD nCount; if (RegQueryInfoKey(hkey, NULL, // address of buffer for class string NULL, // address of size of class string buffer NULL, // reserved &nCount, // address of buffer for number of subkeys NULL, // address of buffer for longest subkey name length NULL, // address of buffer for longest class string length NULL, // address of buffer for number of value entries NULL, // address of buffer for longest value name length NULL, // address of buffer for longest value data length NULL, // address of buffer for security descriptor length NULL // address of buffer for last write time ) == ERROR_SUCCESS) { SUBSCRIPTIONCOOKIE Cookie; m_pCookies = new SUBSCRIPTIONCOOKIE[nCount]; if (NULL != m_pCookies) { TCHAR szKeyName[GUIDSTR_MAX]; hr = S_OK; for (ULONG i = 0; (i < nCount) && (S_OK == hr); i++) { if (RegEnumKey(hkey, i, szKeyName, ARRAYSIZE(szKeyName)) == ERROR_SUCCESS) { HRESULT hrConvert; hrConvert = CLSIDFromString(szKeyName, &Cookie); if (SUCCEEDED(hrConvert)) { ISubscriptionItem *psi; m_pCookies[m_nCount] = Cookie; hr = SubscriptionItemFromCookie(FALSE, &Cookie, &psi); if (SUCCEEDED(hr)) { SUBSCRIPTIONITEMINFO sii; sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO); if (SUCCEEDED(psi->GetSubscriptionItemInfo(&sii))) { // Only count this if it's not a temporary // or the caller asked for temporary items. if ((!(sii.dwFlags & SI_TEMPORARY)) || (dwFlags & SUBSMGRENUM_TEMP)) { m_nCount++; } } psi->Release(); } } } } } else { hr = E_OUTOFMEMORY; } } RegCloseKey(hkey); } return hr; } // IUnknown members STDMETHODIMP CEnumSubscription::QueryInterface(REFIID riid, void **ppv) { HRESULT hr; if (NULL == ppv) { return E_INVALIDARG; } if ((IID_IUnknown == riid) || (IID_IEnumSubscription == riid)) { *ppv = (IEnumSubscription *)this; AddRef(); hr = S_OK; } else { *ppv = NULL; hr = E_NOINTERFACE; } return hr; } STDMETHODIMP_(ULONG) CEnumSubscription::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CEnumSubscription::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } HRESULT CEnumSubscription::CopyRange(ULONG nStart, ULONG nCount, SUBSCRIPTIONCOOKIE *pCookies, ULONG *pnCopied) { ULONG nCopied = 0; ASSERT((NULL != pCookies) && (NULL != pnCopied)); if (m_nCurrent < m_nCount) { ULONG nRemaining = m_nCount - m_nCurrent; nCopied = min(nRemaining, nCount); memcpy(pCookies, m_pCookies + m_nCurrent, nCopied * sizeof(SUBSCRIPTIONCOOKIE)); } *pnCopied = nCopied; return (nCopied == nCount) ? S_OK : S_FALSE; } // IEnumSubscription STDMETHODIMP CEnumSubscription::Next( /* [in] */ ULONG celt, /* [length_is][size_is][out] */ SUBSCRIPTIONCOOKIE *rgelt, /* [out] */ ULONG *pceltFetched) { HRESULT hr; if ((0 == celt) || ((celt > 1) && (NULL == pceltFetched)) || (NULL == rgelt)) { return E_INVALIDARG; } DWORD nFetched; hr = CopyRange(m_nCurrent, celt, rgelt, &nFetched); m_nCurrent += nFetched; if (pceltFetched) { *pceltFetched = nFetched; } return hr; } STDMETHODIMP CEnumSubscription::Skip( /* [in] */ ULONG celt) { HRESULT hr; m_nCurrent += celt; if (m_nCurrent > (m_nCount - 1)) { m_nCurrent = m_nCount; // Passed the last one hr = S_FALSE; } else { hr = S_OK; } return hr; } STDMETHODIMP CEnumSubscription::Reset() { m_nCurrent = 0; return S_OK; } STDMETHODIMP CEnumSubscription::Clone( /* [out] */ IEnumSubscription **ppenum) { HRESULT hr = E_OUTOFMEMORY; *ppenum = NULL; CEnumSubscription *pes = new CEnumSubscription; if (NULL != pes) { pes->m_pCookies = new SUBSCRIPTIONCOOKIE[m_nCount]; if (NULL != pes->m_pCookies) { ULONG nFetched; hr = E_FAIL; pes->m_nCount = m_nCount; CopyRange(0, m_nCount, pes->m_pCookies, &nFetched); ASSERT(m_nCount == nFetched); if (m_nCount == nFetched) { hr = pes->QueryInterface(IID_IEnumSubscription, (void **)ppenum); } } pes->Release(); } return hr; } STDMETHODIMP CEnumSubscription::GetCount( /* [out] */ ULONG *pnCount) { if (NULL == pnCount) { return E_INVALIDARG; } *pnCount = m_nCount; return S_OK; }