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.
 
 
 
 
 
 

657 lines
19 KiB

/*
PLEASE NOTE!
OneStop and MultiUser do not get along well. This code does some hacks to get stuff to work, and
mobsync should not be invoked from the shell while OE is running.
Some assumptions:
There will never be a user 0
*/
/*
File: SyncHndl.cpp
Implementation of OneStop Sync Handler
*/
#include "pch.hxx"
#include "resource.h"
#include "synchndl.h"
#include "syncenum.h"
#include "syncprop.h"
#include "spoolapi.h"
#include "imnact.h"
#include "multiusr.h"
#include "instance.h"
HRESULT CreateInstance_OneStopHandler(IUnknown *pUnkOuter, IUnknown **ppUnknown)
{
HRESULT hr = S_OK;
TraceCall("CreateInstance_OneStopHandler");
// We don't support aggregation and our factory knows it
Assert(NULL == pUnkOuter);
// Shouldn't be getting bad args from the factory either
Assert(NULL != ppUnknown);
*ppUnknown = new COneStopHandler;
if (NULL == *ppUnknown)
hr = E_OUTOFMEMORY;
return hr;
}
COneStopHandler::COneStopHandler():
m_cRef(1), m_pOfflineHandlerItems(NULL), m_pOfflineSynchronizeCallback(NULL),
m_dwSyncFlags(0), m_fInOE(FALSE), m_dwUserID(0)
{
Assert(g_pInstance);
if (SUCCEEDED(CoIncrementInit("COneStopHandler::COneStopHandler", MSOEAPI_START_COMOBJECT, NULL, NULL)))
m_fInit = 1;
else
m_fInit = 0;
}
COneStopHandler::~COneStopHandler()
{
Assert(g_pInstance);
if (m_pOfflineHandlerItems)
OHIL_Release(m_pOfflineHandlerItems);
if(m_fInit)
g_pInstance->CoDecrementInit("COneStopHandler::COneStopHandler", NULL);
}
STDMETHODIMP COneStopHandler::QueryInterface(REFIID riid, LPVOID FAR *ppvObj)
{
TraceCall("COneStopHandler::QueryInterface");
if(!ppvObj)
return E_INVALIDARG;
*ppvObj = NULL;
if (IsEqualIID(riid, IID_IUnknown))
*ppvObj = SAFECAST(this, IUnknown *);
else if (IsEqualIID(riid, IID_ISyncMgrSynchronize))
*ppvObj = SAFECAST(this, ISyncMgrSynchronize *);
else
return E_NOINTERFACE;
InterlockedIncrement(&m_cRef);
return NOERROR;
}
STDMETHODIMP_(ULONG) COneStopHandler::AddRef()
{
TraceCall("COneStopHandler::AddRef");
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) COneStopHandler::Release()
{
TraceCall("COneStopHandler::Release");
LONG cRef = InterlockedDecrement(&m_cRef);
if (cRef > 0)
return (ULONG)cRef;
delete this;
return 0;
}
BOOL CreateOneStopItems(IImnEnumAccounts *pEnum, LPSYNCMGRHANDLERITEMS pOfflineHandlerItems, DWORD dwUserID, HICON *hicn)
{
BOOL bAnything = FALSE;
IImnAccount *pAccount = NULL;
LPWSTR pwsz = NULL;
SYNCMGRHANDLERITEM *pItem = NULL;
CHAR szAcctID[CCHMAX_ACCOUNT_NAME];
CHAR szAcctName[CCHMAX_ACCOUNT_NAME];
WCHAR wszItemName[MAX_SYNCMGRITEMNAME];
DWORD dwAvail;
int cDiff;
ACCTTYPE accttype;
ULONG cb;
HRESULT hr;
// Iterate through the accounts
pEnum->SortByAccountName();
while(SUCCEEDED(pEnum->GetNext(&pAccount)) &&
SUCCEEDED(pAccount->GetPropSz(AP_ACCOUNT_ID, szAcctID, ARRAYSIZE(szAcctID))) &&
SUCCEEDED(pAccount->GetPropSz(AP_ACCOUNT_NAME, szAcctName, ARRAYSIZE(szAcctName))) )
{
if (!(pwsz = PszToUnicode(CP_ACP, szAcctName)))
break;
// Safe to allocate this item, we have enough info to make the node
if (pItem = OHIL_AddItem(pOfflineHandlerItems))
{
StrCpyNA(pItem->szAcctName, szAcctName, ARRAYSIZE(pItem->szAcctName));
StrCpyNW(pItem->offlineItem.wszItemName, pwsz, ARRAYSIZE(pItem->offlineItem.wszItemName));
StrCpyNA(pItem->szAcctID, szAcctID, ARRAYSIZE(pItem->szAcctID));
// Handle the Account GUID
cb = sizeof(SYNCMGRITEMID);
if (FAILED(pAccount->GetProp(AP_UNIQUE_ID, (LPBYTE)&(pItem->offlineItem.ItemID), &cb)))
{
if (FAILED(CoCreateGuid(&(pItem->offlineItem.ItemID))) ||
FAILED(pAccount->SetProp(AP_UNIQUE_ID, (LPBYTE)(&(pItem->offlineItem.ItemID)), sizeof(SYNCMGRITEMID))) ||
FAILED(pAccount->SaveChanges()))
ZeroMemory(&(pItem->offlineItem.ItemID), sizeof(SYNCMGRITEMID));
}
// Need to do something with this...
pItem->offlineItem.wszStatus[0] = 0;
if (SUCCEEDED(pAccount->GetAccountType(&accttype)))
{
if (ACCT_MAIL == accttype)
pItem->offlineItem.hIcon = hicn[1];
else
pItem->offlineItem.hIcon = hicn[2];
pItem->accttype = accttype;
}
else
{
pItem->offlineItem.hIcon = hicn[0];
pItem->accttype = ACCT_LAST;
}
// Default to syncing the server, no folders synced by default
if (SUCCEEDED(pAccount->GetPropDw(AP_AVAIL_OFFLINE, &dwAvail)))
pItem->offlineItem.dwItemState = dwAvail ? SYNCMGRITEMSTATE_CHECKED : 0;
else
// Default to checked
pItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED;
// Default to not roaming for now...
pItem->offlineItem.dwFlags = SYNCMGRITEM_HASPROPERTIES;
pItem->dwUserID = dwUserID;
pItem->offlineItem.cbSize = sizeof(SYNCMGRITEM);
bAnything = TRUE;
}
MemFree(pwsz);
pAccount->Release();
}
return bAnything;
}
STDMETHODIMP COneStopHandler::Initialize(DWORD dwReserved, DWORD dwSyncFlags,
DWORD cbCookie, BYTE const*lpCookie)
{
HRESULT hr = S_FALSE;
IImnEnumAccounts *pEnum = NULL;
HKEY hkey = NULL;
HICON hicn[3] = {NULL, NULL, NULL};
DWORD dwIndex = 0;
DWORD dwItemID = 0;
ULONG ulCount = 0;
ULONG ulTemp = 0;
BOOL bAnything = FALSE;
BOOL fMultiUser;
TCHAR szSubKey[80];
TCHAR szFullKey[MAX_PATH], szFullKey2[MAX_PATH];
FILETIME dummy;
DWORD cb;
DWORD dwUserID;
Assert(g_hLocRes);
Assert(g_pAcctMan);
if (!m_fInit)
return E_FAIL;
// Allocate memory for the list
if (!(m_pOfflineHandlerItems = OHIL_Create()))
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Preload the icons for mail and news
hicn[0] = LoadIcon(g_hLocRes, MAKEINTRESOURCE(idiMailNews));
hicn[1] = LoadIcon(g_hLocRes, MAKEINTRESOURCE(idiMail));
hicn[2] = LoadIcon(g_hLocRes, MAKEINTRESOURCE(idiNews));
// Save the flags away - they are good for the life of this sync
m_dwSyncFlags = dwSyncFlags;
// Were we invoked by OE with the UserID of the current user?
if (m_fInOE = (lpCookie && (sizeof(DWORD) == cbCookie)))
{
// We only care about the current user
if (SUCCEEDED(g_pAcctMan->InitUser(NULL, NULL, 0)))
{
if (SUCCEEDED(g_pAcctMan->Enumerate(SRV_MAIL | SRV_NNTP, &pEnum)))
{
GetCurrentUserID(&m_dwUserID);
CreateOneStopItems(pEnum, m_pOfflineHandlerItems, m_dwUserID, hicn);
pEnum->Release();
}
}
// Always want to handle if OE called us
return S_OK;
}
// Need to enumerate all users in the current profile
// Flush any changes
SaveCurrentUserSettings();
// Are there even any OE users in this profile to worry about?
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_PROFILE_ROOT, c_szRegLM, NULL, KEY_ENUMERATE_SUB_KEYS, &hkey))
goto exit;
hr = E_UNEXPECTED;
cb = ARRAYSIZE(szSubKey);
while (ERROR_SUCCESS == RegEnumKeyEx(hkey, dwIndex++, szSubKey, &cb, 0, NULL, NULL, &dummy))
{
cb = ARRAYSIZE(szSubKey);
// Tell Acct Manager where to look
wnsprintf(szFullKey, ARRAYSIZE(szFullKey), c_szPathFileFmt, c_szRegLM, szSubKey);
wnsprintf(szFullKey2, ARRAYSIZE(szFullKey2), c_szPathFileFmt, szFullKey, c_szIAM);
if (FAILED(g_pAcctMan->InitUser(NULL, szFullKey2, 0)))
continue;
// Does this user have any relevant accounts?
if (FAILED(g_pAcctMan->GetAccountCount(ACCT_NEWS, &ulTemp)))
continue;
else
{
ulCount = ulTemp;
if (FAILED(g_pAcctMan->GetAccountCount(ACCT_MAIL, &ulTemp)))
continue;
ulCount += ulTemp;
if (0 == ulCount)
{
continue;
}
}
if (FAILED(g_pAcctMan->Enumerate(SRV_MAIL | SRV_NNTP, &pEnum)))
continue;
GetCurrentUserID(&dwUserID);
bAnything = CreateOneStopItems(pEnum, m_pOfflineHandlerItems, dwUserID, hicn) || bAnything;
pEnum->Release();
pEnum = NULL;
}
RegCloseKey(hkey);
hkey = NULL;
// If there is nothing to enumerate, don't worry about this sync event
if (!bAnything)
{
hr = S_FALSE;
goto exit;
}
return S_OK;
exit:
if (hkey)
RegCloseKey(hkey);
if (m_pOfflineHandlerItems)
OHIL_Release(m_pOfflineHandlerItems);
SafeRelease(pEnum);
return hr;
}
STDMETHODIMP COneStopHandler::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo)
{
SYNCMGRHANDLERINFO SMHI, *pSMHI;
TCHAR szName[MAX_SYNCMGRHANDLERNAME];
LPWSTR pwsz;
if (!ppSyncMgrHandlerInfo)
return E_INVALIDARG;
*ppSyncMgrHandlerInfo = NULL;
if (LoadIcon(g_hLocRes, MAKEINTRESOURCE(idiMailNews)) &&
LoadString(g_hLocRes, idsAthena, szName, MAX_SYNCMGRHANDLERNAME))
{
if (MemAlloc((LPVOID *)&pSMHI, sizeof(SYNCMGRHANDLERINFO)))
{
#ifdef UNICODE
StrCpyN(pSMHI->wszHandlerName, szName, ARRAYSIZE(pSMHI->wszHandlerName));
#else
if (pwsz = PszToUnicode(CP_ACP, szName))
{
StrCpyNW(pSMHI->wszHandlerName, pwsz, MAX_SYNCMGRHANDLERNAME);
MemFree(pwsz);
}
else
{
MemFree(ppSyncMgrHandlerInfo);
return E_OUTOFMEMORY;
}
#endif
pSMHI->cbSize = sizeof(SYNCMGRHANDLERINFO);
*ppSyncMgrHandlerInfo = pSMHI;
return S_OK;
}
else
return E_OUTOFMEMORY;
}
else
return E_UNEXPECTED;
}
STDMETHODIMP COneStopHandler::EnumSyncMgrItems(ISyncMgrEnumItems** ppenumOffineItems)
{
if (m_pOfflineHandlerItems)
{
*ppenumOffineItems = new CEnumOfflineItems(m_pOfflineHandlerItems, 0);
}
else
{
*ppenumOffineItems = NULL;
}
return *ppenumOffineItems ? NOERROR: E_OUTOFMEMORY;
}
STDMETHODIMP COneStopHandler::GetItemObject(REFSYNCMGRITEMID ItemID, REFIID riid, void** ppv)
{
// Not implemented in OneStop v1 Spec
return E_NOTIMPL;
}
STDMETHODIMP COneStopHandler::ShowProperties(HWND hwnd, REFSYNCMGRITEMID ItemID)
{
DWORD dwLastUser=0;
SYNCMGRHANDLERITEM *pItem;
BOOL fOkToEdit = TRUE;
// We didn't provide any items, how can OneStop ask us about them?
if (!m_pOfflineHandlerItems)
return E_UNEXPECTED;
pItem = m_pOfflineHandlerItems->pFirstOfflineItem;
// This is slow, but shouldn't be many accounts...
while (pItem)
{
if (IsEqualGUID(ItemID, pItem->offlineItem.ItemID))
break;
else
pItem = pItem->pNextOfflineItem;
}
if (pItem)
{
if (dwLastUser != pItem->dwUserID)
{
if (fOkToEdit = SUCCEEDED(SwitchContext(pItem->dwUserID)))
{
dwLastUser = pItem->dwUserID;
}
}
if (fOkToEdit)
ShowPropSheet(hwnd, pItem->szAcctID, pItem->szAcctName, pItem->accttype);
}
else
// Gave us an ItemID we don't know about!
return E_INVALIDARG;
return S_OK;
}
STDMETHODIMP COneStopHandler::SetProgressCallback(ISyncMgrSynchronizeCallback *lpCallBack)
{
LPSYNCMGRSYNCHRONIZECALLBACK pCallbackCurrent = m_pOfflineSynchronizeCallback;
m_pOfflineSynchronizeCallback = lpCallBack;
if (m_pOfflineSynchronizeCallback)
m_pOfflineSynchronizeCallback->AddRef();
if (pCallbackCurrent)
pCallbackCurrent->Release();
return NOERROR;
}
STDMETHODIMP COneStopHandler::PrepareForSync(ULONG cbNumItems, SYNCMGRITEMID* pItemIDs,
HWND hwndParent, DWORD dwReserved)
{
HRESULT hr;
SYNCMGRHANDLERITEM *pItem, *pPrev, *pTemp;
IImnAccount *pAccount;
DWORD dwLastUser;
Assert(g_pAcctMan);
if (cbNumItems > m_pOfflineHandlerItems->dwNumOfflineItems)
{
hr = E_INVALIDARG;
goto exit;
}
if (!m_pOfflineHandlerItems)
{
hr = E_UNEXPECTED;
goto exit;
}
if (!m_pOfflineSynchronizeCallback)
{
hr = E_FAIL;
goto exit;
}
#if 0
if (FAILED(hr = g_pSpooler->Init(NULL, FALSE)))
{
if (FACILITY_ITF == HRESULT_FACILITY(hr))
hr = E_FAIL;
goto exit;
}
#endif
if (m_fInOE)
dwLastUser = m_dwUserID;
else
dwLastUser = 0;
pItem = m_pOfflineHandlerItems->pFirstOfflineItem;
pPrev = NULL;
// Go through all the servers that we know about
while (pItem)
{
ULONG i=0;
BOOL fOKToWrite = TRUE;
// Is current server one that the user asked to sync?
while (i < cbNumItems)
{
if (IsEqualGUID(pItemIDs[i], pItem->offlineItem.ItemID))
break;
else
i++;
}
// No match?
if (cbNumItems == i)
pItem->offlineItem.dwItemState = 0;
else
pItem->offlineItem.dwItemState = 1;
// Make sure the account manager is looking at the right user
if (pItem->dwUserID != dwLastUser)
{
if (fOKToWrite = SUCCEEDED(InitUser(pItem->dwUserID)))
dwLastUser = pItem->dwUserID;
}
// Only save changes if we know the registry is in sync with the account manager
if (fOKToWrite)
{
if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pItem->szAcctID, &pAccount)))
{
if (SUCCEEDED(pAccount->SetPropDw(AP_AVAIL_OFFLINE, pItem->offlineItem.dwItemState)))
pAccount->SaveChanges();
pAccount->Release();
}
}
// Can we delete this item from the list?
if (0 == pItem->offlineItem.dwItemState)
{
if (pPrev)
pPrev->pNextOfflineItem = pItem->pNextOfflineItem;
else
m_pOfflineHandlerItems->pFirstOfflineItem = pItem->pNextOfflineItem;
m_pOfflineHandlerItems->dwNumOfflineItems--;
// Move on to next item
pTemp = pItem;
pItem = pItem->pNextOfflineItem;
MemFree(pTemp);
}
else
{
// Move on to next item
pPrev = pItem;
pItem = pItem->pNextOfflineItem;
}
}
Assert(m_pOfflineHandlerItems->dwNumOfflineItems == cbNumItems);
hr = S_OK;
exit:
m_pOfflineSynchronizeCallback->PrepareForSyncCompleted(hr);
return hr;
}
STDMETHODIMP COneStopHandler::Synchronize(HWND hwndParent)
{
HRESULT hr;
SYNCMGRHANDLERITEM *pItem;
DWORD dwLastUser;
Assert(g_pSpooler);
if (!m_pOfflineSynchronizeCallback)
{
hr = E_FAIL;
goto exit;
}
if (!m_pOfflineHandlerItems)
{
hr = E_UNEXPECTED;
goto exit;
}
if (m_fInOE)
dwLastUser = m_dwUserID;
else
dwLastUser = 0;
pItem = m_pOfflineHandlerItems->pFirstOfflineItem;
while (pItem)
{
BOOL fOkToSync = TRUE;
if (dwLastUser != pItem->dwUserID)
{
if (fOkToSync = SUCCEEDED(SwitchContext(pItem->dwUserID)))
dwLastUser = pItem->dwUserID;
}
if (fOkToSync)
g_pSpooler->StartDelivery(hwndParent, pItem->szAcctID, FOLDERID_INVALID,
DELIVER_UPDATE_ALL | DELIVER_NODIAL);
pItem = pItem->pNextOfflineItem;
}
hr = S_OK;
exit:
m_pOfflineSynchronizeCallback->SynchronizeCompleted(hr);
return hr;
}
STDMETHODIMP COneStopHandler::SetItemStatus(REFSYNCMGRITEMID ItemID, DWORD dwSyncMgrStatus)
{
return E_NOTIMPL;
}
STDMETHODIMP COneStopHandler::ShowError(HWND hWndParent, REFSYNCMGRERRORID ErrorID,
ULONG *pcbNumItems, SYNCMGRITEMID **ppItemIDs)
{
// Can show any synchronization conflicts. Also gives a chance
// to display any errors that occured during synchronization
return E_NOTIMPL;
}
HRESULT SwitchContext(DWORD dwUserID)
{
HRESULT hr = S_OK;
char szUsername[CCH_USERNAME_MAX_LENGTH];
Assert(g_pAcctMan);
if (UserIdToUsername(dwUserID, szUsername, ARRAYSIZE(szUsername)) &&
SwitchToUser(szUsername, FALSE) )
{
// Reinitialize AcctMan
if (FAILED(hr = g_pAcctMan->InitUser(NULL, NULL, 0)) &&
FACILITY_ITF == HRESULT_FACILITY(hr) )
hr = E_FAIL;
}
else
hr = E_FAIL;
return hr;
}
HRESULT InitUser(DWORD dwUserID)
{
HRESULT hr = S_OK;
TCHAR szFullKey[MAX_PATH], szFullKey2[MAX_PATH];
TCHAR szSubKey[80];
Assert(g_pAcctMan);
wnsprintf(szSubKey, ARRAYSIZE(szSubKey), "%08lx", dwUserID);
// Figure out the full path to the Account Info for the current user
wnsprintf(szFullKey, ARRAYSIZE(szFullKey), c_szPathFileFmt, c_szRegLM, szSubKey);
wnsprintf(szFullKey2, ARRAYSIZE(szFullKey2), c_szPathFileFmt, szFullKey, c_szIAM);
// Point account manager to an OE multiuser
// Safe even if acct manager was already inited before - will reload accounts
if (FAILED(hr = (g_pAcctMan->InitUser(NULL, szFullKey, 0))) &&
(FACILITY_ITF == HRESULT_FACILITY(hr)))
hr = E_FAIL;
return hr;
}