windows-nt/Source/XPSP1/NT/multimedia/media/deluxe2/cdnet/getinfo.cpp
2020-09-26 16:20:57 +08:00

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);
}