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