#include "private.h" #include "offsync.h" #include "throttle.h" #include "helper.h" #include "subsmgrp.h" #include #define TF_THISMODULE TF_DELAGENT COfflineSync::COfflineSync() { // Maintain global count of objects DllAddRef(); ASSERT(NULL == m_pThrottler); ASSERT(FALSE == m_fCookiesSpecified); // Initialize object m_cRef = 1; DBG("Creating COfflineSync object"); } COfflineSync::~COfflineSync() { DllRelease(); Cleanup(); DBG("Destroying COfflineSync object"); if (m_pSyncCallback) { m_pSyncCallback->Release(); } SAFERELEASE(m_pSubsMgr2); } void COfflineSync::Cleanup() { if (NULL != m_pThrottler) { m_pThrottler->Unadvise(this); m_pThrottler->Release(); m_pThrottler = NULL; } m_hWndParent = NULL; if (NULL != m_pSyncCallback) { m_pSyncCallback->SynchronizeCompleted(m_hrResult); } m_nItemsToRun = 0; SAFEDELETE(m_pItems); } // // IUnknown members // STDMETHODIMP_(ULONG) COfflineSync::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) COfflineSync::Release(void) { if( 0L != --m_cRef ) return m_cRef; delete this; return 0L; } STDMETHODIMP COfflineSync::QueryInterface(REFIID riid, void ** ppv) { *ppv=NULL; // Validate requested interface if ((IID_IUnknown == riid) || (IID_ISyncMgrSynchronize == riid)) { *ppv = (ISyncMgrSynchronize *)this; } else { return E_NOINTERFACE; } // Addref through the interface ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } // // IOfflineSynchronize members // HRESULT COfflineSync::Initialize(DWORD dwReserved, DWORD dwSyncFlags, DWORD cbCookie, const BYTE *lpCookie) { HRESULT hr = S_OK; if (SYNCMGRFLAG_INVOKE == (dwSyncFlags & SYNCMGRFLAG_EVENTMASK) ) { ASSERT((0 == cbCookie) || (0 == cbCookie % sizeof(SUBSCRIPTIONCOOKIE))); if ((cbCookie != 0) && ((0 != (cbCookie % sizeof(SUBSCRIPTIONCOOKIE))) || (NULL == lpCookie))) { return E_INVALIDARG; } if (cbCookie > 0) { hr = DupItems(cbCookie / sizeof(SUBSCRIPTIONCOOKIE), (SUBSCRIPTIONCOOKIE *)lpCookie); if (SUCCEEDED(hr)) { m_fCookiesSpecified = TRUE; } } } m_dwSyncFlags = dwSyncFlags; return hr; } HRESULT COfflineSync::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo) { HRESULT hr; if (NULL == ppSyncMgrHandlerInfo) { return E_INVALIDARG; } *ppSyncMgrHandlerInfo = (SYNCMGRHANDLERINFO *)CoTaskMemAlloc(sizeof(SYNCMGRHANDLERINFO)); if (NULL != *ppSyncMgrHandlerInfo) { (*ppSyncMgrHandlerInfo)->cbSize = sizeof(SYNCMGRHANDLERINFO); (*ppSyncMgrHandlerInfo)->hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_WEBCHECK)); (*ppSyncMgrHandlerInfo)->SyncMgrHandlerFlags = 0; //SYNCMGRHANDLER_HASPROPERTIES #ifdef UNICODE MLLoadStringW(IDS_SYNCMGR_NAME, (*ppSyncMgrHandlerInfo)->wszHandlerName, ARRAYSIZE((*ppSyncMgrHandlerInfo)->wszHandlerName)); #else CHAR szHandlerName[sizeof((*ppSyncMgrHandlerInfo)->wszHandlerName)]; MLLoadStringA(IDS_SYNCMGR_NAME, szHandlerName, sizeof(szHandlerName)); MultiByteToWideChar(CP_ACP, 0, szHandlerName, -1, (*ppSyncMgrHandlerInfo)->wszHandlerName, ARRAYSIZE((*ppSyncMgrHandlerInfo)->wszHandlerName)); #endif hr = S_OK; } else { hr = E_OUTOFMEMORY; } return hr; } HRESULT COfflineSync::EnumSyncMgrItems(ISyncMgrEnumItems **ppSyncMgrEnumItems) { HRESULT hr; if (NULL == ppSyncMgrEnumItems) { return E_INVALIDARG; } hr = GetSubsMgr2(); if (SUCCEEDED(hr)) { COfflineEnum *pEnum = new COfflineEnum(); if (NULL != pEnum) { ASSERT(((TRUE == m_fCookiesSpecified) && ((m_nItemsToRun > 0) && (NULL != m_pItems))) || ((FALSE == m_fCookiesSpecified) && ((m_nItemsToRun == 0) && (NULL == m_pItems)))) hr = pEnum->Init(m_pSubsMgr2, m_nItemsToRun, m_pItems, ppSyncMgrEnumItems, m_dwSyncFlags); pEnum->Release(); } else { hr = E_OUTOFMEMORY; } } return hr; } HRESULT COfflineSync::GetItemObject(REFSYNCMGRITEMID ItemID, REFIID riid, void **ppv) { return E_NOTIMPL; } HRESULT COfflineSync::ShowProperties(HWND hWndParent, REFSYNCMGRITEMID ItemID) { HRESULT hr = S_OK; HRESULT hrTmp; ISubscriptionItem *pSubsItem; hrTmp = SubscriptionItemFromCookie(FALSE, &ItemID, &pSubsItem); if (SUCCEEDED(hrTmp)) { BSTR bstrURL; hrTmp = ReadBSTR(pSubsItem, c_szPropURL, &bstrURL); if (SUCCEEDED(hrTmp)) { ISubscriptionMgr2 *pSubsMgr2; hrTmp = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr2, (void **)&pSubsMgr2); if (SUCCEEDED(hrTmp)) { BOOL bIsSubscribed; hrTmp = pSubsMgr2->ShowSubscriptionProperties(bstrURL, hWndParent); if (FAILED(pSubsMgr2->IsSubscribed(bstrURL, &bIsSubscribed)) || !bIsSubscribed) { hr = S_SYNCMGR_ITEMDELETED; } pSubsMgr2->Release(); } SysFreeString(bstrURL); } pSubsItem->Release(); } if (NULL != m_pSyncCallback) { m_pSyncCallback->ShowPropertiesCompleted(hr); } return hr; } HRESULT COfflineSync::SetProgressCallback(ISyncMgrSynchronizeCallback *lpCallBack) { SAFERELEASE(m_pSyncCallback); m_pSyncCallback = lpCallBack; if (m_pSyncCallback) { m_pSyncCallback->AddRef(); } return S_OK; } HRESULT COfflineSync::PrepareForSync(ULONG cbNumItems, SYNCMGRITEMID *pItemIDs, HWND hWndParent, DWORD dwReserved) { HRESULT hr; DBG("PrepareForSync"); if ((0 == cbNumItems) || (NULL == pItemIDs)) { hr = E_INVALIDARG; } if (NULL == m_pSyncCallback) { return E_UNEXPECTED; } hr = DupItems(cbNumItems, pItemIDs); m_pSyncCallback->PrepareForSyncCompleted(hr); return hr; } HRESULT COfflineSync::Synchronize(HWND hWndParent) { HRESULT hr; m_hrResult = E_FAIL; m_hWndParent = hWndParent; hr = CThrottler::GetThrottler(&m_pThrottler); if (SUCCEEDED(hr)) { ASSERT(NULL != m_pThrottler); hr = m_pThrottler->Advise(this); if (SUCCEEDED(hr)) { hr = m_pThrottler->RunCookies(m_nItemsToRun, m_pItems, m_dwSyncFlags); } // ************************************************************************ // Don't access m_pThrottler after this without checking for NULL since // we could have released it during the call to RunCookies! // ************************************************************************ } if (FAILED(hr)) { Cleanup(); } return hr; } HRESULT COfflineSync::SetItemStatus(REFSYNCMGRITEMID ItemID, DWORD dwSyncMgrStatus) { HRESULT hr = S_OK; if (NULL != m_pThrottler) { switch (dwSyncMgrStatus) { case SYNCMGRSTATUS_SKIPPED: hr = m_pThrottler->AbortItems(1, &ItemID); break; case SYNCMGRSTATUS_STOPPED: hr = m_pThrottler->AbortItems(m_nItemsToRun, m_pItems); break; } } else { ULONG i; // This means we are getting called before Synchronize is called switch (dwSyncMgrStatus) { case SYNCMGRSTATUS_SKIPPED: for (i = 0; i < m_nItemsToRun; i++) { if (ItemID == m_pItems[i]) { m_pItems[i] = GUID_NULL; } } break; case SYNCMGRSTATUS_STOPPED: m_nItemsToRun = 0; break; } } return hr; } HRESULT COfflineSync::ShowError(HWND hWndParent,REFSYNCMGRERRORID ErrorID) { return E_NOTIMPL; } HRESULT COfflineSync::UpdateBegin( const SUBSCRIPTIONCOOKIE *pSubscriptionCookie) { UpdateProgress(pSubscriptionCookie, -1, -1, -1, S_OK, NULL); return S_OK; } HRESULT COfflineSync::UpdateProgress( const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, long lSizeDownloaded, long lProgressCurrent, long lProgressMax, HRESULT hrStatus, LPCWSTR wszStatus) { HRESULT hr; ASSERT(NULL != m_pSyncCallback); ASSERT(NULL != m_pThrottler); if ((FindCookie(pSubscriptionCookie) != -1) && (NULL != m_pSyncCallback)) { int iProgValue; switch (hrStatus) { case WC_INTERNAL_S_PAUSED: iProgValue = SYNCMGRSTATUS_PAUSED; break; case WC_INTERNAL_S_RESUMING: iProgValue = SYNCMGRSTATUS_RESUMING; break; case WC_INTERNAL_S_PENDING: iProgValue = SYNCMGRSTATUS_PENDING; break; default: iProgValue = SYNCMGRSTATUS_UPDATING; break; } CallSyncMgrProgress(pSubscriptionCookie, wszStatus, iProgValue, lProgressCurrent, lProgressMax); hr = S_OK; } else { hr = S_FALSE; } return hr; } HRESULT COfflineSync::UpdateEnd( const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, long lSizeDownloaded, HRESULT hrResult, LPCWSTR wszResult) { HRESULT hr; int index = FindCookie(pSubscriptionCookie); if (index != -1) { DWORD dwStatus; m_pItems[index] = CLSID_NULL; // Forget about it m_nItemsCompleted++; if (SUCCEEDED(hrResult)) { dwStatus = SYNCMGRSTATUS_SUCCEEDED; } else if (E_ABORT == hrResult) { dwStatus = SYNCMGRSTATUS_SKIPPED; } else { dwStatus = SYNCMGRSTATUS_FAILED; } CallSyncMgrProgress(pSubscriptionCookie, wszResult, dwStatus, 100, 100); AreWeDoneYet(); hr = S_OK; } else { hr = S_FALSE; } return hr; } STDMETHODIMP COfflineSync::ReportError( const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, HRESULT hrError, LPCWSTR wszError) { HRESULT hr; if ((FindCookie(pSubscriptionCookie) != -1) && (NULL != m_pSyncCallback)) { DWORD dwErrorLevel; switch (hrError) { case E_ABORT: dwErrorLevel = SYNCMGRLOGLEVEL_INFORMATION; break; case INET_E_AGENT_MAX_SIZE_EXCEEDED: case INET_E_SCHEDULED_UPDATES_DISABLED: case INET_E_SCHEDULED_UPDATES_RESTRICTED: case INET_E_SCHEDULED_UPDATE_INTERVAL: case INET_E_SCHEDULED_EXCLUDE_RANGE: case INET_E_AGENT_WARNING: dwErrorLevel = SYNCMGRLOGLEVEL_WARNING; break; default: dwErrorLevel = FAILED(hrError) ? SYNCMGRLOGLEVEL_ERROR : SYNCMGRLOGLEVEL_INFORMATION; break; } SYNCMGRLOGERRORINFO errInfo; errInfo.cbSize = sizeof(SYNCMGRLOGERRORINFO); errInfo.mask = SYNCMGRLOGERROR_ITEMID; errInfo.ItemID = *pSubscriptionCookie; m_pSyncCallback->LogError(dwErrorLevel, wszError, &errInfo); hr = S_OK; } else { hr = S_FALSE; } return hr; } HRESULT COfflineSync::CallSyncMgrProgress(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, const WCHAR *lpcStatusText, DWORD dwStatusType, INT iProgValue, INT iMaxValue) { HRESULT hr = S_OK; SYNCMGRPROGRESSITEM smpi; ASSERT(NULL != m_pSyncCallback); smpi.cbSize = sizeof(SYNCMGRPROGRESSITEM); smpi.mask = 0; if (NULL != lpcStatusText) { smpi.mask |= SYNCMGRPROGRESSITEM_STATUSTEXT; smpi.lpcStatusText = lpcStatusText; } if ((DWORD)(-1) != dwStatusType) { smpi.mask |= SYNCMGRPROGRESSITEM_STATUSTYPE; smpi.dwStatusType = dwStatusType; } if (iProgValue >= 0) { smpi.mask |= SYNCMGRPROGRESSITEM_PROGVALUE; smpi.iProgValue = iProgValue; } if (iMaxValue >= 0) { smpi.mask |= SYNCMGRPROGRESSITEM_MAXVALUE; smpi.iMaxValue = iMaxValue; } // We still call progress even if smpi.mask is 0 just in case we should // respond to a cancel. HRESULT hrProgress = m_pSyncCallback->Progress(*pSubscriptionCookie, &smpi); switch(hrProgress) { case S_SYNCMGR_CANCELITEM: m_pThrottler->AbortItems(1, pSubscriptionCookie); break; case S_SYNCMGR_CANCELALL: m_pThrottler->AbortAll(); break; } return hr; } HRESULT COfflineSync::UpdateSyncMgrStatus(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie, LPCWSTR wszStatusMsg, DWORD dwStatus) { HRESULT hr; if ((FindCookie(pSubscriptionCookie) != -1) && (NULL != m_pSyncCallback)) { CallSyncMgrProgress(pSubscriptionCookie, wszStatusMsg, dwStatus, -1, -1); hr = S_OK; } else { hr = S_FALSE; } return hr; } HRESULT COfflineSync::DupItems(ULONG cbNumItems, SUBSCRIPTIONCOOKIE *pItemIDs) { HRESULT hr; ASSERT(0 != cbNumItems); ASSERT(NULL != pItemIDs); ASSERT((0 == m_nItemsToRun) || (TRUE == m_fCookiesSpecified)); if (NULL != m_pItems) { delete [] m_pItems; } m_pItems = new SUBSCRIPTIONCOOKIE[cbNumItems]; if (NULL != m_pItems) { memcpy(m_pItems, pItemIDs, cbNumItems * sizeof(SUBSCRIPTIONCOOKIE)); m_nItemsToRun = cbNumItems; hr = S_OK; } else { hr = E_OUTOFMEMORY; } return hr; } BOOL COfflineSync::AreWeDoneYet() { BOOL rc; if (m_nItemsCompleted != m_nItemsToRun) { rc = FALSE; } else { Cleanup(); rc = TRUE; } return rc; } HRESULT COfflineSync::GetSubsMgr2() { HRESULT hr = S_FALSE; if (NULL == m_pSubsMgr2) { hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr2, (void**)&m_pSubsMgr2); if (FAILED(hr)) { DBG_WARN("Failed to allocate subscription store (aborting)"); } } return hr; } // returns index into m_pItems on success, -1 on failure int COfflineSync::FindCookie(const SUBSCRIPTIONCOOKIE *pCookie) { int index = -1; if (NULL != m_pItems) { for (ULONG i = 0; i < m_nItemsToRun; i++) { if (m_pItems[i] == *pCookie) { index = i; break; } } } return index; } COfflineEnum::COfflineEnum() { ASSERT(NULL == m_pItems); ASSERT(0 == m_iNumItems); // Maintain global count of objects DllAddRef(); // Initialize object m_cRef = 1; } COfflineEnum::~COfflineEnum() { DllRelease(); SAFELOCALFREE(m_pItems); } HRESULT COfflineEnum::LoadItem(ISubscriptionMgr2 *pSubsMgr2, const SUBSCRIPTIONCOOKIE *pCookie, SYNCMGRITEM *pItem, DWORD dwItemState) { HRESULT hr; ISubscriptionItem *pSubItem; ASSERT(NULL != pSubsMgr2); hr = pSubsMgr2->GetItemFromCookie(pCookie, &pSubItem); if (SUCCEEDED(hr)) { BSTR bstrName; pItem->cbSize = sizeof(SYNCMGRITEM); pItem->dwFlags = SYNCMGRITEM_HASPROPERTIES; pItem->ItemID = *pCookie; pItem->dwItemState = dwItemState; DATE dt; if (SUCCEEDED(ReadDATE(pSubItem, c_szPropCompletionTime, &dt))) { FILETIME ft; pItem->dwFlags |= SYNCMGRITEM_LASTUPDATETIME; VariantTimeToFileTime(dt, ft); LocalFileTimeToFileTime(&ft, &pItem->ftLastUpdate); } ReadBSTR(pSubItem, c_szPropName, &bstrName); if (NULL != bstrName) { StrCpyNW(pItem->wszItemName, bstrName, ARRAYSIZE(pItem->wszItemName)); SysFreeString(bstrName); } else { ASSERT(L'\0' == pItem->wszItemName[0]); } pItem->hIcon = LoadItemIcon(pSubItem, FALSE); pSubItem->Release(); } return hr; } HRESULT COfflineEnum::Init(ISubscriptionMgr2 *pSubsMgr2, ULONG nItems, SUBSCRIPTIONCOOKIE *pInitCookies, ISyncMgrEnumItems **ppenum, DWORD dwSyncFlags) { HRESULT hr = E_FAIL; DWORD dwCheckState; ASSERT(NULL != ppenum); if ((NULL == pSubsMgr2) || (NULL == ppenum) || ((nItems > 0) && (pInitCookies == NULL))) { return E_UNEXPECTED; } switch (dwSyncFlags & SYNCMGRFLAG_EVENTMASK) { case SYNCMGRFLAG_CONNECT: // Sync was invoked by a network connect case SYNCMGRFLAG_PENDINGDISCONNECT: // Sync was invoked by a pending network disconnect case SYNCMGRFLAG_MANUAL: // Sync was invoked manually case SYNCMGRFLAG_IDLE: // Sync was programmatically invokd case SYNCMGRFLAG_INVOKE: // Sync was programmatically invokd case SYNCMGRFLAG_SCHEDULED: // Sync was invoked by a scheduled update dwCheckState = SYNCMGRITEMSTATE_CHECKED; break; default: dwCheckState = SYNCMGRITEMSTATE_UNCHECKED; break; } // Enumerate cookies m_iEnumPtr = 0; if (0 == nItems) { IEnumSubscription *pEnumSubscriptions; hr = pSubsMgr2->EnumSubscriptions(0, &pEnumSubscriptions); if (SUCCEEDED(hr)) { ASSERT(NULL != pEnumSubscriptions); pEnumSubscriptions->GetCount(&m_iNumItems); SUBSCRIPTIONCOOKIE *pCookies = (SUBSCRIPTIONCOOKIE *)MemAlloc(LMEM_FIXED, m_iNumItems * sizeof(SUBSCRIPTIONCOOKIE)); m_pItems = (SYNCMGRITEM *)MemAlloc(LMEM_FIXED, m_iNumItems * sizeof(SYNCMGRITEM)); if ((NULL != m_pItems) && (NULL != pCookies)) { hr = pEnumSubscriptions->Next(m_iNumItems, pCookies, &m_iNumItems); SYNCMGRITEM *pItem = m_pItems; for (ULONG i = 0; i < m_iNumItems; i++, pItem++) { hr = LoadItem(pSubsMgr2, &pCookies[i], pItem, dwCheckState); if (FAILED(hr)) { break; } } } else { hr = E_OUTOFMEMORY; } SAFELOCALFREE(pCookies); pEnumSubscriptions->Release(); } } else { m_pItems = (SYNCMGRITEM *)MemAlloc(LMEM_FIXED, nItems * sizeof(SYNCMGRITEM)); if (NULL != m_pItems) { SYNCMGRITEM *pItem = m_pItems; SUBSCRIPTIONCOOKIE *pCurCookie = pInitCookies; m_iNumItems = nItems; for (ULONG i = 0; i < m_iNumItems; i++, pCurCookie++, pItem++) { hr = LoadItem(pSubsMgr2, pCurCookie, pItem, dwCheckState); if (FAILED(hr)) { break; } } } else { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { // If we were invoked programatically, then tell syncmgr to leave // item preferences alone. hr = (nItems == 0) ? S_OK : S_OK; // TODO: S_SYNCMGR_MISSINGITEMS; *ppenum = this; AddRef(); } return hr; } // // IUnknown members // STDMETHODIMP_(ULONG) COfflineEnum::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) COfflineEnum::Release(void) { if( 0L != --m_cRef ) return m_cRef; delete this; return 0L; } STDMETHODIMP COfflineEnum::QueryInterface(REFIID riid, void ** ppv) { *ppv=NULL; // Validate requested interface if ((IID_IUnknown == riid) || (IID_ISyncMgrEnumItems == riid)) { *ppv = (ISyncMgrEnumItems *)this; } else { return E_NOINTERFACE; } // Addref through the interface ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } // IEnumOfflineItems members STDMETHODIMP COfflineEnum::Next(ULONG celt, LPSYNCMGRITEM rgelt, ULONG *pceltFetched) { if ((0 == celt) || ((celt > 1) && (NULL == pceltFetched)) || (NULL == rgelt)) { return E_INVALIDARG; } if (!m_pItems) { return E_FAIL; } *pceltFetched = 0; ULONG ul; for (ul=0; (ul m_iNumItems) { m_iEnumPtr = m_iNumItems; return S_FALSE; } return S_OK; } STDMETHODIMP COfflineEnum::Reset(void) { m_iEnumPtr = 0; return S_OK; } STDMETHODIMP COfflineEnum::Clone(ISyncMgrEnumItems **ppenum) { return E_NOTIMPL; }