//********************************************************************* //* Microsoft Windows ** //* Copyright(c) Microsoft Corp., 1996 ** //********************************************************************* // the CStubBindStatusCallback implements IBindStatusCallback, // IHttpNegotiate. We use it to make a "fake" bind status callback // object when we have headers and post data we would like to apply // to a navigation. We supply this IBindStatusCallback object, and // the URL moniker asks us for headers and post data and use those in // the transaction. #include "priv.h" #include "sccls.h" #include "bindcb.h" CStubBindStatusCallback::CStubBindStatusCallback(LPCWSTR pwzHeaders,LPCBYTE pPostData, DWORD cbPostData, VARIANT_BOOL bOfflineProperty, VARIANT_BOOL bSilentProperty, BOOL bHyperlink, DWORD grBindFlags) : _cRef(1) // _pszHeaders(NULL), _hszPostData(NULL), _cbPostData(0) (don't need to zero-init) { // this is a standalone COM object; need to maintain ref count on our // DLL to ensure it doesn't unload DllAddRef(); if (pwzHeaders) { _pszHeaders = StrDup(pwzHeaders); // allocate for a permanent copy } if (pPostData && cbPostData) { // make a copy of post data and store it _hszPostData = GlobalAlloc(GPTR,cbPostData); if (_hszPostData) { memcpy((LPVOID) _hszPostData,pPostData,cbPostData); _cbPostData = cbPostData; } } _bFrameIsOffline = bOfflineProperty ? TRUE : FALSE; _bFrameIsSilent = bSilentProperty ? TRUE : FALSE; _bHyperlink = bHyperlink ? TRUE : FALSE; _grBindFlags = grBindFlags; TraceMsg(TF_SHDLIFE, "ctor CStubBindStatusCallback %x", this); } HRESULT CStubBSC_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { // aggregation checking is handled in class factory CStubBindStatusCallback * pbsc = new CStubBindStatusCallback(NULL, NULL, 0, FALSE, FALSE, TRUE, 0); if (pbsc) { *ppunk = (IBindStatusCallback *)pbsc; return S_OK; } return E_OUTOFMEMORY; } CStubBindStatusCallback::~CStubBindStatusCallback() { TraceMsg(TF_SHDLIFE, "dtor CBindStatusCallback %x", this); _FreeHeadersAndPostData(); // free any data we still have in this object // release ref count on DLL DllRelease(); } STDMETHODIMP CStubBindStatusCallback::QueryInterface(REFIID riid, LPVOID * ppvObj) { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IBindStatusCallback)) { *ppvObj = SAFECAST(this, IBindStatusCallback*); } else if (IsEqualIID(riid, IID_IHttpNegotiate)) { *ppvObj = SAFECAST(this, IHttpNegotiate*); } else if (IsEqualIID(riid, IID_IMarshal)) { *ppvObj = SAFECAST(this, IMarshal*); } else { *ppvObj = NULL; return E_NOINTERFACE; } AddRef(); // handing out an interface on ourselves; bump up ref count return S_OK; } STDMETHODIMP_(ULONG) CStubBindStatusCallback::AddRef(void) { _cRef++; TraceMsg(TF_SHDREF, "CStubBindStatusCallback(%x)::AddRef called, new _cRef=%d", this, _cRef); return _cRef; } STDMETHODIMP_(ULONG) CStubBindStatusCallback::Release(void) { _cRef--; TraceMsg(TF_SHDREF, "CStubBindStatusCallback(%x)::Release called, new _cRef=%d", this, _cRef); if (_cRef > 0) return _cRef; delete this; return 0; } // // Implementation of IBindStatusCallback begins here // // implements IBindStatusCallback::OnStartBinding STDMETHODIMP CStubBindStatusCallback::OnStartBinding(DWORD grfBSCOption,IBinding *pib) { return S_OK; // we don't care } // implements IBindStatusCallback::GetPriority STDMETHODIMP CStubBindStatusCallback::GetPriority(LONG *pnPriority) { *pnPriority = NORMAL_PRIORITY_CLASS; return S_OK; } // implements IBindStatusCallback::OnLowResource STDMETHODIMP CStubBindStatusCallback::OnLowResource(DWORD reserved) { return S_OK; // we don't care } // implements IBindStatusCallback::OnProgress STDMETHODIMP CStubBindStatusCallback::OnProgress(ULONG ulProgress,ULONG ulProgressMax, ULONG ulStatusCode,LPCWSTR szStatusText) { return S_OK; // we don't care } // implements IBindStatusCallback::OnStopBinding STDMETHODIMP CStubBindStatusCallback::OnStopBinding(HRESULT hresult,LPCWSTR szError) { return S_OK; // we don't care } // implements IBindStatusCallback::GetBindInfo STDMETHODIMP CStubBindStatusCallback::GetBindInfo(DWORD *grfBINDF,BINDINFO *pbindinfo) { HRESULT hr; if (!grfBINDF || !pbindinfo || !pbindinfo->cbSize) return E_INVALIDARG; // call helper function to do fill in BINDINFO struct with appropriate // binding data *grfBINDF = _grBindFlags; hr = BuildBindInfo(grfBINDF,pbindinfo,_hszPostData,_cbPostData, _bFrameIsOffline, _bFrameIsSilent, _bHyperlink, (IBindStatusCallback *) this); return hr; } // implements IBindStatusCallback::OnDataAvailable STDMETHODIMP CStubBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) { ASSERT(FALSE); // should never get called here! return S_OK; } STDMETHODIMP CStubBindStatusCallback::OnObjectAvailable(REFIID riid,IUnknown *punk) { return S_OK; } // // Implementation of IHttpNegotiate begins here // // implements IHttpNegotiate::BeginningTransaction STDMETHODIMP CStubBindStatusCallback::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *ppwzAdditionalHeaders) { // call helper function return BuildAdditionalHeaders(_pszHeaders,(LPCWSTR *) ppwzAdditionalHeaders); } // implements IHttpNegotiate::OnResponse STDMETHODIMP CStubBindStatusCallback::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) { return S_OK; } // // Additional methods on our class begin here // STDMETHODIMP CStubBindStatusCallback::_FreeHeadersAndPostData() { if (_pszHeaders) { LocalFree((HGLOBAL) _pszHeaders); _pszHeaders = NULL; } if (_hszPostData) { GlobalFree(_hszPostData); _hszPostData = NULL; _cbPostData = 0; } return S_OK; } //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::_CanMarshalIID // // Synopsis: Checks whether this object supports marshalling this IID. // // Arguments: [riid] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- inline BOOL CStubBindStatusCallback::_CanMarshalIID(REFIID riid) { // keep this in sync with the QueryInterface return (BOOL) (IsEqualIID(riid,IID_IBindStatusCallback) || IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid, IID_IHttpNegotiate)); } //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::_ValidateMarshalParams // // Synopsis: Validates the standard set parameters that are passed into most // of the IMarshal methods // // Arguments: [riid] -- // [pvInterface] -- // [dwDestContext] -- // [pvDestContext] -- // [mshlflags] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- HRESULT CStubBindStatusCallback::_ValidateMarshalParams(REFIID riid,void *pvInterface, DWORD dwDestContext,void *pvDestContext,DWORD mshlflags) { HRESULT hr = NOERROR; if (_CanMarshalIID(riid)) { // 10/02/96 chrisfra: ask johannp, should we be supporting future contexts // via CoGetStandardMarshal? ASSERT((dwDestContext == MSHCTX_INPROC || dwDestContext == MSHCTX_LOCAL || dwDestContext == MSHCTX_NOSHAREDMEM)); ASSERT((mshlflags == MSHLFLAGS_NORMAL || mshlflags == MSHLFLAGS_TABLESTRONG)); if ( (dwDestContext != MSHCTX_INPROC && dwDestContext != MSHCTX_LOCAL && dwDestContext != MSHCTX_NOSHAREDMEM) || (mshlflags != MSHLFLAGS_NORMAL && mshlflags != MSHLFLAGS_TABLESTRONG)) { hr = E_INVALIDARG; } } else { hr = E_NOINTERFACE; } return hr; } //+--------------------------------------------------------------------------- // // IMarshal methods // //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::GetUnmarshalClass // // Synopsis: // // Arguments: [riid] -- // [pvInterface] -- // [dwDestContext] -- // [pvDestContext] -- // [mshlflags] -- // [pCid] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CStubBindStatusCallback::GetUnmarshalClass(REFIID riid,void *pvInterface, DWORD dwDestContext,void *pvDestContext,DWORD mshlflags,CLSID *pCid) { HRESULT hr; hr = _ValidateMarshalParams(riid, pvInterface, dwDestContext,pvDestContext, mshlflags); if (hr == NOERROR) { *pCid = (CLSID) CLSID_CStubBindStatusCallback; } return hr; } //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::GetMarshalSizeMax // // Synopsis: // // Arguments: [void] -- // [pvInterface] -- // [dwDestContext] -- // [pvDestContext] -- // [mshlflags] -- // [pSize] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CStubBindStatusCallback::GetMarshalSizeMax(REFIID riid,void *pvInterface, DWORD dwDestContext,void *pvDestContext,DWORD mshlflags,DWORD *pSize) { HRESULT hr; if (pSize == NULL) { hr = E_INVALIDARG; } else { hr = _ValidateMarshalParams(riid, pvInterface, dwDestContext,pvDestContext, mshlflags); if (hr == NOERROR) { // size of fBSCBFlags, grBindFlags, postdata, headers. *pSize = (sizeof(DWORD) + 3 * sizeof(DWORD)) + _cbPostData ; if (_pszHeaders) *pSize += ((lstrlen(_pszHeaders) + 1) * sizeof(*_pszHeaders)); } } return hr; } //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::MarshalInterface // // Synopsis: // // Arguments: [REFIID] -- // [riid] -- // [DWORD] -- // [void] -- // [DWORD] -- // [mshlflags] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CStubBindStatusCallback::MarshalInterface(IStream *pistm,REFIID riid, void *pvInterface,DWORD dwDestContext, void *pvDestContext,DWORD mshlflags) { HRESULT hr; DWORD cbLen; DWORD fBSCBFlags; hr = _ValidateMarshalParams(riid, pvInterface, dwDestContext,pvDestContext, mshlflags); if (hr != NOERROR) goto exitPoint; // Write _grBindFlags hr = pistm->Write(&_grBindFlags, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; // Write fBSCBFlags fBSCBFlags = (_bFrameIsOffline ? 1 : 0) + (_bFrameIsSilent ? 2 : 0) ; hr = pistm->Write(&fBSCBFlags, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; // Write headers cbLen = (_pszHeaders ? (lstrlen(_pszHeaders) + 1) * sizeof(TCHAR) : 0); hr = pistm->Write(&cbLen, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; if (cbLen != 0) { hr = pistm->Write(_pszHeaders, cbLen, NULL); if (hr != NOERROR) goto exitPoint; } // Write PostData hr = pistm->Write(&_cbPostData, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; if (_cbPostData != 0) { hr = pistm->Write(_hszPostData, _cbPostData, NULL); if (hr != NOERROR) goto exitPoint; } exitPoint: return hr; } //+--------------------------------------------------------------------------- // // Method: CStubBindStatusCallback::UnmarshalInterface // // Synopsis: Unmarshals an Urlmon interface out of a stream // // Arguments: [REFIID] -- // [void] -- // [ppvObj] -- // // Returns: // // History: 1-19-96 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CStubBindStatusCallback::UnmarshalInterface(IStream *pistm, REFIID riid, void ** ppvObj) { HRESULT hr = NOERROR; DWORD fBSCBFlags; if (ppvObj == NULL) { hr = E_INVALIDARG; } else if (! _CanMarshalIID(riid)) { *ppvObj = NULL; hr = E_NOINTERFACE; } else { *ppvObj = NULL; DWORD cbLen; // Free old values, if any _FreeHeadersAndPostData(); // Read _grBindFlags hr = pistm->Read(&fBSCBFlags, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; _grBindFlags = fBSCBFlags; // Read m_fBSCBFlags hr = pistm->Read(&fBSCBFlags, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; _bFrameIsOffline = fBSCBFlags & 1 ? 1:0; _bFrameIsSilent = fBSCBFlags & 2 ? 1:0; // Read headers hr = pistm->Read(&cbLen, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; if (cbLen != 0) { LPTSTR pszData; pszData = (LPTSTR) LocalAlloc(LPTR, cbLen); if (pszData == NULL) { hr = E_OUTOFMEMORY; goto exitPoint; } hr = pistm->Read(pszData, cbLen, 0); if (hr != NOERROR) { LocalFree(pszData); pszData = NULL; goto exitPoint; } _pszHeaders = pszData; } // Read PostData hr = pistm->Read(&cbLen, sizeof(DWORD), NULL); if (hr != NOERROR) goto exitPoint; if (cbLen != 0) { HGLOBAL hszData; // POST data must be HGLOBAL because the StgMedium requires it // see bindcb.cpp ::GetBindInfo() // This will be freed by the Moniker when it's done with it. hszData = GlobalAlloc(GPTR,cbLen); if (hszData == NULL) { hr = E_OUTOFMEMORY; goto exitPoint; } hr = pistm->Read(hszData, cbLen, 0); if (hr != NOERROR) { GlobalFree(hszData); hszData = NULL; goto exitPoint; } _hszPostData = hszData; _cbPostData = cbLen; } // call QI to get the requested interface hr = QueryInterface(riid, ppvObj); } exitPoint: return hr; } STDMETHODIMP CStubBindStatusCallback::ReleaseMarshalData(IStream *pStm) { // 10/02/96 chrisfra: ask Johannp if this should be seeking past EOD return NOERROR; } STDMETHODIMP CStubBindStatusCallback::DisconnectObject(DWORD dwReserved) { return NOERROR; } // // Global helper functions // /******************************************************************* NAME: fOnProxy SYNOPSIS: returns TRUE if we are have proxy enabled ********************************************************************/ BOOL fOnProxy() { // are we on a proxy? BOOL fRetOnProxy = FALSE; DWORD dwValue; DWORD dwSize = SIZEOF(dwValue); BOOL fDefault = FALSE; SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"), TEXT("ProxyEnable"), NULL, (LPBYTE)&dwValue, &dwSize, FALSE, (LPVOID) &fDefault, SIZEOF(fDefault)); fRetOnProxy = dwValue; return fRetOnProxy; } /******************************************************************* NAME: SetBindfFlagsBasedOnAmbient SYNOPSIS: sets BINDF_OFFLINE if ambient offline and not-connected and sets BINDF_GETFROMCACHE_IF_NET_FAIL if ambient offline and connected ********************************************************************/ void SetBindfFlagsBasedOnAmbient(BOOL fAmbientOffline, DWORD *grfBINDF) { if (fAmbientOffline) { DWORD dwConnectedStateFlags; // We want to set the offline bindf flag if the ambient flag is set // and we're currently not connected. // // If either of these conditions is not true, clear the offline flag // as mshtml may have previously set it. if (FALSE == InternetGetConnectedState(&dwConnectedStateFlags, 0)) { *grfBINDF |= BINDF_OFFLINEOPERATION; *grfBINDF &= ~BINDF_GETFROMCACHE_IF_NET_FAIL; } else { *grfBINDF |= BINDF_GETFROMCACHE_IF_NET_FAIL; *grfBINDF &= ~BINDF_OFFLINEOPERATION; } } else { *grfBINDF &= ~BINDF_OFFLINEOPERATION; } } /******************************************************************* NAME: BuildBindInfo SYNOPSIS: Fills out a BINDINFO structure for a URL moniker NOTES: The point of having this in a global helper function is so we don't have to duplicate this code in multiple implementations of IBindStatusCallback. The caller must pass in an IUnknown to be used as the pUnkForRelease in the STGMEDIUM for post data. If there is post data, this function will AddRef the passed-in IUnknown and return it in the STGMEDIUM structure. The caller (or someone else, if the caller hands it off) must ultimately call Release on pbindinfo->stgmediumData.pUnkForRelease. ********************************************************************/ HRESULT BuildBindInfo(DWORD *grfBINDF,BINDINFO *pbindinfo,HGLOBAL hszPostData, DWORD cbPostData, BOOL bFrameIsOffline, BOOL bFrameIsSilent, BOOL bHyperlink, LPUNKNOWN pUnkForRelease) { DWORD dwConnectedStateFlags = 0; ASSERT(grfBINDF); ASSERT(pbindinfo); ASSERT(pUnkForRelease); HRESULT hres=S_OK; if (!grfBINDF || !pbindinfo || !pbindinfo->cbSize) return E_INVALIDARG; // clear BINDINFO except cbSize ASSERT(sizeof(*pbindinfo) == pbindinfo->cbSize); DWORD cbSize = pbindinfo->cbSize; ZeroMemory(pbindinfo, cbSize); pbindinfo->cbSize = cbSize; *grfBINDF |= BINDF_ASYNCHRONOUS; if (bHyperlink) *grfBINDF |= BINDF_HYPERLINK; SetBindfFlagsBasedOnAmbient(bFrameIsOffline, grfBINDF); if (bFrameIsSilent) *grfBINDF |= BINDF_NO_UI; // default method is GET. Valid ones are _GET, _PUT, _POST, _CUSTOM pbindinfo->dwBindVerb = BINDVERB_GET; // get IE-wide UTF-8 policy by calling urlmon DWORD dwIE = URL_ENCODING_NONE; DWORD dwOutLen = sizeof(DWORD); if (S_OK == UrlMkGetSessionOption( URLMON_OPTION_URL_ENCODING, &dwIE, sizeof(DWORD), &dwOutLen, NULL)) { if (dwIE == URL_ENCODING_ENABLE_UTF8) { pbindinfo->dwOptions |= BINDINFO_OPTIONS_ENABLE_UTF8; } else if (dwIE == URL_ENCODING_DISABLE_UTF8) { pbindinfo->dwOptions |= BINDINFO_OPTIONS_DISABLE_UTF8; } } // if we have postdata set, then we assume this is a POST verb if (hszPostData) { pbindinfo->dwBindVerb = BINDVERB_POST; pbindinfo->stgmedData.tymed = TYMED_HGLOBAL; pbindinfo->stgmedData.hGlobal = hszPostData; // this count should *NOT* include the terminating NULL pbindinfo->cbstgmedData = cbPostData; pbindinfo->stgmedData.pUnkForRelease = pUnkForRelease; // addref on the IUnknown that's holding onto this data so // it knows to stick around; caller must call Release // on the pUnkForRelease when done. pUnkForRelease->AddRef(); // We will still cache the response, but we do not want to // read from cache for a POST transaction. This will keep us // from reading from the cache. *grfBINDF |= BINDF_GETNEWESTVERSION | BINDF_CONTAINER_NOWRITECACHE; } else { ASSERT(pbindinfo->stgmedData.tymed == TYMED_NULL); ASSERT(pbindinfo->stgmedData.hGlobal == NULL); ASSERT(pbindinfo->stgmedData.pUnkForRelease == NULL); } return hres; } #define HDR_LANGUAGE TEXT("Accept-Language:") #define CRLF TEXT("\x0D\x0A") #define HDR_LANGUAGE_CRLF TEXT("Accept-Language: %s\x0D\x0A") /******************************************************************* NAME: BuildAdditionalHeaders SYNOPSIS: Builds HTTP headers to be given to URL moniker ENTRY: pszOurExtraHeaders - headers that we explicitly want to add *ppwzCombinedHeadersOut - on exit, filled in with buffer of default headers plus pszOurExtraHeaders. NOTES: The point of having this in a global helper function is so we don't have to duplicate this code in multiple implementations of IBindStatusCallback. The caller must free *ppwzCombinedHeaders by passing to URLMON, or calling OleFree ********************************************************************/ HRESULT BuildAdditionalHeaders(LPCTSTR pszOurExtraHeaders,LPCWSTR * ppwzCombinedHeadersOut) { TCHAR szLanguage[80]; // what limit on language? DWORD dwLanguage = ARRAYSIZE(szLanguage); static const TCHAR hdr_language[] = HDR_LANGUAGE_CRLF; TCHAR szHeader[ARRAYSIZE(hdr_language) + ARRAYSIZE(szLanguage)]; // NOTE format string length > wnsprintf length int cchHeaders = 0; int cchAddedHeaders = 1; // implied '\0' HRESULT hres = NOERROR; if (!ppwzCombinedHeadersOut) return E_FAIL; *ppwzCombinedHeadersOut = NULL; // If there is no language in the registry, *WE DO NOT SEND THIS HEADER* // S_OK means szLanguage filled in and returned // S_FALSE means call succeeded, but there was no language set // E_* is an error // We treat S_FALSE and E_* the same, no language header sent. if (GetAcceptLanguages(szLanguage, &dwLanguage) == S_OK) { StringCchPrintf(szHeader, ARRAYSIZE(szHeader), hdr_language, szLanguage); cchHeaders = lstrlen(szHeader) + 1; } if (pszOurExtraHeaders) { cchAddedHeaders = lstrlen(pszOurExtraHeaders) + 1; } // If we have headers we added or were sent in, we need to Wide 'em and // give 'em back if (cchAddedHeaders > 1 || cchHeaders > 0) { WCHAR *pwzHeadersForUrlmon = (WCHAR *)CoTaskMemAlloc(sizeof(WCHAR) * (cchHeaders + cchAddedHeaders - 1)); if (pwzHeadersForUrlmon) { if (cchHeaders) { StrCpyN(pwzHeadersForUrlmon, szHeader, cchHeaders); } if (pszOurExtraHeaders) { if (cchHeaders) { StrCpyN(pwzHeadersForUrlmon + cchHeaders - 1, pszOurExtraHeaders, cchAddedHeaders); } else { StrCpyN(pwzHeadersForUrlmon, pszOurExtraHeaders, cchAddedHeaders - 1); } } if (cchHeaders || pszOurExtraHeaders) *ppwzCombinedHeadersOut = pwzHeadersForUrlmon; } else hres = E_OUTOFMEMORY; } else hres = pszOurExtraHeaders == NULL ? S_OK : E_FAIL; return hres; } /******************************************************************* NAME: GetHeadersAndPostData SYNOPSIS: Gets HTTP headers and post data from an IBindStatusCallback ENTRY: IBindStatusCallback - object to ask for headers and post data ppszHeaders - on exit, filled in with pointer to headers, or NULL if none pstgPostData - pointer to a STGMEDIUM to be filled in with post data, if any. NOTES: The caller is responsible for: - calling LocalFree on *ppszHeaders when done with them - calling ReleaseStgMedium on pstgPostData when done with it ********************************************************************/ HRESULT GetHeadersAndPostData(IBindStatusCallback * pBindStatusCallback, LPTSTR * ppszHeaders, STGMEDIUM * pstgPostData, DWORD * pdwPostData, BOOL* pfIsPost) { HRESULT hr = S_OK; ASSERT(pBindStatusCallback); ASSERT(ppszHeaders); ASSERT(pstgPostData); ASSERT(pdwPostData); // clear the out parameters *ppszHeaders = NULL; DWORD grfBINDF; IHttpNegotiate *pinegotiate; BINDINFO binfo; binfo.cbSize = sizeof(binfo); ZeroMemory(pstgPostData, sizeof(*pstgPostData)); *pdwPostData = 0; hr=pBindStatusCallback->GetBindInfo(&grfBINDF, &binfo); if (SUCCEEDED(hr)) { // copy STGMEDIUM with post data to caller *pstgPostData = binfo.stgmedData; *pdwPostData = binfo.cbstgmedData; // clear these so ReleaseBindInfo won't wack it since we are giving it to the caller ZeroMemory(&binfo.stgmedData, sizeof(STGMEDIUM)); binfo.cbstgmedData = 0; if (pfIsPost) { if (*pdwPostData) *pfIsPost = TRUE; else *pfIsPost = FALSE; } hr = pBindStatusCallback->QueryInterface(IID_IHttpNegotiate, (LPVOID *)&pinegotiate); if (SUCCEEDED(hr)) { WCHAR *pwzAdditionalHeaders = NULL; WCHAR wzNull[1]; wzNull[0] = 0; hr=pinegotiate->BeginningTransaction(wzNull, wzNull, 0, &pwzAdditionalHeaders); if (SUCCEEDED(hr) && pwzAdditionalHeaders) { DWORD cchHeaders; cchHeaders = lstrlen(pwzAdditionalHeaders) + 1; // they should *NEVER* be specifying more than a few hundred // bytes or they're going to fail with a number of HTTP servers! LPTSTR pszHeaders = (TCHAR *)LocalAlloc(LPTR, cchHeaders*sizeof(TCHAR)); if (pszHeaders) { LPTSTR pszNext; LPTSTR pszLine; LPTSTR pszLast; StrCpyN(pszHeaders, pwzAdditionalHeaders, cchHeaders); pszLine = pszHeaders; pszLast = pszHeaders + lstrlen(pszHeaders); while (pszLine < pszLast) { pszNext = StrStrI(pszLine, CRLF); if (pszNext == NULL) { // All Headers must be terminated in CRLF! pszLine[0] = '\0'; break; } pszNext += 2; if (!StrCmpNI(pszLine,HDR_LANGUAGE,16)) { MoveMemory(pszLine, pszNext, ((pszLast - pszNext) + 1)*sizeof(TCHAR)); break; } pszLine = pszNext; } // Don't include empty headers if (pszHeaders[0] == '\0') { LocalFree(pszHeaders); pszHeaders = NULL; } } OleFree(pwzAdditionalHeaders); *ppszHeaders = pszHeaders; } pinegotiate->Release(); } ReleaseBindInfo(&binfo); } return hr; } /******************************************************************* NAME: GetTopLevelBindStatusCallback ENTRY: psp - IServiceProvider of ShellBrowser container to query ppBindStatusCallback - if successful, filled in with an IBindStatusCallback on exit SYNOPSIS: Gets the IBindStatusCallback associated with this top level browser. This works from within nested frames. ********************************************************************/ HRESULT GetTopLevelBindStatusCallback(IServiceProvider *psp, IBindStatusCallback **ppBindStatusCallback) { IHlinkFrame *phf; HRESULT hr = psp->QueryService(SID_SHlinkFrame, IID_PPV_ARG(IHlinkFrame, &phf)); if (SUCCEEDED(hr)) { hr = IUnknown_QueryService(phf, IID_IHlinkFrame, IID_PPV_ARG(IBindStatusCallback, ppBindStatusCallback)); phf->Release(); } return hr; } /******************************************************************* NAME: GetTopLevelPendingBindStatusCallback ENTRY: psp - IServiceProvider of ShellBrowser container to query ppBindStatusCallback - if successful, filled in with an IBindStatusCallback on exit SYNOPSIS: Gets the IBindStatusCallback associated with this top level browser. This works from within nested frames. ********************************************************************/ HRESULT GetTopLevelPendingBindStatusCallback(IServiceProvider *psp, IBindStatusCallback **ppBindStatusCallback) { IHlinkFrame *phf; HRESULT hr = psp->QueryService(SID_SHlinkFrame, IID_PPV_ARG(IHlinkFrame, &phf)); if (SUCCEEDED(hr)) { hr = IUnknown_QueryService(phf, SID_PendingBindStatusCallback, IID_PPV_ARG(IBindStatusCallback, ppBindStatusCallback)); phf->Release(); } return hr; } // Global helper function to create a CStubBindStatusCallback HRESULT CStubBindStatusCallback_Create(LPCWSTR pwzHeaders, LPCBYTE pPostData, DWORD cbPostData, VARIANT_BOOL bFrameIsOffline, VARIANT_BOOL bFrameIsSilent, BOOL bHyperlink, DWORD grBindFlags, CStubBindStatusCallback ** ppBindStatusCallback) { ASSERT(ppBindStatusCallback); *ppBindStatusCallback = new CStubBindStatusCallback(pwzHeaders,pPostData, cbPostData, bFrameIsOffline, bFrameIsSilent, bHyperlink, grBindFlags); return (*ppBindStatusCallback ? S_OK : E_OUTOFMEMORY); }