//+---------------------------------------------------------------------------- // File: persist.cxx // // Synopsis: // //----------------------------------------------------------------------------- // Includes ------------------------------------------------------------------- #include #include #include "wininet.h" // Constants ------------------------------------------------------------------ const LARGE_INTEGER LIB_ZERO = { 0, 0 }; const ULONG BUFFER_SIZE = 256; const ULONG CHARS_PER_LINE = 65; const WCHAR LPKPATH[] = L"LPKPath"; const TCHAR SZ_URLMON[] = _T("URLMON.DLL"); const TCHAR SZ_ISVALIDURL[] = _T("IsValidURL"); const TCHAR SZ_URLDOWNLOADTOCACHEFILE[] = _T("URLDownloadToCacheFileW"); typedef HRESULT (STDMETHODCALLTYPE *ISVALIDURL)(LPBC, LPCWSTR, DWORD); typedef HRESULT (STDMETHODCALLTYPE *URLDOWNLOADTOCACHEFILE)(LPUNKNOWN,LPCWSTR,LPWSTR, DWORD,DWORD,LPBINDSTATUSCALLBACK); //+---------------------------------------------------------------------------- // // Member: FindInStream // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- HRESULT CLicenseManager::FindInStream( IStream * pstm, BYTE * pbData, ULONG cbData) { BYTE bByte; ULONG cbRead; ULONG ibData = 0; HRESULT hr; Assert(pstm); Assert(pbData); Assert(cbData); // Read through the stream looking for the data for (;;) { // Read a byte of data hr = pstm->Read(&bByte, sizeof(BYTE), &cbRead); if (hr) goto Cleanup; if (!cbRead) break; if (bByte == pbData[ibData]) { ibData++; if (ibData >= cbData) break; } } // If the data was found, return success hr = (ibData == cbData ? S_OK : E_FAIL); Cleanup: return hr; } //+---------------------------------------------------------------------------- // // Member: GetClassID // // Synopsis: Return the object's CLSID // // Arguments: pclsid - Location at which to return the object's CLSID // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::GetClassID( CLSID * pclsid) { if (!pclsid) return E_INVALIDARG; *pclsid = CLSID_LicenseManager; return S_OK; } //+---------------------------------------------------------------------------- // // Member: IsDirty // // Synopsis: Return whether the object is dirty or not // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::IsDirty() { return (_fDirty ? S_OK : S_FALSE); } //+---------------------------------------------------------------------------- // // Member: Load // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::Load( IStream * pstm) { CBufferedStream bstm(pstm); LARGE_INTEGER libCur; ULARGE_INTEGER uibSize; HRESULT hr; Assert(_fPersistStream || _fPersistPBag); if (!pstm) return E_INVALIDARG; if (_fLoaded) return E_UNEXPECTED; // Prepare the buffered stream for use hr = bstm.SetBufferSize(BUFFER_SIZE); if (hr) goto Cleanup; // Determine the size of the stream hr = pstm->Seek(LIB_ZERO, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&libCur); if (hr) goto Cleanup; hr = pstm->Seek(LIB_ZERO, STREAM_SEEK_END, &uibSize); if (hr) goto Cleanup; hr = pstm->Seek(libCur, STREAM_SEEK_SET, NULL); if (hr) goto Cleanup; // Load from the buffered stream Assert(!uibSize.HighPart); hr = Load(&bstm, uibSize.LowPart); Cleanup: return hr; } //+---------------------------------------------------------------------------- // // Member: Load // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- HRESULT CLicenseManager::Load( IStream * pstm, ULONG cbSize) { CMemoryStream mstm; ULARGE_INTEGER uibSize = { cbSize, 0 }; char * psz = NULL; OLECHAR * polechar = NULL; DWORD cch; DWORD cchMax = 0; DWORD cLic; DWORD iLic; BOOL fSkipClass; HRESULT hr; // Scan for the LPK version identifier and skip over it cch = ::lstrlenA(g_pszLPKVersion1); hr = FindInStream(pstm, (BYTE *)g_pszLPKVersion1, cch); if (hr) goto Cleanup; // Allocate a memory-based stream to hold the binary data hr = mstm.SetSize(uibSize); if (hr) goto Cleanup; // Convert and load the LPK identifier hr = DecodeMIME64(pstm, &mstm, NULL); if (hr) goto Cleanup; Verify(SUCCEEDED(mstm.Seek(LIB_ZERO, STREAM_SEEK_SET, NULL))); hr = mstm.Read((void *)&_guidLPK, sizeof(_guidLPK), NULL); if (hr) goto Cleanup; // Convert and load the number of CLSID-License pairs Verify(SUCCEEDED(mstm.Seek(LIB_ZERO, STREAM_SEEK_SET, NULL))); hr = DecodeMIME64(pstm, &mstm, NULL); if (hr) goto Cleanup; Verify(SUCCEEDED(mstm.Seek(LIB_ZERO, STREAM_SEEK_SET, NULL))); hr = mstm.Read((void *)&cLic, sizeof(DWORD), NULL); if (hr) goto Cleanup; hr = _aryLic.SetSize((int)cLic); if (hr) goto Cleanup; ::memset((LICENSE *)_aryLic, 0, sizeof(LICENSE)*cLic); // Convert the remainder of the stream and from it load each CLSID-License pair // (If, somehow, invalid CLSIDs end up in the stream, skip over them during load) for (iLic = 0; iLic < cLic; ) { Verify(SUCCEEDED(mstm.Seek(LIB_ZERO, STREAM_SEEK_SET, NULL))); hr = DecodeMIME64(pstm, &mstm, NULL); if (hr) goto Cleanup; Verify(SUCCEEDED(mstm.Seek(LIB_ZERO, STREAM_SEEK_SET, NULL))); hr = mstm.Read((void *)&(_aryLic[iLic].clsid), sizeof(_aryLic[0].clsid), NULL); if (hr) goto Cleanup; fSkipClass = (_aryLic[iLic].clsid == CLSID_NULL); hr = mstm.Read((void *)&cch, sizeof(DWORD), NULL); if (hr) goto Cleanup; if (cch > cchMax) { delete [] psz; delete [] polechar; psz = new char[cch*sizeof(OLECHAR)]; // Review:JulianJ polechar = new OLECHAR[cch]; if (!psz || !polechar) { hr = E_OUTOFMEMORY; goto Cleanup; } cchMax = cch; } Assert(psz); Assert(polechar); // // REVIEW JulianJ read cch*2 bytes as we persisted entire string // hr = mstm.Read((void *)psz, cch*sizeof(OLECHAR), NULL); if (hr) goto Cleanup; if (!fSkipClass) { #if 1 ::memcpy(polechar, psz, cch*sizeof(OLECHAR)); #else #ifndef _PPCMAC ::MultiByteToWideChar(CP_ACP, 0, psz, cch, polechar, cch); #else ::memcpy(polechar, psz, cch); #endif #endif _aryLic[iLic].bstrLic = ::SysAllocStringLen(polechar, cch); if (!_aryLic[iLic].bstrLic) { hr = E_OUTOFMEMORY; goto Cleanup; } iLic++; } else { cLic--; } } // Ensure the array size is correct (in case any classes were skipped during load) if (cLic < (DWORD)_aryLic.Size()) { Verify(SUCCEEDED(_aryLic.SetSize(cLic))); } Cleanup: delete [] psz; delete [] polechar; _fLoaded = TRUE; return hr; } //+---------------------------------------------------------------------------- // // Member: Save // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::Save( IStream * pstm, BOOL fClearDirty) { CBufferedStream bstm(pstm, CHARS_PER_LINE, FALSE); ULARGE_INTEGER uibCur; TCHAR szString[MAX_PATH]; DWORD cbBuf = 0; DWORD cbLic; DWORD cb; BYTE * pb = NULL; BYTE * pbNext; int iLic; HRESULT hr = S_OK; Assert(_fPersistStream); if (!pstm) return E_INVALIDARG; // If this is a new LPK, generate an identifying GUID if (_guidLPK == GUID_NULL) { Assert(!_fLoaded); Verify(SUCCEEDED(::CoCreateGuid(&_guidLPK))); } // Write the text header to the LPK for (iLic=IDS_COPYTEXT; iLic <= IDS_COPYTEXT_MAX; iLic++) { cb = ::LoadString((HINSTANCE)g_hinst, iLic, szString, ARRAY_SIZE(szString)); hr = pstm->Write(szString, cb, NULL); if (hr) goto Cleanup; hr = pstm->Write(SZ_NEWLINE, CB_NEWLINE, NULL); if (hr) goto Cleanup; } // Write the version GUID to the LPK hr = pstm->Write(g_pszLPKVersion1, ::lstrlenA(g_pszLPKVersion1), NULL); if (hr) goto Cleanup; hr = pstm->Write(SZ_NEWLINE, CB_NEWLINE, NULL); if (hr) goto Cleanup; // Prepare the buffered stream as the target for encoding hr = bstm.SetBufferSize(BUFFER_SIZE); if (hr) goto Cleanup; // Write the identifying GUID to the LPK hr = EncodeMIME64((BYTE *)&_guidLPK, sizeof(_guidLPK), &bstm, NULL); if (hr) goto Cleanup; hr = bstm.Write(SZ_NEWLINE, CB_NEWLINE, NULL); if (hr) goto Cleanup; // Write the number of CLSID-License pairs to the LPK cb = (DWORD)_aryLic.Size(); hr = EncodeMIME64((BYTE *)&cb, sizeof(cb), &bstm, NULL); if (hr) goto Cleanup; hr = bstm.Write(SZ_NEWLINE, CB_NEWLINE, NULL); if (hr) goto Cleanup; // Write each CLSID-License pair to the LPK // (If the array contains empty entries, they are still persisted; this is necessary // because the number of entries persisted must match the count already written) for (iLic = 0; iLic < _aryLic.Size(); iLic++) { // Determine the amount of class data and ensure the buffer is sufficiently large cbLic = ::SysStringLen(_aryLic[iLic].bstrLic); cb = sizeof(CLSID) + sizeof(DWORD) + (sizeof(OLECHAR) * cbLic); if (cb > cbBuf) { cbBuf = cb; delete [] pb; pb = new BYTE[cbBuf]; if (!pb) { hr = E_OUTOFMEMORY; goto Cleanup; } } pbNext = pb; // Fill the buffer with the persistent state of the class *((CLSID *)pbNext) = _aryLic[iLic].clsid; pbNext += sizeof(CLSID); *((DWORD *)pbNext) = cbLic; pbNext += sizeof(DWORD); // // REVIEW JulianJ, Weird - we seem to get back a length prefixed ansi string! // #if 1 memcpy(pbNext, _aryLic[iLic].bstrLic, cbLic * (sizeof(OLECHAR))); #else #ifndef _PPCMAC ::WideCharToMultiByte(CP_ACP, 0, _aryLic[iLic].bstrLic, cbLic, (LPSTR)pbNext, cbLic, NULL, NULL); #else ::memcpy(pbNext, _aryLic[iLic].bstrLic, cbLic); #endif #endif // Encode the class to the stream hr = EncodeMIME64(pb, cb, &bstm, NULL); if (hr) goto Cleanup; hr = bstm.Write(SZ_NEWLINE, CB_NEWLINE, NULL); if (hr) goto Cleanup; } // Flush the buffered stream and mark the end of data // (Since not all streams support Seek and SetSize, errors from those methods // are ignored; since the stream contains a count, it can be safely loaded // without truncating unnecessary bytes) hr = bstm.Flush(); if (hr) goto Cleanup; Verify(SUCCEEDED(pstm->Seek(LIB_ZERO, STREAM_SEEK_CUR, &uibCur))); Verify(SUCCEEDED(pstm->SetSize(uibCur))); Cleanup: delete [] pb; _fLoaded = TRUE; _fDirty = !fClearDirty && SUCCEEDED(hr); return hr; } //+---------------------------------------------------------------------------- // // Member: GetSizeMax // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::GetSizeMax( ULARGE_INTEGER * pcbSize) { if (!pcbSize) return E_INVALIDARG; pcbSize->LowPart = pcbSize->HighPart = 0; return E_NOTIMPL; } //+---------------------------------------------------------------------------- // // Member: InitNew // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::InitNew() { if (_fLoaded) return E_UNEXPECTED; _fLoaded = TRUE; return S_OK; } //+---------------------------------------------------------------------------- // // Member: Load // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::Load( IPropertyBag * pPropBag, IErrorLog * pErrorLog) { HMODULE hURLMon = NULL; VARIANT var; HRESULT hr; Assert(_fPersistPBag); if (!pPropBag) return E_INVALIDARG; if (_fLoaded) return E_UNEXPECTED; ::VariantInit(&var); V_VT(&var) = VT_BSTR; V_BSTR(&var) = NULL; // Read the path from which to load the LPK file hr = pPropBag->Read(LPKPATH, &var, pErrorLog); if (!hr) { CFileStream fstm; FARPROC fpIsValidURL; FARPROC fpURLDownloadToCacheFileW; WCHAR szCachedFilename[MAX_PATH]; // Load the URL moniker library hURLMon = (HMODULE)::LoadLibrary(SZ_URLMON); if (!hURLMon) { hr = GetWin32Hresult(); goto Cleanup; } // Check the path, if it is for an absolute URL, reject it // (Only relative URLs are accepted) fpIsValidURL = GetProcAddress(hURLMon, SZ_ISVALIDURL); if (!fpIsValidURL) { hr = GetWin32Hresult(); goto Cleanup; } hr = (*((ISVALIDURL)fpIsValidURL))(NULL, V_BSTR(&var), 0); if (hr == S_OK) { hr = E_INVALIDARG; goto Cleanup; } // Download the .LPK to a locally cached file fpURLDownloadToCacheFileW = GetProcAddress(hURLMon, SZ_URLDOWNLOADTOCACHEFILE); if (!fpURLDownloadToCacheFileW) { hr = GetWin32Hresult(); goto Cleanup; } // // Get the service provider from our site // IServiceProvider * pServiceProvider; hr = GetSite(IID_IServiceProvider, (void**)&pServiceProvider); if (!SUCCEEDED(hr)) goto Cleanup; // // Get an IBindHost from the service provider // IBindHost *pBindHost; hr = pServiceProvider->QueryService( SID_IBindHost, IID_IBindHost, (void**)&pBindHost); pServiceProvider->Release(); if (!SUCCEEDED(hr)) goto Cleanup; // // Now create a full moniker // IMoniker *pMoniker; hr = pBindHost->CreateMoniker(V_BSTR(&var), NULL, &pMoniker,0); pBindHost->Release(); if (!SUCCEEDED(hr)) goto Cleanup; // // Create a bind context // IBindCtx * pBindCtx; hr = CreateBindCtx(0, &pBindCtx); if (!SUCCEEDED(hr)) { pMoniker->Release(); goto Cleanup; } // // Extract display name // LPOLESTR wszFullLPKPath; hr = pMoniker->GetDisplayName(pBindCtx, NULL, &wszFullLPKPath); pMoniker->Release(); pBindCtx->Release(); if (!SUCCEEDED(hr)) goto Cleanup; hr = (*((URLDOWNLOADTOCACHEFILE)fpURLDownloadToCacheFileW))( _pUnkOuter, wszFullLPKPath, szCachedFilename, URLOSTRM_GETNEWESTVERSION, 0, NULL); CoTaskMemFree(wszFullLPKPath); if (hr) goto Cleanup; // Open a stream on the file and load from the stream hr = fstm.Init(szCachedFilename, GENERIC_READ); if (!hr) { CBufferedStream mstm(&fstm); ULONG cbSize; hr = mstm.SetBufferSize(BUFFER_SIZE); if (hr) goto Cleanup; Verify(SUCCEEDED(fstm.GetFileSize(&cbSize))); hr = Load(&mstm, cbSize); } } Cleanup: _fLoaded = TRUE; ::VariantClear(&var); if (hURLMon) { ::FreeLibrary(hURLMon); } return hr; } //+---------------------------------------------------------------------------- // // Member: Save // // Synopsis: // // Arguments: // // Returns: // //----------------------------------------------------------------------------- STDMETHODIMP CLicenseManager::Save( IPropertyBag * pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) { UNREF(pPropBag); UNREF(fClearDirty); UNREF(fSaveAllProperties); return E_NOTIMPL; }