You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1586 lines
44 KiB
1586 lines
44 KiB
//------------------------------------------------------------------------
|
|
//
|
|
// Tabular Data Control
|
|
// Copyright (C) Microsoft Corporation, 1996, 1997
|
|
//
|
|
// File: TDCCtl.cpp
|
|
//
|
|
// Contents: Implementation of the CTDCCtl ActiveX control.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include <simpdata.h>
|
|
#include "TDCIds.h"
|
|
#include "TDC.h"
|
|
#include <MLang.h>
|
|
#include "Notify.h"
|
|
#include "TDCParse.h"
|
|
#include "TDCArr.h"
|
|
#include "TDCCtl.h"
|
|
#include "locale.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Function: EmptyBSTR()
|
|
//
|
|
// Synopsis: Indicates whether the given BSTR object represents an
|
|
// empty string.
|
|
//
|
|
// Arguments: bstr String to test
|
|
//
|
|
// Returns: TRUE if 'bstr' represents an empty string
|
|
// FALSE otherwise.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
inline boolean EmptyBSTR(BSTR bstr)
|
|
{
|
|
return bstr == NULL || bstr[0] == 0;
|
|
}
|
|
|
|
void
|
|
ClearInterfaceFn(IUnknown ** ppUnk)
|
|
{
|
|
IUnknown * pUnk;
|
|
|
|
pUnk = *ppUnk;
|
|
*ppUnk = NULL;
|
|
if (pUnk)
|
|
pUnk->Release();
|
|
}
|
|
|
|
// For some reason the standard definition of VARIANT_TRUE (0xffff) generates
|
|
// truncation warnings when assigned to a VARIANT_BOOL
|
|
#define TDCVARIANT_TRUE -1
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: CTDCCtl()
|
|
//
|
|
// Synopsis: Class constructor
|
|
//
|
|
// Arguments: None
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
CTDCCtl::CTDCCtl()
|
|
{
|
|
m_cbstrFieldDelim = DEFAULT_FIELD_DELIM;
|
|
m_cbstrRowDelim = DEFAULT_ROW_DELIM;
|
|
m_cbstrQuoteChar = DEFAULT_QUOTE_CHAR;
|
|
m_fUseHeader = FALSE;
|
|
m_fSortAscending = TRUE;
|
|
m_fAppendData = FALSE;
|
|
m_pSTD = NULL;
|
|
m_pArr = NULL;
|
|
m_pUnify = NULL;
|
|
m_pEventBroker = new CEventBroker(this);
|
|
m_pDataSourceListener = NULL;
|
|
// ;begin_internal
|
|
m_pDATASRCListener = NULL;
|
|
// ;end_internal
|
|
m_pBSC = NULL;
|
|
m_enumFilterCriterion = (OSPCOMP) 0;
|
|
m_fDataURLChanged = FALSE;
|
|
m_lTimer = 0;
|
|
m_fCaseSensitive = TRUE;
|
|
m_hrDownloadStatus = S_OK;
|
|
m_fInReset = FALSE;
|
|
|
|
// Create an MLANG object
|
|
//
|
|
m_nCodePage = 0; // use default from host
|
|
{
|
|
HRESULT hr;
|
|
|
|
m_pML = NULL;
|
|
hr = CoCreateInstance(CLSID_CMultiLanguage, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IMultiLanguage,
|
|
(void**) &m_pML);
|
|
// Don't set the default Charset here. Leave m_nCodepage set
|
|
// to 0 to indicate default charset. Later we'll try to query
|
|
// our host's default charset, and failing that we'll use CP_ACP.
|
|
_ASSERTE(SUCCEEDED(hr) && m_pML != NULL);
|
|
}
|
|
|
|
m_lcidRead = 0x0000; // use default from host
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: ~CTDCCtl()
|
|
//
|
|
// Synopsis: Class destructor
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
CTDCCtl::~CTDCCtl()
|
|
{
|
|
ULONG cRef = _ThreadModel::Decrement(&m_dwRef);
|
|
|
|
ClearInterface(&m_pSTD);
|
|
|
|
if (cRef ==0)
|
|
{
|
|
TimerOff();
|
|
ReleaseTDCArr(FALSE);
|
|
|
|
if (m_pEventBroker)
|
|
{
|
|
m_pEventBroker->Release();
|
|
m_pEventBroker = NULL;
|
|
}
|
|
ClearInterface(&m_pDataSourceListener);
|
|
// ;begin_internal
|
|
ClearInterface(&m_pDATASRCListener);
|
|
// ;end_internal
|
|
ClearInterface(&m_pML);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// These set/get methods implement the control's properties,
|
|
// copying values to and from class members. They perform no
|
|
// other processing apart from argument validation.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTDCCtl::get_ReadyState(LONG *plReadyState)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (m_pEventBroker == NULL)
|
|
{
|
|
// We must provide a ReadyState whether we want to or not, or our
|
|
// host can never go COMPLETE.
|
|
*plReadyState = READYSTATE_COMPLETE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = m_pEventBroker->GetReadyState(plReadyState);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_ReadyState(LONG lReadyState)
|
|
{
|
|
// We don't allow setting of Ready State, but take advantage of a little
|
|
// kludge here to update our container's impression of our readystate
|
|
FireOnChanged(DISPID_READYSTATE);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_FieldDelim(BSTR* pbstrFieldDelim)
|
|
{
|
|
*pbstrFieldDelim = m_cbstrFieldDelim.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_FieldDelim(BSTR bstrFieldDelim)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (bstrFieldDelim == NULL || bstrFieldDelim[0] == 0)
|
|
{
|
|
m_cbstrFieldDelim = DEFAULT_FIELD_DELIM;
|
|
if (m_cbstrFieldDelim == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
m_cbstrFieldDelim = bstrFieldDelim;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_RowDelim(BSTR* pbstrRowDelim)
|
|
{
|
|
*pbstrRowDelim = m_cbstrRowDelim.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_RowDelim(BSTR bstrRowDelim)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (bstrRowDelim == NULL || bstrRowDelim[0] == 0)
|
|
{
|
|
m_cbstrRowDelim = DEFAULT_ROW_DELIM;
|
|
if (m_cbstrRowDelim == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
m_cbstrRowDelim = bstrRowDelim;
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_TextQualifier(BSTR* pbstrTextQualifier)
|
|
{
|
|
*pbstrTextQualifier = m_cbstrQuoteChar.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_TextQualifier(BSTR bstrTextQualifier)
|
|
{
|
|
m_cbstrQuoteChar = bstrTextQualifier;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_EscapeChar(BSTR* pbstrEscapeChar)
|
|
{
|
|
*pbstrEscapeChar = m_cbstrEscapeChar.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_EscapeChar(BSTR bstrEscapeChar)
|
|
{
|
|
m_cbstrEscapeChar = bstrEscapeChar;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_UseHeader(VARIANT_BOOL* pfUseHeader)
|
|
{
|
|
*pfUseHeader = (VARIANT_BOOL)m_fUseHeader;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_UseHeader(VARIANT_BOOL fUseHeader)
|
|
{
|
|
m_fUseHeader = fUseHeader;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_SortColumn(BSTR* pbstrSortColumn)
|
|
{
|
|
*pbstrSortColumn = m_cbstrSortColumn.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_SortColumn(BSTR bstrSortColumn)
|
|
{
|
|
m_cbstrSortColumn = bstrSortColumn;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_SortAscending(VARIANT_BOOL* pfSortAscending)
|
|
{
|
|
*pfSortAscending = m_fSortAscending ? TDCVARIANT_TRUE : VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_SortAscending(VARIANT_BOOL fSortAscending)
|
|
{
|
|
m_fSortAscending = fSortAscending ? TRUE : FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_FilterValue(BSTR* pbstrFilterValue)
|
|
{
|
|
*pbstrFilterValue = m_cbstrFilterValue.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_FilterValue(BSTR bstrFilterValue)
|
|
{
|
|
m_cbstrFilterValue = bstrFilterValue;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_FilterCriterion(BSTR* pbstrFilterCriterion)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwchCriterion;
|
|
|
|
switch (m_enumFilterCriterion)
|
|
{
|
|
case OSPCOMP_EQ: pwchCriterion = L"="; break;
|
|
case OSPCOMP_LT: pwchCriterion = L"<"; break;
|
|
case OSPCOMP_LE: pwchCriterion = L"<="; break;
|
|
case OSPCOMP_GE: pwchCriterion = L">="; break;
|
|
case OSPCOMP_GT: pwchCriterion = L">"; break;
|
|
case OSPCOMP_NE: pwchCriterion = L"<>"; break;
|
|
default: pwchCriterion = L"??"; break;
|
|
}
|
|
*pbstrFilterCriterion = SysAllocString(pwchCriterion);
|
|
hr = (*pbstrFilterCriterion == NULL) ? E_OUTOFMEMORY : S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_FilterCriterion(BSTR bstrFilterCriterion)
|
|
{
|
|
m_enumFilterCriterion = (OSPCOMP) 0;
|
|
if (bstrFilterCriterion != NULL)
|
|
{
|
|
switch (bstrFilterCriterion[0])
|
|
{
|
|
case L'<':
|
|
if (bstrFilterCriterion[1] == 0)
|
|
m_enumFilterCriterion = OSPCOMP_LT;
|
|
else if (bstrFilterCriterion[2] == 0)
|
|
{
|
|
if (bstrFilterCriterion[1] == L'>')
|
|
m_enumFilterCriterion = OSPCOMP_NE;
|
|
else if (bstrFilterCriterion[1] == L'=')
|
|
m_enumFilterCriterion = OSPCOMP_LE;
|
|
}
|
|
break;
|
|
case L'>':
|
|
if (bstrFilterCriterion[1] == 0)
|
|
m_enumFilterCriterion = OSPCOMP_GT;
|
|
else if (bstrFilterCriterion[1] == L'=' && bstrFilterCriterion[2] == 0)
|
|
m_enumFilterCriterion = OSPCOMP_GE;
|
|
break;
|
|
case L'=':
|
|
if (bstrFilterCriterion[1] == 0)
|
|
m_enumFilterCriterion = OSPCOMP_EQ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return SUCCESS, even on an invalid value; otherwise the
|
|
// frameworks using the control will panic and abandon all hope.
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_FilterColumn(BSTR* pbstrFilterColumn)
|
|
{
|
|
*pbstrFilterColumn = m_cbstrFilterColumn.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_FilterColumn(BSTR bstrFilterColumn)
|
|
{
|
|
m_cbstrFilterColumn = bstrFilterColumn;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_CharSet(BSTR* pbstrCharSet)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*pbstrCharSet = NULL;
|
|
|
|
if (m_pML != NULL)
|
|
{
|
|
MIMECPINFO info;
|
|
|
|
hr = m_pML->GetCodePageInfo(m_nCodePage, &info);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pbstrCharSet = SysAllocString(info.wszWebCharset);
|
|
if (*pbstrCharSet == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_CharSet(BSTR bstrCharSet)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (m_pML != NULL)
|
|
{
|
|
MIMECSETINFO info;
|
|
|
|
hr = m_pML->GetCharsetInfo(bstrCharSet, &info);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_nCodePage = info.uiInternetEncoding;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_Language(BSTR* pbstrLanguage)
|
|
{
|
|
if (m_pArr)
|
|
{
|
|
return m_pArr->getLocale(pbstrLanguage);
|
|
}
|
|
|
|
*pbstrLanguage = m_cbstrLanguage.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_Language_(LPWCH pwchLanguage)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LCID lcid;
|
|
|
|
hr = m_pML->GetLcidFromRfc1766(&lcid, pwchLanguage);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_cbstrLanguage = pwchLanguage;
|
|
m_lcidRead = lcid;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_Language(BSTR bstrLanguage)
|
|
{
|
|
return put_Language_(bstrLanguage);
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_DataURL(BSTR* pbstrDataURL)
|
|
{
|
|
*pbstrDataURL = m_cbstrDataURL.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_DataURL(BSTR bstrDataURL)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_cbstrDataURL = bstrDataURL;
|
|
m_fDataURLChanged = TRUE;
|
|
return hr;
|
|
}
|
|
|
|
// ;begin_internal
|
|
#ifdef NEVER
|
|
STDMETHODIMP CTDCCtl::get_RefreshInterval(LONG* plTimer)
|
|
{
|
|
*plTimer = m_lTimer;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_RefreshInterval(LONG lTimer)
|
|
{
|
|
m_lTimer = lTimer;
|
|
if (m_lTimer > 0)
|
|
TimerOn(m_lTimer * 1000);
|
|
else
|
|
TimerOff();
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
// ;end_internal
|
|
|
|
STDMETHODIMP CTDCCtl::get_Filter(BSTR* pbstrFilterExpr)
|
|
{
|
|
*pbstrFilterExpr = m_cbstrFilterExpr.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_Filter(BSTR bstrFilterExpr)
|
|
{
|
|
m_cbstrFilterExpr = bstrFilterExpr;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_Sort(BSTR* pbstrSortExpr)
|
|
{
|
|
*pbstrSortExpr = m_cbstrSortExpr.Copy();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_Sort(BSTR bstrSortExpr)
|
|
{
|
|
m_cbstrSortExpr = bstrSortExpr;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_AppendData(VARIANT_BOOL* pfAppendData)
|
|
{
|
|
*pfAppendData = m_fAppendData ? TDCVARIANT_TRUE : VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_AppendData(VARIANT_BOOL fAppendData)
|
|
{
|
|
m_fAppendData = fAppendData ? TRUE : FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_CaseSensitive(VARIANT_BOOL* pfCaseSensitive)
|
|
{
|
|
*pfCaseSensitive = m_fCaseSensitive ? TDCVARIANT_TRUE : VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::put_CaseSensitive(VARIANT_BOOL fCaseSensitive)
|
|
{
|
|
m_fCaseSensitive = fCaseSensitive ? TRUE : FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTDCCtl::get_OSP(OLEDBSimpleProviderX ** ppISTD)
|
|
{
|
|
// Return an OSP if we have one, but don't create one on demand!
|
|
// (Otherwise property bag load stuff will cause us to create an
|
|
// OSP prematurely).
|
|
*ppISTD = NULL;
|
|
if (m_pSTD)
|
|
{
|
|
*ppISTD = (OLEDBSimpleProviderX *)m_pSTD;
|
|
m_pSTD->AddRef();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: UpdateReadyState
|
|
//
|
|
// Synopsis: Vectors to the event brokers ReadyState, if there is one.
|
|
// ;begin_internal
|
|
// Note, we have to be able to set our readystate and fire change
|
|
// events on it, whether or not creation of the broker succeeded,
|
|
// or we prevent our host container from reaching
|
|
// READYSTATE_COMPLETE, which is not acceptable. We therefore
|
|
// have to duplicate some of the broker's work here. This makes
|
|
// me wonder whether the broker architecture was a good idea.
|
|
// ;end_internal
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// Error codes as per Reset() upon error.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
void
|
|
CTDCCtl::UpdateReadyState(LONG lReadyState)
|
|
{
|
|
if (m_pEventBroker)
|
|
m_pEventBroker->UpdateReadyState(lReadyState);
|
|
else
|
|
{
|
|
// We have no broker, but our host is still waiting for us to
|
|
// go READYSTATE_COMPLETE. We fire the OnChange here noting that
|
|
// get_ReadyState with no broker will return COMPLETE.
|
|
FireOnChanged(DISPID_READYSTATE);
|
|
FireOnReadyStateChanged();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: _OnTimer()
|
|
//
|
|
// Synopsis: Handles an internal timer event by refreshing the control.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// Error codes as per Reset() upon error.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTDCCtl::_OnTimer()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pArr != NULL && m_pArr->GetLoadState() == CTDCArr::LS_LOADED)
|
|
{
|
|
m_fDataURLChanged = TRUE;
|
|
hr = Reset();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: msDataSourceObject()
|
|
//
|
|
// Synopsis: Yields an ISimpleTabularData interface for this control.
|
|
// If this is the first call, a load operation is initiated
|
|
// reading data from the control's specified DataURL property.
|
|
// An STD object is created to point to the control's embedded
|
|
// TDCArr object.
|
|
//
|
|
// Arguments: qualifier Ignored - must be an empty BSTR.
|
|
// ppUnk Pointer to returned interface [OUT]
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// E_INVALIDARG if 'qualifier' isn't an empty BSTR.
|
|
// E_OUTOFMEMORY if non enough memory could be allocated to
|
|
// complete the construction of the interface.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTDCCtl::msDataSourceObject(BSTR qualifier, IUnknown **ppUnk)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
*ppUnk = NULL; // NULL in case of failure
|
|
|
|
if (!EmptyBSTR(qualifier))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto error;
|
|
}
|
|
|
|
// Was there a previous attempt to load this page that failed?
|
|
// (Probably due to security or file not found or something).
|
|
if (m_hrDownloadStatus)
|
|
{
|
|
hr = m_hrDownloadStatus;
|
|
goto error;
|
|
}
|
|
|
|
if (m_pArr == NULL)
|
|
{
|
|
// We don't have a valid TDC to give back, probably have to try
|
|
// downloading one.
|
|
UpdateReadyState(READYSTATE_LOADED);
|
|
hr = CreateTDCArr(FALSE);
|
|
if (hr)
|
|
goto error;
|
|
}
|
|
|
|
_ASSERTE(m_pArr != NULL);
|
|
|
|
if (m_pSTD == NULL)
|
|
{
|
|
OutputDebugStringX(_T("Creating an STD COM object\n"));
|
|
|
|
// fetch ISimpleTabularData interface pointer
|
|
m_pArr->QueryInterface(IID_OLEDBSimpleProvider, (void**)&m_pSTD);
|
|
_ASSERTE(m_pSTD != NULL);
|
|
}
|
|
|
|
// Return the STD if we have one, otherwise it stays NULL
|
|
if (m_pSTD && m_pArr->GetLoadState() >= CTDCArr::LS_LOADING_HEADER_AVAILABLE)
|
|
{
|
|
*ppUnk = (OLEDBSimpleProviderX *) m_pSTD;
|
|
m_pSTD->AddRef(); // We must AddRef the STD we return!
|
|
}
|
|
|
|
cleanup:
|
|
return hr;
|
|
|
|
error:
|
|
UpdateReadyState(READYSTATE_COMPLETE);
|
|
goto cleanup;
|
|
}
|
|
|
|
// Override IPersistPropertyBagImpl::Load
|
|
STDMETHODIMP
|
|
CTDCCtl::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown *pSTD;
|
|
|
|
// Find out our Ambient Charset. We need to know this surprisingly
|
|
// early in life.
|
|
VARIANT varCodepage;
|
|
// 0 means user didn't set one, so ask our container.
|
|
VariantInit(&varCodepage);
|
|
GetAmbientProperty(DISPID_AMBIENT_CODEPAGE, varCodepage);
|
|
|
|
// Ultimate default is Latin-1
|
|
m_nAmbientCodePage = (varCodepage.vt == VT_UI4)
|
|
? (ULONG)varCodepage.lVal
|
|
: CP_1252;
|
|
|
|
// ignore Unicode ambient codepage - we want to allow non-Unicode
|
|
// data files from Unicode pages. If the data file is Unicode,
|
|
// we'll find out anyway when we see the Unicode signature.
|
|
if (m_nAmbientCodePage == UNICODE_CP ||
|
|
m_nAmbientCodePage == UNICODE_REVERSE_CP)
|
|
{
|
|
m_nAmbientCodePage = CP_1252;
|
|
}
|
|
|
|
// Do normal load
|
|
// IPersistPropertyBagImpl<CTDCCtl>
|
|
hr = IPersistPropertyBagImpl<CTDCCtl>::Load(pPropBag, pErrorLog);
|
|
|
|
// and then start download, if we can
|
|
(void)msDataSourceObject(NULL, &pSTD);
|
|
|
|
// If we actually got an STD, we should release it. This won't really
|
|
// make it go away, since we still have the ref from the QI. This is
|
|
// a bit of a kludge that we should clean up later.
|
|
ClearInterface(&pSTD);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: CreateTDCArr()
|
|
//
|
|
// Synopsis: Creates the control's embedded TDCArr object.
|
|
// Initiates a data download from the DataURL property.
|
|
//
|
|
// Arguments: fAppend Flag indicating whether data should be
|
|
// appended to an existing TDC object.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// E_OUTOFMEMORY if non enough memory could be allocated to
|
|
// complete the construction of the TDCArr object.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTDCCtl::CreateTDCArr(boolean fAppend)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pEventBroker == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Error;
|
|
}
|
|
|
|
// Iff we're appending is m_pArr allowed to be non-null here.
|
|
_ASSERT ((m_pArr != NULL) == !!fAppend);
|
|
|
|
if (m_pArr == NULL)
|
|
{
|
|
m_pArr = new CTDCArr();
|
|
if (m_pArr == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
|
|
hr = m_pArr->Init(m_pEventBroker, m_pML);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
}
|
|
|
|
hr = InitiateDataLoad(fAppend);
|
|
if (hr)
|
|
goto Error;
|
|
|
|
// We decide something is not async if it finished loading during
|
|
// the InitiateDataLoad call.
|
|
m_pArr->SetIsAsync(!(m_pArr->GetLoadState()==CTDCArr::LS_LOADED));
|
|
|
|
Cleanup:
|
|
return hr;
|
|
|
|
Error:
|
|
if (!fAppend)
|
|
{
|
|
ClearInterface(&m_pArr);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: ReleaseTDCArr()
|
|
//
|
|
// Synopsis: Releases the control's embedded TDCArr object.
|
|
// Releases the control's CTDCUnify and CTDCTokenise objects.
|
|
// Releases the old event broker and re-creates it if replacing.
|
|
//
|
|
// Arguments: fReplacingTDCArr Flag indicating whether a new TDCArr object
|
|
// will be created.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// Error code upon failure.
|
|
// E_OUTOFMEMORY if non enough memory could be allocated to
|
|
// complete the construction of the new CEventBroker object.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTDCCtl::ReleaseTDCArr(boolean fReplacingTDCArr)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TerminateDataLoad(m_pBSC);
|
|
|
|
// Release the reference to the current TDCArr object
|
|
//
|
|
if (m_pArr != NULL)
|
|
{
|
|
m_pArr->Release();
|
|
m_pArr = NULL;
|
|
|
|
// Since we've shut down the CTDCArr object, we should release
|
|
// it's OLEDBSimplerProviderListener sink.
|
|
if (m_pEventBroker)
|
|
{
|
|
m_pEventBroker->SetSTDEvents(NULL);
|
|
}
|
|
|
|
if (fReplacingTDCArr)
|
|
{
|
|
// Release our previous Event Broker.
|
|
if (m_pEventBroker)
|
|
{
|
|
m_pEventBroker->Release();
|
|
m_pEventBroker = NULL;
|
|
}
|
|
|
|
// Create a new event broker.
|
|
m_pEventBroker = new CEventBroker(this);
|
|
if (m_pEventBroker == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the DataSourceListener for the new event broker.
|
|
m_pEventBroker->SetDataSourceListener(m_pDataSourceListener);
|
|
|
|
// ;begin_internal
|
|
m_pEventBroker->SetDATASRCListener(m_pDATASRCListener);
|
|
// ;end_internal
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
const IID IID_IDATASRCListener = {0x3050f380,0x98b5,0x11cf,{0xbb,0x82,0x00,0xaa,0x00,0xbd,0xce,0x0b}};
|
|
const IID IID_DataSourceListener = {0x7c0ffab2,0xcd84,0x11d0,{0x94,0x9a,0x00,0xa0,0xc9,0x11,0x10,0xed}};
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: addDataSourceListener()
|
|
//
|
|
// Synopsis: Sets the COM object which should receive notification
|
|
// events.
|
|
//
|
|
// Arguments: pEvent Pointer to COM object to receive notification
|
|
// events, or NULL if no notifications to be sent.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// Error code upon failure.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTDCCtl::addDataSourceListener(IUnknown *pListener)
|
|
{
|
|
|
|
if (m_pEventBroker != NULL)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IUnknown * pDatasrcListener;
|
|
|
|
// Make sure this is the interface we expect
|
|
hr = pListener->QueryInterface(IID_DataSourceListener,
|
|
(void **)&pDatasrcListener);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pEventBroker->
|
|
SetDataSourceListener((DataSourceListener *)pDatasrcListener);
|
|
|
|
// Clear any previous
|
|
ClearInterface (&m_pDataSourceListener);
|
|
// and remember the new.
|
|
m_pDataSourceListener = (DataSourceListener *)pDatasrcListener;
|
|
}
|
|
// ;begin_internal
|
|
else
|
|
{
|
|
// The definition of this interface was changed from IDATASRCListener to
|
|
// DataSourceListener. To make sure we don't cause crashes, we QI to
|
|
// determine which one we were handed.
|
|
hr = pListener->QueryInterface(IID_IDATASRCListener,
|
|
(void **)&pDatasrcListener);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pEventBroker->
|
|
SetDATASRCListener((DATASRCListener *) pDatasrcListener);
|
|
|
|
// Clear any previous
|
|
ClearInterface (&m_pDATASRCListener);
|
|
// and remember the new.
|
|
m_pDATASRCListener = (DATASRCListener *)pDatasrcListener;
|
|
}
|
|
}
|
|
// ;end_internal
|
|
return hr;
|
|
}
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: Reset()
|
|
//
|
|
// Synopsis: Reset the control's filter/sort criteria.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// Error code upon failure.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTDCCtl::Reset()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// The next query to msDataSourceObject should get a new STD
|
|
ClearInterface(&m_pSTD);
|
|
|
|
// Infinite recursive calls to Reset can occur if script code calls reset
|
|
// from within the datasetchanged event. This isn't a good idea.
|
|
if (m_fInReset)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
m_fInReset = TRUE;
|
|
|
|
// Clear any previous error
|
|
m_hrDownloadStatus = S_OK;
|
|
|
|
if (m_fDataURLChanged)
|
|
{
|
|
if (!m_fAppendData)
|
|
{
|
|
// Release previous TDC array with "replacing" flag.
|
|
hr = ReleaseTDCArr(TRUE);
|
|
if (!SUCCEEDED(hr)) // possible memory failure
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Read the new data into a TDC arry, appending if specified.
|
|
hr = CreateTDCArr((BOOL)m_fAppendData);
|
|
}
|
|
else if (m_pArr != NULL)
|
|
{
|
|
// Re-apply the sort and filter criteria
|
|
hr = m_pArr->SetSortFilterCriteria(bstrConstructSortExpr(),
|
|
bstrConstructFilterExpr(),
|
|
m_fCaseSensitive ? 1 : 0);
|
|
}
|
|
|
|
m_fInReset = FALSE;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: bstrConstructSortExpr()
|
|
//
|
|
// Synopsis: Constructs a sort expression from the Sort property or
|
|
// (for backward compatibility) from the SortColumn/SortAscending
|
|
// properties.
|
|
//
|
|
// This method only exists to isolate backward-compatibility
|
|
// with the old-fashioned sort properties.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: The constructed sort expression.
|
|
//
|
|
// NB! It is the caller's responsibility to free the string returned.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
BSTR
|
|
CTDCCtl::bstrConstructSortExpr()
|
|
{
|
|
BSTR bstr = NULL;
|
|
|
|
if (!EmptyBSTR(m_cbstrSortExpr))
|
|
bstr = SysAllocString(m_cbstrSortExpr);
|
|
else if (!EmptyBSTR(m_cbstrSortColumn))
|
|
{
|
|
// Use the old-fashioned sort properties
|
|
// Construct a sort expression of the form:
|
|
// <SortColumn> or
|
|
// -<SortColumn>
|
|
//
|
|
if (m_fSortAscending)
|
|
bstr = SysAllocString(m_cbstrSortColumn);
|
|
else
|
|
{
|
|
bstr = SysAllocStringLen(NULL, SysStringLen(m_cbstrSortColumn) + 1);
|
|
if (bstr != NULL)
|
|
{
|
|
bstr[0] = L'-';
|
|
wch_cpy(&bstr[1], m_cbstrSortColumn);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bstr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: bstrConstructFilterExpr()
|
|
//
|
|
// Synopsis: Constructs a filter expression from the Filter property or
|
|
// (for backward compatibility) from the FilterColumn/FilterValue/
|
|
// FilterCriterion properties.
|
|
//
|
|
// This method only exists to isolate backward-compatibility
|
|
// with the old-fashioned filter properties.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: The constructed filter expression
|
|
//
|
|
// NB! It is the caller's responsibility to free the string returned.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
BSTR
|
|
CTDCCtl::bstrConstructFilterExpr()
|
|
{
|
|
BSTR bstr = NULL;
|
|
|
|
if (!EmptyBSTR(m_cbstrFilterExpr))
|
|
bstr = SysAllocString(m_cbstrFilterExpr);
|
|
else if (!EmptyBSTR(m_cbstrFilterColumn))
|
|
{
|
|
// Use the old-fashioned filter properties
|
|
// Construct a sort expression of the form:
|
|
// <FilterColumn> <FilterCriterion> "<FilterValue>"
|
|
//
|
|
BSTR bstrFilterOp;
|
|
HRESULT hr;
|
|
|
|
hr = get_FilterCriterion(&bstrFilterOp);
|
|
if (!SUCCEEDED(hr))
|
|
goto Cleanup;
|
|
bstr = SysAllocStringLen(NULL,
|
|
SysStringLen(m_cbstrFilterColumn) +
|
|
SysStringLen(bstrFilterOp) +
|
|
1 +
|
|
SysStringLen(m_cbstrFilterValue) +
|
|
1);
|
|
if (bstr != NULL)
|
|
{
|
|
DWORD pos = 0;
|
|
|
|
wch_cpy(&bstr[pos], m_cbstrFilterColumn);
|
|
pos = wch_len(bstr);
|
|
wch_cpy(&bstr[pos], bstrFilterOp);
|
|
pos = wch_len(bstr);
|
|
bstr[pos++] = L'"';
|
|
wch_cpy(&bstr[pos], m_cbstrFilterValue);
|
|
pos = wch_len(bstr);
|
|
bstr[pos++] = L'"';
|
|
bstr[pos] = 0;
|
|
}
|
|
SysFreeString(bstrFilterOp);
|
|
}
|
|
Cleanup:
|
|
return bstr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: TerminateDataLoad()
|
|
//
|
|
// Synopsis: Stop the current data load operation.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTDCCtl::TerminateDataLoad(CMyBindStatusCallback<CTDCCtl> *pBSC)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// if the termination isn't for the current download, ignore it (bug 104042)
|
|
if (pBSC != m_pBSC)
|
|
goto done;
|
|
|
|
// Make sure if we call Reset() right away now, we don't re-download
|
|
// the data.
|
|
m_fDataURLChanged = FALSE;
|
|
|
|
m_pBSC = NULL; // Block any outstanding OnData calls
|
|
|
|
if (m_pEventBroker)
|
|
m_pEventBroker->m_pBSC = NULL; // kill all
|
|
|
|
if (m_pUnify != NULL)
|
|
delete m_pUnify;
|
|
|
|
m_pUnify = NULL;
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: InitiateDataLoad()
|
|
//
|
|
// Synopsis: Start loading data from the control's DataURL property.
|
|
//
|
|
// Arguments: fAppend Flag to indicate whether data should be
|
|
// appended to an existing TDCArr object.
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// E_OUTOFMEMORY if not enough memory could be allocated to
|
|
// complete the download.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTDCCtl::InitiateDataLoad(boolean fAppend)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WCHAR wchFieldDelim = (!m_cbstrFieldDelim) ? 0 : m_cbstrFieldDelim[0];
|
|
WCHAR wchRowDelim = (!m_cbstrRowDelim) ? 0 : m_cbstrRowDelim[0];
|
|
// Default quote char to double-quote, not NULL
|
|
WCHAR wchQuoteChar = (!m_cbstrQuoteChar) ? 0 : m_cbstrQuoteChar[0];
|
|
WCHAR wchEscapeChar = (!m_cbstrEscapeChar) ? 0 : m_cbstrEscapeChar[0];
|
|
|
|
//
|
|
// Default LCID
|
|
//
|
|
if (0==m_lcidRead)
|
|
{
|
|
hr = GetAmbientLocaleID(m_lcidRead);
|
|
if (FAILED(hr))
|
|
{
|
|
// Ultimate default is US locale -- sort of Web global
|
|
// language default.
|
|
put_Language_(L"en-us");
|
|
}
|
|
}
|
|
|
|
if (EmptyBSTR(m_cbstrDataURL))
|
|
{
|
|
hr = S_FALSE; // quiet failure
|
|
goto Error;
|
|
}
|
|
|
|
OutputDebugStringX(_T("Initiating Data Download\n"));
|
|
|
|
// No data load should currently be in progress -
|
|
// This data load has been initiated on the construction of a new
|
|
// TDCArr object, or appending to an existing loaded TDCArr object.
|
|
// Any currently running data load would have been
|
|
// terminated by the call to ReleaseTDCArr().
|
|
//
|
|
|
|
_ASSERT(m_pUnify == NULL);
|
|
_ASSERT(m_pBSC == NULL);
|
|
|
|
|
|
m_hrDownloadStatus = S_OK;
|
|
|
|
// Create a pipeline of objects to process the URL data
|
|
//
|
|
// CMyBindStatusCallback -> CTDCUnify -> CTDCTokenise -> CTDCArr
|
|
//
|
|
|
|
CComObject<CMyBindStatusCallback<CTDCCtl> >::CreateInstance(&m_pBSC);
|
|
|
|
if (m_pBSC == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Error;
|
|
}
|
|
hr = m_pArr->StartDataLoad(m_fUseHeader ? TRUE : FALSE,
|
|
bstrConstructSortExpr(),
|
|
bstrConstructFilterExpr(),
|
|
m_lcidRead,
|
|
m_pBSC,
|
|
fAppend,
|
|
m_fCaseSensitive ? 1 : 0);
|
|
if (!SUCCEEDED(hr))
|
|
goto Error;
|
|
|
|
m_pUnify = new CTDCUnify();
|
|
if (m_pUnify == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
m_pUnify->Create(m_nCodePage, m_nAmbientCodePage, m_pML);
|
|
|
|
// Init tokenizer
|
|
m_pUnify->InitTokenizer(m_pArr, wchFieldDelim, wchRowDelim,
|
|
wchQuoteChar, wchEscapeChar);
|
|
|
|
|
|
|
|
m_fSecurityChecked = FALSE;
|
|
|
|
// Start (and maybe perform) actual download.
|
|
// If we're within a Reset() call, always force a "reload" of the data
|
|
// from the server -- i.e. turn on BINDF_GETNEWESTVERSION to make sure
|
|
// sure the cache data isn't stale.
|
|
hr = m_pBSC->StartAsyncDownload(this, OnData, m_cbstrDataURL, m_spClientSite, TRUE,
|
|
m_fInReset == TRUE);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
// m_hrDownloadStatus remembers the first (if any) error that occured during
|
|
// the OnData callbacks. Unlike an error returning from StartAsyncDownload,
|
|
// this doesn't necessarily cause us to throw away the TDC array.
|
|
hr = m_hrDownloadStatus;
|
|
if (!SUCCEEDED(hr))
|
|
m_pBSC = NULL;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
|
|
Error:
|
|
TerminateDataLoad(m_pBSC);
|
|
if (m_pEventBroker)
|
|
{
|
|
// Fire data set changed to indicate query failed,
|
|
m_pEventBroker->STDDataSetChanged();
|
|
// and go complete.
|
|
UpdateReadyState(READYSTATE_COMPLETE);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: SecurityCheckDataURL(pszURL)
|
|
//
|
|
// Synopsis: Check that the data URL is within the same security zone
|
|
// as the document that loaded the control.
|
|
//
|
|
// Arguments: URL to check
|
|
//
|
|
// Returns: S_OK upon success.
|
|
// E_INVALID if the security check failed or we failed to get
|
|
// an interface that we needed
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
// ;begin_internal
|
|
// Wendy Richards(v-wendri) 6/6/97
|
|
// Copied this here because I couldn't link without it. The version
|
|
// of URLMON.LIB I have does not have this symbol exported
|
|
// ;end_internal
|
|
|
|
EXTERN_C const IID IID_IInternetHostSecurityManager;
|
|
|
|
#define MAX_SEC_ID 256
|
|
|
|
STDMETHODIMP CTDCCtl::SecurityCheckDataURL(LPOLESTR pszURL)
|
|
{
|
|
CComQIPtr<IServiceProvider, &IID_IServiceProvider> pSP(m_spClientSite);
|
|
CComPtr<IInternetSecurityManager> pSM;
|
|
CComPtr<IInternetHostSecurityManager> pHSM;
|
|
CComPtr<IMoniker> pMoniker;
|
|
|
|
BYTE bSecIDHost[MAX_SEC_ID], bSecIDURL[MAX_SEC_ID];
|
|
DWORD cbSecIDHost = MAX_SEC_ID, cbSecIDURL = MAX_SEC_ID;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
USES_CONVERSION;
|
|
|
|
// If we're running under the timer, it's quite possible our ClientSite will
|
|
// disappear out from under us. We'll obviously fail the security check,
|
|
// but things are shutting down anyway..
|
|
if (pSP==NULL)
|
|
goto Cleanup;
|
|
|
|
hr = CoInternetCreateSecurityManager(pSP, &pSM, 0L);
|
|
if (!SUCCEEDED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = pSP->QueryService(IID_IInternetHostSecurityManager,
|
|
IID_IInternetHostSecurityManager,
|
|
(LPVOID *)&pHSM);
|
|
if (!SUCCEEDED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = pHSM->GetSecurityId(bSecIDHost, &cbSecIDHost, 0L);
|
|
if (!SUCCEEDED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = pSM->GetSecurityId(OLE2W(pszURL), bSecIDURL, &cbSecIDURL, 0L);
|
|
if (!SUCCEEDED(hr))
|
|
goto Cleanup;
|
|
|
|
if (cbSecIDHost != cbSecIDURL)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (memcmp(bSecIDHost, bSecIDURL, cbSecIDHost) != 0)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
#ifdef ATLTRACE
|
|
LPOLESTR pszHostName = NULL;
|
|
TCHAR *pszFailPass = hr ? _T("Failed") : _T("Passed");
|
|
GetHostURL(m_spClientSite, &pszHostName);
|
|
ATLTRACE(_T("CTDCCtl: %s security check on %S referencing %S\n"), pszFailPass,
|
|
pszHostName, pszURL);
|
|
bSecIDHost[cbSecIDHost] = 0;
|
|
bSecIDURL[cbSecIDURL] = 0;
|
|
ATLTRACE(_T("CTDCCtl: Security ID Host %d bytes: %s\n"), cbSecIDHost, bSecIDHost);
|
|
ATLTRACE(_T("CTDCCtl: Security ID URL %d bytes: %s\n"), cbSecIDURL, bSecIDURL);
|
|
CoTaskMemFree(pszHostName);
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Method: OnData()
|
|
//
|
|
// Synopsis: Accepts a chunk of data loaded from a URL and parses it.
|
|
//
|
|
// Arguments: pBSC The invoking data transfer object.
|
|
// pBytes Character buffer containing data.
|
|
// dwSize Count of the number of bytes in 'pBytes'.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
void CTDCCtl::OnData(CMyBindStatusCallback<CTDCCtl> *pBSC, BYTE *pBytes, DWORD dwSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CTDCUnify::ALLOWDOMAINLIST nAllowDomainList;
|
|
|
|
if (pBSC != m_pBSC)
|
|
{
|
|
OutputDebugStringX(_T("OnData called from invalid callback object\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
// ignore callbacks from an aborted download
|
|
if (m_hrDownloadStatus == E_ABORT)
|
|
goto Cleanup;
|
|
|
|
// Process this chunk of data
|
|
//
|
|
hr = m_pUnify->ConvertByteBuffer(pBytes, dwSize);
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
// not enough data has shown up yet, just keep going
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (hr)
|
|
goto Error;
|
|
|
|
if (!m_fSecurityChecked)
|
|
{
|
|
// this forces the code below to check the DataURL, unless the allow_domain
|
|
// list needs checking and it passes. In that case, we need only check
|
|
// the protocols, not the whole URL.
|
|
hr = E_FAIL;
|
|
|
|
if (!m_pUnify->ProcessedAllowDomainList())
|
|
{
|
|
// Note that we MUST check for the allow domain list at the
|
|
// front of every file, even if it's on the same host. This
|
|
// is to make sure if we always strip off the @!allow_domain line.
|
|
nAllowDomainList = m_pUnify->CheckForAllowDomainList();
|
|
|
|
switch (nAllowDomainList)
|
|
{
|
|
// Don't have enough chars to tell yet.
|
|
case CTDCUnify::ALLOW_DOMAINLIST_DONTKNOW:
|
|
if (pBytes != NULL && dwSize != 0)
|
|
{
|
|
// Return without errors or arborting.
|
|
// Presumably the next data packet will bring more info.
|
|
return;
|
|
}
|
|
_ASSERT(FAILED(hr));
|
|
break;
|
|
|
|
case CTDCUnify::ALLOW_DOMAINLIST_NO:
|
|
_ASSERT(FAILED(hr));
|
|
break;
|
|
|
|
case CTDCUnify::ALLOW_DOMAINLIST_YES:
|
|
// The file is decorated. Now check the domain list
|
|
// against our host domain name.
|
|
hr = SecurityMatchAllowDomainList();
|
|
#ifdef ATLTRACE
|
|
if (!hr) ATLTRACE(_T("CTDCCtl: @!allow_domain list matched."));
|
|
else ATLTRACE(_T("CTDCCtl: @!allow_domain list did not match"));
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Unless we passed the previous security check, we still have to
|
|
// do the next one.
|
|
if (FAILED(hr))
|
|
{
|
|
if (FAILED(hr = SecurityCheckDataURL(m_pBSC->m_pszURL)))
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
hr = SecurityMatchProtocols(m_pBSC->m_pszURL);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
}
|
|
|
|
|
|
// Set m_fSecurityChecked only if it passes security. This is in case for some
|
|
// reason we get more callbacks before the StopTransfer takes affect.
|
|
m_fSecurityChecked = TRUE;
|
|
}
|
|
|
|
if (pBytes != NULL && dwSize != 0)
|
|
{
|
|
OutputDebugStringX(_T("OnData called with data buffer\n"));
|
|
|
|
// Normal case, we can process data!
|
|
hr = m_pUnify->AddWcharBuffer(FALSE);
|
|
if (hr == E_ABORT)
|
|
goto Error;
|
|
}
|
|
else if (pBytes == NULL || dwSize == 0)
|
|
{
|
|
OutputDebugStringX(_T("OnData called with empty (terminating) buffer\n"));
|
|
|
|
// No more data - trigger an EOF
|
|
//
|
|
hr = m_pUnify->AddWcharBuffer(TRUE); // last chance to parse any stragglers
|
|
if (hr == E_ABORT)
|
|
goto Error;
|
|
|
|
if (m_pArr!=NULL)
|
|
hr = m_pArr->EOF();
|
|
|
|
TerminateDataLoad(pBSC);
|
|
}
|
|
|
|
Cleanup:
|
|
// Void fn - can't return an error code ...
|
|
//
|
|
if (SUCCEEDED(m_hrDownloadStatus))
|
|
m_hrDownloadStatus = hr;
|
|
return;
|
|
|
|
Error:
|
|
// Security failure.
|
|
// Abort the current download
|
|
if (m_pBSC && m_pBSC->m_spBinding)
|
|
{
|
|
(void) m_pBSC->m_spBinding->Abort();
|
|
|
|
// also delete the downloaded file from the internet cache
|
|
m_pBSC->DeleteDataFile();
|
|
}
|
|
|
|
m_hrDownloadStatus = hr;
|
|
|
|
// Notify data set changed for the abort
|
|
if (m_pEventBroker != NULL)
|
|
{
|
|
hr = m_pEventBroker->STDDataSetChanged();
|
|
// and go complete.
|
|
UpdateReadyState(READYSTATE_COMPLETE);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Utility routine to get our
|
|
//
|
|
HRESULT
|
|
GetHostURL(IOleClientSite *pSite, LPOLESTR *ppszHostName)
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IMoniker> spMoniker;
|
|
CComPtr<IBindCtx> spBindCtx;
|
|
|
|
if (!pSite)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = pSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_CONTAINER,
|
|
&spMoniker);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = CreateBindCtx(0, &spBindCtx);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = spMoniker->GetDisplayName(spBindCtx, NULL, ppszHostName);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTDCCtl::SecurityMatchProtocols(LPOLESTR pszURL)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
LPOLESTR pszHostURL = NULL;
|
|
LPWCH pszPostHostProtocol;
|
|
LPWCH pszPostProtocol;
|
|
|
|
if (FAILED(GetHostURL(m_spClientSite, &pszHostURL)))
|
|
goto Cleanup;
|
|
|
|
pszPostHostProtocol = wch_chr(pszHostURL, _T(':'));
|
|
pszPostProtocol = wch_chr(pszURL, _T(':'));
|
|
if (!pszPostHostProtocol || !pszPostProtocol)
|
|
goto Cleanup;
|
|
else
|
|
{
|
|
int ccChars1 = pszPostHostProtocol - pszHostURL;
|
|
int ccChars2 = pszPostProtocol - pszURL;
|
|
if (ccChars1 != ccChars2)
|
|
goto Cleanup;
|
|
else if (wch_ncmp(pszHostURL, pszURL, ccChars1) != 0)
|
|
goto Cleanup;
|
|
}
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
if (pszHostURL)
|
|
CoTaskMemFree(pszHostURL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTDCCtl::SecurityMatchAllowDomainList()
|
|
{
|
|
HRESULT hr;
|
|
WCHAR swzHostDomain[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
DWORD cchHostDomain = INTERNET_MAX_HOST_NAME_LENGTH;
|
|
LPOLESTR pszHostName = NULL;
|
|
|
|
hr = GetHostURL(m_spClientSite, &pszHostName);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = CoInternetParseUrl(pszHostName, PARSE_DOMAIN, 0, swzHostDomain, cchHostDomain,
|
|
&cchHostDomain, 0);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
hr = m_pUnify->MatchAllowDomainList(swzHostDomain);
|
|
|
|
Cleanup:
|
|
CoTaskMemFree(pszHostName);
|
|
return hr;
|
|
}
|