1568 lines
44 KiB
C++
1568 lines
44 KiB
C++
|
/******************************Module*Header*******************************\
|
||
|
* Module Name: getinfo.cpp
|
||
|
*
|
||
|
* Author: David Stewart [dstewart]
|
||
|
*
|
||
|
* Copyright (c) 1998 Microsoft Corporation. All rights reserved.
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#include <TCHAR.H>
|
||
|
#include <objbase.h>
|
||
|
#include <mmsystem.h> //for mci commands
|
||
|
#include <urlmon.h>
|
||
|
#include <hlguids.h> //for IID_IBindStatusCallback
|
||
|
#include "getinfo.h"
|
||
|
#include "netres.h"
|
||
|
#include "wininet.h"
|
||
|
#include "condlg.h"
|
||
|
#include "..\main\mmfw.h"
|
||
|
#include "..\cdopt\cdopt.h"
|
||
|
#include "mapi.h"
|
||
|
#include <stdio.h>
|
||
|
|
||
|
extern HINSTANCE g_dllInst;
|
||
|
|
||
|
#define MODE_OK 0
|
||
|
#define MODE_MULTIPLE 1
|
||
|
#define MODE_NOT_FOUND 2
|
||
|
|
||
|
#define FRAMES_PER_SECOND 75
|
||
|
#define FRAMES_PER_MINUTE (60*FRAMES_PER_SECOND)
|
||
|
#define MAX_UPLOAD_URL_LENGTH 1500
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
#define URLFUNCTION "URLOpenStreamW"
|
||
|
#define CANONFUNCTION "InternetCanonicalizeUrlW"
|
||
|
#else
|
||
|
#define URLFUNCTION "URLOpenStreamA"
|
||
|
#define CANONFUNCTION "InternetCanonicalizeUrlA"
|
||
|
#endif
|
||
|
|
||
|
HWND g_hwndParent = NULL;
|
||
|
extern HINSTANCE g_hURLMon;
|
||
|
LPCDOPT g_pNetOpt = NULL;
|
||
|
LPCDDATA g_pNetData = NULL;
|
||
|
BOOL g_fCancelDownload = FALSE; //ANY ACCESS MUST BE SURROUNDED by Enter/Leave g_Critical
|
||
|
IBinding* g_pBind = NULL; //ANY ACCESS MUST BE SURROUNDED by Enter/Leave g_Critical
|
||
|
long g_lNumDownloadingThreads = 0; //MUST USE InterlockedIncrement/Decrement
|
||
|
BOOL g_fDownloadDone = FALSE;
|
||
|
BOOL g_fDBWriteFailure = FALSE;
|
||
|
extern CRITICAL_SECTION g_Critical;
|
||
|
extern CRITICAL_SECTION g_BatchCrit;
|
||
|
|
||
|
DWORD WINAPI SpawnSingleDownload(LPVOID pParam);
|
||
|
DWORD WINAPI SpawnBatchDownload(LPVOID pParam);
|
||
|
DWORD WINAPI DoBatchDownload(LPCDBATCH pBatchList, HWND hwndParent);
|
||
|
BOOL DoDownload(TCHAR* url, TCHAR* szFilename, HWND hwndParent);
|
||
|
|
||
|
CCDNet::CCDNet()
|
||
|
{
|
||
|
m_dwRef = 0;
|
||
|
}
|
||
|
|
||
|
CCDNet::~CCDNet()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCDNet::QueryInterface(REFIID riid, void** ppv)
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
if (IID_IUnknown == riid || IID_ICDNet == riid)
|
||
|
{
|
||
|
*ppv = this;
|
||
|
}
|
||
|
|
||
|
if (NULL==*ppv)
|
||
|
{
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CCDNet::AddRef(void)
|
||
|
{
|
||
|
return ++m_dwRef;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CCDNet::Release(void)
|
||
|
{
|
||
|
if (0!=--m_dwRef)
|
||
|
return m_dwRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCDNet::SetOptionsAndData(void* pOpts, void* pData)
|
||
|
{
|
||
|
g_pNetOpt = (LPCDOPT)pOpts;
|
||
|
g_pNetData = (LPCDDATA)pData;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//this is a start to implementing the "upload via http" case rather than the
|
||
|
//upload via mail case
|
||
|
BOOL UploadToProvider(LPCDPROVIDER pProvider, LPCDTITLE pTitle, HWND hwndParent)
|
||
|
{
|
||
|
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
|
||
|
TCHAR szMainURL[INTERNET_MAX_URL_LENGTH];
|
||
|
TCHAR szFilename[MAX_PATH];
|
||
|
|
||
|
//get the InternetCanonicalizeURL function
|
||
|
typedef BOOL (PASCAL *CANPROC)(LPCTSTR, LPTSTR, LPDWORD, DWORD);
|
||
|
CANPROC canProc = NULL;
|
||
|
|
||
|
HMODULE hNet = LoadLibrary(TEXT("WININET.DLL"));
|
||
|
if (hNet!=NULL)
|
||
|
{
|
||
|
canProc = (CANPROC)GetProcAddress(hNet,CANONFUNCTION);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pProvider && pTitle && canProc)
|
||
|
{
|
||
|
//check for provider URL
|
||
|
if (_tcslen(pProvider->szProviderUpload)>0)
|
||
|
{
|
||
|
TCHAR szTempCan[MAX_PATH*2];
|
||
|
|
||
|
//create the URL to send
|
||
|
wsprintf(szMainURL,TEXT("%s%s"),pProvider->szProviderUpload,pTitle->szTitleQuery);
|
||
|
_tcscpy(szURL,szMainURL);
|
||
|
|
||
|
//add title
|
||
|
_tcscat(szURL,TEXT("&t="));
|
||
|
DWORD dwSize = sizeof(szTempCan);
|
||
|
canProc(pTitle->szTitle,szTempCan,&dwSize,0);
|
||
|
_tcscat(szURL,szTempCan);
|
||
|
|
||
|
//add artist
|
||
|
_tcscat(szURL,TEXT("&a="));
|
||
|
dwSize = sizeof(szTempCan);
|
||
|
canProc(pTitle->szArtist,szTempCan,&dwSize,0);
|
||
|
_tcscat(szURL,szTempCan);
|
||
|
|
||
|
//add tracks
|
||
|
TCHAR szTrack[MAX_PATH];
|
||
|
for (DWORD i = 0; i < pTitle->dwNumTracks; i++)
|
||
|
{
|
||
|
wsprintf(szTrack,TEXT("&%u="),i+1);
|
||
|
|
||
|
dwSize = sizeof(szTempCan);
|
||
|
canProc(pTitle->pTrackTable[i].szName,szTempCan,&dwSize,0);
|
||
|
|
||
|
if ((_tcslen(szURL) + _tcslen(szTrack) + _tcslen(szTempCan)) >
|
||
|
MAX_UPLOAD_URL_LENGTH-sizeof(TCHAR))
|
||
|
{
|
||
|
//we're coming close to the limit. Send what we have and start rebuilding
|
||
|
if (!g_fCancelDownload)
|
||
|
{
|
||
|
if (DoDownload(szURL,szFilename, hwndParent))
|
||
|
{
|
||
|
DeleteFile(szFilename);
|
||
|
} //end if "upload" successful
|
||
|
else
|
||
|
{
|
||
|
//bad upload, don't bother sending the rest
|
||
|
//probably a timeout
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//reset the URL to just the provider + toc
|
||
|
_tcscpy(szURL,szMainURL);
|
||
|
} //end if length
|
||
|
|
||
|
_tcscat(szURL,szTrack);
|
||
|
_tcscat(szURL,szTempCan);
|
||
|
} //end for track
|
||
|
|
||
|
//send it
|
||
|
if (!g_fCancelDownload)
|
||
|
{
|
||
|
if (DoDownload(szURL,szFilename, hwndParent))
|
||
|
{
|
||
|
DeleteFile(szFilename);
|
||
|
} //end if "upload" successful
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
} //end if url exists
|
||
|
} //end if state OK
|
||
|
|
||
|
if (hNet)
|
||
|
{
|
||
|
FreeLibrary(hNet);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI UploadThread(LPVOID pParam)
|
||
|
{
|
||
|
InterlockedIncrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
|
||
|
//this will block us against the batch download happening, too
|
||
|
EnterCriticalSection(&g_BatchCrit);
|
||
|
|
||
|
LPCDTITLE pTitle = (LPCDTITLE)pParam;
|
||
|
HWND hwndParent = g_hwndParent;
|
||
|
|
||
|
int nTries = 0;
|
||
|
int nSuccessful = 0;
|
||
|
|
||
|
if (pTitle)
|
||
|
{
|
||
|
LPCDOPTIONS pOptions = g_pNetOpt->GetCDOpts(); // Get the options, needed for provider list
|
||
|
|
||
|
if (pOptions && pOptions->pCurrentProvider) // Make sure we have providers
|
||
|
{
|
||
|
LPCDPROVIDER pProviderList = NULL;
|
||
|
LPCDPROVIDER pProvider = NULL;
|
||
|
|
||
|
g_pNetOpt->CreateProviderList(&pProviderList); // Get the sorted provider list
|
||
|
pProvider = pProviderList; // Get the head of the list
|
||
|
|
||
|
while ((pProvider) && (!g_fCancelDownload))
|
||
|
{
|
||
|
nTries++;
|
||
|
if (UploadToProvider(pProvider,pTitle,hwndParent))
|
||
|
{
|
||
|
nSuccessful++;
|
||
|
}
|
||
|
pProvider = pProvider->pNext;
|
||
|
}
|
||
|
|
||
|
g_pNetOpt->DestroyProviderList(&pProviderList);
|
||
|
} //end if providers
|
||
|
}
|
||
|
|
||
|
//addref'ed before thread was created
|
||
|
g_pNetOpt->Release();
|
||
|
g_pNetData->Release();
|
||
|
|
||
|
long status = UPLOAD_STATUS_NO_PROVIDERS;
|
||
|
|
||
|
if ((nSuccessful != nTries) && (nSuccessful > 0))
|
||
|
{
|
||
|
status = UPLOAD_STATUS_SOME_PROVIDERS;
|
||
|
}
|
||
|
|
||
|
if ((nSuccessful == nTries) && (nSuccessful > 0))
|
||
|
{
|
||
|
status = UPLOAD_STATUS_ALL_PROVIDERS;
|
||
|
}
|
||
|
|
||
|
if (g_fCancelDownload)
|
||
|
{
|
||
|
status = UPLOAD_STATUS_CANCELED;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&g_BatchCrit);
|
||
|
InterlockedDecrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
|
||
|
//post message saying we're done
|
||
|
PostMessage(hwndParent,WM_NET_DONE,(WPARAM)g_dllInst,status);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCDNet::Upload(LPCDTITLE pTitle, HWND hwndParent)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
DWORD dwHow;
|
||
|
BOOL fConnected;
|
||
|
|
||
|
if (g_pNetOpt && g_pNetData && pTitle) // Make sure we are in a valid state
|
||
|
{
|
||
|
fConnected = _InternetGetConnectedState(&dwHow,0,TRUE); // Make sure we are connected to net
|
||
|
|
||
|
if (fConnected) // Make sure we are in a valid state
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
g_fCancelDownload = FALSE;
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
|
||
|
DWORD dwThreadID;
|
||
|
HANDLE hNetThread = NULL;
|
||
|
|
||
|
g_hwndParent = hwndParent;
|
||
|
g_pNetOpt->AddRef();
|
||
|
g_pNetData->AddRef();
|
||
|
hNetThread = CreateThread(NULL,0,UploadThread,(void*)pTitle,0,&dwThreadID);
|
||
|
if (hNetThread)
|
||
|
{
|
||
|
CloseHandle(hNetThread);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
} //end if connected
|
||
|
} //end if options and data ok
|
||
|
|
||
|
return (hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(BOOL) CCDNet::CanUpload()
|
||
|
{
|
||
|
BOOL retcode = FALSE;
|
||
|
|
||
|
if (g_pNetOpt && g_pNetData) // Make sure we are in a valid state
|
||
|
{
|
||
|
//check all providers to be sure at least one has upload capability
|
||
|
LPCDOPTIONS pOptions = g_pNetOpt->GetCDOpts(); // Get the options, needed for provider list
|
||
|
|
||
|
if (pOptions && pOptions->pCurrentProvider) // Make sure we have providers
|
||
|
{
|
||
|
LPCDPROVIDER pProviderList = NULL;
|
||
|
LPCDPROVIDER pProvider = NULL;
|
||
|
|
||
|
g_pNetOpt->CreateProviderList(&pProviderList); // Get the sorted provider list
|
||
|
pProvider = pProviderList; // Get the head of the list
|
||
|
|
||
|
while (pProvider)
|
||
|
{
|
||
|
if (_tcslen(pProvider->szProviderUpload) > 0)
|
||
|
{
|
||
|
retcode = TRUE;
|
||
|
}
|
||
|
pProvider = pProvider->pNext;
|
||
|
} //end while
|
||
|
|
||
|
g_pNetOpt->DestroyProviderList(&pProviderList);
|
||
|
} //end if providers
|
||
|
} //end if set up properly
|
||
|
|
||
|
return (retcode);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCDNet::Download(DWORD dwDeviceHandle, TCHAR chDrive, DWORD dwMSID, LPCDTITLE pTitle, BOOL fManual, HWND hwndParent)
|
||
|
{
|
||
|
if (g_pNetOpt==NULL)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (g_pNetData==NULL)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
if (FAILED(g_pNetData->CheckDatabase(hwndParent)))
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
CGetInfoFromNet netinfo(dwDeviceHandle,
|
||
|
dwMSID,
|
||
|
hwndParent);
|
||
|
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
g_fCancelDownload = FALSE;
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
|
||
|
BOOL fResult = netinfo.DoIt(fManual, pTitle, chDrive);
|
||
|
|
||
|
return fResult ? S_OK : E_FAIL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(BOOL) CCDNet::IsDownloading()
|
||
|
{
|
||
|
BOOL retcode = FALSE;
|
||
|
|
||
|
if (g_lNumDownloadingThreads > 0)
|
||
|
{
|
||
|
retcode = TRUE;
|
||
|
}
|
||
|
|
||
|
return (retcode);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCDNet::CancelDownload()
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
if (g_pBind)
|
||
|
{
|
||
|
g_pBind->Abort();
|
||
|
}
|
||
|
g_fCancelDownload = TRUE;
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
while (IsDownloading())
|
||
|
{
|
||
|
Sleep(10);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
struct CBindStatusCallback : IBindStatusCallback
|
||
|
{
|
||
|
///// object state
|
||
|
ULONG m_cRef; // object reference count
|
||
|
BOOL m_fAbort; // set to true if we want this to abort
|
||
|
HWND m_hMessage; // callback window
|
||
|
IStream* m_pStream; // holds downloaded data
|
||
|
|
||
|
///// construction and destruction
|
||
|
CBindStatusCallback(IStream* pStream, HWND hwndParent);
|
||
|
~CBindStatusCallback();
|
||
|
|
||
|
///// IUnknown methods
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
|
||
|
STDMETHODIMP_(ULONG) AddRef();
|
||
|
STDMETHODIMP_(ULONG) Release();
|
||
|
|
||
|
///// IBindStatusCallback methods
|
||
|
STDMETHODIMP OnStartBinding(DWORD dwReserved, IBinding *pib);
|
||
|
STDMETHODIMP GetPriority(LONG *pnPriority);
|
||
|
STDMETHODIMP OnLowResource(DWORD reserved);
|
||
|
STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,
|
||
|
ULONG ulStatusCode, LPCWSTR szStatusText);
|
||
|
STDMETHODIMP OnStopBinding(HRESULT hresult, LPCWSTR szError);
|
||
|
STDMETHODIMP GetBindInfo(DWORD *grfBINDF, BINDINFO *pbindinfo);
|
||
|
STDMETHODIMP OnDataAvailable(DWORD grfBSCF, DWORD dwSize,
|
||
|
FORMATETC *pformatetc, STGMEDIUM *pstgmed);
|
||
|
STDMETHODIMP OnObjectAvailable(REFIID riid, IUnknown *punk);
|
||
|
};
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CBindStatusCallback Creation & Destruction
|
||
|
//
|
||
|
CBindStatusCallback::CBindStatusCallback(IStream* pStream, HWND hwndParent)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
m_cRef = 0;
|
||
|
m_fAbort = FALSE;
|
||
|
m_pStream = pStream;
|
||
|
m_pStream->AddRef();
|
||
|
m_hMessage = hwndParent;
|
||
|
|
||
|
PostMessage(m_hMessage,WM_NET_STATUS,(WPARAM)g_dllInst,IDS_STRING_CONNECTING);
|
||
|
}
|
||
|
|
||
|
|
||
|
CBindStatusCallback::~CBindStatusCallback()
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
if (g_pBind)
|
||
|
{
|
||
|
g_pBind->Release();
|
||
|
g_pBind = NULL;
|
||
|
}
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
|
||
|
if( m_pStream )
|
||
|
{
|
||
|
m_pStream->Release();
|
||
|
m_pStream = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CBindStatusCallback IUnknown Methods
|
||
|
//
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::QueryInterface(REFIID riid, LPVOID *ppvObj)
|
||
|
{
|
||
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
||
|
IsEqualIID(riid, IID_IBindStatusCallback))
|
||
|
{
|
||
|
*ppvObj = (IBindStatusCallback *) this;
|
||
|
AddRef();
|
||
|
return NOERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppvObj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CBindStatusCallback::AddRef()
|
||
|
{
|
||
|
InterlockedIncrement((LONG*)&m_cRef);
|
||
|
return m_cRef;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CBindStatusCallback::Release()
|
||
|
{
|
||
|
ULONG cRef = m_cRef;
|
||
|
if (InterlockedDecrement((LONG*)&m_cRef) == 0)
|
||
|
{
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return cRef-1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CBindStatusCallback IBindStatusCallback Methods
|
||
|
//
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding *pib)
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
g_pBind = pib;
|
||
|
g_pBind->AddRef();
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::GetPriority(LONG *pnPriority)
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnLowResource(DWORD reserved)
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax,
|
||
|
ULONG ulStatusCode, LPCWSTR szStatusText)
|
||
|
{
|
||
|
int nResID = 0;
|
||
|
|
||
|
switch (ulStatusCode)
|
||
|
{
|
||
|
case (BINDSTATUS_FINDINGRESOURCE) : nResID = IDS_STRING_FINDINGRESOURCE; break;
|
||
|
case (BINDSTATUS_CONNECTING) : nResID = IDS_STRING_CONNECTING; break;
|
||
|
case (BINDSTATUS_REDIRECTING) : nResID = IDS_STRING_REDIRECTING; break;
|
||
|
case (BINDSTATUS_BEGINDOWNLOADDATA) : nResID = IDS_STRING_BEGINDOWNLOAD; break;
|
||
|
case (BINDSTATUS_DOWNLOADINGDATA) : nResID = IDS_STRING_DOWNLOAD; break;
|
||
|
case (BINDSTATUS_ENDDOWNLOADDATA) : nResID = IDS_STRING_ENDDOWNLOAD; break;
|
||
|
case (BINDSTATUS_SENDINGREQUEST) : nResID = IDS_STRING_SENDINGREQUEST; break;
|
||
|
} //end switch
|
||
|
|
||
|
if (nResID > 0)
|
||
|
{
|
||
|
PostMessage(m_hMessage,WM_NET_STATUS,(WPARAM)g_dllInst,nResID);
|
||
|
}
|
||
|
|
||
|
if (( m_fAbort ) || (g_fCancelDownload))
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
g_fCancelDownload = TRUE;
|
||
|
g_fDownloadDone = TRUE;
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
return E_ABORT;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnStopBinding(HRESULT hresult, LPCWSTR szError)
|
||
|
{
|
||
|
EnterCriticalSection(&g_Critical);
|
||
|
if (g_pBind)
|
||
|
{
|
||
|
g_pBind->Release();
|
||
|
g_pBind = NULL;
|
||
|
}
|
||
|
LeaveCriticalSection(&g_Critical);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::GetBindInfo(DWORD *pgrfBINDF, BINDINFO *pbindinfo)
|
||
|
{
|
||
|
*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
|
||
|
pbindinfo->cbSize = sizeof(BINDINFO);
|
||
|
pbindinfo->szExtraInfo = NULL;
|
||
|
ZeroMemory(&pbindinfo->stgmedData, sizeof(STGMEDIUM));
|
||
|
pbindinfo->grfBindInfoF = 0;
|
||
|
pbindinfo->dwBindVerb = BINDVERB_GET;
|
||
|
pbindinfo->szCustomVerb = NULL;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize,
|
||
|
FORMATETC *pformatetc, STGMEDIUM *pstgmed)
|
||
|
{
|
||
|
// fill our stream with the data from the stream passed to us
|
||
|
if( m_pStream )
|
||
|
{
|
||
|
ULARGE_INTEGER cb;
|
||
|
|
||
|
cb.LowPart = dwSize;
|
||
|
cb.HighPart = 0;
|
||
|
if( pstgmed && pstgmed->pstm )
|
||
|
{
|
||
|
pstgmed->pstm->CopyTo( m_pStream, cb, NULL, NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Notify owner when download is complete
|
||
|
if( grfBSCF & BSCF_LASTDATANOTIFICATION )
|
||
|
{
|
||
|
g_fDownloadDone = TRUE;
|
||
|
|
||
|
if( m_pStream )
|
||
|
{
|
||
|
m_pStream->Release();
|
||
|
m_pStream = NULL;
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown *punk)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CGetInfoFromNet
|
||
|
|
||
|
CGetInfoFromNet::CGetInfoFromNet(DWORD cdrom, DWORD dwMSID, HWND hwndParent)
|
||
|
{
|
||
|
DevHandle = cdrom;
|
||
|
m_MS = dwMSID;
|
||
|
g_hwndParent = hwndParent;
|
||
|
}
|
||
|
|
||
|
CGetInfoFromNet::~CGetInfoFromNet()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BOOL CGetInfoFromNet::DoIt(BOOL fManual, LPCDTITLE pTitle, TCHAR chDrive)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
int nMode = CONNECTION_GETITNOW;
|
||
|
|
||
|
if (!fManual)
|
||
|
{
|
||
|
if (g_lNumDownloadingThreads == 0)
|
||
|
{
|
||
|
//if no threads are running already,
|
||
|
//check the connection, possibly prompting the user
|
||
|
nMode = ConnectionCheck(g_hwndParent,g_pNetOpt, chDrive);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nMode == CONNECTION_DONOTHING)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//if passed-in ID is not > 0, then we don't want to scan current disc
|
||
|
if ((m_MS > 0) && (pTitle == NULL))
|
||
|
{
|
||
|
m_Tracks = readtoc();
|
||
|
|
||
|
if (m_Tracks > 0)
|
||
|
{
|
||
|
BuildQuery();
|
||
|
}
|
||
|
} //if msid is greater than 0
|
||
|
|
||
|
if (nMode == CONNECTION_BATCH)
|
||
|
{
|
||
|
if (m_MS > 0)
|
||
|
{
|
||
|
AddToBatch(m_Tracks,m_Query);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//we need to determine now whether we spawn a batching thread or a single-item downloader
|
||
|
g_fDBWriteFailure = FALSE;
|
||
|
DWORD dwThreadID;
|
||
|
HANDLE hNetThread = NULL;
|
||
|
|
||
|
//addref the global pointers before entering the thread
|
||
|
g_pNetOpt->AddRef();
|
||
|
g_pNetData->AddRef();
|
||
|
|
||
|
if (m_MS > 0)
|
||
|
{
|
||
|
//need to create a batch item for this thread to use
|
||
|
LPCDBATCH pBatch = new CDBATCH;
|
||
|
pBatch->fRemove = FALSE;
|
||
|
pBatch->fFresh = TRUE;
|
||
|
pBatch->pNext = NULL;
|
||
|
|
||
|
if (!pTitle)
|
||
|
{
|
||
|
pBatch->dwTitleID = m_MS;
|
||
|
pBatch->dwNumTracks = m_Tracks;
|
||
|
pBatch->szTitleQuery = new TCHAR[_tcslen(m_Query)+1];
|
||
|
_tcscpy(pBatch->szTitleQuery,m_Query);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pBatch->dwTitleID = pTitle->dwTitleID;
|
||
|
pBatch->dwNumTracks = pTitle->dwNumTracks;
|
||
|
if (pTitle->szTitleQuery)
|
||
|
{
|
||
|
pBatch->szTitleQuery = new TCHAR[_tcslen(pTitle->szTitleQuery)+1];
|
||
|
_tcscpy(pBatch->szTitleQuery,pTitle->szTitleQuery);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pBatch->szTitleQuery = new TCHAR[_tcslen(m_Query)+1];
|
||
|
_tcscpy(pBatch->szTitleQuery,m_Query);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hNetThread = CreateThread(NULL,0,SpawnSingleDownload,(void*)pBatch,0,&dwThreadID);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hNetThread = CreateThread(NULL,0,SpawnBatchDownload,(void*)NULL,0,&dwThreadID);
|
||
|
}
|
||
|
|
||
|
if (hNetThread)
|
||
|
{
|
||
|
CloseHandle(hNetThread);
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
|
||
|
return (fRet);
|
||
|
}
|
||
|
|
||
|
int CGetInfoFromNet::readtoc()
|
||
|
{
|
||
|
DWORD dwRet;
|
||
|
MCI_SET_PARMS mciSet;
|
||
|
|
||
|
ZeroMemory( &mciSet, sizeof(mciSet) );
|
||
|
|
||
|
mciSet.dwTimeFormat = MCI_FORMAT_MSF;
|
||
|
mciSendCommand( DevHandle, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID)&mciSet );
|
||
|
|
||
|
MCI_STATUS_PARMS mciStatus;
|
||
|
long lAddress, lStartPos, lDiskLen;
|
||
|
int i;
|
||
|
|
||
|
ZeroMemory( &mciStatus, sizeof(mciStatus) );
|
||
|
mciStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
||
|
|
||
|
//
|
||
|
// NOTE: none of the mciSendCommand calls below bother to check the
|
||
|
// return code. This is asking for trouble... but if the
|
||
|
// commands fail we cannot do much about it.
|
||
|
//
|
||
|
dwRet = mciSendCommand( DevHandle, MCI_STATUS,
|
||
|
MCI_STATUS_ITEM, (DWORD_PTR)(LPVOID)&mciStatus);
|
||
|
|
||
|
int tracks = -1;
|
||
|
tracks = (UCHAR)mciStatus.dwReturn;
|
||
|
|
||
|
mciStatus.dwItem = MCI_STATUS_POSITION;
|
||
|
for ( i = 0; i < tracks; i++ )
|
||
|
{
|
||
|
|
||
|
mciStatus.dwTrack = i + 1;
|
||
|
dwRet = mciSendCommand( DevHandle, MCI_STATUS,
|
||
|
MCI_STATUS_ITEM | MCI_TRACK,
|
||
|
(DWORD_PTR)(LPVOID)&mciStatus);
|
||
|
|
||
|
lAddress = (long)mciStatus.dwReturn;
|
||
|
|
||
|
//converts "packed" time into pure frames
|
||
|
lAddress = (MCI_MSF_MINUTE(lAddress) * FRAMES_PER_MINUTE) +
|
||
|
(MCI_MSF_SECOND(lAddress) * FRAMES_PER_SECOND) +
|
||
|
(MCI_MSF_FRAME( lAddress));
|
||
|
|
||
|
m_toc[i] = lAddress;
|
||
|
|
||
|
if (i==0)
|
||
|
{
|
||
|
lStartPos = lAddress;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mciStatus.dwItem = MCI_STATUS_LENGTH;
|
||
|
dwRet = mciSendCommand( DevHandle, MCI_STATUS,
|
||
|
MCI_STATUS_ITEM, (DWORD_PTR)(LPVOID)&mciStatus);
|
||
|
|
||
|
/*
|
||
|
** Convert the total disk length into frames
|
||
|
*/
|
||
|
lAddress = (long)mciStatus.dwReturn;
|
||
|
lDiskLen = (MCI_MSF_MINUTE(lAddress) * FRAMES_PER_MINUTE) +
|
||
|
(MCI_MSF_SECOND(lAddress) * FRAMES_PER_SECOND) +
|
||
|
(MCI_MSF_FRAME( lAddress));
|
||
|
|
||
|
/*
|
||
|
** Now, determine the absolute start position of the sentinel
|
||
|
** track. That is, the special track that marks the end of the
|
||
|
** disk.
|
||
|
*/
|
||
|
lAddress = lStartPos + lDiskLen + 1; //dstewart: add one for true time
|
||
|
|
||
|
m_toc[i] = lAddress;
|
||
|
|
||
|
return (tracks);
|
||
|
}
|
||
|
|
||
|
void CGetInfoFromNet::BuildQuery()
|
||
|
{
|
||
|
wsprintf(m_Query,TEXT("cd=%X"),m_Tracks);
|
||
|
|
||
|
//add each frame stattime to query, include end time of disc
|
||
|
TCHAR tempstr[MAX_PATH];
|
||
|
for (int i = 0; i < m_Tracks+1; i++)
|
||
|
{
|
||
|
wsprintf(tempstr,TEXT("+%X"),m_toc[i]);
|
||
|
_tcscat(m_Query,tempstr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CGetInfoFromNet::AddToBatch(int nNumTracks, TCHAR* szQuery)
|
||
|
{
|
||
|
if ((g_pNetData) && (g_pNetOpt))
|
||
|
{
|
||
|
g_pNetData->AddToBatch(m_MS, szQuery, nNumTracks);
|
||
|
LPCDOPTIONS pOptions = g_pNetOpt->GetCDOpts();
|
||
|
if (pOptions)
|
||
|
{
|
||
|
pOptions->dwBatchedTitles = g_pNetData->GetNumBatched();
|
||
|
g_pNetOpt->DownLoadCompletion(0,NULL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CopyStreamToFile( IStream* pStream, HANDLE hFile )
|
||
|
{
|
||
|
TCHAR achBuf[512];
|
||
|
ULONG cb = 1;
|
||
|
DWORD dwWritten;
|
||
|
LARGE_INTEGER dlib;
|
||
|
|
||
|
dlib.LowPart = 0;
|
||
|
dlib.HighPart = 0;
|
||
|
pStream->Seek( dlib, STREAM_SEEK_SET, NULL );
|
||
|
pStream->Read( achBuf, 512, &cb );
|
||
|
while( cb )
|
||
|
{
|
||
|
if( FALSE == WriteFile( hFile, achBuf, cb, &dwWritten, NULL ))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
pStream->Read( achBuf, 512, &cb );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL DoDownload(TCHAR* url, TCHAR* szFilename, HWND hwndParent)
|
||
|
{
|
||
|
TCHAR szPath[_MAX_PATH];
|
||
|
TCHAR sz[_MAX_PATH];
|
||
|
BOOL fGotFileName = FALSE;
|
||
|
|
||
|
// Get a file name
|
||
|
if(GetTempPath(_MAX_PATH, szPath))
|
||
|
{
|
||
|
if(GetTempFileName(szPath, TEXT("cdd"), 0, sz))
|
||
|
{
|
||
|
fGotFileName = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!fGotFileName)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IStream* pStream = NULL;
|
||
|
|
||
|
g_fDownloadDone = FALSE;
|
||
|
if (FAILED(CreateStreamOnHGlobal( NULL, TRUE, &pStream )))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
//pStream was addref'ed by createstreamonhgobal
|
||
|
|
||
|
CBindStatusCallback* pCDC = new CBindStatusCallback(pStream, hwndParent);
|
||
|
if(!pCDC)
|
||
|
{
|
||
|
pStream->Release();
|
||
|
return FALSE;
|
||
|
}
|
||
|
pCDC->AddRef();
|
||
|
|
||
|
HRESULT hr = E_NOTIMPL;
|
||
|
|
||
|
if (g_hURLMon == NULL)
|
||
|
{
|
||
|
g_hURLMon = LoadLibrary(TEXT("URLMON.DLL"));
|
||
|
}
|
||
|
|
||
|
if (g_hURLMon!=NULL)
|
||
|
{
|
||
|
typedef BOOL (PASCAL *URLDOWNLOADPROC)(LPUNKNOWN, LPCTSTR, DWORD, LPBINDSTATUSCALLBACK);
|
||
|
URLDOWNLOADPROC URLDownload = (URLDOWNLOADPROC)GetProcAddress(g_hURLMon,URLFUNCTION);
|
||
|
|
||
|
if (URLDownload!=NULL)
|
||
|
{
|
||
|
#ifdef DBG
|
||
|
OutputDebugString(url);
|
||
|
OutputDebugString(TEXT("\n"));
|
||
|
#endif
|
||
|
hr = URLDownload(NULL, url, 0, pCDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(FAILED(hr))
|
||
|
{
|
||
|
pCDC->Release();
|
||
|
pStream->Release();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pCDC->Release();
|
||
|
|
||
|
if (g_fCancelDownload)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Create the file for writing
|
||
|
HANDLE hFileWrite = CreateFile(sz, GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
|
||
|
if( hFileWrite != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
CopyStreamToFile( pStream, hFileWrite );
|
||
|
CloseHandle( hFileWrite );
|
||
|
}
|
||
|
|
||
|
pStream->Release();
|
||
|
|
||
|
_tcscpy(szFilename,sz);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//dialog box handler for multiple hits
|
||
|
INT_PTR CALLBACK MultiHitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch (message)
|
||
|
{
|
||
|
case WM_INITDIALOG :
|
||
|
{
|
||
|
TCHAR* szFilename = (TCHAR*)lParam;
|
||
|
TCHAR szTemp[MAX_PATH];
|
||
|
TCHAR szArtist[MAX_PATH];
|
||
|
TCHAR szTitle[MAX_PATH];
|
||
|
int i = 1;
|
||
|
|
||
|
_tcscpy(szTitle,TEXT("."));
|
||
|
|
||
|
while (_tcslen(szTitle)>0)
|
||
|
{
|
||
|
wsprintf(szTemp,TEXT("Title%i"),i);
|
||
|
GetPrivateProfileString(TEXT("CD"),szTemp,TEXT(""),szTitle,sizeof(szTitle)/sizeof(TCHAR),szFilename);
|
||
|
wsprintf(szTemp,TEXT("Artist%i"),i);
|
||
|
GetPrivateProfileString(TEXT("CD"),szTemp,TEXT(""),szArtist,sizeof(szArtist)/sizeof(TCHAR),szFilename);
|
||
|
i++;
|
||
|
|
||
|
if (_tcslen(szTitle)>0)
|
||
|
{
|
||
|
wsprintf(szTemp,TEXT("%s (%s)"),szTitle,szArtist);
|
||
|
SendDlgItemMessage(hwnd,IDC_LIST_DISCS,LB_ADDSTRING,0,(LPARAM)szTemp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SendDlgItemMessage(hwnd,IDC_LIST_DISCS,LB_SETCURSEL,0,0);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND :
|
||
|
{
|
||
|
if (LOWORD(wParam)==IDCANCEL)
|
||
|
{
|
||
|
EndDialog(hwnd,-1);
|
||
|
}
|
||
|
|
||
|
if (LOWORD(wParam)==IDOK)
|
||
|
{
|
||
|
LRESULT nSel = SendDlgItemMessage(hwnd,IDC_LIST_DISCS,LB_GETCURSEL,0,0);
|
||
|
EndDialog(hwnd,nSel+1);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL ResolveMultiples(TCHAR* szFilename, BOOL fCurrent, HWND hwndParent)
|
||
|
{
|
||
|
//special case ... sometimes, this comes back with <2 hits!!!
|
||
|
//in this case, go ahead and ask for URL1
|
||
|
|
||
|
TCHAR sznewurl[INTERNET_MAX_URL_LENGTH];
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("URL2"),TEXT(""),sznewurl,sizeof(sznewurl)/sizeof(TCHAR),szFilename);
|
||
|
|
||
|
INT_PTR nSelection = 0;
|
||
|
|
||
|
if (_tcslen(sznewurl)==0)
|
||
|
{
|
||
|
nSelection = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (fCurrent)
|
||
|
{
|
||
|
nSelection = DialogBoxParam(g_dllInst, MAKEINTRESOURCE(IDD_MULTIPLE_HITS),
|
||
|
hwndParent, MultiHitDlgProc, (LPARAM)szFilename );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nSelection > 0)
|
||
|
{
|
||
|
TCHAR szSelected[MAX_PATH];
|
||
|
wsprintf(szSelected,TEXT("URL%i"),nSelection);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),szSelected,TEXT(""),sznewurl,sizeof(sznewurl)/sizeof(TCHAR),szFilename);
|
||
|
|
||
|
DeleteFile(szFilename);
|
||
|
|
||
|
if (DoDownload(sznewurl,szFilename, hwndParent))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//no more cover art in first version
|
||
|
#if 0
|
||
|
void TranslateTempCoverToFinal(TCHAR* szCurrent, TCHAR* szFinal, long discid, TCHAR* extension)
|
||
|
{
|
||
|
//we want to put the cover art in a "coverart" subdir relative to whereever CD player is
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
GetModuleFileName(NULL,szPath,sizeof(szPath));
|
||
|
|
||
|
TCHAR* szPathEnd;
|
||
|
szPathEnd = _tcsrchr(szPath, TEXT('\\'))+sizeof(TCHAR);
|
||
|
_tcscpy(szPathEnd,TEXT("coverart\\"));
|
||
|
|
||
|
CreateDirectory(szPath,NULL); //create the coverart subdir
|
||
|
|
||
|
wsprintf(szFinal,TEXT("%s%08X%s"),szPath,discid,extension);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
DWORD GetNextDisc(long lOriginal, LPCDBATCH* ppBatch)
|
||
|
{
|
||
|
DWORD discid = (DWORD)-1;
|
||
|
|
||
|
//only do the batch if no discid was passed in originally to thread
|
||
|
if (lOriginal < 1)
|
||
|
{
|
||
|
if (*ppBatch!=NULL)
|
||
|
{
|
||
|
*ppBatch = (*ppBatch)->pNext;
|
||
|
if (*ppBatch != NULL)
|
||
|
{
|
||
|
discid = (*ppBatch)->dwTitleID;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (discid);
|
||
|
}
|
||
|
|
||
|
LPCDPROVIDER GetNewProvider(LPCDPROVIDER pList, LPCDPROVIDER pCurrent, LPCDPROVIDER pDefault)
|
||
|
{
|
||
|
//find the next provider that isn't the current
|
||
|
if (pCurrent == pDefault)
|
||
|
{
|
||
|
//we've just done the current provider, so go to the head of the list next
|
||
|
pCurrent = pList;
|
||
|
if (pCurrent == pDefault)
|
||
|
{
|
||
|
//if the default was also the head of the list, go to the next and return
|
||
|
pCurrent = pCurrent->pNext;
|
||
|
}
|
||
|
return (pCurrent);
|
||
|
}
|
||
|
|
||
|
//get the next entry on the list
|
||
|
pCurrent = pCurrent->pNext;
|
||
|
|
||
|
//is the next entry the same as the default entry? if so, move on one more
|
||
|
if (pCurrent == pDefault)
|
||
|
{
|
||
|
pCurrent = pCurrent->pNext;
|
||
|
}
|
||
|
|
||
|
return (pCurrent);
|
||
|
}
|
||
|
|
||
|
//if szProvider is NULL, szURL is filled in with "just the query" ...
|
||
|
//if szProvider is not NULL, it is prepended to the query in szURL
|
||
|
int GetTracksAndQuery(LPCDBATCH pBatch, TCHAR* szURL, TCHAR* szProvider)
|
||
|
{
|
||
|
if (pBatch == NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nReturn = pBatch->dwNumTracks;
|
||
|
|
||
|
if (szProvider != NULL)
|
||
|
{
|
||
|
wsprintf(szURL,TEXT("%s%s"),szProvider,pBatch->szTitleQuery);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_tcscpy(szURL,pBatch->szTitleQuery);
|
||
|
}
|
||
|
|
||
|
return nReturn;
|
||
|
}
|
||
|
|
||
|
void WINAPI AddTitleToDatabase(DWORD dwDiscID, DWORD dwTracks, TCHAR *szURL, TCHAR *szTempFile)
|
||
|
{
|
||
|
LPCDTITLE pCDTitle = NULL;
|
||
|
TCHAR tempstr[CDSTR];
|
||
|
BOOL fContinue = TRUE;
|
||
|
DWORD dwMenus = 0;
|
||
|
|
||
|
while (fContinue)
|
||
|
{
|
||
|
TCHAR szMenuIndex[10];
|
||
|
TCHAR szMenuEntry[INTERNET_MAX_URL_LENGTH];
|
||
|
wsprintf(szMenuIndex,TEXT("MENU%i"),dwMenus+1);
|
||
|
|
||
|
GetPrivateProfileString( TEXT("CD"), szMenuIndex, TEXT(""),
|
||
|
szMenuEntry, sizeof(szMenuEntry)/sizeof(TCHAR), szTempFile );
|
||
|
|
||
|
if (_tcslen(szMenuEntry)>0)
|
||
|
{
|
||
|
dwMenus++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fContinue = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(g_pNetData->CreateTitle(&pCDTitle, dwDiscID, dwTracks, dwMenus)))
|
||
|
{
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("TITLE"),TEXT(""),tempstr,sizeof(tempstr)/sizeof(TCHAR),szTempFile);
|
||
|
_tcscpy(pCDTitle->szTitle,tempstr);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("ARTIST"),TEXT(""),tempstr,sizeof(tempstr)/sizeof(TCHAR),szTempFile);
|
||
|
_tcscpy(pCDTitle->szArtist,tempstr);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("LABEL"),TEXT(""),tempstr,sizeof(tempstr)/sizeof(TCHAR),szTempFile);
|
||
|
_tcscpy(pCDTitle->szLabel,tempstr);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("COPYRIGHT"),TEXT(""),tempstr,sizeof(tempstr)/sizeof(TCHAR),szTempFile);
|
||
|
_tcscpy(pCDTitle->szCopyright,tempstr);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("RELEASEDATE"),TEXT(""),tempstr,sizeof(tempstr)/sizeof(TCHAR),szTempFile);
|
||
|
_tcscpy(pCDTitle->szDate,tempstr);
|
||
|
|
||
|
g_pNetData->SetTitleQuery(pCDTitle, szURL);
|
||
|
|
||
|
for (int i = 1; i < (int) dwTracks + 1; i++)
|
||
|
{
|
||
|
TCHAR tempstrtrack[10];
|
||
|
TCHAR tempstrtitle[CDSTR];
|
||
|
wsprintf(tempstrtrack,TEXT("TRACK%i"),i);
|
||
|
GetPrivateProfileString(TEXT("CD"),tempstrtrack,TEXT(""),tempstrtitle,sizeof(tempstrtitle)/sizeof(TCHAR),szTempFile);
|
||
|
|
||
|
if (_tcslen(tempstrtitle) == 0)
|
||
|
{
|
||
|
TCHAR strFormat[CDSTR];
|
||
|
LoadString(g_dllInst,IDS_STRING_DEFAULTTRACK,strFormat,sizeof(strFormat)/sizeof(TCHAR));
|
||
|
wsprintf(tempstrtitle,strFormat,i);
|
||
|
}
|
||
|
|
||
|
_tcscpy(pCDTitle->pTrackTable[i-1].szName,tempstrtitle);
|
||
|
}
|
||
|
|
||
|
for (i = 1; i < (int) (dwMenus + 1); i++)
|
||
|
{
|
||
|
TCHAR tempstrmenu[10];
|
||
|
TCHAR tempstrmenuvalue[CDSTR+INTERNET_MAX_URL_LENGTH+(3*sizeof(TCHAR))]; //3 = two colons and a terminating null
|
||
|
wsprintf(tempstrmenu,TEXT("MENU%i"),i);
|
||
|
GetPrivateProfileString(TEXT("CD"),tempstrmenu,TEXT(""),tempstrmenuvalue,sizeof(tempstrmenuvalue)/sizeof(TCHAR),szTempFile);
|
||
|
|
||
|
//need to split menu into its component parts
|
||
|
if (_tcslen(tempstrmenuvalue)!=0)
|
||
|
{
|
||
|
TCHAR* szNamePart;
|
||
|
szNamePart = _tcsstr(tempstrmenuvalue,URL_SEPARATOR);
|
||
|
|
||
|
TCHAR* szURLPart;
|
||
|
szURLPart = _tcsstr(tempstrmenuvalue,URL_SEPARATOR);
|
||
|
if (szURLPart!=NULL)
|
||
|
{
|
||
|
//need to move past two colons
|
||
|
szURLPart = _tcsinc(szURLPart);
|
||
|
szURLPart = _tcsinc(szURLPart);
|
||
|
}
|
||
|
|
||
|
if (szNamePart!=NULL)
|
||
|
{
|
||
|
*szNamePart = '\0';
|
||
|
}
|
||
|
|
||
|
if (tempstrmenuvalue)
|
||
|
{
|
||
|
if (_tcslen(tempstrmenuvalue) >= sizeof(pCDTitle->pMenuTable[i-1].szMenuText)/sizeof(TCHAR))
|
||
|
{
|
||
|
tempstrmenuvalue[sizeof(pCDTitle->pMenuTable[i-1].szMenuText)/sizeof(TCHAR) - 1] = TEXT('\0'); // Trunc string to max len
|
||
|
}
|
||
|
_tcscpy(pCDTitle->pMenuTable[i-1].szMenuText,tempstrmenuvalue);
|
||
|
}
|
||
|
|
||
|
if (szURLPart)
|
||
|
{
|
||
|
g_pNetData->SetMenuQuery(&(pCDTitle->pMenuTable[i-1]), szURLPart);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_pNetData->UnlockTitle(pCDTitle,TRUE);
|
||
|
|
||
|
//at this point, if the title is not in the database, we have a major problem
|
||
|
if (!g_pNetData->QueryTitle(dwDiscID))
|
||
|
{
|
||
|
g_fDBWriteFailure = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fDBWriteFailure = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IsCertifiedProvider(LPCDPROVIDER pProvider, TCHAR *szTempFile)
|
||
|
{
|
||
|
BOOL fCertified = TRUE;
|
||
|
TCHAR szCert[MAX_PATH];
|
||
|
|
||
|
GetPrivateProfileString(TEXT("CD"),TEXT("CERTIFICATE"),TEXT(""),szCert,sizeof(szCert)/sizeof(TCHAR),szTempFile);
|
||
|
|
||
|
fCertified = g_pNetOpt->VerifyProvider(pProvider,szCert);
|
||
|
|
||
|
return(fCertified);
|
||
|
}
|
||
|
|
||
|
void UpdatePropertyPage(DWORD dwDiscID, BOOL fDownloading, HWND hwndParent)
|
||
|
{
|
||
|
if (g_pNetOpt)
|
||
|
{
|
||
|
LPCDUNIT pUnit = g_pNetOpt->GetCDOpts()->pCDUnitList;
|
||
|
|
||
|
while (pUnit!=NULL)
|
||
|
{
|
||
|
if (pUnit->dwTitleID == dwDiscID)
|
||
|
{
|
||
|
pUnit->fDownLoading = fDownloading;
|
||
|
PostMessage(hwndParent,WM_NET_DB_UPDATE_DISC,0,(LPARAM)pUnit); //Tell the UI we changed status of disc
|
||
|
break;
|
||
|
}
|
||
|
pUnit = pUnit->pNext;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL WINAPI DownloadBatch(LPCDBATCH pBatch, LPCDPROVIDER pProvider, LPDWORD pdwMultiHit, LPBOOL pfTimeout, HWND hwndParent)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
DWORD dwTracks;
|
||
|
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
|
||
|
TCHAR szTempFile[MAX_PATH];
|
||
|
DWORD dwDiscID;
|
||
|
|
||
|
dwTracks = GetTracksAndQuery(pBatch, szURL, pProvider->szProviderURL);
|
||
|
dwDiscID = pBatch->dwTitleID;
|
||
|
*pfTimeout = FALSE;
|
||
|
|
||
|
UpdatePropertyPage(pBatch->dwTitleID, TRUE, hwndParent); //tell prop page ui that disc is downloading
|
||
|
|
||
|
if (dwTracks > 0 && dwDiscID != 0)
|
||
|
{
|
||
|
if (DoDownload(szURL,szTempFile,hwndParent))
|
||
|
{
|
||
|
if (IsCertifiedProvider(pProvider, szTempFile))
|
||
|
{
|
||
|
int nMode = GetPrivateProfileInt(TEXT("CD"),TEXT("MODE"),MODE_NOT_FOUND,szTempFile);
|
||
|
|
||
|
if (nMode == MODE_NOT_FOUND)
|
||
|
{
|
||
|
DeleteFile(szTempFile);
|
||
|
}
|
||
|
else if (nMode == MODE_MULTIPLE)
|
||
|
{
|
||
|
if (pdwMultiHit)
|
||
|
{
|
||
|
(*pdwMultiHit)++;
|
||
|
}
|
||
|
|
||
|
if (!ResolveMultiples(szTempFile,TRUE,hwndParent))
|
||
|
{
|
||
|
DeleteFile(szTempFile);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nMode = MODE_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nMode == MODE_OK)
|
||
|
{
|
||
|
GetTracksAndQuery(pBatch,szURL,NULL); //reset szURL to lose the provider
|
||
|
AddTitleToDatabase(dwDiscID, dwTracks, szURL, szTempFile);
|
||
|
DeleteFile(szTempFile);
|
||
|
fSuccess = TRUE;
|
||
|
} //end if mode ok
|
||
|
} //end if certified provider
|
||
|
} //end if download ok
|
||
|
else
|
||
|
{
|
||
|
*pfTimeout = TRUE;
|
||
|
}
|
||
|
} //end if valid query
|
||
|
|
||
|
UpdatePropertyPage(pBatch->dwTitleID, FALSE, hwndParent); //tell prop page ui that disc is no longer downloading
|
||
|
|
||
|
return(fSuccess);
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI SpawnSingleDownload(LPVOID pParam)
|
||
|
{
|
||
|
InterlockedIncrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
|
||
|
LPCDBATCH pBatch = (LPCDBATCH)pParam;
|
||
|
HWND hwndParent = g_hwndParent;
|
||
|
|
||
|
if (pBatch)
|
||
|
{
|
||
|
DoBatchDownload(pBatch,hwndParent);
|
||
|
|
||
|
//if download failed, add to batch if not already in db
|
||
|
//but only do this if batching is turned on
|
||
|
LPCDOPTIONS pOptions = g_pNetOpt->GetCDOpts();
|
||
|
if (pOptions)
|
||
|
{
|
||
|
LPCDOPTDATA pOptionData = pOptions->pCDData;
|
||
|
if (pOptionData)
|
||
|
{
|
||
|
if (pOptionData->fBatchEnabled)
|
||
|
{
|
||
|
if (!g_pNetData->QueryTitle(pBatch->dwTitleID))
|
||
|
{
|
||
|
g_pNetData->AddToBatch(pBatch->dwTitleID, pBatch->szTitleQuery, pBatch->dwNumTracks);
|
||
|
pOptions->dwBatchedTitles = g_pNetData->GetNumBatched();
|
||
|
PostMessage(hwndParent,WM_NET_DB_UPDATE_BATCH,0,0); //Tell the UI we changed number in batch
|
||
|
} //end if not in db
|
||
|
} //if batching is on
|
||
|
} //end if option data
|
||
|
} //end if poptions
|
||
|
|
||
|
delete [] pBatch->szTitleQuery;
|
||
|
delete pBatch;
|
||
|
}
|
||
|
|
||
|
//addref'ed before thread was created
|
||
|
g_pNetOpt->Release();
|
||
|
g_pNetData->Release();
|
||
|
|
||
|
InterlockedDecrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI SpawnBatchDownload(LPVOID pParam)
|
||
|
{
|
||
|
InterlockedIncrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
|
||
|
LPCDBATCH pBatchList = NULL;
|
||
|
HWND hwndParent = g_hwndParent;
|
||
|
|
||
|
if (g_pNetData)
|
||
|
{
|
||
|
if (SUCCEEDED(g_pNetData->LoadBatch(NULL,&pBatchList)))
|
||
|
{
|
||
|
DoBatchDownload(pBatchList,hwndParent);
|
||
|
g_pNetData->UnloadBatch(pBatchList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//addref'ed before thread was created
|
||
|
g_pNetOpt->Release();
|
||
|
g_pNetData->Release();
|
||
|
|
||
|
InterlockedDecrement((LONG*)&g_lNumDownloadingThreads);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI DoBatchDownload(LPCDBATCH pBatchList, HWND hwndParent)
|
||
|
{
|
||
|
EnterCriticalSection(&g_BatchCrit);
|
||
|
|
||
|
BOOL retcode = FALSE;
|
||
|
DWORD dwHow;
|
||
|
BOOL fConnected;
|
||
|
DWORD dwCurrent = 0;
|
||
|
DWORD dwOther = 0;
|
||
|
DWORD dwMultiHit = 0;
|
||
|
DWORD dwTimedOut = 0;
|
||
|
|
||
|
fConnected = _InternetGetConnectedState(&dwHow,0,TRUE); // Make sure we are connected to net
|
||
|
|
||
|
if (fConnected && g_pNetOpt && g_pNetData) // Make sure we are in a valid state
|
||
|
{
|
||
|
LPCDOPTIONS pOptions = g_pNetOpt->GetCDOpts(); // Get the options, needed for provider list
|
||
|
|
||
|
if (pOptions && pOptions->pCurrentProvider) // Make sure we have providers
|
||
|
{
|
||
|
LPCDPROVIDER pProviderList = NULL;
|
||
|
LPCDPROVIDER pProvider = NULL;
|
||
|
|
||
|
g_pNetOpt->CreateProviderList(&pProviderList); // Get the sorted provider list
|
||
|
pProvider = pProviderList; // Get the head of the list
|
||
|
|
||
|
LPCDBATCH pBatch;
|
||
|
|
||
|
if (pBatchList)
|
||
|
{
|
||
|
while (pProvider && !g_fCancelDownload) // loop thru providers, but check current first and only once.
|
||
|
{
|
||
|
BOOL fNotifiedUIProvider = FALSE;
|
||
|
pBatch = pBatchList;
|
||
|
|
||
|
while (pBatch && !g_fCancelDownload && !pProvider->fTimedOut) // We will loop thru each batched title
|
||
|
{
|
||
|
BOOL fAttemptDownload = TRUE; // Assume we are going to try to download all in batch
|
||
|
if (pBatch->fRemove)
|
||
|
{
|
||
|
fAttemptDownload = FALSE; //we've already tried this disc on one provider and got it
|
||
|
}
|
||
|
|
||
|
if (fAttemptDownload)
|
||
|
{
|
||
|
if (!fNotifiedUIProvider)
|
||
|
{
|
||
|
PostMessage(hwndParent,WM_NET_CHANGEPROVIDER,0,(LPARAM)pProvider); //Tell the UI who the provider is
|
||
|
fNotifiedUIProvider = TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL fTimeout = FALSE;
|
||
|
|
||
|
if (DownloadBatch(pBatch, pProvider, &dwMultiHit, &fTimeout, hwndParent)) // attempt to download this batch
|
||
|
{
|
||
|
pBatch->fRemove = TRUE; // This batch download succeeded, mark for termination from batch
|
||
|
|
||
|
if (pProvider == pOptions->pCurrentProvider)
|
||
|
{
|
||
|
dwCurrent++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwOther++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pProvider->fTimedOut = fTimeout;
|
||
|
}
|
||
|
|
||
|
//check to see if db write failed
|
||
|
if (g_fDBWriteFailure)
|
||
|
{
|
||
|
//let the UI know
|
||
|
PostMessage(hwndParent,WM_NET_DB_FAILURE,0,0);
|
||
|
|
||
|
//get out of the batch loop
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//let ui know that something happened with this disc
|
||
|
PostMessage(hwndParent,WM_NET_DONE,(WPARAM)g_dllInst,pBatch->dwTitleID);
|
||
|
|
||
|
//increment the meter if we know this is the last time we're
|
||
|
//visiting this particular disc ... either it was found, or
|
||
|
//we are out of possible places to look
|
||
|
if ((pBatch->fRemove) || (pProvider->pNext == NULL))
|
||
|
{
|
||
|
PostMessage(hwndParent,WM_NET_INCMETER,(WPARAM)g_dllInst,pBatch->dwTitleID);
|
||
|
}
|
||
|
|
||
|
} //end attempt on disc
|
||
|
|
||
|
pBatch = pBatch->pNext;
|
||
|
} //end batch
|
||
|
|
||
|
if (g_fDBWriteFailure)
|
||
|
{
|
||
|
//get out of the provider loop
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pProvider = pProvider->pNext; //providers are "in order"
|
||
|
} //end while cycling providers
|
||
|
|
||
|
} //end if load batch OK
|
||
|
|
||
|
//check to see if ALL providers timed out ... possible net problem
|
||
|
BOOL fAllFailed = TRUE;
|
||
|
pProvider = pProviderList;
|
||
|
while (pProvider!=NULL)
|
||
|
{
|
||
|
if (!pProvider->fTimedOut)
|
||
|
{
|
||
|
fAllFailed = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
pProvider = pProvider->pNext;
|
||
|
}
|
||
|
|
||
|
if (fAllFailed)
|
||
|
{
|
||
|
//let the UI know
|
||
|
PostMessage(hwndParent,WM_NET_NET_FAILURE,0,0);
|
||
|
}
|
||
|
|
||
|
g_pNetOpt->DestroyProviderList(&pProviderList);
|
||
|
} //end if pointers ok
|
||
|
|
||
|
#ifdef DBG
|
||
|
// Ok, output some interesting stat's about what happened.
|
||
|
{
|
||
|
TCHAR str[255];
|
||
|
wsprintf(str, TEXT("current = %d, other = %d, multihits = %d\n"), dwCurrent, dwOther, dwMultiHit);
|
||
|
OutputDebugString(str);
|
||
|
}
|
||
|
#endif
|
||
|
} //end if connected to net and pointers ok
|
||
|
|
||
|
if (!fConnected)
|
||
|
{
|
||
|
//may be a net problem
|
||
|
if ((dwHow & (INTERNET_CONNECTION_MODEM|INTERNET_CONNECTION_LAN)) == 0)
|
||
|
{
|
||
|
PostMessage(hwndParent,WM_NET_NET_FAILURE,0,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PostMessage(hwndParent,WM_NET_DONE,(WPARAM)g_dllInst,(LPARAM) 0); //fBadGuy ? -1 : 0);
|
||
|
|
||
|
LeaveCriticalSection(&g_BatchCrit);
|
||
|
|
||
|
return (retcode);
|
||
|
}
|
||
|
|
||
|
|