// BrowCap.cpp : Implementation of CBrowserCap #include "stdafx.h" #include "BrwCap.h" #include "BrowCap.h" #include "CapMap.h" #include "context.h" static const DISPID FIRST_DYNAMIC_DISPID = 10000; #ifdef DBG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #define MAX_RESSTRINGSIZE 512 ///////////////////////////////////////////////////////////////////////////// // CBrowserFactory HRESULT CBrowserFactory::QueryInterface(const IID &riid, void **ppvObj) { if (riid == IID_IUnknown || riid == IID_IClassFactory) { *ppvObj = this; AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } } ULONG CBrowserFactory::AddRef() { InterlockedIncrement(&m_cRefs); return m_cRefs; } ULONG CBrowserFactory::Release() { if (InterlockedDecrement(&m_cRefs) == 0) { delete this; return 0; } else return m_cRefs; } HRESULT CBrowserFactory::CreateInstance(IUnknown *pUnkOuter, const IID &riid, void **ppvObj) { USES_CONVERSION; HRESULT hr; CContext cxt; IRequestDictionary *pDictCookies = NULL; if (FAILED(hr = cxt.Init(CContext::get_Request))) return hr; String strBrowser; CComPtr piDispUserAgent; CComVariant varUserAgent; if (FAILED(hr = cxt.Request()->get_Item(L"HTTP_USER_AGENT", &piDispUserAgent))) return hr; varUserAgent = piDispUserAgent; varUserAgent.ChangeType(VT_BSTR); strBrowser = OLE2T(V_BSTR(&varUserAgent)); _Module.Lock(); CBrowserCap *pBrowserCapObj = _Module.CapMap()->LookUp(strBrowser.c_str()); _Module.Unlock(); if (pBrowserCapObj == NULL) return E_FAIL; // IF there is a cookie, then clone the browscap object we got and add cookie properties // Otherwise, when there is no cookie, QueryInterface for "riid". // CComVariant varBrowscapCookie; if (SUCCEEDED(cxt.Request()->get_Cookies(&pDictCookies))) { IReadCookie *pReadCookie = NULL; // Intermediate dictionary ptr CComVariant varCookieName; // current key CComVariant varCookieValue; // value of "varCookieName" IEnumVARIANT *pEnumKeys; // Get the BROWSCAP cookie if (FAILED(pDictCookies->get_Item(CComVariant(L"BROWSCAP"), &varBrowscapCookie))) goto LReleaseDict; // If the cookie exists, it will be an IDispatch. Otherwise it will be VT_EMPTY if (V_VT(&varBrowscapCookie) == VT_DISPATCH) { // Clone the cookie. Since LookUp DID NOT AddRef(), we don't need to Release() // the pBrowserCapObj. Thus cloning to the same pointer is OK here. // the clone will have a refcount of zero, so the QueryInterface call at the // end is also correct. // if (FAILED(hr = pBrowserCapObj->Clone(&pBrowserCapObj))) goto LReleaseDict; hr = V_DISPATCH(&varBrowscapCookie)->QueryInterface(IID_IReadCookie, reinterpret_cast(&pReadCookie)); _ASSERT (SUCCEEDED(hr)); // Iterate over all cookie values if (FAILED(hr = pReadCookie->get__NewEnum(reinterpret_cast(&pEnumKeys)))) goto LReleaseDict; while (pEnumKeys->Next(1, &varCookieName, NULL) == S_OK) { // Expecting a string _ASSERT (V_VT(&varCookieName) == VT_BSTR); // read the cookie value -- better succeed hr = pReadCookie->get_Item(varCookieName, &varCookieValue); _ASSERT (SUCCEEDED(hr) && V_VT(&varCookieValue) == VT_BSTR); // Store key & value in dictionary (over-rides previous settings) pBrowserCapObj->AddProperty(OLE2T(V_BSTR(&varCookieName)), OLE2T(V_BSTR(&varCookieValue)), TRUE); // Clear "varCookieName" to prevent leak. Since we pass address to Next(), C++ cleanup won't happen on its own varCookieName.Clear(); } pEnumKeys->Release(); } LReleaseDict: pDictCookies->Release(); if (pReadCookie) pReadCookie->Release(); if (FAILED(hr)) return hr; } return pBrowserCapObj->QueryInterface(riid, ppvObj); } ///////////////////////////////////////////////////////////////////////////// // CBrowserCap CBrowserCap::CBrowserCap() { } CBrowserCap::~CBrowserCap() { } STDMETHODIMP CBrowserCap::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IBrowserCap, }; for (int i=0;i= FIRST_DYNAMIC_DISPID ) { if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { char szClass[25], szDescription[80]; LoadString(IDS_ERROR_SOURCE, szClass); LoadString(IDS_ERROR_PROP_RO, szDescription); pexcepinfo->scode = E_FAIL; pexcepinfo->bstrSource = SysAllocString(T2OLE(szClass)); pexcepinfo->bstrDescription = SysAllocString(T2OLE(szDescription)); pexcepinfo->wCode = WORD(dispidMember); return DISP_E_EXCEPTION; } UINT i = dispidMember - FIRST_DYNAMIC_DISPID; CLock csT(m_strmapProperties); if (i < m_strmapProperties.size()) // dynamic property -- get value { rc = VariantCopy(pvarResult, &m_strmapProperties[i]); } else // property does not exist, return empty { V_VT(pvarResult) = VT_BSTR; V_BSTR(pvarResult) = SysAllocString(L"unknown"); rc = S_OK; } } else { rc = IDispatchImpl::Invoke( dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } } catch( _com_error& ce ) { rc = ce.Error(); } catch( ... ) { rc = E_FAIL; } return rc; } STDMETHODIMP CBrowserCap::GetIDsOfNames( REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid ) { HRESULT rc = E_FAIL; try { // first get the disp IDs of the known methods rc = IDispatchImpl::GetIDsOfNames( riid, rgszNames, cNames, lcid, rgdispid); if (rc == DISP_E_UNKNOWNNAME) { // IDs for other methods are based on property list ID offset after last known method ID // this allows a client to say browsercap.Cookies instead of browsercap( "Cookies" ). // A property that does not exist is set to a value beyond the end of the strmap. // (this tells Invoke not to bother with "get_Value") This trick only works because // properties are all set at creation time and cannot be added later. // rc = S_OK; for (UINT i = 0; i < cNames; i++) if (rgdispid[i] == DISPID_UNKNOWN && (rgdispid[i] = DispatchID(rgszNames[i])) == DISPID_UNKNOWN) rc = DISP_E_UNKNOWNNAME; } } catch (_com_error& ce) { rc = ce.Error(); } catch (...) { rc = E_FAIL; } return rc; } void CBrowserCap::AddProperty(TCHAR *szKey, TCHAR *szValue, BOOL fOverwriteProperty) { USES_CONVERSION; CComVariant varT; // See if the key already exists, since the first key written into the // dictionary wins. (This is to make sure that the parent UA string property // never overwrites the child.) // _tcslwr(szKey); if (!fOverwriteProperty && m_strmapProperties.find(szKey) != m_strmapProperties.end()) return; if (szValue[0] == _T('#')) varT = _ttol(szValue+1); else if (_tcsncicmp(szValue, _T("TRUE"), 5) == 0) varT = true; else if (_tcsncicmp(szValue, _T("FALSE"), 6) == 0) varT = false; else varT = T2CW(szValue); m_strmapProperties[szKey] = varT; } STDMETHODIMP CBrowserCap::get_Value(BSTR bstrName, VARIANT * pVal) { USES_CONVERSION; CLock csT(m_strmapProperties); TSafeStringMap::iterator itProp = m_strmapProperties.find(_tcslwr(OLE2T(bstrName))); if (itProp == m_strmapProperties.end()) { V_VT(pVal) = VT_BSTR; V_BSTR(pVal) = SysAllocString(L"unknown"); return V_BSTR(pVal) != NULL? S_OK : E_OUTOFMEMORY; } else return VariantCopy(pVal, &(*itProp).second); } DISPID CBrowserCap::DispatchID( LPOLESTR szName ) { USES_CONVERSION; static const TCHAR *szOnStartPage = _T("onstartpage"); static const TCHAR *szOnEndPage = _T("onendpage"); TCHAR *szT = _tcslwr(OLE2T(szName)); if ((szT == NULL) || (_tcscmp(szT, szOnStartPage) == 0) || (_tcscmp(szT, szOnEndPage) == 0)) return DISPID_UNKNOWN; CLock csT(m_strmapProperties); String strName = szT; TSafeStringMap::iterator iter = m_strmapProperties.find(strName); if (iter == m_strmapProperties.end()) return m_strmapProperties.size() + FIRST_DYNAMIC_DISPID; else return (iter - m_strmapProperties.begin()) + FIRST_DYNAMIC_DISPID; } HRESULT CBrowserCap::Clone(CBrowserCap **ppBrowserCapCopy) { if ((*ppBrowserCapCopy = new CComObject) == NULL) return E_OUTOFMEMORY; (*ppBrowserCapCopy)->FinalConstruct(); // Create FTM CLock csT(m_strmapProperties); TSafeStringMap::iterator iter; for (iter = m_strmapProperties.begin(); iter < m_strmapProperties.end(); ++iter) (*ppBrowserCapCopy)->m_strmapProperties[(*iter).first] = (*iter).second; return S_OK; } void CBrowserCap::LoadString( UINT nID, TCHAR *szText ) { if (::LoadString(_Module.GetResourceInstance(), nID, szText, MAX_RESSTRINGSIZE) == 0) { _tcscpy(szText, _T("?? Unknown (Can't find resource)")); } }