windows-nt/Source/XPSP1/NT/enduser/windows.com/wuau/wuaueng/wuaueng.cpp
2020-09-26 16:20:57 +08:00

1072 lines
32 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2000
//
// File: wuaueng.cpp
//
//--------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
//Handle for Download Events
ENGINE_EVENTS EngineEvents;
AUCatalog *gpAUcatalog;
inline BOOL FServiceDisabled(void)
{
return (AUSTATE_DISABLED == gpState->GetState());
}
//cancel download if any
void CancelDownload(void)
{
if (NULL != gpAUcatalog && gpAUcatalog->m_audownloader.getID() != GUID_NULL)
{
gpAUcatalog->m_audownloader.DrizzleOperation(DRIZZLEOPS_CANCEL);
}
gpState->SetDisconnected(FALSE); //In case the disabled happened during TRANSIENT_ERROR we don't want to keep the flag set to disconnected
}
DWORD RandomWaitTimeBeforeDetect()
{
return (ULONGLONG)AU_TWENTY_TWO_HOURS - ((ULONGLONG) AU_TWENTY_TWO_HOURS * rand() * AU_RANDOMIZATION_WINDOW ) /( (ULONGLONG) RAND_MAX * 100); //precision to hundredth
}
//void setAuStateDisconnected(BOOL fDisconnected);
BOOL FDisabledDuringDownload(void)
{
BOOL fRet = FALSE;
if (FServiceDisabled())
{
DEBUGMSG("WUAUENG Detected Disabled State during download");
CancelDownload();
fRet = TRUE;
}
return fRet;
}
#define ISERVICE_FINISHED 0
#define ISERVICE_DISABLED 1
#define WAIT_SERVICE_FINISHED 0
#define WAIT_SERVICE_DISABLED 1
#define WAIT_NOT_NEEDED 2
#define WAIT_CONNECTION_FOUND 3
#define WAIT_DONE 4
//fixcode: don't always need to persist in registry
DWORD MWFMO(DWORD dwTimeout, DWORD dwMinTimeout = 1)
{
HANDLE hEvents[2];
hEvents[ISERVICE_FINISHED] = ghServiceFinished;
hEvents[ISERVICE_DISABLED] = ghServiceDisabled;
if (FAILED(setLastWaitTimeout(dwTimeout)))
{
DEBUGMSG("WUAUENG setLastWaitTimeout failed with error %d", GetLastError());
}
return MsgWaitForMultipleObjectsEx(2, hEvents, dwTimeToWait(dwTimeout, dwMinTimeout), QS_POSTMESSAGE, MWMO_INPUTAVAILABLE );
}
DWORD _MyMWFMO(DWORD dwTimeout, DWORD dwMinTimeout = 1);
//dwTimeout in seconds
#define MyMWFMO(dwTimeout) _MyMWFMO(dwTimeout)
// wait until a timeout happens or we get a service finished event
DWORD _MyMWFMO(DWORD dwTimeout, DWORD dwMinTimeout)
{
DWORD dwRet = WAIT_TIMEOUT;
while (1)
{
dwRet = MWFMO(dwTimeout, dwMinTimeout);
if (WAIT_TIMEOUT == dwRet)
{
DEBUGMSG("WUAUENG MWFMO timed out");
// dwRet = WAIT_TIMEOUT;
goto Done;
}
else if (WAIT_OBJECT_0 + ISERVICE_FINISHED == dwRet)
{
DEBUGMSG("WUAUENG MWFMO Service Finished");
dwRet = WAIT_SERVICE_FINISHED;
goto Done;
}
else if (WAIT_OBJECT_0 + ISERVICE_DISABLED == dwRet)
{
DEBUGMSG("WUAUENG MWFMO Engine Changed to Disabled\n");
dwRet = WAIT_SERVICE_DISABLED;
goto Done;
}
else
{
//we expect possible meaningful message here
//leave it untouch in the queue
MSG msg;
if (0 == PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DEBUGMSG("MyMWFMO got no message. Possible error!");
continue; //no message got
}
if ( ((AUMSG_ENG_START <= msg.message) && (AUMSG_ENG_END >= msg.message))
|| (WM_QUIT == msg.message))
{
DEBUGMSG("MyMWFMO got user msg %#lx, repost it to the queue", msg.message);
PostThreadMessage(gdwWorkerThreadId, msg.message, msg.wParam, msg.lParam);
dwRet = msg.message;
goto Done;
}
else
{
DEBUGMSG("WUAUENG MWFMO got system message %#lx", msg.message);
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
}
Done:
if (WAIT_SERVICE_FINISHED != dwRet)
{
removeLastWaitKey();
}
return dwRet;
}
//** WaitForConnection() will wait for connection or
//** will bail out if Service Finished or service disabled or getting a msg if fIgnoreNonDetectMsg is FALSE
DWORD WaitForConnection(BOOL fIngoreNonDetectMsg = FALSE)
{
static BOOL s_fWasConnected = FALSE;
DWORD dwRet;
DEBUGMSG("WUAUENG : Polling for connection");
do
{
do
{
if (AUSTATE_DETECT_PENDING == gpState->GetState() &&
gpState->IsUnableToConnect())
{
CAUEventLog aueventlog(g_hInstance);
aueventlog.LogEvent(
EVENTLOG_WARNING_TYPE,
IDS_MSG_Download,
IDS_MSG_UnableToConnect);
gpState->SetDetectionStartTime(TRUE);
}
if (IsConnected(gpState->GetIdentServerURL(), !gpState->fInCorpWU()))
{
break;
}
dwRet = MyMWFMO(AU_TEN_MINS);
if (WAIT_TIMEOUT != dwRet)
{
if (!fIngoreNonDetectMsg ||!IsValidAUMsg(dwRet) || AUMSG_DETECT == dwRet )
{
goto Done;
}
}
s_fWasConnected = FALSE;
}
while (1);
if (!s_fWasConnected)
{
DEBUGMSG("Found Connection");
if (AUSTATE_DOWNLOAD_PENDING != gpState->GetState())
{
BOOL fWait = TRUE;
#ifdef DBG
//wait 5 mins by Default if 'ConnectWait' regvalue does not exist
// or is set to 1
DWORD dwConnectWait;
if (SUCCEEDED(GetRegDWordValue(REG_AUCONNECTWAIT, &dwConnectWait))
&& 0 == dwConnectWait)
{
fWait = FALSE;
}
#endif
if (fWait)
{
DEBUGMSG("Wait for 5 mins ");
dwRet = MyMWFMO(AU_FIVE_MINS);
if (WAIT_TIMEOUT != dwRet)
{
if (!fIngoreNonDetectMsg || !IsValidAUMsg(dwRet) || AUMSG_DETECT == dwRet)
{
goto Done;
}
}
}
if (!IsConnected(gpState->GetIdentServerURL(), !gpState->fInCorpWU()))
{
continue;
}
}
s_fWasConnected = TRUE;
}
break;
}
while (1);
DEBUGMSG("WUAUENG : Connection found. Polling end");
dwRet = WAIT_CONNECTION_FOUND;
Done:
return dwRet;
}
HRESULT PauseDownload(BOOL fPause)
{
HRESULT hrRet = E_FAIL;
if (NULL != gpAUcatalog)
{
hrRet = gpAUcatalog->m_audownloader.DrizzleOperation(fPause? DRIZZLEOPS_PAUSE: DRIZZLEOPS_RESUME);
}
DEBUGMSG("PauseDownload return %#lx", hrRet);
return hrRet;
}
HRESULT GetEvtHandles(AUEVTHANDLES *pAuEvtHandles)
{
HRESULT hr = E_FAIL;
HANDLE hSourceProcess = NULL;
//#define IEVT_ENGINESTATE 0
#define IEVT_NOTIFYCLIENT 0
struct
{
HANDLE hSource;
HANDLE hTarget;
} rhandles [] = {/*{ghEngineState,0},*/{ghNotifyClient,0}};
if (NULL == ghClientHandles.hClientProcess())
{
goto Done;
}
hSourceProcess = GetCurrentProcess();
for ( int i = 0; i < (ARRAYSIZE(rhandles)); i++)
{
if (!DuplicateHandle(
hSourceProcess, // handle to source process
rhandles[i].hSource, // handle to duplicate
ghClientHandles.hClientProcess(), // handle to target process
&rhandles[i].hTarget, // duplicate handle
0, // requested access
FALSE, // handle inheritance option
DUPLICATE_SAME_ACCESS // optional actions
))
{
DEBUGMSG("WUAUENG DuplicateHandle for rhandles[%d] failed with %#lx", i, GetLastError());
//should not close target handle cuz it is in target process
goto Done;
}
}
#ifdef _WIN64
pAuEvtHandles->ulNotifyClient = (LONG64) rhandles[IEVT_NOTIFYCLIENT].hTarget;
#else
pAuEvtHandles->ulNotifyClient = (LONG) rhandles[IEVT_NOTIFYCLIENT].hTarget;
#endif
hr = S_OK;
Done:
return hr;
}
DWORD AvailableSessions(void)
{
DWORD dwRet = 0;
//
// For win2K, because we're not deleting sessions as soon as we receive
// logoff notifications, the array gAdminSesssion might be out-of-date
// in a given point in time. We need to validate the array before
// saying to client things about it.
// Remove any old sessions from our array if there's any
//
if (IsWin2K())
{
DEBUGMSG("WUAUENG Client is querying the number of sessions available; forcing rebuilt of the session cache (win2k)");
gAdminSessions.ValidateCachedSessions();
}
for (int iSession = 0; iSession < gAdminSessions.CSessions(); iSession++)
{
DWORD dwAdminSession;
if (gAdminSessions.m_FGetNextSession(&dwAdminSession) && FSessionActive(dwAdminSession))
{
dwRet ++;
}
}
// DEBUGMSG("AvailableSessions return %d", dwRet);
return dwRet;
}
HRESULT HrCreateNewCatalog()
{
HRESULT hr = E_FAIL;
SafeDeleteNULL(gpAUcatalog);
gpAUcatalog = new AUCatalog;
if (NULL == gpAUcatalog)
{
goto Done;
}
if (FAILED(hr = gpAUcatalog->Init()))
{
SafeDeleteNULL(gpAUcatalog);
}
Done:
return hr;
}
/* This function will return only in case of selfupdate. Normally it will keep on looping
* sleeping on various timeouts when necessary
*/
DWORD CompleteLastMyMWFMO(void)
{
DWORD dwRet = WAIT_NOT_NEEDED;
DWORD dwTimeout;
HRESULT hr;
hr = getLastWaitTimeout(&dwTimeout);
MyMWFMO(0); //call user api to create the thread queue
SetEvent(ghWorkerThreadMsgQueueCreation);
if (FAILED(hr))
{
DEBUGMSG("WUAUENG no need to complete last wait");
goto Done;
}
DEBUGMSG("WUAUENG is going to complete last wait %d ", dwTimeout);
dwRet = MyMWFMO(dwTimeout);
Done:
return dwRet;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// return S_FALSE if selfupdate happens
// return S_OK otherwise
// pdwRet points to return code: service finish, service disabled, selfupdate done, or detect msg
HRESULT PerformSelfUpdate(DWORD *pdwRet)
{
HRESULT hr= S_OK;
DEBUGMSG("Doing selfupdate");
DEBUGMSG("Selfupdate waiting for connection");
DEBUGMSG("Wait for internet connection...");
while ( WAIT_CONNECTION_FOUND == (*pdwRet = WaitForConnection(TRUE))
&& ((hr = SelfUpdate()) != S_OK) )
{
switch (hr)
{
case S_FALSE: //selfupdate happens
DEBUGMSG("Telling wuauserv.dll to reload wuaueng.dll");
gPingStatus.PingSelfUpdate(TRUE, URLLOGSTATUS_Pending, 0);
*pdwRet = WAIT_DONE;
goto Done;
default:
DEBUGMSG("Error during selfupdate (%#lx), timeout=%d secs", hr, dwTimeToWait(AU_ONE_DAY));
gPingStatus.PingSelfUpdate(TRUE, URLLOGSTATUS_Failed, hr);
*pdwRet = MyMWFMO(AU_ONE_DAY);
if (WAIT_TIMEOUT != *pdwRet)
{
DEBUGMSG("WUAUENG need to abort wait during SelfUpdate");
goto Done;
}
break;
}
}
if (WAIT_CONNECTION_FOUND == *pdwRet)
{
*pdwRet = WAIT_DONE;
}
DEBUGMSG("Finished self update cycle");
Done:
return hr;
}
void ResumeDownloadIfNeccesary(void)
{
if (FDownloadIsPaused())
{
PauseDownload(FALSE);
DEBUGMSG("WUAUENG Resuming download job");
}
}
void PingSuccessfulDownloads(void)
{
UINT uItemCount = gpAUcatalog->m_ItemList.Count();
for (UINT i = 0; i < uItemCount; i++)
{
AUCatalogItem &item = gpAUcatalog->m_ItemList[i];
if (item.fSelected())
{
BSTR bstrItemId = item.bstrID();
if (NULL != bstrItemId)
{
USES_IU_CONVERSION;
gPingStatus.PingDownload(
TRUE,
URLLOGSTATUS_Success,
0,
W2T(bstrItemId));
}
#ifdef DBG
else
{
DEBUGMSG("WUAUENG title for item %d is NULL!", i);
}
#endif
}
}
}
void ResetState(void)
{
gpState->SetState(AUSTATE_DETECT_PENDING);
PostThreadMessage(gdwWorkerThreadId, AUMSG_DETECT, 0, 0);
}
void ResetState(BOOL *pfWaitB4Detect, DWORD *pdwWaitB4Detect, BOOL fError)
{
AUASSERT(NULL != pfWaitB4Detect);
AUASSERT(NULL != pdwWaitB4Detect);
*pfWaitB4Detect = TRUE;
*pdwWaitB4Detect = fError ? AU_FIVE_HOURS : RandomWaitTimeBeforeDetect();
ResetState();
}
HRESULT UpdateProc(WORKER_THREAD_INIT_DATA & initData)
{
HRESULT hr = S_OK;
DWORD dwRet;
DWORD dwLastWait ;
BOOL fReloadAfterSelfUpdate = FALSE;
MSG msg;
UINT uFirstMsg;
static BOOL s_fWaitBeforeDetect;
static DWORD s_dwWaitB4Detect;
uFirstMsg = initData.uFirstMsg;
s_fWaitBeforeDetect = initData.fWaitB4Detect;
s_dwWaitB4Detect = initData.dwWaitB4Detect;
dwLastWait = CompleteLastMyMWFMO();
switch (dwLastWait)
{
case WAIT_SERVICE_FINISHED:
goto Done;
case WAIT_TIMEOUT:
case WAIT_NOT_NEEDED:
if (IsValidAUMsg(uFirstMsg))
{
DEBUGMSG("Update post first msg %#x", uFirstMsg);
PostThreadMessage(gdwWorkerThreadId, uFirstMsg, 0, 0);
}
break;
case WAIT_SERVICE_DISABLED:
default: //msg got
break;
}
DWORD dwRet2 ;
while(WAIT_OBJECT_0 + 1 == (dwRet2 = MsgWaitForMultipleObjectsEx(1, &ghServiceFinished, INFINITE, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE )))
{
if (0 == PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DEBUGMSG("WUAUENG no message to retrieve. Possible error!");
continue;
}
if (WM_QUIT == msg.message)
{
break;
}
switch(msg.message)
{
case AUMSG_INIT:
{
//we wait for the first connection so that we don't prompt users who never
//make a connection to the internet. no need to have them config AU if they have no
//intention of using the internet
dwRet = WaitForConnection();
switch (dwRet)
{
case WAIT_SERVICE_FINISHED:
DEBUGMSG("WUAUENG detected that Service finished during WaitForConnection in AUSTATE_OUTOFBOX");
goto Done;
case WAIT_CONNECTION_FOUND:
break;
case WAIT_SERVICE_DISABLED:
default:
continue;
}
dwRet = dwLastWait;
if (WAIT_NOT_NEEDED == dwLastWait)
{
//there was no previous wait for 24 hours
DEBUGMSG("WUAUENG Out of box, waiting 24 hours (%d secs)", dwSecsToWait(AU_ONE_DAY));
dwRet = MyMWFMO(AU_ONE_DAY);
}
switch (dwRet)
{
case WAIT_TIMEOUT:
{
#if 0 //commented out for bug 493789
DWORD dwRet4;
hr = PerformSelfUpdate(&dwRet4);
if (WAIT_SERVICE_FINISHED == dwRet4)
{
setLastWaitTimeout(0); //no wait next time
goto Done;
}
#endif
gpState->SetState(AUSTATE_NOT_CONFIGURED);
DEBUGMSG("WUAUENG waiting for user to configure AU");
#if 0 //commented out for bug 493789
if (S_FALSE == hr)
{
fReloadAfterSelfUpdate = TRUE;
goto Done;
}
#endif
break;
}
case WAIT_SERVICE_FINISHED:
goto Done;
case WAIT_SERVICE_DISABLED:
default:
continue;
}
break;
}
case AUMSG_EULA_ACCEPTED:
DEBUGMSG("WUAUENG Msg:Eula accepted, state -> Detect Pending");
ResetState();
break;
case AUMSG_DETECT:
if (s_fWaitBeforeDetect)
{
s_fWaitBeforeDetect = FALSE;
DEBUGMSG("WUAUENG Wait %d secs before detection", s_dwWaitB4Detect);
DWORD dwRet3 = MyMWFMO(s_dwWaitB4Detect);
s_dwWaitB4Detect = 0;
if (WAIT_SERVICE_DISABLED == dwRet3)
{
break;
}
if (WAIT_SERVICE_FINISHED == dwRet3)
{
goto Done;
}
}
DEBUGMSG("WUAUENG Msg:Detect");
DEBUGMSG("--------------------------------------------------------");
DEBUGMSG("Set new detection start time");
gpState->SetDetectionStartTime(FALSE);
DEBUGMSG("Read in au option");
if (FAILED(hr = gpState->HrInit()))
{
DEBUGMSG("AU state object fail to init with error %#lx", hr);
goto Done;
}
CancelDownload();
if (FAILED(HrCreateNewCatalog()))
{ //fixcode: what is the expected behavior here?
return E_FAIL;
}
hr = PerformSelfUpdate(&dwRet);
if (S_FALSE == hr)
{
fReloadAfterSelfUpdate = TRUE;
goto Done;
}
switch (dwRet)
{
case WAIT_SERVICE_FINISHED:
goto Done;
case WAIT_SERVICE_DISABLED:
gpState->RemoveDetectionStartTime();
continue;
case WAIT_DONE:
break;
default: //detect msg got
DEBUGMSG("Detect msg got while detecting");
continue;
}
hr = gpAUcatalog->DetectItems();
if (FServiceDisabled())
{
gpState->RemoveDetectionStartTime();
break;
}
if (SUCCEEDED(hr))
{
//Restart counting period w/ no connection at the beginning of the next cycle.
gpState->RemoveDetectionStartTime();
}
if (S_OK == hr)
{
DEBUGMSG("WUAUENG Catalog built");
gpState->SetState(AUSTATE_DETECT_COMPLETE);
DEBUGMSG("WUAUENG State->Detect complete");
}
else if (S_FALSE == hr)
{
DEBUGMSG("WUAUENG No items in catalog, sleeping a while before next detection");
ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, FALSE); //wait normal interval time
}
else
{
DEBUGMSG("WUAUENG Couldn't build catalog");
ResetState(&s_fWaitBeforeDetect, &s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error
}
break;
case AUMSG_DOWNLOAD:
{
DEBUGMSG("WUAUENG Msg:Download");
gfDownloadStarted = FALSE;
gpState->SetState(AUSTATE_DOWNLOAD_PENDING);
dwRet =WaitForConnection(TRUE);
switch (dwRet)
{
case WAIT_SERVICE_FINISHED:
DEBUGMSG("WUAUENG detected that Service finished during WaitForConnection in AUMSG_DOWNLOAD");
goto Done;
case WAIT_CONNECTION_FOUND:
break;
case WAIT_SERVICE_DISABLED:
default:
continue;
}
if (S_OK != gpAUcatalog->ValidateItems(TRUE))
{
DEBUGMSG("WUAUENG Catalog validation failed or no items, State->Detect Pending");
ResetState();
break;
}
DEBUGMSG("WUAUENG catalog:validateCatalog finished");
EngineEvents.CreateEvents();
// queue up items for download
if (S_OK != (hr = gpAUcatalog->DownloadItems()))
{
if (S_FALSE == hr)
{
DEBUGMSG("WUAUENG Catalog download items skipped because no items were selected");
}
else
{
DEBUGMSG("WUAUENG Catalog download items failed");
}
EngineEvents.CloseEvents();
ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, FALSE);
break;
}
gfDownloadStarted = TRUE;
ghClientHandles.ClientStateChange(); //notify client again for status change
do
{
dwRet = MsgWaitForMultipleObjectsEx( EngineEvents.cEvents(), EngineEvents.grEventHandles(), INFINITE, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE );
if (WAIT_OBJECT_0 + IDOWNLOAD_COMPLETE_EVT == dwRet) //Download finished
{
if (FDisabledDuringDownload())
{
goto CloseHandle;
}
DEBUGMSG("WUAUENG file download done");
ghClientHandles.ClientRemoveTrayIcon();
//Validate downloaded cabs before continuing
AUASSERT(gpAUcatalog);
BSTR bstrErrorItemId = NULL; //should not be freed
if(FAILED(hr = gpAUcatalog->ValidateDownloadedCabs(&bstrErrorItemId)))
{
USES_IU_CONVERSION;
DEBUGMSG("ValidateDownloadedCabs: Checksum failed, error: %#lx", hr);
//bstrErrorItemId will be NULL if it was some error other than ERROR_CRC
if(NULL != bstrErrorItemId)
{
//Pingback failure with itemId
gPingStatus.PingDownload(
TRUE,
URLLOGSTATUS_Failed,
hr,
W2T(bstrErrorItemId));
}
//Reset to detect pending
ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error
goto CloseHandle;
}
if (!gpState->fOptionSchedInstall())
{
DEBUGMSG("WUAUENG download complete, ready for UNSCHEDULED install");
LogEvent_ItemList(
EVENTLOG_INFORMATION_TYPE,
IDS_MSG_Installation,
IDS_MSG_InstallReady_Unscheduled);
}
PingSuccessfulDownloads();
DEBUGMSG("WUAUENG validating items to prune out items already installed");
if ( gpAUcatalog->ValidateItems(FALSE) != S_OK)
{
DEBUGMSG("WUAUENG Validation failed OR no items left in catalog, State->Detect Pending");
ResetState();
}
else
{
DEBUGMSG(" Items still applicable, State->Download Complete");
gpState->SetState(AUSTATE_DOWNLOAD_COMPLETE);
}
break;
}
else if (((WAIT_OBJECT_0 + IDOWNLOAD_TRANSIENT_ERROR_EVT) == dwRet) || //Transient error - connection lost
((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet)) //Download in progress - connection recuperated
{
BOOL fCheckDisconnect;
fCheckDisconnect = ((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet);
//AuStateAux = GetState();
// setState again (and trigger Engine Change State) only if:
// - It is not disconnected and you got Transient Error Event
// - It is disconnected and you got a Download in Progress Event
if ( fCheckDisconnect == gpState->fDisconnected() )
{
gpState->SetDisconnected(!fCheckDisconnect);
gpState->SetState(AUSTATE_DOWNLOAD_PENDING); //relaunch client if it is not launched and notify client of new state
}
}
else if ((WAIT_OBJECT_0 + IDOWNLOAD_SERVICE_FINISH) == dwRet) //Service Finished
{
DEBUGMSG("WUAUENG Detected Service Finished while wating for download to be done");
goto Done;
}
else if ((WAIT_OBJECT_0 + IDOWNLOAD_SERVICE_DISABLED) == dwRet) //Engine State, should be due to disabled
{
if (FDisabledDuringDownload())
{
goto CloseHandle;
}
}
else if ((WAIT_OBJECT_0 + IDOWNLOAD_DOWNLOAD_CANCELED) == dwRet)
{
if (JOB_ERROR == gpAUcatalog->m_audownloader.m_FinishReason)
{
DEBUGMSG("WUAUENG got error during download, wait for sometime b4 redetect");
ResetState(&s_fWaitBeforeDetect,&s_dwWaitB4Detect, TRUE); //wait shorter time cuz of error
}
else
{
DEBUGMSG("WUAUENG download job got canceled, State -> Detect Pending");
ResetState();
}
goto CloseHandle;
}
else if ((WAIT_OBJECT_0 + IDOWNLOAD_MESSAGE) == dwRet) //Messages
{
MSG msg2;
PeekMessage(&msg2, NULL, NULL, NULL, PM_REMOVE); //we don't expect meaningful message here
TranslateMessage(&msg2);// Translates virtual key codes
DispatchMessage(&msg2); // Dispatches message to window
if ( msg2.message != WM_USER ) //WM_USER is the one to pump to drizzle for processing
{
DEBUGMSG("WUAUENG dispatched message %#lx during downloading", msg2.message);
}
}
else if (((WAIT_ABANDONED_0 + IDOWNLOAD_COMPLETE_EVT) == dwRet) ||
((WAIT_ABANDONED_0 + IDOWNLOAD_TRANSIENT_ERROR_EVT) == dwRet)||
((WAIT_ABANDONED_0 + IDOWNLOAD_DOWNLOAD_IN_PROGRESS) == dwRet)||
((WAIT_ABANDONED_0 + IDOWNLOAD_SERVICE_FINISH) == dwRet)||
(WAIT_FAILED == dwRet))
{ //fixcode: when will this actually happen?
DEBUGMSG("WUAUENG Error in Download Loop with MsgWaitForMultipleObjectsEx");
ResetState();
goto CloseHandle;
}
else
{
DEBUGMSG("WUAUENG Unexpected returned value dwRet = %d in n Download Loop with MsgWaitForMultipleObjectsEx", dwRet);
}
}
while ( 1 );
CloseHandle:
EngineEvents.CloseEvents();
}
break;
case AUMSG_POST_INSTALL:
{
DEBUGMSG("WUAUENG install done, sleeping a while before next detection");
// fixcode: should use ResetState() instead
s_fWaitBeforeDetect = TRUE;
s_dwWaitB4Detect = RandomWaitTimeBeforeDetect();
PostThreadMessage(gdwWorkerThreadId, AUMSG_DETECT, 0, 0);
break;
}
case AUMSG_VALIDATE_CATALOG:
DEBUGMSG("WUAUENG: validating catalog offline");
if ( gpAUcatalog->ValidateItems(FALSE) != S_OK)
{
DEBUGMSG("WUAUENG Validation failed OR no items left in catalog, State->Detect Pending");
ResetState();
}
SetEvent(ghValidateCatalog);
break;
case AUMSG_LOG_EVENT:
DEBUGMSG("WUAUENG: logging the Ready To Install (Scheduled) event");
if (gpState->fShouldScheduledInstall())
{
LogEvent_ScheduledInstall();
}
break;
default:
DEBUGMSG("WUAUENG Received unknown msg %#lx", msg.message);
TranslateMessage( &msg );
DispatchMessage( &msg );
break;
}
}
if (WAIT_OBJECT_0 == dwRet2)
{
DEBUGMSG("Update() exit in response to service finish event");
}
Done:
DEBUGMSG("WUAUENG Update func returning");
// we may ask wuauserv.dll to reload us.
return fReloadAfterSelfUpdate ? S_FALSE : S_OK;
}
void saveSelection(VARIANT *selection)
{
// fixcode this return should return an error
long n = 0;
DEBUGMSG("Start saveSelection");
WaitForSingleObject(ghMutex, INFINITE);
if ( FAILED(SafeArrayGetUBound(selection->parray, 1, &n)))
{
DEBUGMSG("WUAUENG SafeArrayGetUBond failed");
goto done;
}
if (((n + 1) / 2) != gpAUcatalog->m_ItemList.Count())
{
AUASSERT(FALSE);
DEBUGMSG("WUAUENG got unmatched number of items from client");
goto done;
}
for ( long i = 0; i < (n + 1) / 2; i++ )
{
long dex = i * 2;
VARIANT var;
VariantInit(&var);
if ( FAILED(SafeArrayGetElement(selection->parray, &dex, &var)) )
{
DEBUGMSG("SafeArrayGetElement failed");
continue;
}
BOOL fMatch = (WUCompareStringI(var.bstrVal, gpAUcatalog->m_ItemList[i].bstrID()) == CSTR_EQUAL);
VariantClear(&var);
if ( fMatch )
{
if ( SUCCEEDED(SafeArrayGetElement(selection->parray, &++dex, &var)) )
{
gpAUcatalog->m_ItemList[i].SetStatus(var.lVal);
// DEBUGMSG("Status for item %S is now %d", gpAUcatalog->m_ItemList[i].bstrID(), gpAUcatalog->m_ItemList[i].dwStatus());
}
}
else
{
DEBUGMSG("item ids did not match for saving selections");
}
}
// gpAUcatalog->m_ItemList.DbgDump();
gpAUcatalog->Serialize();
done:
ReleaseMutex(ghMutex);
DEBUGMSG("End saveSelection");
}
HRESULT StartDownload(void)
{
//DEBUGMSG("WUAUENG ::StartDownload called");
if ( AUSTATE_DETECT_COMPLETE != gpState->GetState() )
{
DEBUGMSG("WUAUENG ::StartDownload state incorrect");
return E_FAIL;
}
PostThreadMessage(gdwWorkerThreadId, AUMSG_DOWNLOAD, 0, 0);
return S_OK;
}
HRESULT GetUpdatesList(VARIANT *vList)
{
HRESULT hr = E_FAIL;
DWORD dwWait;
dwWait = WaitForSingleObject(ghMutex, INFINITE);
if (WAIT_FAILED == dwWait)
{
DEBUGMSG("WUAUENG GetUpdateList got WAIT_ABANDONED");
}
else
DEBUGMSG("WUAUENG Getting Updates list dWait=%d",dwWait);
if ( (AUSTATE_DETECT_COMPLETE != gpState->GetState()) && (AUSTATE_DOWNLOAD_COMPLETE != gpState->GetState()) )
{
goto Done;
}
hr = gpAUcatalog->getUpdatesList(vList);
Done:
ReleaseMutex(ghMutex);
return hr;
}
HRESULT GetInstallXML(/*[out]*/ BSTR *pbstrCatalogXML, /*[out]*/ BSTR *pbstrDownloadXML)
{
// DEBUGMSG("::GetInstallXML");
HRESULT hr = E_FAIL;
DWORD dwWait;
dwWait = WaitForSingleObject(ghMutex, INFINITE);
if (WAIT_FAILED == dwWait)
{
DEBUGMSG("WUAUENG GetInstallXML got WAIT_ABANDONED");
}
else
{
DEBUGMSG("WUAUENG Getting Updates list dWait=%d",dwWait);
}
hr = gpAUcatalog->GetInstallXML(pbstrCatalogXML, pbstrDownloadXML);
//Done:
ReleaseMutex(ghMutex);
return hr;
}
HRESULT GetDownloadStatus(UINT *pPercentage, DWORD *pdwnldStatus, BOOL fCareAboutConnection)
{
DWORD dwComplete;
DWORD dwstatus;
HRESULT hr;
if ( AUSTATE_DOWNLOAD_PENDING != gpState->GetState() )
{
*pPercentage = (AUSTATE_DOWNLOAD_COMPLETE == gpState->GetState()) ? 100 : 0 ;
*pdwnldStatus = DWNLDSTATUS_DOWNLOADING; //for trayicon to show 100%
//DEBUGMSG("WUAUENG %% complete = %d", *pPercentage);
return S_OK;
}
*pPercentage = 0;
if (fCareAboutConnection && !gfDownloadStarted)
{
DEBUGMSG("WUAUENG Download status is checking for connection");
*pdwnldStatus = DWNLDSTATUS_CHECKING_CONNECTION;
return S_OK;
}
WaitForSingleObject(ghMutex, INFINITE);
hr = gpAUcatalog->m_audownloader.getStatus(&dwComplete, &dwstatus);
ReleaseMutex(ghMutex);
if (FAILED(hr))
{
*pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING;
return S_OK;
}
*pPercentage = (int)dwComplete;
//DEBUGMSG("WUAUENG %% complete = %d", *pPercentage);
switch (dwstatus)
{
case BG_JOB_STATE_TRANSFERRING:
case BG_JOB_STATE_TRANSFERRED: //for trayicon to show 100%, as in the beginning of the routine
{
*pdwnldStatus = DWNLDSTATUS_DOWNLOADING;
break;
}
case BG_JOB_STATE_SUSPENDED:
{
*pdwnldStatus = DWNLDSTATUS_PAUSED;
break;
}
case BG_JOB_STATE_ERROR:
case BG_JOB_STATE_TRANSIENT_ERROR:
case BG_JOB_STATE_ACKNOWLEDGED:
case BG_JOB_STATE_CANCELLED:
case BG_JOB_STATE_QUEUED:
case BG_JOB_STATE_CONNECTING:
{
*pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING;
break;
}
default:
{
DEBUGMSG("WUAUENG GetDownloadStatus got an unexpected BG_JOB_STATE %d", dwstatus);
*pdwnldStatus = DWNLDSTATUS_NOT_DOWNLOADING;
}
}
return S_OK;
}