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.
527 lines
12 KiB
527 lines
12 KiB
#include <windows.h>
|
|
#include "download.h"
|
|
#include "util.h"
|
|
|
|
#define BUFFERSIZE 4096
|
|
char g_szBuffer[BUFFERSIZE];
|
|
|
|
#define TIMEOUT_PERIOD 120
|
|
|
|
|
|
CDownloader::CDownloader()
|
|
{
|
|
_pBnd = NULL;
|
|
_cRef = 1;
|
|
_pStm = NULL;
|
|
_hDL = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
_hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
CDownloader::~CDownloader()
|
|
{
|
|
if(_hDL)
|
|
CloseHandle(_hDL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::QueryInterface(const GUID &riid,void **ppv )
|
|
{
|
|
*ppv = NULL ;
|
|
if( IsEqualGUID(riid,IID_IUnknown) )
|
|
{
|
|
*ppv = (IUnknown *) (IBindStatusCallback *)this;
|
|
}
|
|
else if (IsEqualGUID(riid,IID_IBindStatusCallback) )
|
|
{
|
|
*ppv = (IBindStatusCallback *) this;
|
|
}
|
|
else if (IsEqualGUID(riid, IID_IAuthenticate))
|
|
{
|
|
*ppv = (IAuthenticate *) this;
|
|
}
|
|
else if (IsEqualGUID(riid,IID_IHttpNegotiate))
|
|
{
|
|
*ppv = (IHttpNegotiate*) this;
|
|
}
|
|
|
|
|
|
if (*ppv)
|
|
{
|
|
// increment our reference count before we hand out our interface
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
return(NOERROR);
|
|
}
|
|
|
|
return( E_NOINTERFACE );
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDownloader::AddRef()
|
|
{
|
|
return(++_cRef);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDownloader::Release()
|
|
{
|
|
if(!--_cRef)
|
|
{
|
|
delete this;
|
|
return(0);
|
|
}
|
|
return( _cRef );
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo)
|
|
{
|
|
// clear BINDINFO but keep its size
|
|
DWORD cbSize = pbindInfo->cbSize;
|
|
ZeroMemory( pbindInfo, cbSize );
|
|
pbindInfo->cbSize = cbSize;
|
|
|
|
*grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_RESYNCHRONIZE;
|
|
pbindInfo->dwBindVerb = BINDVERB_GET;
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::OnStartBinding(DWORD /*grfBSCOption*/,IBinding *p)
|
|
{
|
|
_pBnd = p;
|
|
_pBnd->AddRef();
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::GetPriority(LONG *pnPriority)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
STDMETHODIMP CDownloader::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText)
|
|
{
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CDownloader::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM *pstgmed)
|
|
{
|
|
// bring in major changes here
|
|
HRESULT hr = NOERROR;
|
|
|
|
DWORD dwRead = 0;
|
|
DWORD dwReadThisCall = 0;
|
|
DWORD dwWritten = 0;
|
|
|
|
if(!_pStm)
|
|
{
|
|
_pStm = pstgmed->pstm;
|
|
_pStm->AddRef();
|
|
}
|
|
|
|
|
|
// should ignore WAIT_TIMEOUT while getting bytes from urlmon
|
|
_fTimeoutValid = FALSE;
|
|
|
|
do
|
|
{
|
|
hr = _pStm->Read(g_szBuffer, BUFFERSIZE, &dwRead);
|
|
if( SUCCEEDED(hr) || ( (hr == E_PENDING) && (dwRead > 0) ) )
|
|
{
|
|
if(_hFile)
|
|
if(!WriteFile(_hFile, g_szBuffer, dwRead, &dwWritten, NULL))
|
|
{
|
|
hr = E_FAIL;
|
|
Abort();
|
|
}
|
|
}
|
|
} while (hr == NOERROR);
|
|
|
|
_uTickCount = 0;
|
|
_fTimeoutValid = TRUE; // should increment dwTickCount if WAIT_TIMEOUT occurs now
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::OnObjectAvailable(REFIID riid, IUnknown *punk)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDownloader::OnLowResource(DWORD reserved)
|
|
{
|
|
_pBnd->Abort();
|
|
return(S_OK);
|
|
}
|
|
|
|
STDMETHODIMP CDownloader::OnStopBinding(HRESULT hrError, LPCWSTR szError)
|
|
{
|
|
_fTimeoutValid = FALSE;
|
|
|
|
if((hrError == E_ABORT) && _fTimeout)
|
|
{
|
|
// This is the timeout case
|
|
_hDLResult = INET_E_CONNECTION_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
// this is all other cases
|
|
_hDLResult = hrError;
|
|
}
|
|
|
|
SetEvent(_hDL);
|
|
return(NOERROR);
|
|
}
|
|
|
|
/* IAuthenticate::Authenticate
|
|
*/
|
|
|
|
STDMETHODIMP CDownloader::Authenticate(HWND *phwnd,
|
|
LPWSTR *pszUserName, LPWSTR *pszPassword)
|
|
{
|
|
if (!phwnd || !pszUserName || !pszPassword)
|
|
return E_POINTER;
|
|
|
|
*pszUserName = NULL;
|
|
*pszPassword = NULL;
|
|
*phwnd = GetDesktopWindow() ;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
|
|
{
|
|
HRESULT hr = CreateURLMoniker(0,url,ppmkr);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
HRESULT CDownloader::DoDownload(LPCSTR pszUrl, LPCSTR lpszFilename)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
BOOL fQuit = FALSE;
|
|
DWORD dwRet;
|
|
|
|
|
|
if(!pszUrl) return E_INVALIDARG;
|
|
|
|
WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];
|
|
MultiByteToWideChar(CP_ACP, 0, pszUrl, -1,wszUrl, sizeof(wszUrl)/sizeof(wszUrl[0]));
|
|
|
|
|
|
IMoniker *ptmpmkr;
|
|
|
|
hr = GetAMoniker(wszUrl, &ptmpmkr );
|
|
|
|
IBindCtx * pBindCtx = 0;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if(SUCCEEDED(::CreateBindCtx(0,&pBindCtx)))
|
|
hr = ::RegisterBindStatusCallback(pBindCtx, (IBindStatusCallback *) this, 0, 0) ;
|
|
}
|
|
|
|
_fTimeout = FALSE;
|
|
_fTimeoutValid = TRUE;
|
|
_uTickCount = 0;
|
|
|
|
|
|
if(lpszFilename)
|
|
{
|
|
// Create the file
|
|
_hFile = CreateFile(lpszFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if(_hFile == INVALID_HANDLE_VALUE)
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = ptmpmkr->BindToStorage(pBindCtx, 0, IID_IStream, (void**)&_pStm );
|
|
|
|
// we need this here because it synchronus *FAIL* case,
|
|
// we Set the event in onstopbinding, but we skip the loop below so it
|
|
// never gets reset.
|
|
// If BindToStorage fails without even sending onstopbinding, we are resetting
|
|
// an unsignalled event, which is OK.
|
|
if(FAILED(hr))
|
|
ResetEvent(_hDL);
|
|
|
|
// here we wait for Bind to complete
|
|
//Wait for download event or abort
|
|
while(SUCCEEDED(hr) && !fQuit)
|
|
{
|
|
dwRet = MsgWaitForMultipleObjects(1, &_hDL, FALSE, 1000, QS_ALLINPUT);
|
|
if(dwRet == WAIT_OBJECT_0)
|
|
{
|
|
// Download is finished
|
|
hr = _hDLResult;
|
|
ResetEvent(_hDL);
|
|
break;
|
|
}
|
|
else if(dwRet == WAIT_TIMEOUT) // our wait has expired
|
|
{
|
|
if(_fTimeoutValid)
|
|
_uTickCount++;
|
|
|
|
// if our tick count is past threshold, abort the download
|
|
// BUGBUG: What about synch. case? We can't time out
|
|
if(_uTickCount >= TIMEOUT_PERIOD)
|
|
{
|
|
_fTimeout = TRUE;
|
|
Abort();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MSG msg;
|
|
// read all of the messages in this next loop
|
|
// removing each message as we read it
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
// if it's a quit message we're out of here
|
|
if (msg.message == WM_QUIT)
|
|
fQuit = TRUE;
|
|
else
|
|
{
|
|
// otherwise dispatch it
|
|
DispatchMessage(&msg);
|
|
} // end of PeekMessage while loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// clean up all our stuff
|
|
if(_hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(_hFile);
|
|
|
|
_hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if(_pBnd)
|
|
{
|
|
_pBnd->Release();
|
|
_pBnd = 0;
|
|
}
|
|
|
|
|
|
if(_pStm)
|
|
{
|
|
_pStm->Release();
|
|
_pStm = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CDownloader::Abort()
|
|
{
|
|
if(_pBnd)
|
|
{
|
|
_pBnd->Abort();
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CDownloader::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved,
|
|
LPWSTR * pszAdditionalHeaders)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CDownloader:: OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders,
|
|
LPWSTR * pszAdditionalResquestHeaders)
|
|
{
|
|
WriteToLog("\nResponse Header\n\n");
|
|
char sz[1024];
|
|
WideCharToMultiByte(CP_ACP, 0, szResponseHeaders, -1, sz, sizeof(sz), NULL, NULL);
|
|
WriteToLog(sz);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
CSiteMgr::CSiteMgr()
|
|
{
|
|
m_pSites = NULL;
|
|
m_pCurrentSite = NULL;
|
|
}
|
|
|
|
|
|
CSiteMgr::~CSiteMgr()
|
|
{
|
|
while(m_pSites)
|
|
{
|
|
SITEINFO* pTemp = m_pSites;
|
|
m_pSites = m_pSites->pNextSite;
|
|
ResizeBuffer(pTemp->lpszUrl, 0, 0);
|
|
ResizeBuffer(pTemp->lpszSiteName, 0, 0);
|
|
ResizeBuffer(pTemp, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CSiteMgr::GetNextSite(LPTSTR* lpszUrl, LPTSTR* lpszName)
|
|
{
|
|
if(!m_pSites)
|
|
return FALSE;
|
|
|
|
if(!m_pCurrentSite)
|
|
{
|
|
m_pCurrentSite = m_pSites;
|
|
}
|
|
|
|
*lpszUrl = m_pCurrentSite->lpszUrl;
|
|
*lpszName = m_pCurrentSite->lpszSiteName;
|
|
m_pCurrentSite = m_pCurrentSite->pNextSite;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CSiteMgr::Initialize(LPCTSTR lpszUrl)
|
|
{
|
|
CDownloader cdwn;
|
|
TCHAR szFileName[MAX_PATH], szTempPath[MAX_PATH];
|
|
|
|
if(!GetTempPath(sizeof(szTempPath), szTempPath))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
GetLanguageString(m_szLang);
|
|
|
|
LPTSTR lpszDatUrl = (LPTSTR)ResizeBuffer(NULL, lstrlen(lpszUrl) + 3 + sizeof("PatchSites.dat"), FALSE);
|
|
wsprintf(lpszDatUrl, "%s/PatchSites.dat", lpszUrl);
|
|
|
|
CombinePaths(szTempPath, "PatchSites.dat", szFileName);
|
|
|
|
WriteToLog("Downloading %1\n", lpszDatUrl);
|
|
HRESULT hr = cdwn.DoDownload(lpszDatUrl, szFileName);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
WriteToLog("Downloading %1 failed with error code:%2!lx!\n", lpszDatUrl, hr);
|
|
DeleteFile(szFileName);
|
|
ResizeBuffer(lpszDatUrl, 0, 0);
|
|
return hr;
|
|
}
|
|
|
|
BOOL flag = TRUE;
|
|
|
|
hr = ParseSitesFile(szFileName);
|
|
if(hr == E_UNEXPECTED && !m_pSites && flag)
|
|
{
|
|
flag = FALSE;
|
|
lstrcpy(m_szLang, "EN");
|
|
hr = ParseSitesFile(szFileName);
|
|
}
|
|
|
|
DeleteFile(szFileName);
|
|
ResizeBuffer(lpszDatUrl, 0, 0);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSiteMgr::ParseSitesFile(LPTSTR pszPath)
|
|
{
|
|
DWORD dwSize;
|
|
LPSTR pBuf, pCurrent, pEnd;
|
|
|
|
|
|
HANDLE hfile = CreateFile(pszPath, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if(hfile == INVALID_HANDLE_VALUE)
|
|
return E_FAIL;
|
|
|
|
|
|
dwSize = GetFileSize(hfile, NULL);
|
|
pBuf = (LPSTR)ResizeBuffer(NULL, dwSize + 1, FALSE);
|
|
|
|
if(!pBuf)
|
|
{
|
|
CloseHandle(hfile);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// Copy contents of file to our buffer
|
|
|
|
ReadFile(hfile, pBuf, dwSize, &dwSize, NULL);
|
|
|
|
pCurrent = pBuf;
|
|
pEnd = pBuf + dwSize;
|
|
*pEnd = 0;
|
|
|
|
// One pass thru replacing \n or \r with \0
|
|
while(pCurrent <= pEnd)
|
|
{
|
|
if(*pCurrent == '\r' || *pCurrent == '\n')
|
|
*pCurrent = 0;
|
|
pCurrent++;
|
|
}
|
|
|
|
pCurrent = pBuf;
|
|
while(1)
|
|
{
|
|
while(pCurrent <= pEnd && *pCurrent == 0)
|
|
pCurrent++;
|
|
|
|
// we are now pointing at begginning of line or pCurrent > pBuf
|
|
if(pCurrent > pEnd)
|
|
break;
|
|
|
|
ParseAndAllocateDownloadSite(pCurrent);
|
|
pCurrent += lstrlen(pCurrent);
|
|
}
|
|
|
|
ResizeBuffer(pBuf, 0, 0);
|
|
CloseHandle(hfile);
|
|
|
|
if(!m_pSites)
|
|
return E_UNEXPECTED;
|
|
else
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
BOOL CSiteMgr::ParseAndAllocateDownloadSite(LPTSTR psz)
|
|
{
|
|
char szUrl[1024];
|
|
char szName[256];
|
|
char szlang[256];
|
|
char szregion[256];
|
|
BOOL bQueryTrue = TRUE;
|
|
|
|
GetStringField(psz, 0, szUrl, sizeof(szUrl));
|
|
GetStringField(psz,1, szName, sizeof(szName));
|
|
GetStringField(psz, 2, szlang, sizeof(szlang));
|
|
GetStringField(psz, 3, szregion, sizeof(szregion));
|
|
|
|
if(szUrl[0] == 0 || szName[0] == 0 || szlang[0] == 0 || szregion[0] == 0)
|
|
return NULL;
|
|
|
|
if(lstrcmpi(szlang, m_szLang))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SITEINFO* pNewSite = (SITEINFO*) ResizeBuffer(NULL, sizeof(SITEINFO), FALSE);
|
|
pNewSite->lpszSiteName = (LPTSTR) ResizeBuffer(NULL, lstrlen(szName) + 1, FALSE);
|
|
lstrcpy(pNewSite->lpszSiteName, szName);
|
|
|
|
pNewSite->lpszUrl = (LPTSTR) ResizeBuffer(NULL, lstrlen(szUrl) + 1, FALSE);
|
|
lstrcpy(pNewSite->lpszUrl, szUrl);
|
|
|
|
pNewSite->pNextSite = m_pSites;
|
|
m_pSites = pNewSite;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|