// Protocol.cpp -- Implementation for class CIOITnetProtocol #include "StdAfx.h" // Creation: HRESULT CIOITnetProtocol::Create (IUnknown *punkOuter, REFIID riid, PPVOID ppv) { CIOITnetProtocol *pNP = New CIOITnetProtocol(punkOuter); return FinishSetup(pNP? pNP->m_ImpIOITnetProtocol.Init() : STG_E_INSUFFICIENTMEMORY, pNP, riid, (PPVOID) ppv ); } // Constructor and Destructor: CIOITnetProtocol::CImpIOITnetProtocol::CImpIOITnetProtocol (CIOITnetProtocol *pBackObj, IUnknown *pUnkOuter) : IOITnetProtocol(pBackObj, pUnkOuter) , IOITnetProtocolInfo(pBackObj, pUnkOuter) { m_grfSTI = 0; m_grfBINDF = 0; m_szTempPath[0] = 0; ZeroMemory(&m_BindInfo, sizeof(m_BindInfo)); m_pwcsURL = NULL; m_pcsDisplayName = NULL; m_pOIProtSink = NULL; m_pOIBindInfo = NULL; m_pStream = NULL; } CIOITnetProtocol::CImpIOITnetProtocol::~CImpIOITnetProtocol(void) { if (m_pcsDisplayName) { UnlockUrlCacheEntryFile(m_pcsDisplayName, 0); delete [] m_pcsDisplayName; } if (m_pwcsURL) delete [] m_pwcsURL; if (m_pOIProtSink) m_pOIProtSink->Release(); if (m_pOIBindInfo) m_pOIBindInfo->Release(); if (m_pStream) m_pStream->Release(); if (m_szTempPath[0]) DeleteFile(m_szTempPath); } // Initialing routine: HRESULT CIOITnetProtocol::CImpIOITnetProtocol::Init() { return NO_ERROR; } // IOITnetProtocolRoot interfaces: void STDMETHODCALLTYPE MapSurrogateCharacters (PWCHAR pwcsBuffer) { PWCHAR pwcsDest = pwcsBuffer; for (;;) { WCHAR wc = *pwcsBuffer++; *pwcsDest++ = wc; if (!wc) break; if (wc == L'%' && pwcsBuffer[0] && pwcsBuffer[1]) { WCHAR wcSurrogate = 0; WCHAR wcFirst = pwcsBuffer[0]; WCHAR wcSecond = pwcsBuffer[1]; if (wcFirst >= L'0' && wcFirst <= L'9') wcSurrogate = wcFirst - L'0'; else if (wcFirst >= L'A' && wcFirst <= L'F') wcSurrogate = 10 + wcFirst - L'A'; else if (wcFirst >= L'a' && wcFirst <= L'f') wcSurrogate = 10 + wcFirst - L'a'; else continue; wcSurrogate <<= 4; if (wcSecond >= L'0' && wcSecond <= L'9') wcSurrogate |= wcSecond - L'0'; else if (wcSecond >= L'A' && wcSecond <= L'F') wcSurrogate |= 10 + wcSecond - L'A'; else if (wcSecond >= L'a' && wcSecond <= L'f') wcSurrogate |= 10 + wcSecond - L'a'; else continue; pwcsDest[-1] = wcSurrogate; pwcsBuffer += 2; } } } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Start ( /* [in] */ LPCWSTR szUrl, /* [in] */ IOInetProtocolSink __RPC_FAR *pOIProtSink, /* [in] */ IOInetBindInfo __RPC_FAR *pOIBindInfo, /* [in] */ DWORD grfSTI, /* [in] */ DWORD dwReserved ) { DWORD cwc = wcsLen(szUrl); PWCHAR pwcsBuffer = PWCHAR(_alloca((cwc + 1) * sizeof(WCHAR))); if (!pwcsBuffer) return E_OUTOFMEMORY; CopyMemory(pwcsBuffer, szUrl, sizeof(WCHAR) * (cwc + 1)); PWCHAR pwcsExternalPath = NULL; PWCHAR pwcsInternalPath = NULL; HRESULT hr = DisectUrl(pwcsBuffer, NULL, &pwcsExternalPath, &pwcsInternalPath); if (!SUCCEEDED(hr)) { if (hr != INET_E_DEFAULT_ACTION && (grfSTI & PI_PARSE_URL)) return S_FALSE; else return hr; } hr = AssembleUrl(NULL, 0, &cwc, L"ms-its", pwcsExternalPath, pwcsInternalPath); RonM_ASSERT(hr == E_OUTOFMEMORY); m_pwcsURL = New WCHAR[cwc]; if (!m_pwcsURL) return E_OUTOFMEMORY; DWORD cwcRequired = 0; hr = AssembleUrl(m_pwcsURL, cwc, &cwcRequired, L"ms-its", pwcsExternalPath, pwcsInternalPath); RonM_ASSERT(hr == S_OK); m_pOIProtSink = pOIProtSink; m_pOIBindInfo = pOIBindInfo; m_grfSTI = grfSTI; m_pOIProtSink->AddRef(); m_pOIBindInfo->AddRef(); m_BindInfo.cbSize = sizeof(BINDINFO); hr = GetBindInfo(&m_grfBINDF, &m_BindInfo); if (!SUCCEEDED(hr)) return hr; if (grfSTI & PI_PARSE_URL) return ParseAndBind(FALSE); if (!(grfSTI & PI_FORCE_ASYNC)) return ParseAndBind(TRUE); PROTOCOLDATA protdata; protdata.grfFlags = PI_FORCE_ASYNC; protdata.dwState = ITS_BIND_DATA; protdata.pData = NULL; protdata.cbData = 0; Switch(&protdata); return E_PENDING; } HRESULT STDMETHODCALLTYPE CopyStreamToFile(const WCHAR *pwcsFilePath, IStream *pStreamSrc) { IStream *pStream; IFSStorage *pFSS = NULL; HRESULT hr = CFileSystemStorage::Create(NULL, IID_IFSStorage, (VOID **) &pFSS); if (!SUCCEEDED(hr)) return hr; hr = pFSS->FSOpenStream((const WCHAR *) pwcsFilePath, STGM_READWRITE | STGM_SHARE_DENY_NONE, &pStream ); pFSS->Release(); if (!SUCCEEDED(hr)) return hr; RonM_ASSERT(pStreamSrc); STATSTG statstg; hr = pStreamSrc->Stat(&statstg, STATFLAG_NONAME); if (!SUCCEEDED(hr)) return hr; #ifdef _DEBUG hr = #endif // _DEBUG pStreamSrc->Seek(CLINT(0).Li(), STREAM_SEEK_SET, NULL); RonM_ASSERT(hr == S_OK); hr = pStreamSrc->CopyTo(pStream, statstg.cbSize, NULL, NULL); pStream->Release(); if (!SUCCEEDED(hr)) return hr; #ifdef _DEBUG hr = #endif // _DEBUG pStreamSrc->Seek(CLINT(0).Li(), STREAM_SEEK_SET, NULL); RonM_ASSERT(hr == S_OK); return hr; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::ParseAndBind(BOOL fBind) { WCHAR *pwcsURL = m_pwcsURL; WCHAR *pwcsURLActual = NULL; BOOL fNoFile = (m_grfBINDF & BINDF_NEEDFILE) == 0; // FALSE; BOOL fNoReadCache = m_grfBINDF & (BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE | BINDF_PRAGMA_NO_CACHE | BINDF_DIRECT_READ ); BOOL fNoWriteCache = m_grfBINDF & (BINDF_NOWRITECACHE | BINDF_PRAGMA_NO_CACHE | BINDF_DIRECT_READ ); IBindCtx *pBCtx = NULL; IMoniker *pMK = NULL; PWCHAR pwcsExtension = NULL; PWCHAR pwcsMimeType = NULL; PWCHAR pwcsStreamName = NULL; ULONG chEaten = 0; DWORD cbSample = 0; HRESULT hr = CreateBindCtx(0, &pBCtx); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; hr = CStorageMoniker::CreateStorageMoniker(NULL, pBCtx, pwcsURL, &chEaten, &pMK); if (!fBind) { if (hr != S_OK) hr = S_FALSE; goto exit_ParseAndBind; } if (!SUCCEEDED(hr)) goto exit_ParseAndBind; hr = pMK->BindToStorage(pBCtx, NULL, IID_IStream, (VOID **) &m_pStream); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; hr = pMK->GetDisplayName(NULL, NULL, &pwcsURLActual); RonM_ASSERT(hr == S_OK); { UINT cwc = wcsLen(pwcsURLActual); WCHAR *pwc = pwcsURLActual + cwc; for (;;) { WCHAR wc = *--pwc; if (!pwcsExtension && wc == L'.') pwcsExtension = pwc + 1; if (wc == L':' || wc == L'/' || wc == L'\\') { pwc++; break; } RonM_ASSERT(--cwc); } pwcsStreamName = pwc; ReportProgress(BINDSTATUS_SENDINGREQUEST, (const WCHAR *) pwc); } STATSTG statstg; hr = m_pStream->Stat(&statstg, STATFLAG_NONAME); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; RonM_ASSERT(statstg.cbSize.HighPart == 0); BYTE abSample[CB_SAMPLE]; if (pwcsExtension && pwcsExtension[0]) { UINT cwc = wcsLen(pwcsExtension-1); UINT cb = sizeof(WCHAR) * (cwc + 1); char *pcsExtension = PCHAR(_alloca(cb)); if (!pcsExtension) { hr = E_OUTOFMEMORY; goto exit_ParseAndBind; } cb = WideCharToMultiByte(GetACP(), WC_COMPOSITECHECK, pwcsExtension-1, cwc + 1, pcsExtension, cb, NULL, NULL ); if (!cb) { hr = E_FAIL; goto exit_ParseAndBind; } HKEY hkeyMime; if (RegOpenKeyEx(HKEY_CLASSES_ROOT, pcsExtension, 0, KEY_QUERY_VALUE, &hkeyMime) == ERROR_SUCCESS ) { cb = CB_SAMPLE; if (RegQueryValueEx(hkeyMime, "Content Type", NULL, NULL, abSample, (DWORD *) &cb) == ERROR_SUCCESS && cb > 0 ) { PWCHAR pwc = (PWCHAR) (OLEHeap()->Alloc(cb * sizeof(WCHAR))); if (pwc) { UINT cwc = MultiByteToWideChar (GetACP(), MB_PRECOMPOSED, (const char *) abSample, cb, pwc, cb ); if (cwc) pwcsMimeType = pwc; else OLEHeap()->Free(pwc); } } RegCloseKey(hkeyMime); } } if (!pwcsMimeType) { DWORD cbRead; cbSample = statstg.cbSize.LowPart < CB_SAMPLE? statstg.cbSize.LowPart : CB_SAMPLE; hr = m_pStream->Read(abSample, cbSample, &cbRead); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; if (cbRead != cbSample) { hr = E_FAIL; goto exit_ParseAndBind; } m_pStream->Seek(CLINT(0).Li(), STREAM_SEEK_SET, NULL); hr = pFindMimeFromData(NULL, pwcsStreamName, abSample, cbSample, NULL, 0, &pwcsMimeType, 0); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; } if (pwcsMimeType) { // The test below is a hack to get around a bug in UrlMon. // UrlMon incorrectly tells us we need to copy HTML files into the cache. if (!wcsicmp_0x0409(pwcsMimeType, L"text/html")) fNoFile = TRUE; ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, pwcsMimeType); } if (!fNoFile) { CHAR acsFilePath[MAX_PATH]; WCHAR awcsFilePath[MAX_PATH]; hr = StreamToIEFile(m_pStream, m_pwcsURL, m_pcsDisplayName, acsFilePath, awcsFilePath, m_szTempPath, pMK, fNoWriteCache, fNoReadCache ); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; hr = ReportProgress(BINDSTATUS_CACHEFILENAMEAVAILABLE, awcsFilePath); if (!SUCCEEDED(hr)) goto exit_ParseAndBind; } hr = ReportData(BSCF_FIRSTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE, statstg.cbSize.LowPart, statstg.cbSize.LowPart ); // The call to ReportData may indirectly call the Terminate method. So we have // to be very careful about references to m_pOIProtSink afterwards. if (SUCCEEDED(hr) && m_pOIProtSink) hr = ReportProgress(BSCF_LASTDATANOTIFICATION, NULL); if (SUCCEEDED(hr) && m_pOIProtSink) ReportResult(hr, 0, 0); exit_ParseAndBind: if (pwcsURLActual) OLEHeap()->Free(pwcsURLActual); if (pMK) pMK->Release(); if (pBCtx) pBCtx->Release(); if (hr != NO_ERROR) ReportResult(hr, 0, 0); return hr; } HRESULT STDMETHODCALLTYPE StreamToIEFile (IStream *pStreamSrc, PWCHAR pwcsDisplayName, PCHAR &pcsDisplayName, PCHAR pcsFileName, PWCHAR pwcsFileName, PCHAR pcsTempFile, IMoniker *pmk, BOOL fNoWriteCache, BOOL fNoReadCache ) { HRESULT hr = NO_ERROR; PWCHAR pwcsExtension = NULL; PWCHAR pwc = NULL; PCHAR pcsExtension = ""; STATSTG statstg; hr = pStreamSrc->Stat(&statstg, STATFLAG_NONAME); if (!SUCCEEDED(hr)) goto exit_StreamToIEFile; RonM_ASSERT(statstg.cbSize.HighPart == 0); FILETIME ftLastModified; ftLastModified.dwLowDateTime = 0; ftLastModified.dwHighDateTime = 0; // pmk->GetTimeOfLastChange(pBCtx, NULL, &ftLastModified); pwc = pwcsDisplayName + wcsLen(pwcsDisplayName); for (;;) { WCHAR wc = *--pwc; if (!pwcsExtension && wc == L'.') pwcsExtension = pwc + 1; if (wc == L':' || wc == L'/' || wc == L'\\') { pwc++; break; } RonM_ASSERT(pwc > pwcsDisplayName); } if (pwcsExtension && pwcsExtension[0]) { UINT cwc = wcsLen(pwcsExtension); UINT cb = sizeof(WCHAR) * (cwc + 1); pcsExtension = PCHAR(_alloca(cb)); if (!pcsExtension) { hr = E_OUTOFMEMORY; goto exit_StreamToIEFile; } cb = WideCharToMultiByte(GetACP(), WC_COMPOSITECHECK, pwcsExtension, cwc + 1, pcsExtension, cb, NULL, NULL ); if (!cb) { hr = E_FAIL; goto exit_StreamToIEFile; } } if (fNoWriteCache) { DWORD cbPath= GetTempPath(MAX_PATH, pcsTempFile); if (!cbPath) lstrcpyA(pcsTempFile, ".\\"); char szPrefix[4] = "IMT"; // BugBug! May need to make this a random string. char szFullPath[MAX_PATH]; if (!GetTempFileName(pcsTempFile, szPrefix, 0, szFullPath)) { hr = CFSLockBytes::CImpILockBytes::STGErrorFromFSError(GetLastError()); pcsTempFile[0] = 0; goto exit_StreamToIEFile; } lstrcpyA(pcsTempFile, szFullPath); char *pch = pcsTempFile + lstrlenA(pcsTempFile); for (;;) { if (pch == pcsTempFile) { RonM_ASSERT(FALSE); hr = E_UNEXPECTED; DeleteFile(pcsTempFile); pcsTempFile[0] = 0; goto exit_StreamToIEFile; } if ('.' == *--pch) { ++pch; break; } } UINT cbExtension = lstrlenA(pcsExtension); if (pch + cbExtension - pcsTempFile >= MAX_PATH) { hr = E_UNEXPECTED; DeleteFile(pcsTempFile); pcsTempFile[0] = 0; goto exit_StreamToIEFile; } CopyMemory(pch, pcsExtension, cbExtension + 1); if (!MoveFileEx(szFullPath, pcsTempFile, MOVEFILE_REPLACE_EXISTING)) { hr = E_UNEXPECTED; DeleteFile(szFullPath); pcsTempFile[0] = 0; goto exit_StreamToIEFile; } UINT cwc = MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, pcsTempFile, 1 + lstrlenA(pcsTempFile), pwcsFileName, MAX_PATH ); if (cwc == 0) { hr = E_FAIL; DeleteFile(pcsTempFile); pcsTempFile [0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } hr = CopyStreamToFile((const WCHAR *)pwcsFileName, pStreamSrc); if (!SUCCEEDED(hr)) { DeleteFile(pcsTempFile); pcsTempFile[0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } lstrcpyA(pcsFileName, pcsTempFile); } else { UINT cwc = wcsLen(pwcsDisplayName); UINT cb = sizeof(WCHAR) * (cwc + 1); pcsDisplayName = New char[cb]; if (!pcsDisplayName) { hr = E_OUTOFMEMORY; goto exit_StreamToIEFile; } cb = WideCharToMultiByte(GetACP(), WC_COMPOSITECHECK, pwcsDisplayName, cwc + 1, pcsDisplayName, cb, NULL, NULL ); if (!cb) { hr = INET_E_DOWNLOAD_FAILURE; delete [] pcsDisplayName; pcsDisplayName = NULL; goto exit_StreamToIEFile; } BOOL fResult = FALSE; DWORD dwCEISize = 0; ULONG ulErr = 0; if (!fNoReadCache) { RonM_ASSERT(dwCEISize == 0); fResult = RetrieveUrlCacheEntryFile(pcsDisplayName, NULL, &dwCEISize, 0); RonM_ASSERT(!fResult); ulErr= GetLastError(); if (ulErr == ERROR_INSUFFICIENT_BUFFER) { dwCEISize += 4; // To work around a bug in the RetrieveUrlCacheEntryFile; // It sometimes gives an incorrect size immediately after // data has been copied to the cache. INTERNET_CACHE_ENTRY_INFOA *pCEI = (INTERNET_CACHE_ENTRY_INFOA *) _alloca(dwCEISize); if (!pCEI) { hr = E_OUTOFMEMORY; goto exit_StreamToIEFile; } pCEI->dwStructSize = sizeof(INTERNET_CACHE_ENTRY_INFOA); fResult = RetrieveUrlCacheEntryFile(pcsDisplayName, pCEI, &dwCEISize, 0); ulErr = GetLastError(); if (fResult) if ( pCEI->LastModifiedTime.dwLowDateTime == ftLastModified.dwLowDateTime && pCEI->LastModifiedTime.dwHighDateTime == ftLastModified.dwHighDateTime && pCEI->dwSizeLow == statstg.cbSize.LowPart && pCEI->dwSizeHigh == statstg.cbSize.HighPart ) { lstrcpyA(pcsFileName, pCEI->lpszLocalFileName); cwc = MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, pcsFileName, 1 + lstrlenA(pcsFileName), pwcsFileName, MAX_PATH ); RonM_ASSERT(cwc != 0); } else { UnlockUrlCacheEntryFile(pcsDisplayName, 0); fResult = FALSE; } } } if (!fResult) { fResult = CreateUrlCacheEntryA(pcsDisplayName, statstg.cbSize.LowPart, pcsExtension, pcsFileName, 0 ); if (!fResult) { hr = INET_E_CANNOT_INSTANTIATE_OBJECT; delete [] pcsDisplayName; pcsDisplayName = NULL; goto exit_StreamToIEFile; } cwc = MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, pcsFileName, 1 + lstrlenA(pcsFileName), pwcsFileName, MAX_PATH ); hr = CopyStreamToFile((const WCHAR *) pwcsFileName, pStreamSrc); if (!fResult) { hr = INET_E_CANNOT_INSTANTIATE_OBJECT; delete [] pcsDisplayName; pcsDisplayName = NULL; pcsFileName[0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } FILETIME ftExpire; ftExpire.dwLowDateTime = 0; ftExpire.dwHighDateTime = 0; fResult = CommitUrlCacheEntryA(pcsDisplayName, pcsFileName, ftExpire, ftLastModified, NORMAL_CACHE_ENTRY, NULL, 0, pcsExtension, 0 ); if (!fResult) { ulErr= GetLastError(); hr = INET_E_CANNOT_INSTANTIATE_OBJECT; delete [] pcsDisplayName; pcsDisplayName = NULL; pcsFileName[0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } dwCEISize = 0; fResult = RetrieveUrlCacheEntryFile(pcsDisplayName, NULL, &dwCEISize, 0); RonM_ASSERT(!fResult); ulErr= GetLastError(); if (ulErr == ERROR_INSUFFICIENT_BUFFER) { dwCEISize += 4; // To work around a bug in the RetrieveUrlCacheEntryFile; // It sometimes gives an incorrect size immediately after // data has been copied to the cache. INTERNET_CACHE_ENTRY_INFOA *pCEI = (INTERNET_CACHE_ENTRY_INFOA *) _alloca(dwCEISize); if (!pCEI) { hr = E_OUTOFMEMORY; delete [] pcsDisplayName; pcsDisplayName = NULL; pcsFileName[0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } pCEI->dwStructSize = sizeof(INTERNET_CACHE_ENTRY_INFOA); fResult = RetrieveUrlCacheEntryFile(pcsDisplayName, pCEI, &dwCEISize, 0); if (!fResult) { ulErr= GetLastError(); RonM_ASSERT(FALSE); hr = INET_E_CANNOT_INSTANTIATE_OBJECT; delete [] pcsDisplayName; pcsDisplayName = NULL; pcsFileName[0] = 0; pwcsFileName[0] = 0; goto exit_StreamToIEFile; } } } } return NO_ERROR; exit_StreamToIEFile: return hr; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Continue (/* [in] */ PROTOCOLDATA __RPC_FAR *pProtocolData) { switch (pProtocolData->dwState) { case ITS_BIND_DATA: return ParseAndBind(TRUE); default: RonM_ASSERT(FALSE); return INET_E_INVALID_REQUEST; } } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Abort ( /* [in] */ HRESULT hrReason, /* [in] */ DWORD dwOptions ) { return ReportResult(E_ABORT, 0, 0); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Terminate (/* [in] */ DWORD dwOptions) { if (m_pwcsURL) { delete [] m_pwcsURL; m_pwcsURL = NULL; } if (m_pOIProtSink) { m_pOIProtSink->Release(); m_pOIProtSink = NULL; } if (m_pOIBindInfo) { m_pOIBindInfo->Release(); m_pOIBindInfo = NULL; } return NO_ERROR; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Suspend(void) { return NO_ERROR; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Resume(void) { return NO_ERROR; } // IOITnetProtocol interfaces: HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Read ( /* [length_is][size_is][out] */ void __RPC_FAR *pv, /* [in] */ ULONG cb, /* [out] */ ULONG __RPC_FAR *pcbRead ) { if (m_pStream) return m_pStream->Read(pv, cb, pcbRead); else return INET_E_DATA_NOT_AVAILABLE; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Seek ( /* [in] */ LARGE_INTEGER dlibMove, /* [in] */ DWORD dwOrigin, /* [out] */ ULARGE_INTEGER __RPC_FAR *plibNewPosition ) { if (m_pStream) return m_pStream->Seek(dlibMove, dwOrigin, plibNewPosition); else return INET_E_DATA_NOT_AVAILABLE; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::LockRequest (/* [in] */ DWORD dwOptions) { return NO_ERROR; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::UnlockRequest(void) { return NO_ERROR; } HRESULT STDMETHODCALLTYPE DisectUrl (PWCHAR pwcsUrlBuffer, PWCHAR *ppwcProtocolName, PWCHAR *ppwcExternalPath, PWCHAR *ppwcInternalPath ) { PWCHAR pwcProtocolName = NULL, pwcExternalPath = NULL, pwcInternalPath = NULL; MapSurrogateCharacters(pwcsUrlBuffer); PWCHAR pwc = wcsChr((const WCHAR *) pwcsUrlBuffer, L':'); if (!pwc) return URL_E_INVALID_SYNTAX; *pwc++ = 0; pwcProtocolName = pwcsUrlBuffer; if ( L'@' == *pwcProtocolName && !wcsicmp_0x0409((const WCHAR *) (pwcProtocolName+1), L"msitstore") ) { pwcProtocolName = L"mk:@msitstore"; } else if (!wcsicmp_0x0409((const WCHAR *)pwcProtocolName, L"mk")) { // This URL begins with "mk:". We handle entries which // begin with "mk:@MSITStore:" // We treat the @ as part of the protocol name. // So we must first put the colon separator back. pwc[-1] = L':'; if (L'@' != *pwc) return URL_E_INVALID_SYNTAX; PWCHAR pwcClassName = pwc + 1; pwc = wcsChr((const WCHAR *) pwcClassName, L':'); if (!pwc) return URL_E_INVALID_SYNTAX; *pwc++ = 0; if (wcsicmp_0x0409((const WCHAR *)pwcClassName, L"msitstore")) return INET_E_DEFAULT_ACTION; } else if (!wcsicmp_0x0409((const WCHAR *)pwcsUrlBuffer, L"its")) { } else if (!wcsicmp_0x0409((const WCHAR *)pwcsUrlBuffer, L"ms-its")) { } else return INET_E_DEFAULT_ACTION; pwcExternalPath = pwc; pwc += wcsLen(pwc); for (; pwc > pwcExternalPath; ) { WCHAR wc = *--pwc; if (wc == L':') if (pwc > pwcExternalPath && L':' == *--pwc) { *pwc = 0; pwc += 2; break; } } if (pwc == pwcExternalPath) pwc += wcsLen(pwc); if (!*pwcExternalPath || wcsLen(pwcExternalPath) >= MAX_PATH) return URL_E_INVALID_SYNTAX; pwcInternalPath = pwc; for (;*pwc; pwc++); if (pwcInternalPath == pwc) pwcInternalPath = L"/"; if (wcsLen(pwcInternalPath) >= MAX_PATH) return URL_E_INVALID_SYNTAX; if (ppwcProtocolName) *ppwcProtocolName = pwcProtocolName; if (ppwcExternalPath) *ppwcExternalPath = pwcExternalPath; if (ppwcInternalPath) *ppwcInternalPath = pwcInternalPath; return NO_ERROR; } HRESULT STDMETHODCALLTYPE AssembleUrl (PWCHAR pwcsResult, DWORD cwcBuffer, DWORD *pcwcRequired, PWCHAR pwcsProtocolName, PWCHAR pwcsExternalPath, PWCHAR pwcsInternalPath ) { UINT cwc = wcsLen(pwcsProtocolName) + wcsLen(pwcsExternalPath) + wcsLen(pwcsInternalPath) + 4; *pcwcRequired = cwc; if (cwc > cwcBuffer) return E_OUTOFMEMORY; wcsCpy(pwcsResult, pwcsProtocolName); wcsCat(pwcsResult, L":"); wcsCat(pwcsResult, pwcsExternalPath); wcsCat(pwcsResult, L"::"); wcsCat(pwcsResult, pwcsInternalPath); return NO_ERROR; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::ParseUrl ( /* [in] */ LPCWSTR pwzUrl, /* [in] */ PARSEACTION ParseAction, /* [in] */ DWORD dwParseFlags, /* [out] */ LPWSTR pwzResult, /* [in] */ DWORD cchResult, /* [out] */ DWORD __RPC_FAR *pcchResult, /* [in] */ DWORD dwReserved ) { switch (ParseAction) { case PARSE_CANONICALIZE: case PARSE_SECURITY_URL: { // First we make a working copy of the URL. UINT cwc = wcsLen(pwzUrl); UINT cb = sizeof(WCHAR) * (cwc + 1); PWCHAR pwcsUrl = PWCHAR(_alloca(cb)); if (!pwcsUrl) return E_OUTOFMEMORY; CopyMemory(pwcsUrl, pwzUrl, cb); PWCHAR pwcProtocolName = NULL, pwcExternalPath = NULL, pwcInternalPath = NULL; BOOL fLocalFilePath = FALSE; HRESULT hr = DisectUrl(pwcsUrl, &pwcProtocolName, &pwcExternalPath, &pwcInternalPath); if (!SUCCEEDED(hr)) return hr; // Here we copy the external path string to a buffer because // it may be a partial local path which needs to be mapped into // a full path. WCHAR awcsExternalPath[MAX_PATH]; RonM_ASSERT(wcsLen(pwcExternalPath) < MAX_PATH); wcsCpy(awcsExternalPath, pwcExternalPath); PWCHAR pwc = wcsChr((const WCHAR *) awcsExternalPath, L':'); if (pwc && pwc[1] == L':') // Ignore a "::" separator pwc = NULL; // Here we're special casing non-protocol references to a file. // We recognize those situations by looking for a protocol prefix. // Protocol prefixes have the form : // where is always longer than one character. if (!pwc || (pwc - awcsExternalPath == 1)) { fLocalFilePath = TRUE; if (FindRootStorageFile(awcsExternalPath) == S_OK) pwcExternalPath = awcsExternalPath; } if (ParseAction == PARSE_SECURITY_URL) { UINT cwcExt = 1 + wcsLen(pwcExternalPath); if (fLocalFilePath) { // It's a local file. Got to prefix the result with // "File://". RonM_ASSERT(7 == wcsLen(L"File://")); *pcchResult = cwcExt + 7; if (cchResult < (cwcExt + 7)) return E_OUTOFMEMORY; CopyMemory(pwzResult, L"File://", 7 * sizeof(WCHAR)); pwzResult += 7; } else { *pcchResult = cwcExt; if (cwcExt > cchResult) return E_OUTOFMEMORY; } CopyMemory(pwzResult, pwcExternalPath, cwcExt * sizeof(WCHAR)); return NO_ERROR; } cwc = wcsLen(pwcInternalPath); BOOL fStorage = pwcInternalPath[cwc-1] == L'/' || pwcInternalPath[cwc-1] == L'\\'; WCHAR awcsInternalPath[MAX_PATH]; hr = ResolvePath(awcsInternalPath, L"/", (const WCHAR *) pwcInternalPath, fStorage); if (!SUCCEEDED(hr)) return hr; hr = AssembleUrl(pwzResult, cchResult, pcchResult, pwcProtocolName, pwcExternalPath, awcsInternalPath ); return hr; } case PARSE_FRIENDLY: case PARSE_ROOTDOCUMENT: case PARSE_DOCUMENT: case PARSE_ANCHOR: case PARSE_ENCODE: case PARSE_DECODE: case PARSE_PATH_FROM_URL: case PARSE_URL_FROM_PATH: case PARSE_LOCATION: case PARSE_MIME: case PARSE_SECURITY_DOMAIN: default: return INET_E_DEFAULT_ACTION; } } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::CombineUrl ( /* [in] */ LPCWSTR pwzBaseUrl, /* [in] */ LPCWSTR pwzRelativeUrl, /* [in] */ DWORD dwCombineFlags, /* [out] */ LPWSTR pwzResult, /* [in] */ DWORD cchResult, /* [out] */ DWORD __RPC_FAR *pcchResult, /* [in] */ DWORD dwReserved ) { // First we make a working copy of the URL. UINT cwc = wcsLen(pwzBaseUrl); UINT cb = sizeof(WCHAR) * (cwc + 1); PWCHAR pwcsUrl = PWCHAR(_alloca(cb)); if (!pwcsUrl) return E_OUTOFMEMORY; CopyMemory(pwcsUrl, pwzBaseUrl, cb); PWCHAR pwcProtocolName = NULL, pwcExternalPath = NULL, pwcInternalPath = NULL; HRESULT hr = DisectUrl(pwcsUrl, &pwcProtocolName, &pwcExternalPath, &pwcInternalPath); if (!SUCCEEDED(hr)) return hr; WCHAR awcsInternalPath[MAX_PATH]; if (*pwzRelativeUrl == L'#') { // Special case for intra page relative URLs. // BugBug! How many other special case do we need to add to match // the behavior of file:// and html:// ?? if (wcsLen(pwcInternalPath) + wcsLen(pwzRelativeUrl) >= MAX_PATH) return INET_E_INVALID_URL; wcsCpy(awcsInternalPath, pwcInternalPath); wcsCat(awcsInternalPath, pwzRelativeUrl); } else { // Here we assume that the relative url is a stream path suffix. // Since we'll be combining with pwzRelativeUrl we must first // truncate the internal path at the last storage name separator. PWCHAR pwc = pwcInternalPath + wcsLen(pwcInternalPath); for (;;) { WCHAR wc = *--pwc; if (wc == L'/' || wc == L'\\') break; } *++pwc = 0; cwc = wcsLen(pwzRelativeUrl); if (!cwc) return URL_E_INVALID_SYNTAX; WCHAR wc = pwzRelativeUrl[cwc - 1]; hr = ResolvePath(awcsInternalPath, (const WCHAR *) pwcInternalPath, (const WCHAR *) pwzRelativeUrl, (wc == L'/' || wc == L'\\') ); if (!SUCCEEDED(hr)) return hr; } hr = AssembleUrl(pwzResult, cchResult, pcchResult, pwcProtocolName, pwcExternalPath, awcsInternalPath ); return hr; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::CompareUrl ( /* [in] */ LPCWSTR pwzUrl1, /* [in] */ LPCWSTR pwzUrl2, /* [in] */ DWORD dwCompareFlags ) { DWORD cwc1 = wcsLen(pwzUrl1); PWCHAR pwcsBuffer1 = PWCHAR(_alloca((cwc1 + 1) * sizeof(WCHAR))); if (!pwcsBuffer1) return E_OUTOFMEMORY; CopyMemory(pwcsBuffer1, pwzUrl1, sizeof(WCHAR) * (cwc1 + 1)); PWCHAR pwcsExternalPath1 = NULL; PWCHAR pwcsInternalPath1 = NULL; HRESULT hr = DisectUrl(pwcsBuffer1, NULL, &pwcsExternalPath1, &pwcsInternalPath1); if (!SUCCEEDED(hr)) return hr; WCHAR awcsExternalPath1[MAX_PATH]; if (wcsLen(pwcsExternalPath1) < MAX_PATH) { wcsCpy(awcsExternalPath1, pwcsExternalPath1); PWCHAR pwc = wcsChr((const WCHAR *) awcsExternalPath1, L':'); if (pwc && pwc[1] == L':') // Ignore a "::" separator pwc = NULL; // Here we're special casing non-protocol references to a file. // We recognize those situations by looking for a protocol prefix. // Protocol prefixes have the form : // where is always longer than one character. if (!pwc || (pwc - awcsExternalPath1 == 1)) { if (FindRootStorageFile(awcsExternalPath1) == S_OK) pwcsExternalPath1 = awcsExternalPath1; } } DWORD cwc2 = wcsLen(pwzUrl2); PWCHAR pwcsBuffer2 = PWCHAR(_alloca((cwc2 + 1) * sizeof(WCHAR))); if (!pwcsBuffer2) return E_OUTOFMEMORY; CopyMemory(pwcsBuffer2, pwzUrl2, sizeof(WCHAR) * (cwc2 + 1)); PWCHAR pwcsExternalPath2 = NULL; PWCHAR pwcsInternalPath2 = NULL; hr = DisectUrl(pwcsBuffer2, NULL, &pwcsExternalPath2, &pwcsInternalPath2); if (!SUCCEEDED(hr)) return hr; WCHAR awcsExternalPath2[MAX_PATH]; if (wcsLen(pwcsExternalPath2) < MAX_PATH) { wcsCpy(awcsExternalPath2, pwcsExternalPath2); PWCHAR pwc = wcsChr((const WCHAR *) awcsExternalPath2, L':'); if (pwc && pwc[1] == L':') // Ignore a "::" separator pwc = NULL; // Here we're special casing non-protocol references to a file. // We recognize those situations by looking for a protocol prefix. // Protocol prefixes have the form : // where is always longer than one character. if (!pwc || (pwc - awcsExternalPath2 == 1)) { if (FindRootStorageFile(awcsExternalPath2) == S_OK) pwcsExternalPath2 = awcsExternalPath2; } } if (wcsicmp_0x0409((const WCHAR *) pwcsExternalPath1, (const WCHAR *) pwcsExternalPath2)) return S_FALSE; if (wcsicmp_0x0409((const WCHAR *) pwcsInternalPath1, (const WCHAR *) pwcsInternalPath2)) return S_FALSE; return S_OK; } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::QueryInfo ( /* [in] */ LPCWSTR pwzUrl, /* [in] */ QUERYOPTION QueryOption, /* [in] */ DWORD dwQueryFlags, /* [size_is][out][in] */ LPVOID pBuffer, /* [in] */ DWORD cbBuffer, /* [out][in] */ DWORD __RPC_FAR *pcbBuf, /* [in] */ DWORD dwReserved ) { switch (QueryOption) { case QUERY_CAN_NAVIGATE: // What does this really mean? if (pcbBuf) *pcbBuf = sizeof(DWORD); if (cbBuffer < sizeof(DWORD)) return E_FAIL; *(DWORD *) pBuffer = TRUE; return NO_ERROR; case QUERY_EXPIRATION_DATE: case QUERY_TIME_OF_LAST_CHANGE: // case QUERY_CONTEXT_ENCODING: case QUERY_REFRESH: case QUERY_RECOMBINE: default: // RonM_ASSERT(FALSE); return E_NOTIMPL; } } #ifdef PROFILING // These wrapper functions are defined for the profiling version // of the code so that we can measure how much time is consumed // by callbacks into URLMON. HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::Switch (PROTOCOLDATA __RPC_FAR *pProtocolData) { return m_pOIProtSink->Switch(pProtocolData); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::ReportProgress (ULONG ulStatusCode, LPCWSTR szStatusText) { return m_pOIProtSink->ReportProgress(ulStatusCode, szStatusText); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::ReportData (DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax) { return m_pOIProtSink->ReportData(grfBSCF, ulProgress, ulProgressMax); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::ReportResult (HRESULT hrResult, DWORD dwError, LPCWSTR szResult) { return m_pOIProtSink->ReportResult(hrResult, dwError, szResult); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::GetBindInfo (DWORD __RPC_FAR *grfBINDF, BINDINFO __RPC_FAR *pbindinfo ) { return m_pOIBindInfo->GetBindInfo(grfBINDF, pbindinfo); } HRESULT STDMETHODCALLTYPE CIOITnetProtocol::CImpIOITnetProtocol::GetBindString (ULONG ulStringType, LPOLESTR __RPC_FAR *ppwzStr, ULONG cEl, ULONG __RPC_FAR *pcElFetched ) { return m_pOIBindInfo->GetBindString(ulStringType, ppwzStr, cEl, pcElFetched); } #endif // PROFILING