// This is a part of the Active Template Library. // Copyright (C) Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Active Template Library Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Active Template Library product. #ifndef __ATLSOAP_H__ #define __ATLSOAP_H__ #pragma once #if (defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)) #error require winsock2.h -- include before you include #endif #if ((_WIN32_WINNT < 0x0400) && (_WIN32_WINDOWS <= 0x0400)) #error require _WIN32_WINNT >= 0x0400 or _WIN32_WINDOWS > 0x0400 #endif #ifndef ATLSOAP_TRACE #ifdef _ATLSOAP_TRACE_XML #define ATLSOAP_TRACE(__data, __len) AtlSoapTraceXML(__data, __len) #else #define ATLSOAP_TRACE(__data, __len) __noop #endif #endif // ATLSOAP_TRACE // override this macro to ATL_BASE64_FLAG_NOCRLF if you do // not want Base64-encoded binary data to contain CRLFs #ifndef ATLSOAP_BASE64_FLAGS #define ATLSOAP_BASE64_FLAGS ATL_BASE64_FLAG_NONE #endif // ATLSOAP_BASE64_FLAGS [ emitidl(restricted) ]; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma warning(push) #pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible #pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible #pragma warning(disable: 4061) // enumerate 'enum value' in switch of enum 'enum type' is not explicitly handled by a case label #ifndef _CPPUNWIND #pragma warning(disable: 4702) // unreachable code #endif // _CPPUNWIND #ifndef ATLSOAP_NOWININET #include #ifndef ATLSOAPINET_CLIENT #define ATLSOAPINET_CLIENT _T("VCSoapClient") #endif #endif #ifndef _ATL_NO_DEFAULT_LIBS #pragma comment(lib, "msxml2.lib") #ifndef ATLSOAP_NOWININET #pragma comment(lib, "wininet.lib") #endif #endif #define _ATLSOAP_MAKEWIDESTR( str ) L ## str #define ATLSOAP_MAKEWIDESTR( str ) _ATLSOAP_MAKEWIDESTR( str ) #pragma pack(push,_ATL_PACKING) namespace ATL { ATL_NOINLINE inline void AtlSoapTraceXML(LPBYTE pdwData, DWORD dwLen) { HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdOut != INVALID_HANDLE_VALUE) { DWORD dwWritten; WriteFile(hStdOut, "\n-----------------------------------------------------------------\n", sizeof("\n-----------------------------------------------------------------\n")-1, &dwWritten, NULL); WriteFile(hStdOut, pdwData, dwLen, &dwWritten, NULL); WriteFile(hStdOut, "\n-----------------------------------------------------------------\n", sizeof("\n-----------------------------------------------------------------\n")-1, &dwWritten, NULL); } } //////////////////////////////////////////////////////////////////////////////// // // IStreamImpl - stub IStream implementation class // //////////////////////////////////////////////////////////////////////////////// class IStreamImpl : public IStream { public: HRESULT __stdcall Read(void * /*pDest*/, ULONG /*nMaxLen*/, ULONG * /*pnRead*/) { return E_NOTIMPL; } HRESULT __stdcall Write(const void * /*pv*/, ULONG /*cb*/, ULONG * /*pcbWritten*/) { return E_NOTIMPL; } HRESULT __stdcall Seek(LARGE_INTEGER /*dlibMove*/, DWORD /*dwOrigin*/, ULARGE_INTEGER * /*pLibNewPosition*/) { return E_NOTIMPL; } HRESULT __stdcall SetSize(ULARGE_INTEGER /*libNewSize*/) { return E_NOTIMPL; } HRESULT __stdcall CopyTo(IStream * /*pStream*/, ULARGE_INTEGER /*cb*/, ULARGE_INTEGER * /*pcbRead*/, ULARGE_INTEGER * /*pcbWritten*/) { return E_NOTIMPL; } HRESULT __stdcall Commit(DWORD /*grfCommitFlags*/) { return E_NOTIMPL; } HRESULT __stdcall Revert() { return E_NOTIMPL; } HRESULT __stdcall LockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/) { return E_NOTIMPL; } HRESULT __stdcall UnlockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/) { return E_NOTIMPL; } HRESULT __stdcall Stat(STATSTG * /*pstatstg*/, DWORD /*grfStatFlag*/) { return E_NOTIMPL; } HRESULT __stdcall Clone(IStream ** /*ppstm*/) { return E_NOTIMPL; } }; // class IStreamImpl //////////////////////////////////////////////////////////////////////////////// // // CStreamOnServerContext // //////////////////////////////////////////////////////////////////////////////// class CStreamOnServerContext : public IStreamImpl { public: HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_IStream) || InlineIsEqualGUID(riid, IID_ISequentialStream)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } private: IHttpServerContext * m_pServerContext; DWORD m_dwBytesRead; public: CStreamOnServerContext(IHttpServerContext *pServerContext = NULL) : m_pServerContext(pServerContext), m_dwBytesRead(0) { } void SetServerContext(IHttpServerContext *pServerContext) { ATLASSUME( m_pServerContext == NULL ); m_pServerContext = pServerContext; } HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead) { ATLENSURE( pDest != NULL ); ATLASSUME( m_pServerContext != NULL ); DWORD dwToRead = __min(m_pServerContext->GetTotalBytes()-m_dwBytesRead, nMaxLen); if (ReadClientData(m_pServerContext, (LPSTR) pDest, &dwToRead, m_dwBytesRead) != FALSE) { m_dwBytesRead+= dwToRead; if (pnRead != NULL) { *pnRead = dwToRead; } return S_OK; } ATLTRACE( _T("ATLSOAP: CStreamOnServerContext::Read -- ReadClientData failed.\r\n") ); return E_FAIL; } }; // class CStreamOnServerContext //////////////////////////////////////////////////////////////////////////////// // // CReadStreamOnSocket // //////////////////////////////////////////////////////////////////////////////// template class CReadStreamOnSocket : public IStreamImpl { public: HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_IStream) || InlineIsEqualGUID(riid, IID_ISequentialStream)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } private: CAtlHttpClientT * m_pSocket; LPCSTR m_szBuffer; LPCSTR m_szCurr; long m_nBodyLen; public: CReadStreamOnSocket() : m_pSocket(NULL), m_szBuffer(NULL), m_szCurr(NULL), m_nBodyLen(0) { } BOOL Init(CAtlHttpClientT *pSocket) { ATLENSURE( pSocket != NULL ); m_pSocket = pSocket; m_szBuffer = (LPCSTR) pSocket->GetBody(); ATLSOAP_TRACE( (LPBYTE) pSocket->GetBody(), pSocket->GetBodyLength() ); if (m_szBuffer != NULL) { m_szCurr = m_szBuffer; m_nBodyLen = pSocket->GetBodyLength(); if (m_nBodyLen != 0) { return TRUE; } } ATLTRACE( _T("ATLSOAP: CReadStreamOnSocket::Init failed.\r\n") ); return FALSE; } HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead) { ATLASSERT( pDest != NULL ); ATLASSUME( m_pSocket != NULL ); ATLASSUME( m_szBuffer != NULL ); if (pnRead != NULL) { *pnRead = 0; } long nRead = (int) (m_szCurr-m_szBuffer); if (nRead < m_nBodyLen) { long nLength = __min((int)(m_nBodyLen-nRead), (LONG) nMaxLen); Checked::memcpy_s(pDest, nMaxLen, m_szCurr, nLength); m_szCurr+= nLength; if (pnRead != NULL) { *pnRead = (ULONG) nLength; } } return S_OK; } }; // class CReadStreamOnSocket //////////////////////////////////////////////////////////////////////////////// // // CWriteStreamOnCString // //////////////////////////////////////////////////////////////////////////////// class CWriteStreamOnCString : public IWriteStream { public: CStringA m_str; virtual ~CWriteStreamOnCString() { } HRESULT WriteStream(LPCSTR szOut, int nLen, LPDWORD pdwWritten) { ATLENSURE_RETURN( szOut != NULL ); if (nLen < 0) { nLen = (int) strlen(szOut); } _ATLTRY { m_str.Append(szOut, nLen); } _ATLCATCHALL() { return E_OUTOFMEMORY; } if (pdwWritten != NULL) { *pdwWritten = (DWORD) nLen; } return S_OK; } HRESULT FlushStream() { return S_OK; } void Cleanup() { m_str.Empty(); } }; // class CWriteStreamOnCString //////////////////////////////////////////////////////////////////////////////// // // Namespaces // //////////////////////////////////////////////////////////////////////////////// #define SOAPENV_NAMESPACEA "http://schemas.xmlsoap.org/soap/envelope/" #define SOAPENV_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENV_NAMESPACEA ) #define SOAPENC_NAMESPACEA "http://schemas.xmlsoap.org/soap/encoding/" #define SOAPENC_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENC_NAMESPACEA ) #define XSI_NAMESPACEA "http://www.w3.org/2001/XMLSchema-instance" #define XSI_NAMESPACEW ATLSOAP_MAKEWIDESTR( XSI_NAMESPACEA ) #define XSD_NAMESPACEA "http://www.w3.org/2001/XMLSchema" #define XSD_NAMESPACEW ATLSOAP_MAKEWIDESTR( XSD_NAMESPACEA ) #ifndef ATLSOAP_GENERIC_NAMESPACE #define ATLSOAP_GENERIC_NAMESPACE L"http://www.tempuri.org" #endif //////////////////////////////////////////////////////////////////////////////// // // Helpers // //////////////////////////////////////////////////////////////////////////////// inline HRESULT GetAttribute( __in ISAXAttributes *pAttributes, __in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName, __out_ecount_part(*pcchValue, *pcchValue) const wchar_t **pwszValue, __inout int *pcchValue, __in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0) { if (!pAttributes || !wszAttrName || !pwszValue || !pcchValue) { return E_INVALIDARG; } *pwszValue = NULL; *pcchValue = 0; if (!wszNamespace) { return (pAttributes->getValueFromQName(wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL); } return (pAttributes->getValueFromName(wszNamespace, cchNamespace, wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL); } inline HRESULT GetAttribute( __in ISAXAttributes *pAttributes, __in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName, __inout CStringW &strValue, __in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0) { const wchar_t *wszValue = NULL; int cchValue = 0; if (!pAttributes || !wszAttrName) { return E_INVALIDARG; } HRESULT hr; if (!wszNamespace) { hr = (pAttributes->getValueFromQName(wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL); } else { hr = (pAttributes->getValueFromName(wszNamespace, cchNamespace, wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL); } if (hr == S_OK) { _ATLTRY { strValue.SetString(wszValue, cchValue); } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: GetAttribute -- out of memory.\r\n") ); hr = E_OUTOFMEMORY; } } return hr; } inline const wchar_t *SkipWhitespace(const wchar_t *wsz) { while (*wsz && iswspace(*wsz)) ++wsz; return wsz; } } // namespace ATL #pragma pack(pop) //////////////////////////////////////////////////////////////////////////////// // // BLOB data type - use this struct when you want to send BLOB data // the attribute provider and proxy generator will only properly special // case blob data when using this struct. // //////////////////////////////////////////////////////////////////////////////// [ export ] typedef struct _tagATLSOAP_BLOB { unsigned long size; unsigned char *data; } ATLSOAP_BLOB; #ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS #define _ATL_VALIDATE_PARAMETER_END(p)\ do \ { \ if(*(p) !='\0') \ return E_FAIL; \ } while(0) #else #define _ATL_VALIDATE_PARAMETER_END(p) #endif // All non-integral types have specializations which // will be called. The following function will be called // only for integral types #pragma push_macro("max") #pragma push_macro("min") #undef max #undef min template inline HRESULT AtlGetSAXValue(T * pVal , const wchar_t * wsz , int cch ) { __int64 nVal = *pVal; if (FAILED(AtlGetSAXValue(&nVal, wsz, cch))) return E_FAIL; #ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS if(nVal < std::numeric_limits::min() || nVal > std::numeric_limits::max()) return E_FAIL; #endif *pVal = T(nVal); return S_OK; } #pragma pop_macro("max") #pragma pop_macro("min") //////////////////////////////////////////////////////////////////////////////// // // AtlGetXMLValue (for IXMLDOMDocument) - get the real type from the XML data // /////////////////////////////////////////////////////////////////////////////// // // generic IXMLDOMNode template function // delegates to AtlGetSAXValue // template inline HRESULT AtlGetXMLValue(IXMLDOMNode *pParam, T *pVal) { CComBSTR bstrVal; HRESULT hr = AtlGetXMLValue(pParam, &bstrVal); if (SUCCEEDED(hr)) { hr = AtlGetSAXValue(pVal, bstrVal, bstrVal.Length()); } return hr; } // specialization for BSTR template <> inline HRESULT AtlGetXMLValue(IXMLDOMNode *pParam, BSTR *pbstrVal) { if (pParam == NULL) { return E_INVALIDARG; } if (pbstrVal == NULL) { return E_POINTER; } CComPtr spChild; if (pParam->get_firstChild(&spChild) == S_OK) { CComPtr spXmlChild; if (spChild->get_firstChild(&spXmlChild) == S_OK) { return (pParam->get_xml(pbstrVal) == S_OK ? S_OK : E_FAIL); } } return (pParam->get_text(pbstrVal) == S_OK) ? S_OK : E_FAIL; } //////////////////////////////////////////////////////////////////////////////// // // AtlGetSAXValue - (for SAX or generic) get the real type from the XML data // //////////////////////////////////////////////////////////////////////////////// template <> inline HRESULT AtlGetSAXValue(bool *pVal, __in_z const wchar_t *wsz, int cch) { ATLENSURE( wsz != NULL ); if (!pVal) { return E_POINTER; } *pVal = false; HRESULT hr = E_FAIL; switch (wsz[0]) { case L'1': { if (cch==1) { *pVal = true; hr = S_OK; } break; } case L'0': { if (cch==1) { *pVal = false; hr = S_OK; } break; } case L't': { if (cch==sizeof("true")-1 && !wcsncmp(wsz, L"true", cch)) { *pVal = true; hr = S_OK; } break; } case L'f': { if (cch==sizeof("false")-1 && !wcsncmp(wsz, L"false", cch)) { *pVal = false; hr = S_OK; } break; } } return hr; } template <> inline HRESULT AtlGetSAXValue<__int64>(__int64 *pVal, __in_z const wchar_t *wsz, int cch) { ATLENSURE_RETURN( wsz != NULL ); if (!pVal) { return E_POINTER; } _ATLTRY { CFixedStringT wstr(wsz, cch); const wchar_t *pStart = ATL::SkipWhitespace(static_cast(wstr)); const wchar_t *pEnd; __int64 i = 0; errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast(&pEnd), 10); if (errnoValue == ERANGE) { return E_FAIL;//overflow or underflow case } pEnd = ATL::SkipWhitespace(pEnd); _ATL_VALIDATE_PARAMETER_END(pEnd); *pVal = i; } _ATLCATCHALL() { return E_OUTOFMEMORY; } return S_OK; } template <> inline HRESULT AtlGetSAXValue(unsigned __int64 *pVal, __in_z const wchar_t *wsz, int cch) { ATLENSURE_RETURN( wsz != NULL ); if (!pVal) { return E_POINTER; } _ATLTRY { CFixedStringT wstr(wsz, cch); const wchar_t *pStart = ATL::SkipWhitespace(static_cast(wstr)); const wchar_t *pEnd; unsigned __int64 i = 0; errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast(&pEnd), 10); if (errnoValue == ERANGE) { return E_FAIL;//overflow or underflow case } pEnd = ATL::SkipWhitespace(pEnd); _ATL_VALIDATE_PARAMETER_END(pEnd); *pVal = i; } _ATLCATCHALL() { return E_OUTOFMEMORY; } return S_OK; } template <> inline HRESULT AtlGetSAXValue(double *pVal, __in_z const wchar_t *wsz, int cch) { ATLENSURE_RETURN( wsz != NULL ); if (!pVal) { return E_POINTER; } if ((cch == 3) && (wsz[0]==L'I') && (!wcsncmp(wsz, L"INF", cch))) { *(((int *) pVal)+0) = 0x0000000; *(((int *) pVal)+1) = 0x7FF00000; } else if ((cch == 3) && (wsz[0]==L'N') && (!wcsncmp(wsz, L"NaN", cch))) { *(((int *) pVal)+0) = 0x0000000; *(((int *) pVal)+1) = 0xFFF80000; } else if ((cch == 4) && (wsz[1]==L'I') && (!wcsncmp(wsz, L"-INF", cch))) { *(((int *) pVal)+0) = 0x0000000; *(((int *) pVal)+1) = 0xFFF00000; } else { errno_t errnoValue = 0; _ATLTRY { CFixedStringT wstr(wsz, cch); const wchar_t *pStart = ATL::SkipWhitespace(static_cast(wstr)); const wchar_t *pEnd; double d = 0.0; errnoValue = AtlStrToNum(&d, pStart, const_cast(&pEnd)); pEnd = ATL::SkipWhitespace(pEnd); _ATL_VALIDATE_PARAMETER_END(pEnd); *pVal = d; } _ATLCATCHALL() { return E_OUTOFMEMORY; } if ((*pVal == -HUGE_VAL) || (*pVal == HUGE_VAL) || (errnoValue == ERANGE)) { return E_FAIL; } } return S_OK; } template <> inline HRESULT AtlGetSAXValue(float *pVal, __in_z const wchar_t *wsz, int cch) { ATLASSERT( wsz != NULL ); if (!pVal) { return E_POINTER; } double d = *pVal; if (SUCCEEDED(AtlGetSAXValue(&d, wsz, cch))) { #ifdef _ATL_SOAP_PARAMETER_VALIDATIONS if(d > FLT_MAX || d < -FLT_MAX) return E_FAIL; #endif *pVal = (float) d; return S_OK; } return E_FAIL; } template <> inline HRESULT AtlGetSAXValue(BSTR *pVal, __in_z const wchar_t *wsz, int cch) { ATLASSERT( wsz != NULL ); if (pVal == NULL) { return E_POINTER; } *pVal = SysAllocStringLen(wsz, cch); return ((*pVal != NULL) ? S_OK : E_OUTOFMEMORY); } inline HRESULT AtlGetSAXBlobValue( ATLSOAP_BLOB *pVal, const wchar_t *wsz, int cch, IAtlMemMgr *pMemMgr, bool bHex = false) { ATLENSURE_RETURN( wsz != NULL ); ATLENSURE_RETURN( pMemMgr != NULL ); if (pVal == NULL) { return E_POINTER; } if (pVal->data != NULL) { return E_INVALIDARG; } pVal->data = NULL; pVal->size = 0; int nLength = AtlUnicodeToUTF8(wsz, cch, NULL, 0); if (nLength != 0) { char * pSrc = (char *) pMemMgr->Allocate(nLength); if (pSrc != NULL) { nLength = AtlUnicodeToUTF8(wsz, cch, pSrc, nLength); if (nLength != 0) { pVal->data = (unsigned char *) pMemMgr->Allocate(nLength); if (pVal->data != NULL) { BOOL bRet; int nDataLength = nLength; if (!bHex) { bRet = Base64Decode(pSrc, nLength, pVal->data, &nDataLength); } else { bRet = AtlHexDecode(pSrc, nLength, pVal->data, &nDataLength); } if (bRet) { pVal->size = nDataLength; } } } pMemMgr->Free(pSrc); } } if (pVal->size == 0) { if (pVal->data != NULL) { pMemMgr->Free(pVal->data); pVal->data = NULL; } } return S_OK; } //////////////////////////////////////////////////////////////////////////////// // // AtlGenXMLValue template and specializations // //////////////////////////////////////////////////////////////////////////////// template inline HRESULT AtlGenXMLValue(__in IWriteStream *pStream, __in T *pVal) { if ((pStream == NULL) || (pVal == NULL)) { return E_INVALIDARG; } // // delegate to CWriteStreamHelper // CWriteStreamHelper s(pStream); return (s.Write(*pVal) == TRUE ? S_OK : E_FAIL); } #ifdef _NATIVE_WCHAR_T_DEFINED template <> inline HRESULT AtlGenXMLValue(__in IWriteStream *pStream, __in wchar_t *pVal) { return AtlGenXMLValue(pStream, (unsigned short *)pVal); } #endif template <> inline HRESULT AtlGenXMLValue(__in IWriteStream *pStream, __deref_inout_z wchar_t **pVal) { if ((pStream == NULL) || (*pVal == NULL)) { return E_INVALIDARG; } wchar_t *wszWrite = *pVal; int nSrcLen = (int)wcslen(*pVal); int nCnt = EscapeXML(*pVal, nSrcLen, NULL, 0); if (nCnt > nSrcLen) { nCnt++; wszWrite = (wchar_t *)calloc((nCnt),sizeof(wchar_t)); if (wszWrite == NULL) { return E_OUTOFMEMORY; } nCnt = EscapeXML(*pVal, nSrcLen, wszWrite, nCnt); if (nCnt == 0) { free(wszWrite); return E_FAIL; } wszWrite[nCnt] = L'\0'; nSrcLen = nCnt; } nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, NULL, 0); HRESULT hr = E_FAIL; if ((nCnt == 0) || (nCnt == nSrcLen)) { CWriteStreamHelper s(pStream); hr = (s.Write(wszWrite) == TRUE ? S_OK : E_FAIL); } else { nCnt++; CHeapPtr szWrite; szWrite.AllocateBytes((size_t)(nCnt)); if (szWrite != NULL) { nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, szWrite, nCnt); if (nCnt != 0) { hr = pStream->WriteStream(szWrite, nCnt, NULL); } } else { ATLTRACE( _T("ATLSOAP: AtlGenXMLValue -- out of memory.\r\n") ); hr = E_OUTOFMEMORY; } } if (wszWrite != *pVal) { free(wszWrite); } return hr; } template <> inline HRESULT AtlGenXMLValue(IWriteStream *pStream, double *pVal) { if ((pStream == NULL) || (pVal == NULL)) { return E_INVALIDARG; } HRESULT hr; switch (_fpclass(*pVal)) { case _FPCLASS_SNAN: case _FPCLASS_QNAN: { hr = pStream->WriteStream("NaN", 3, NULL); break; } case _FPCLASS_NINF: { hr = pStream->WriteStream("-INF", 4, NULL); break; } case _FPCLASS_PINF: { hr = pStream->WriteStream("INF", 3, NULL); break; } case _FPCLASS_NZ: { hr = pStream->WriteStream("-0", 2, NULL); break; } default: { /*** * 2 = sign + decimal point * ndec = decimal digits * 5 = exponent letter (e or E), exponent sign, three digits exponent * 1 = extra space for rounding * 1 = string terminator '\0' ***/ const int ndec = 512; CHAR szBuf[ndec+9]; szBuf[0] = '\0'; Checked::gcvt_s(szBuf, _countof(szBuf), *pVal, ndec); size_t nLen = strlen(szBuf); if (nLen && szBuf[nLen-1] == '.') { szBuf[--nLen] = '\0'; } hr = pStream->WriteStream(szBuf, (int)nLen, NULL); break; } } return hr; } template <> inline HRESULT AtlGenXMLValue(IWriteStream *pStream, float *pVal) { if ((pStream == NULL) || (pVal == NULL)) { return E_INVALIDARG; } double d = *pVal; return AtlGenXMLValue(pStream, &d); } template <> inline HRESULT AtlGenXMLValue(IWriteStream *pStream, bool *pVal) { if ((pStream == NULL) || (pVal == NULL)) { return E_INVALIDARG; } if (*pVal == true) { return pStream->WriteStream("true", sizeof("true")-1, NULL); } return pStream->WriteStream("false", sizeof("false")-1, NULL); } inline HRESULT AtlGenXMLBlobValue( IWriteStream *pStream, ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr, bool bHex = false) { if ((pStream == NULL) || (pVal == NULL) || (pMemMgr == NULL)) { return E_INVALIDARG; } HRESULT hr = E_FAIL; int nLength; if (!bHex) { nLength = Base64EncodeGetRequiredLength(pVal->size, ATLSOAP_BASE64_FLAGS); } else { nLength = AtlHexEncodeGetRequiredLength(pVal->size); } char *pEnc = (char *) pMemMgr->Allocate(nLength); if (pEnc != NULL) { BOOL bRet; if (!bHex) { bRet = Base64Encode(pVal->data, pVal->size, pEnc, &nLength, ATLSOAP_BASE64_FLAGS); } else { bRet = AtlHexEncode(pVal->data, pVal->size, pEnc, &nLength); } if (bRet) { hr = pStream->WriteStream(pEnc, nLength, NULL); } pMemMgr->Free(pEnc); } return hr; } template inline HRESULT AtlCleanupValue(T * /*pVal*/) { return S_OK; } inline HRESULT AtlCleanupBlobValue(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr) { if ((pVal == NULL) || (pMemMgr == NULL)) { return E_INVALIDARG; } if (pVal->data != NULL) { pMemMgr->Free(pVal->data); pVal->data = NULL; pVal->size = 0; } return S_OK; } template <> inline HRESULT AtlCleanupValue(ATLSOAP_BLOB *pVal) { ATLTRACE( _T("Warning: AtlCleanupValue was called -- assuming CRT allocator.\r\n") ); if (pVal == NULL) { return E_INVALIDARG; } if (pVal->data != NULL) { free(pVal->data); pVal->data = NULL; pVal->size = 0; } return S_OK; } template <> inline HRESULT AtlCleanupValue(BSTR *pVal) { if (pVal == NULL) { // should never happen ATLASSERT( FALSE ); return E_INVALIDARG; } if ((*pVal) != NULL) { // null strings are okay SysFreeString(*pVal); *pVal = NULL; } return S_OK; } template inline HRESULT AtlCleanupValueEx(T *pVal, IAtlMemMgr *pMemMgr) { pMemMgr; return AtlCleanupValue(pVal); } template <> inline HRESULT AtlCleanupValueEx(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr) { return AtlCleanupBlobValue(pVal, pMemMgr); } // single dimensional arrays template inline HRESULT AtlCleanupArray(T *pArray, int nCnt) { if (pArray == NULL) { return E_INVALIDARG; } for (int i=0; i inline HRESULT AtlCleanupArrayEx(T *pArray, int nCnt, IAtlMemMgr *pMemMgr) { if (pArray == NULL) { return E_INVALIDARG; } for (int i=0; i inline HRESULT AtlCleanupArrayMD(T *pArray, const int *pDims) { if ((pArray == NULL) || (pDims == NULL)) { return E_INVALIDARG; } // calculate size int nCnt = 1; for (int i=1; i<=pDims[0]; i++) { nCnt*= pDims[i]; } return AtlCleanupArray(pArray, nCnt); } template inline HRESULT AtlCleanupArrayMDEx(T *pArray, const int *pDims, IAtlMemMgr *pMemMgr) { if ((pArray == NULL) || (pDims == NULL)) { return E_INVALIDARG; } // calculate size int nCnt = 1; for (int i=1; i<=pDims[0]; i++) { nCnt*= pDims[i]; } return AtlCleanupArrayEx(pArray, nCnt, pMemMgr); } #pragma pack(push,_ATL_PACKING) namespace ATL { //////////////////////////////////////////////////////////////////////////////// // // CSAXSoapErrorHandler // //////////////////////////////////////////////////////////////////////////////// class CSAXSoapErrorHandler : public ISAXErrorHandler { private: CFixedStringT m_strParseError; public: virtual ~CSAXSoapErrorHandler() { } HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (!ppv) { return E_POINTER; } if (InlineIsEqualGUID(riid, __uuidof(ISAXErrorHandler)) || InlineIsEqualGUID(riid, __uuidof(IUnknown))) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } const CStringW& GetParseError() { return m_strParseError; } HRESULT __stdcall error( ISAXLocator *pLocator, const wchar_t *wszErrorMessage, HRESULT hrErrorCode) { (pLocator); (wszErrorMessage); (hrErrorCode); ATLTRACE( _T("ATLSOAP: parse error: %ws\r\n"), wszErrorMessage ); _ATLTRY { m_strParseError = wszErrorMessage; } _ATLCATCHALL() { return E_FAIL; } return hrErrorCode; } HRESULT __stdcall fatalError( ISAXLocator *pLocator, const wchar_t *wszErrorMessage, HRESULT hrErrorCode) { (pLocator); (wszErrorMessage); (hrErrorCode); ATLTRACE( _T("ATLSOAP: fatal parse error: %ws\r\n"), wszErrorMessage ); _ATLTRY { m_strParseError = wszErrorMessage; } _ATLCATCHALL() { return E_FAIL; } return hrErrorCode; } HRESULT __stdcall ignorableWarning( ISAXLocator *pLocator, const wchar_t *wszErrorMessage, HRESULT hrErrorCode) { (pLocator); (wszErrorMessage); (hrErrorCode); ATLTRACE( _T("ATLSOAP: ignorable warning: %ws\r\n"), wszErrorMessage ); return hrErrorCode; } }; //////////////////////////////////////////////////////////////////////////////// // // ISAXContentHandlerImpl // //////////////////////////////////////////////////////////////////////////////// class ISAXContentHandlerImpl : public ISAXContentHandler { public: // // ISAXContentHandler interface // HRESULT __stdcall putDocumentLocator(ISAXLocator * /*pLocator*/) { return S_OK; } HRESULT __stdcall startDocument() { return S_OK; } HRESULT __stdcall endDocument() { return S_OK; } HRESULT __stdcall startPrefixMapping( const wchar_t * /*wszPrefix*/, int /*cchPrefix*/, const wchar_t * /*wszUri*/, int /*cchUri*/) { return S_OK; } HRESULT __stdcall endPrefixMapping( const wchar_t * /*wszPrefix*/, int /*cchPrefix*/) { return S_OK; } HRESULT __stdcall startElement( const wchar_t * /*wszNamespaceUri*/, int /*cchNamespaceUri*/, const wchar_t * /*wszLocalName*/, int /*cchLocalName*/, const wchar_t * /*wszQName*/, int /*cchQName*/, ISAXAttributes * /*pAttributes*/) { return S_OK; } HRESULT __stdcall endElement( const wchar_t * /*wszNamespaceUri*/, int /*cchNamespaceUri*/, const wchar_t * /*wszLocalName*/, int /*cchLocalName*/, const wchar_t * /*wszQName*/, int /*cchQName*/) { return S_OK; } HRESULT __stdcall characters( const wchar_t * /*wszChars*/, int /*cchChars*/) { return S_OK; } HRESULT __stdcall ignorableWhitespace( const wchar_t * /*wszChars*/, int /*cchChars*/) { return S_OK; } HRESULT __stdcall processingInstruction( const wchar_t * /*wszTarget*/, int /*cchTarget*/, const wchar_t * /*wszData*/, int /*cchData*/) { return S_OK; } HRESULT __stdcall skippedEntity( const wchar_t * /*wszName*/, int /*cchName*/) { return S_OK; } }; // class ISAXContentHandlerImpl //////////////////////////////////////////////////////////////////////////////// // // SAX skip element handler utility class // (skip an element and all its child elements) // //////////////////////////////////////////////////////////////////////////////// class CSkipHandler : public ISAXContentHandlerImpl { public: virtual ~CSkipHandler() { } HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_ISAXContentHandler)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } private: DWORD m_dwReset; CComPtr m_spReader; CComPtr m_spParent; DWORD DisableReset(DWORD dwCnt = 1) { m_dwReset += dwCnt; return m_dwReset; } DWORD EnableReset() { if (m_dwReset > 0) { --m_dwReset; } return m_dwReset; } public: CSkipHandler(ISAXContentHandler *pParent = NULL, ISAXXMLReader *pReader = NULL) : m_spParent(pParent), m_spReader(pReader), m_dwReset(1) { } void SetParent(ISAXContentHandler *pParent) { m_spParent = pParent; } void DetachParent() { m_spParent.Detach(); } void SetReader(ISAXXMLReader *pReader) { m_spReader = pReader; } HRESULT __stdcall startElement( const wchar_t * /*wszNamespaceUri*/, int /*cchNamespaceUri*/, const wchar_t * /*wszLocalName*/, int /*cchLocalName*/, const wchar_t * /*wszQName*/, int /*cchQName*/, ISAXAttributes * /*pAttributes*/) { DisableReset(); return S_OK; } HRESULT __stdcall endElement( const wchar_t * /*wszNamespaceUri*/, int /*cchNamespaceUri*/, const wchar_t * /*wszLocalName*/, int /*cchLocalName*/, const wchar_t * /*wszQName*/, int /*cchQName*/) { if (EnableReset() == 0) { m_spReader->putContentHandler(m_spParent); } return S_OK; } }; // class CSkipHandler //////////////////////////////////////////////////////////////////////////////// // // SAX string builder class // //////////////////////////////////////////////////////////////////////////////// class CSAXStringBuilder : public ISAXContentHandlerImpl { public: HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_ISAXContentHandler)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } private: ISAXContentHandler * m_pParent; ISAXXMLReader * m_pReader; DWORD m_dwReset; CFixedStringT m_str; DWORD DisableReset(DWORD dwReset = 1) { m_dwReset+= dwReset; return m_dwReset; } DWORD EnableReset() { if (m_dwReset > 0) { --m_dwReset; } return m_dwReset; } public: CSAXStringBuilder(ISAXXMLReader *pReader = NULL, ISAXContentHandler *pParent = NULL) :m_pReader(pReader), m_pParent(pParent), m_dwReset(0) { } virtual ~CSAXStringBuilder() { } void SetReader(ISAXXMLReader *pReader) { m_pReader = pReader; } void SetParent(ISAXContentHandler *pParent) { m_pParent = pParent; } const CStringW& GetString() { return m_str; } void Clear() { m_str.Empty(); m_dwReset = 0; } HRESULT __stdcall startElement( const wchar_t * /*wszNamespaceUri*/, int /*cchNamespaceUri*/, const wchar_t * /*wszLocalName*/, int /*cchLocalName*/, const wchar_t *wszQName, int cchQName, ISAXAttributes *pAttributes) { if (m_dwReset == 0) { // if there is unescaped, nested XML, must disable // an additional time for the first element DisableReset(); } DisableReset(); int nAttrs = 0; HRESULT hr = pAttributes->getLength(&nAttrs); _ATLTRY { if (SUCCEEDED(hr)) { m_str.Append(L"<", 1); m_str.Append(wszQName, cchQName); const wchar_t *wszAttrNamespaceUri = NULL; const wchar_t *wszAttrLocalName = NULL; const wchar_t *wszAttrQName = NULL; const wchar_t *wszAttrValue = NULL; int cchAttrUri = 0; int cchAttrLocalName = 0; int cchAttrQName = 0; int cchAttrValue = 0; for (int i=0; igetName(i, &wszAttrNamespaceUri, &cchAttrUri, &wszAttrLocalName, &cchAttrLocalName, &wszAttrQName, &cchAttrQName); if (FAILED(hr)) { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") ); break; } m_str.Append(L" ", 1); m_str.Append(wszAttrQName, cchAttrQName); hr = pAttributes->getValue(i, &wszAttrValue, &cchAttrValue); if (FAILED(hr)) { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") ); break; } m_str.Append(L"=\"", sizeof("=\"")-1); if (cchAttrValue != 0) { m_str.Append(wszAttrValue, cchAttrValue); } m_str.Append(L"\"", 1); } if (SUCCEEDED(hr)) { m_str.Append(L">", 1); } } } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- out of memory.\r\n") ); hr = E_OUTOFMEMORY; } return hr; } HRESULT __stdcall endElement( const wchar_t * wszNamespaceUri, int cchNamespaceUri, const wchar_t * wszLocalName, int cchLocalName, const wchar_t *wszQName, int cchQName) { HRESULT hr = S_OK; _ATLTRY { if (EnableReset() == 0) { hr = m_pParent->characters((LPCWSTR) m_str, m_str.GetLength()); if (SUCCEEDED(hr)) { hr = m_pParent->endElement(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, wszQName, cchQName); } m_pReader->putContentHandler(m_pParent); } if (m_dwReset > 0) { m_str.Append(L"", 1); } } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::endElement -- out of memory.\r\n") ); hr = E_OUTOFMEMORY; } return hr; } HRESULT __stdcall characters( const wchar_t *wszChars, int cchChars) { _ATLTRY { m_str.Append(wszChars, cchChars); } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::characters -- out of memory.\r\n") ); return E_OUTOFMEMORY; } return S_OK; } HRESULT __stdcall ignorableWhitespace( const wchar_t *wszChars, int cchChars) { _ATLTRY { m_str.Append(wszChars, cchChars); } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::ignorableWhitespace -- out of memory.\r\n") ); return E_OUTOFMEMORY; } return S_OK; } }; // class CSAXStringBuilder } // namespace ATL #pragma pack(pop) //////////////////////////////////////////////////////////////////////////////// // // SOAP data structure definitions // //////////////////////////////////////////////////////////////////////////////// // // ***************************** WARNING ***************************** // THESE STRUCTURES ARE INTERNAL ONLY, FOR USE WITH THE ATL SERVER SOAP // ATTRIBUTES. USERS SHOULD NOT USE THESE TYPES DIRECTLY. ABSOLUTELY NO // GUARANTEES ARE MADE ABOUT BACKWARD COMPATIBILITY FOR DIRECT USE OF // THESE TYPES. // //////////////////////////////////////////////////////////////////////////////// // // BEGIN PRIVATE DEFINITIONS // //////////////////////////////////////////////////////////////////////////////// inline HRESULT AtlSoapGetArraySize(ISAXAttributes *pAttributes, size_t *pnSize, const wchar_t **pwszTypeStart = NULL, const wchar_t **pwszTypeEnd = NULL) { if (pnSize == NULL) { return E_POINTER; } if (pAttributes == NULL) { return E_INVALIDARG; } *pnSize = 0; HRESULT hr = S_OK; _ATLTRY { const wchar_t *wszTmp; int cch; hr = GetAttribute(pAttributes, L"arrayType", sizeof("arrayType")-1, &wszTmp, &cch, SOAPENC_NAMESPACEW, sizeof(SOAPENC_NAMESPACEA)-1); if ((SUCCEEDED(hr)) && (wszTmp != NULL)) { hr = E_FAIL; CFixedStringT wstrArrayType(wszTmp, cch); const wchar_t *wsz = static_cast(wstrArrayType); const wchar_t *wszTypeStart = NULL; const wchar_t *wszTypeEnd = NULL; // skip spaces while (iswspace(*wsz) != 0) { wsz++; } // no need to walk the string if the caller is not interested if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL)) { wszTypeStart = wsz; wszTypeEnd = wcschr(wszTypeStart, L':'); if (wszTypeEnd != NULL) { wszTypeStart = wszTypeEnd+1; } } // SOAP Section 5 encodings are of the form: // :arrayType="[dim1(,dim_i)*] // for example: SOAP-ENC:arrayType="xsd:string[2,4]" wsz = wcschr(wsz, L'['); if (wsz != NULL) { wszTypeEnd = wsz-1; if (wsz[1] == ']') { return S_FALSE; } *pnSize = 1; // get the size of each dimension while (wsz != NULL) { wsz++; int nDim = _wtoi(wsz); if (nDim < 0) { hr = E_FAIL; break; } *pnSize *= (size_t) nDim; if (!nDim) { break; } wsz = wcschr(wsz, L','); } if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL)) { *pwszTypeStart = wszTypeStart; *pwszTypeEnd = wszTypeEnd; } hr = S_OK; } } else { // not a section-5 encoding hr = S_FALSE; } } _ATLCATCHALL() { hr = E_OUTOFMEMORY; } return hr; } inline size_t AtlSoapGetArrayDims(const int *pDims) { if (pDims == NULL) { return 0; } size_t nRet = 1; for (int i=1; i<=pDims[0]; i++) { nRet *= pDims[i]; } return nRet; } enum SOAPFLAGS { SOAPFLAG_NONE = 0x00000000, SOAPFLAG_IN = 0x00000001, SOAPFLAG_OUT = 0x00000002, SOAPFLAG_RETVAL = 0x00000004, SOAPFLAG_DYNARR = 0x00000008, SOAPFLAG_FIXEDARR = 0x00000010, SOAPFLAG_MUSTUNDERSTAND = 0x00000020, SOAPFLAG_UNKSIZE = 0x00000040, SOAPFLAG_READYSTATE = 0x00000080, SOAPFLAG_FIELD = 0x00000100, SOAPFLAG_NOMARSHAL = 0x00000200, SOAPFLAG_NULLABLE = 0x00000400, SOAPFLAG_DOCUMENT = 0x00000800, SOAPFLAG_RPC = 0x00001000, SOAPFLAG_LITERAL = 0x00002000, SOAPFLAG_ENCODED = 0x00004000, SOAPFLAG_PID = 0x00008000, SOAPFLAG_PAD = 0x00010000, SOAPFLAG_CHAIN = 0x00020000, SOAPFLAG_SIZEIS = 0x00040000, SOAPFLAG_DYNARRWRAPPER = 0x00080000 }; enum SOAPMAPTYPE { SOAPMAP_ERR = 0, SOAPMAP_ENUM, SOAPMAP_FUNC, SOAPMAP_STRUCT, SOAPMAP_UNION, SOAPMAP_HEADER, SOAPMAP_PARAM }; struct _soapmap; struct _soapmapentry { ULONG nHash; const char * szField; const WCHAR * wszField; int cchField; int nVal; DWORD dwFlags; size_t nOffset; const int * pDims; const _soapmap * pChain; int nSizeIs; ULONG nNamespaceHash; const char *szNamespace; const wchar_t *wszNamespace; int cchNamespace; }; struct _soapmap { ULONG nHash; const char * szName; const wchar_t * wszName; int cchName; int cchWName; SOAPMAPTYPE mapType; const _soapmapentry * pEntries; size_t nElementSize; size_t nElements; int nRetvalIndex; DWORD dwCallFlags; ULONG nNamespaceHash; const char *szNamespace; const wchar_t *wszNamespace; int cchNamespace; }; enum SOAPTYPES { SOAPTYPE_ERR = -2, SOAPTYPE_UNK = -1, SOAPTYPE_STRING = 0, SOAPTYPE_BOOLEAN, SOAPTYPE_FLOAT, SOAPTYPE_DOUBLE, SOAPTYPE_DECIMAL, SOAPTYPE_DURATION, SOAPTYPE_HEXBINARY, SOAPTYPE_BASE64BINARY, SOAPTYPE_ANYURI, SOAPTYPE_ID, SOAPTYPE_IDREF, SOAPTYPE_ENTITY, SOAPTYPE_NOTATION, SOAPTYPE_QNAME, SOAPTYPE_NORMALIZEDSTRING, SOAPTYPE_TOKEN, SOAPTYPE_LANGUAGE, SOAPTYPE_IDREFS, SOAPTYPE_ENTITIES, SOAPTYPE_NMTOKEN, SOAPTYPE_NMTOKENS, SOAPTYPE_NAME, SOAPTYPE_NCNAME, SOAPTYPE_INTEGER, SOAPTYPE_NONPOSITIVEINTEGER, SOAPTYPE_NEGATIVEINTEGER, SOAPTYPE_LONG, SOAPTYPE_INT, SOAPTYPE_SHORT, SOAPTYPE_BYTE, SOAPTYPE_NONNEGATIVEINTEGER, SOAPTYPE_UNSIGNEDLONG, SOAPTYPE_UNSIGNEDINT, SOAPTYPE_UNSIGNEDSHORT, SOAPTYPE_UNSIGNEDBYTE, SOAPTYPE_POSITIVEINTEGER, SOAPTYPE_DATETIME, SOAPTYPE_TIME, SOAPTYPE_DATE, SOAPTYPE_GMONTH, SOAPTYPE_GYEARMONTH, SOAPTYPE_GYEAR, SOAPTYPE_GMONTHDAY, SOAPTYPE_GDAY, SOAPTYPE_USERBASE = 0x00001000 }; inline ULONG AtlSoapHashStr(const char * sz) { ULONG nHash = 0; while (*sz != 0) { nHash = (nHash<<5)+nHash+(*sz); sz++; } return nHash; } inline ULONG AtlSoapHashStr(const wchar_t * sz) { ULONG nHash = 0; while (*sz != 0) { nHash = (nHash<<5)+nHash+(*sz); sz++; } return nHash; } inline ULONG AtlSoapHashStr(const char * sz, int cch) { ULONG nHash = 0; for (int i=0; i m_spReader; CSAXStringBuilder m_stringBuilder; CSkipHandler m_skipHandler; const wchar_t *m_wszSoapPrefix; int m_cchSoapPrefix; public: virtual ~CSoapFaultParser() { m_skipHandler.DetachParent(); } // IUnknown interface HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_ISAXContentHandler)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } // constructor CSoapFaultParser(CSoapFault *pFault, ISAXXMLReader *pReader) :m_pFault(pFault), m_dwState(STATE_ERROR), m_spReader(pReader) { ATLASSERT( pFault != NULL ); ATLASSERT( pReader != NULL ); } // ISAXContentHandler interface HRESULT __stdcall startElement( const wchar_t * wszNamespaceUri, int cchNamespaceUri, const wchar_t * wszLocalName, int cchLocalName, const wchar_t * /*wszQName*/, int /*cchQName*/, ISAXAttributes * /*pAttributes*/) { struct _faultmap { const wchar_t *wszTag; int cchTag; DWORD dwState; }; const static _faultmap s_faultParseMap[] = { { L"Envelope", sizeof("Envelope")-1, CSoapFaultParser::STATE_ENVELOPE }, { L"Body", sizeof("Body")-1, CSoapFaultParser::STATE_BODY }, { L"Header", sizeof("Header")-1, CSoapFaultParser::STATE_BODY }, { L"Fault", sizeof("Fault")-1, CSoapFaultParser::STATE_START }, { L"faultcode", sizeof("faultcode")-1, CSoapFaultParser::STATE_FAULTCODE }, { L"faultstring", sizeof("faultstring")-1, CSoapFaultParser::STATE_FAULTSTRING }, { L"faultactor", sizeof("faultactor")-1, CSoapFaultParser::STATE_FAULTACTOR }, { L"detail", sizeof("detail")-1, CSoapFaultParser::STATE_DETAIL } }; if (m_spReader.p == NULL) { ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- ISAXXMLReader is NULL.\r\n" ) ); return E_INVALIDARG; } m_dwState &= ~STATE_RESET; for (int i=0; i<(sizeof(s_faultParseMap)/sizeof(s_faultParseMap[0])); i++) { if ((cchLocalName == s_faultParseMap[i].cchTag) && (!wcsncmp(wszLocalName, s_faultParseMap[i].wszTag, cchLocalName))) { DWORD dwState = s_faultParseMap[i].dwState; if ((dwState & (STATE_START | STATE_ENVELOPE | STATE_BODY)) == 0) { m_stringBuilder.SetReader(m_spReader); m_stringBuilder.SetParent(this); m_stringBuilder.Clear(); m_spReader->putContentHandler( &m_stringBuilder ); } else { if ((dwState <= m_dwState) || (cchNamespaceUri != sizeof(SOAPENV_NAMESPACEA)-1) || (wcsncmp(wszNamespaceUri, SOAPENV_NAMESPACEW, cchNamespaceUri))) { ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) ); return E_FAIL; } } m_dwState = dwState; return S_OK; } } if (m_dwState > STATE_START) { m_dwState = STATE_SKIP; m_skipHandler.SetReader(m_spReader); m_skipHandler.SetParent(this); m_spReader->putContentHandler( &m_skipHandler ); return S_OK; } ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) ); return E_FAIL; } HRESULT __stdcall startPrefixMapping( const wchar_t * wszPrefix, int cchPrefix, const wchar_t * wszUri, int cchUri) { if ((cchUri == sizeof(SOAPENV_NAMESPACEA)-1) && (!wcsncmp(wszUri, SOAPENV_NAMESPACEW, cchUri))) { m_wszSoapPrefix = wszPrefix; m_cchSoapPrefix = cchPrefix; } return S_OK; } HRESULT __stdcall characters( const wchar_t * wszChars, int cchChars); }; extern __declspec(selectany) const int ATLS_SOAPFAULT_CNT = 4; class CSoapFault { private: struct _faultcode { const wchar_t *wsz; int cch; const wchar_t *wszFaultString; int cchFaultString; SOAP_ERROR_CODE errCode; }; static const _faultcode s_faultCodes[]; public: // members SOAP_ERROR_CODE m_soapErrCode; CStringW m_strFaultCode; CStringW m_strFaultString; CStringW m_strFaultActor; CStringW m_strDetail; CSoapFault() : m_soapErrCode(SOAP_E_UNK) { } HRESULT SetErrorCode( const wchar_t *wsz, const wchar_t *wszSoapPrefix, int cch = -1, int cchSoapPrefix = -1, bool bSetFaultString = true) { if ((wsz == NULL) || (wszSoapPrefix == NULL)) { return E_INVALIDARG; } if (cch == -1) { cch = (int) wcslen(wsz); } while (*wsz && iswspace(*wsz)) { ++wsz; --cch; } if (cchSoapPrefix == -1) { cchSoapPrefix = (int) wcslen(wszSoapPrefix); } const wchar_t *wszLocalName = wcschr(wsz, L':'); if (wszLocalName == NULL) { // faultCode must be QName ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- faultCode is not a QName.\r\n" ) ); return E_FAIL; } // make sure the namespace of the fault is the // SOAPENV namespace if ((cchSoapPrefix != (int)(wszLocalName-wsz)) || (wcsncmp(wsz, wszSoapPrefix, cchSoapPrefix))) { ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- fault namespace is incorrect.\r\n" ) ); return E_FAIL; } wszLocalName++; cch -= (int) (wszLocalName-wsz); _ATLTRY { for (int i=0; i spReader; if (pReader != NULL) { spReader = pReader; } else { if (FAILED(spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER))) { ATLTRACE( _T("ATLSOAP: CSoapFault::ParseFault -- CoCreateInstance of SAXXMLReader failed.\r\n" ) ); return E_FAIL; } } Clear(); CSoapFaultParser parser(const_cast(this), spReader); spReader->putContentHandler(&parser); CComVariant varStream; varStream = static_cast(pStream); HRESULT hr = spReader->parse(varStream); spReader->putContentHandler(NULL); return hr; } HRESULT GenerateFault(IWriteStream *pWriteStream) { if ((pWriteStream == NULL) || (m_soapErrCode == SOAP_E_UNK)) { return E_INVALIDARG; } ATLASSERT( (m_soapErrCode == SOAP_E_UNK) || (m_soapErrCode == SOAP_E_VERSION_MISMATCH) || (m_soapErrCode == SOAP_E_MUST_UNDERSTAND) || (m_soapErrCode == SOAP_E_CLIENT) || (m_soapErrCode == SOAP_E_SERVER) ); HRESULT hr = S_OK; _ATLTRY { const wchar_t *wszFaultCode = NULL; if (m_strFaultCode.GetLength() == 0) { for (int i=0; i<4; i++) { if (s_faultCodes[i].errCode == m_soapErrCode) { if (m_strFaultString.GetLength() == 0) { m_strFaultString.SetString(s_faultCodes[i].wszFaultString, s_faultCodes[i].cchFaultString); } wszFaultCode = s_faultCodes[i].wsz; break; } } } if (wszFaultCode == NULL) { if (m_strFaultCode.GetLength() != 0) { wszFaultCode = m_strFaultCode; } else { ATLTRACE( _T("CSoapFault::GenerateFault -- missing/invalid fault code.\r\n") ); return E_FAIL; } } const LPCSTR s_szErrorFormat = "" "" "" "SOAP:%ws" "%ws" "%s%ws%s" "%ws" "" "" ""; CStringA strFault; strFault.Format(s_szErrorFormat, wszFaultCode, m_strFaultString, m_strFaultActor.GetLength() ? "" : "", m_strFaultActor, m_strFaultActor.GetLength() ? "" : "", m_strDetail); hr = pWriteStream->WriteStream(strFault, strFault.GetLength(), NULL); } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSoapFault::GenerateFault -- out of memory.\r\n" ) ); hr = E_OUTOFMEMORY; } return hr; } void Clear() { m_soapErrCode = SOAP_E_UNK; m_strFaultCode.Empty(); m_strFaultString.Empty(); m_strFaultActor.Empty(); m_strDetail.Empty(); } }; // class CSoapFault #define DECLARE_SOAP_FAULT(__name, __faultstring, __errcode) \ { L ## __name, sizeof(__name)-1, L ## __faultstring, sizeof(__faultstring), __errcode }, __declspec(selectany) const CSoapFault::_faultcode CSoapFault::s_faultCodes[] = { DECLARE_SOAP_FAULT("VersionMismatch", "SOAP Version Mismatch Error", SOAP_E_VERSION_MISMATCH) DECLARE_SOAP_FAULT("MustUnderstand", "SOAP Must Understand Error", SOAP_E_MUST_UNDERSTAND) DECLARE_SOAP_FAULT("Client", "SOAP Invalid Request", SOAP_E_CLIENT) DECLARE_SOAP_FAULT("Server", "SOAP Server Application Faulted", SOAP_E_SERVER) }; ATL_NOINLINE inline HRESULT __stdcall CSoapFaultParser::characters( const wchar_t * wszChars, int cchChars) { if (m_pFault == NULL) { return E_INVALIDARG; } if (m_dwState & STATE_RESET) { return S_OK; } HRESULT hr = E_FAIL; _ATLTRY { switch (m_dwState) { case STATE_FAULTCODE: if (m_pFault->m_soapErrCode == SOAP_E_UNK) { hr = m_pFault->SetErrorCode(wszChars, m_wszSoapPrefix, cchChars, m_cchSoapPrefix, false); } break; case STATE_FAULTSTRING: if (m_pFault->m_strFaultString.GetLength() == 0) { m_pFault->m_strFaultString.SetString(wszChars, cchChars); hr = S_OK; } break; case STATE_FAULTACTOR: if (m_pFault->m_strFaultActor.GetLength() == 0) { m_pFault->m_strFaultActor.SetString(wszChars, cchChars); hr = S_OK; } break; case STATE_DETAIL: if (m_pFault->m_strDetail.GetLength() == 0) { m_pFault->m_strDetail.SetString(wszChars, cchChars); hr = S_OK; } break; case STATE_START: case STATE_ENVELOPE : case STATE_BODY : case STATE_SKIP: hr = S_OK; break; default: // should never get here ATLASSERT( FALSE ); break; } } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSoapFaultParser::characters -- out of memory.\r\n" ) ); hr = E_OUTOFMEMORY; } m_dwState |= STATE_RESET; return hr; } //////////////////////////////////////////////////////////////////////////////// // // CSoapRootHandler - the class that does most of the work // //////////////////////////////////////////////////////////////////////////////// #ifndef ATLSOAP_STACKSIZE // 16 will be plenty for the 99% case #define ATLSOAP_STACKSIZE 16 #endif #ifndef ATLSOAP_GROWARRAY #define ATLSOAP_GROWARRAY 10 #endif class CSoapRootHandler : public ISAXContentHandlerImpl { private: friend class _CSDLGenerator; // // state constants // const static DWORD SOAP_START = 0; const static DWORD SOAP_ENVELOPE = 1; const static DWORD SOAP_HEADERS = 2; const static DWORD SOAP_BODY = 3; const static DWORD SOAP_PARAMS = 4; const static DWORD SOAP_CALLED = 5; const static DWORD SOAP_RESPONSE = 6; const static DWORD SOAP_HEADERS_DONE = 7; // // hash values for SOAP namespaces and elements // const static ULONG SOAP_ENV = 0x5D3574E2; const static ULONG SOAP_ENC = 0xBD62724B; const static ULONG ENVELOPE = 0xDBE6009E; const static ULONG HEADER = 0xAF4DFFC9; const static ULONG BODY = 0x0026168E; // // XSD Names // struct XSDEntry { wchar_t * wszName; char * szName; int cchName; }; const static XSDEntry s_xsdNames[]; // // CBitVector - a dynamically sized bit vector class // class CBitVector { private: // 64 bits will handle the 99% case unsigned __int64 m_nBits; // when we need to grow unsigned __int64 * m_pBits; size_t m_nSize; bool Grow(size_t nIndex) { // Think carefully // In our current implementation, CHAR_BIT==8, and sizeof(m_nBits)==8. Easy to confuse the two. // We do math in bits, so this is our max size ATLENSURE(nIndex= m_nSize) { return false; } size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT); size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT); return ((m_pBits[i] >> nBits) & 0x01); } bool SetBit(size_t nIndex) { if (nIndex >= m_nSize) { if (!Grow(nIndex)) { return false; } } size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT); size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT); m_pBits[i] |= (((unsigned __int64) 1) << nBits); return true; } void Clear() { if (m_pBits == &m_nBits) { m_nBits = 0; } else { memset(m_pBits, 0x00, (m_nSize/CHAR_BIT)); } } ~CBitVector() { if (m_pBits != &m_nBits) { free(m_pBits); } m_pBits = &m_nBits; m_nSize = sizeof(m_nBits)*CHAR_BIT; } void RelocateFixup() { if (m_nSize <= sizeof(m_nBits)*CHAR_BIT) { m_pBits = &m_nBits; } } }; // class CBitVector // // Parsing State // struct ParseState { void *pvElement; DWORD dwFlags; size_t nAllocSize; size_t nExpectedElements; size_t nElement; const _soapmap *pMap; const _soapmapentry *pEntry; // mark when we get an item CBitVector vec; size_t nDepth; ParseState(void *pvElement_ = NULL, DWORD dwFlags_ = 0, size_t nAllocSize_ = 0, size_t nExpectedElements_ = 0, size_t nElement_ = 0, const _soapmap *pMap_ = NULL, const _soapmapentry *pEntry_ = NULL) : pvElement(pvElement_), dwFlags(dwFlags_), nAllocSize(nAllocSize_), nExpectedElements(nExpectedElements_), nElement(nElement_), pMap(pMap_), pEntry(pEntry_), nDepth(0) { vec.Clear(); } ParseState(const ParseState& that) { pvElement = that.pvElement; dwFlags = that.dwFlags; nAllocSize = that.nAllocSize; nExpectedElements = that.nExpectedElements; nElement = that.nElement; pMap = that.pMap; pEntry = that.pEntry; nDepth = that.nDepth; vec.Clear(); } ~ParseState() { pvElement = NULL; dwFlags = 0; nAllocSize = 0; nExpectedElements = 0; nElement = 0; pMap = NULL; pEntry = NULL; nDepth = 0; vec.Clear(); } void RelocateFixup() { vec.RelocateFixup(); } }; // struct ParseState class CParseStateElementTraits : public CDefaultElementTraits { public: // CBitVector relocate fixup static void RelocateElements( ParseState* pDest, ParseState* pSrc, size_t nElements ) { CDefaultElementTraits::RelocateElements(pDest, pSrc, nElements); // fixup CBitVector for (size_t i=0; iWriteStream("", sizeof("")-1, NULL); } HRESULT StartHeaders(IWriteStream *pStream, const _soapmap *pMap) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); HRESULT hr = pStream->WriteStream("dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) != (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) { // qualify document/literal by default // For this version, ATL Server will not respect // the elementForm* attributes in an XSD schema hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL); } } } else { // rpc/encoded hr = pStream->WriteStream(">", sizeof(">")-1, NULL); } } return hr; } HRESULT EndHeaders(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream("", sizeof("")-1, NULL); } virtual HRESULT StartBody(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream( "", sizeof("")-1, NULL); } HRESULT EndBody(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream("", sizeof("")-1, NULL); } HRESULT EndEnvelope(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream("", sizeof("")-1, NULL); } virtual HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0; virtual HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0; virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pEntry != NULL ); // output name HRESULT hr = pStream->WriteStream("<", 1, NULL); if (SUCCEEDED(hr)) { const char *szHeaderNamespace = NULL; int cchHeaderNamespace = 0; if ((pMap != NULL) && (pMap->mapType == SOAPMAP_HEADER) && ((pEntry->pChain != NULL) && (pEntry->pChain->szNamespace !=NULL)) || (pEntry->szNamespace != NULL)) { hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL); if (SUCCEEDED(hr)) { szHeaderNamespace = pEntry->pChain ? pEntry->pChain->szNamespace : pEntry->szNamespace; cchHeaderNamespace = pEntry->pChain ? pEntry->pChain->cchNamespace : pEntry->cchNamespace; } } if (SUCCEEDED(hr)) { if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0) { hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL); } else { hr = pStream->WriteStream("return", sizeof("return")-1, NULL); } if (SUCCEEDED(hr)) { if (szHeaderNamespace != NULL) { ATLASSERT( cchHeaderNamespace != 0 ); hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(szHeaderNamespace, cchHeaderNamespace, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL); } } } } } } if (SUCCEEDED(hr)) { if (pEntry->dwFlags & SOAPFLAG_MUSTUNDERSTAND) { // output mustUnderstand hr = pStream->WriteStream(" soap:mustUnderstand=\"1\"", sizeof(" soap:mustUnderstand=\"1\"")-1, NULL); } } return hr; } HRESULT EndEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pEntry != NULL ); HRESULT hr = pStream->WriteStream("mapType == SOAPMAP_HEADER) && ((pEntry->pChain != NULL) && (pEntry->pChain->szNamespace !=NULL)) || (pEntry->szNamespace != NULL)) { hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL); } if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0) { hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL); } else { hr = pStream->WriteStream("return", sizeof("return")-1, NULL); } if (SUCCEEDED(hr)) { hr = pStream->WriteStream(">", 1, NULL); } } return hr; } }; // class CResponseGenerator class CDocLiteralGenerator : public CResponseGenerator { public: HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); HRESULT hr = S_OK; // output type name hr = pStream->WriteStream("<", 1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL); if (SUCCEEDED(hr)) { if ((pMap->mapType == SOAPMAP_FUNC) && (bClient == false) && (pMap->dwCallFlags & SOAPFLAG_PID)) { hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL); if (FAILED(hr)) { return hr; } } if (pMap->mapType == SOAPMAP_FUNC) { hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL); } } } else { hr = pStream->WriteStream(">", 1, NULL); } } } return hr; } HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); HRESULT hr = pStream->WriteStream("WriteStream(pMap->szName, pMap->cchName, NULL); if (SUCCEEDED(hr)) { if ((pMap->mapType == SOAPMAP_FUNC) && (bClient == false) && (pMap->dwCallFlags & SOAPFLAG_PID)) { hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL); if (FAILED(hr)) { return hr; } } hr = pStream->WriteStream(">", 1, NULL); } } return hr; } }; // class CDocLiteralGenerator class CPIDGenerator : public CDocLiteralGenerator { }; class CPADGenerator : public CDocLiteralGenerator { public: virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pEntry != NULL ); HRESULT hr = __super::StartEntry(pStream, pMap, pEntry); if (SUCCEEDED(hr) && (pMap->dwCallFlags & SOAPFLAG_PAD)) { hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL); } } } return hr; } }; // class CPADGenerator class CRpcEncodedGenerator : public CResponseGenerator { public: HRESULT StartBody(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream( "", sizeof("")-1, NULL); } HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); (bClient); // unused for rpc/encoded HRESULT hr = pStream->WriteStream("WriteStream(pMap->szName, pMap->cchName, NULL); if (SUCCEEDED(hr)) { if (pMap->mapType == SOAPMAP_FUNC) { hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL); if (SUCCEEDED(hr)) { ATLASSERT( pMap->szNamespace != NULL ); hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL); } } } else { hr = pStream->WriteStream(">", 1, NULL); } } } return hr; } HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); (bClient); // unused for rpc/encoded HRESULT hr = pStream->WriteStream("WriteStream(pMap->szName, pMap->cchName, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(">", 1, NULL); } } return hr; } }; // class CRpcEncodedGenerator // // members // CAtlArray m_stateStack; size_t m_nState; DWORD m_dwState; CComPtr m_spReader; CSAXStringBuilder m_stringBuilder; CSkipHandler m_skipHandler; IAtlMemMgr * m_pMemMgr; static CCRTHeap m_crtHeap; bool m_bClient; void *m_pvParam; bool m_bNullCheck; bool m_bChildCheck; bool m_bCharacters; size_t m_nDepth; typedef CFixedStringT REFSTRING; // used for rpc/encoded messages with href's typedef CAtlMap > REFMAP; REFMAP m_refMap; // // Implementation helpers // HRESULT PushState(void *pvElement = NULL, const _soapmap *pMap = NULL, const _soapmapentry *pEntry = NULL, DWORD dwFlags = 0, size_t nAllocSize = 0, size_t nExpectedElements = 0, size_t nElement = 0) { if (m_stateStack.IsEmpty()) { // 16 will be plenty for the 99% case if (!m_stateStack.SetCount(0, 16)) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } } size_t nCnt = m_stateStack.GetCount(); m_nState = m_stateStack.Add(); if (m_stateStack.GetCount() <= nCnt) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } ParseState &state = m_stateStack[m_nState]; state.pvElement = pvElement; state.dwFlags = dwFlags; state.nAllocSize = nAllocSize; state.nExpectedElements = nExpectedElements; state.nElement = nElement; state.pMap = pMap; state.pEntry = pEntry; state.nDepth = m_nDepth; return S_OK; } ParseState& GetState() { return m_stateStack[m_nState]; } void PopState(bool bForce = false) { if ((m_nState != 0) || (bForce != false)) { m_stateStack.RemoveAt(m_nState); --m_nState; } } BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck, int cchNamespaceUriCheck, const wchar_t *wszNamespaceUriCheck, int cchLocalName, const wchar_t *wszLocalName, int cchNamespaceUri, const wchar_t *wszNamespaceUri) { ATLENSURE(wszLocalName); ATLENSURE(wszLocalNameCheck); ATLENSURE(wszNamespaceUri); ATLENSURE(wszNamespaceUriCheck); if (cchLocalName == cchLocalNameCheck && cchNamespaceUri == cchNamespaceUriCheck && !wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName) && !wcsncmp(wszNamespaceUri, wszNamespaceUriCheck, cchNamespaceUri)) { return TRUE; } return FALSE; } ATL_FORCEINLINE BOOL IsEqualString(const wchar_t *wszStr1, int cchStr1, const wchar_t *wszStr2, int cchStr2) { ATLENSURE( wszStr1 != NULL ); ATLENSURE( wszStr2 != NULL ); ATLENSURE( cchStr1 >= 0 ); ATLENSURE( cchStr2 >= 0 ); if (cchStr1 == cchStr2) { return !wcsncmp(wszStr1, wszStr2, cchStr2); } return FALSE; } ATL_FORCEINLINE BOOL IsEqualStringHash(const wchar_t *wszStr1, int cchStr1, ULONG nHash1, const wchar_t *wszStr2, int cchStr2, ULONG nHash2) { ATLENSURE( wszStr1 != NULL ); ATLENSURE( wszStr2 != NULL ); ATLENSURE( cchStr1 >= 0 ); ATLENSURE( cchStr2 >= 0 ); if (nHash1 == nHash2) { return IsEqualString(wszStr1, cchStr1, wszStr2, cchStr2); } return FALSE; } BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck, int cchLocalName, const wchar_t *wszLocalName) { if (cchLocalName == cchLocalNameCheck && !wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName)) { return TRUE; } return FALSE; } void SetOffsetValue(void *pBase, void *pSrc, size_t nOffset) { void **ppDest = (void **)(((unsigned char *)pBase)+nOffset); *ppDest = pSrc; } bool IsRpcEncoded() { if ((m_stateStack[0].pMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) == (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) { return true; } return false; } HRESULT ValidateArrayEntry( ParseState& state, const wchar_t *wszLocalName, int cchLocalName) { (cchLocalName); (wszLocalName); ATLASSERT( state.pEntry != NULL ); // SOAP Section 5.4.2 // check number of elements if (state.nElement == state.nExpectedElements) { // too many elements if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::ValidateArrayEntry -- too many elements.\r\n" ) ); return E_FAIL; } ATLASSERT( IsRpcEncoded() == false ); // see if we need to allocate more if (state.nElement == state.nAllocSize) { unsigned char **ppArr = (unsigned char **)state.pvElement; size_t nNewElement=0; HRESULT hr=E_FAIL; if(FAILED(hr=::ATL::AtlMultiply(&nNewElement, state.nElement, static_cast(2)))) { return hr; } hr = AllocateArray(state.pEntry, (void **)ppArr, __max(nNewElement, ATLSOAP_GROWARRAY), state.nElement); if (SUCCEEDED(hr)) { state.nAllocSize = __max((state.nElement)*2, ATLSOAP_GROWARRAY); } return hr; } } return S_OK; } HRESULT CheckID( const wchar_t *wszNamespaceUri, const wchar_t *wszLocalName, int cchLocalName, ISAXAttributes *pAttributes) { (cchLocalName); (wszLocalName); (wszNamespaceUri); ATLASSERT( pAttributes != NULL ); const wchar_t *wsz = NULL; int cch = 0; HRESULT hr = GetAttribute(pAttributes, L"id", sizeof("id")-1, &wsz, &cch); if ((hr == S_OK) && (wsz != NULL)) { const REFMAP::CPair *p = NULL; _ATLTRY { REFSTRING strRef(wsz, cch); p = m_refMap.Lookup(strRef); if (p == NULL) { return S_FALSE; } } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } ATLASSERT( IsRpcEncoded() == true ); const ParseState& state = p->m_value; // disallow href-chaining hr = CheckHref(state.pEntry, state.pvElement, pAttributes); if (hr != S_FALSE) { return E_FAIL; } hr = S_OK; // do array stuff if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) { hr = GetSection5Info(state, state.pEntry, pAttributes); } else { // only structs and arrays are allowed for hrefs ATLASSERT( state.pEntry->pChain != NULL ); ATLASSERT( state.pEntry->pChain->mapType == SOAPMAP_STRUCT ); // structs must have child entries m_bChildCheck = state.pEntry->pChain->nElements != 0; if (S_OK != PushState(state.pvElement, state.pEntry->pChain, state.pEntry, state.dwFlags, 0, state.pEntry->pChain->nElements)) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\n" ) ); hr = E_OUTOFMEMORY; } } m_refMap.DisableAutoRehash(); m_refMap.RemoveAtPos(const_cast(p)); m_refMap.EnableAutoRehash(); return hr; } return S_FALSE; } HRESULT GetElementEntry( ParseState& state, const wchar_t *wszNamespaceUri, const wchar_t *wszLocalName, int cchLocalName, ISAXAttributes *pAttributes, const _soapmapentry **ppEntry) { ATLENSURE_RETURN( state.pMap != NULL ); ATLENSURE_RETURN( ppEntry != NULL ); *ppEntry = NULL; const _soapmapentry *pEntries = state.pMap->pEntries; DWORD dwIncludeFlags; DWORD dwExcludeFlags; HRESULT hr = CheckID(wszNamespaceUri, wszLocalName, cchLocalName, pAttributes); if (hr != S_FALSE) { if (hr == S_OK) { hr = S_FALSE; } return hr; } if (m_bClient != false) { dwIncludeFlags = SOAPFLAG_OUT; dwExcludeFlags = SOAPFLAG_IN; } else { dwIncludeFlags = SOAPFLAG_IN; dwExcludeFlags = SOAPFLAG_OUT; } ULONG nHash = AtlSoapHashStr(wszLocalName, cchLocalName); for (size_t i=0; pEntries[i].nHash != 0; i++) { if (nHash == pEntries[i].nHash && ((pEntries[i].dwFlags & dwIncludeFlags) || ((pEntries[i].dwFlags & dwExcludeFlags) == 0)) && IsEqualElement(pEntries[i].cchField, pEntries[i].wszField, cchLocalName, wszLocalName)/* && !wcscmp(wszNamespaceUri, wszNamespace)*/) { // check bit vector if (state.vec.GetBit(i) == false) { if (state.vec.SetBit(i) == false) { return E_OUTOFMEMORY; } } else { // already received this element ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- duplicate element was sent.\r\n" ) ); return E_FAIL; } state.nElement++; *ppEntry = &pEntries[i]; return S_OK; } } ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- element not found: %.*ws.\r\n" ), cchLocalName, wszLocalName ); return E_FAIL; } HRESULT CheckMustUnderstandHeader(ISAXAttributes *pAttributes) { ATLASSERT( pAttributes != NULL ); const wchar_t* wszMustUnderstand; int cchMustUnderstand; bool bMustUnderstand= false; if (SUCCEEDED(GetAttribute(pAttributes, L"mustUnderstand", sizeof("mustUnderstand")-1, &wszMustUnderstand, &cchMustUnderstand, SOAPENV_NAMESPACEW, sizeof(SOAPENV_NAMESPACEA)-1)) && (wszMustUnderstand != NULL)) { if (FAILED(AtlGetSAXValue(&bMustUnderstand, wszMustUnderstand, cchMustUnderstand))) { bMustUnderstand = true; } } if (bMustUnderstand == false) { ATLASSERT( GetReader() != NULL ); m_skipHandler.SetReader(GetReader()); m_skipHandler.SetParent(this); return GetReader()->putContentHandler( &m_skipHandler ); } else { SoapFault(SOAP_E_MUST_UNDERSTAND, NULL, 0); } ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckMustUnderstandHeader -- unknown \"mustUnderstand\" SOAP Header was received.\r\n" ) ); return E_FAIL; } HRESULT AllocateArray( const _soapmapentry *pEntry, void **ppArr, size_t nElements, size_t nCurrElements = 0) { ATLENSURE_RETURN( ppArr != NULL ); ATLENSURE_RETURN( pEntry != NULL ); size_t nElementSize; if (pEntry->nVal != SOAPTYPE_UNK) { nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal); } else // UDT { ATLENSURE_RETURN( pEntry->pChain != NULL ); nElementSize = pEntry->pChain->nElementSize; } if (nElementSize != 0) { if (*ppArr == NULL) { ATLASSERT( nCurrElements == 0 ); size_t nBytes=0; HRESULT hr=S_OK; if( FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nElements))) { return hr; } *ppArr = m_pMemMgr->Allocate(nBytes); } else // *ppArr != NULL { ATLASSERT( nCurrElements != 0 ); size_t nBytes=0; HRESULT hr=S_OK; if( FAILED(hr=::ATL::AtlAdd(&nBytes, nElements, nCurrElements)) || FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nBytes))) { return hr; } *ppArr = m_pMemMgr->Reallocate(*ppArr, nBytes); } } else { // internal error ATLASSERT( FALSE ); return E_FAIL; } if (*ppArr == NULL) { return E_OUTOFMEMORY; } memset(((unsigned char *)(*ppArr))+(nCurrElements*nElementSize), 0x00, nElements*nElementSize); return S_OK; } HRESULT GetSection5Info( const ParseState& state, const _soapmapentry *pEntry, ISAXAttributes *pAttributes) { ATLENSURE_RETURN( pEntry != NULL ); ATLENSURE_RETURN( pAttributes != NULL ); HRESULT hr; if (IsRpcEncoded() != false) { // check for href // we ONLY do this for rpc/encoded (required for interop) // NOTE: ATL Server does not support object graphs, so // only single-reference elements are allowed hr = CheckHref(pEntry, state.pvElement, pAttributes, pEntry->dwFlags, SOAPFLAG_READYSTATE); if (hr != S_FALSE) { return hr; } } size_t nElements; DWORD dwFlags = 0; hr = AtlSoapGetArraySize(pAttributes, &nElements); if (FAILED(hr)) { return hr; } size_t nAllocSize = 0; size_t nElementsPush = 0; if (pEntry->dwFlags & SOAPFLAG_DYNARR) { // set size_is value ATLENSURE_RETURN( state.pMap != NULL ); int *pnSizeIs = (int *)(((unsigned char *)state.pvElement)+ (state.pMap->pEntries[pEntry->nSizeIs].nOffset)); if (hr != S_OK) { if (IsRpcEncoded()) { // rpc/encoded requires soapenc:arrayType attribute return E_FAIL; } nElements = ATLSOAP_GROWARRAY; nAllocSize = ATLSOAP_GROWARRAY; dwFlags |= SOAPFLAG_UNKSIZE; *pnSizeIs = 0; } else { *pnSizeIs = (int)nElements; if (nElements == 0) { // soapenc:arrayType="type[0]" // treat as null array m_bNullCheck = true; // push an emtpy state return PushState(); } nElementsPush = nElements; } void *p = NULL; hr = AllocateArray(pEntry, &p, nElements); if (hr != S_OK) { return hr; } SetOffsetValue(state.pvElement, p, pEntry->nOffset); } else { // for fixed-size arrays, we know the number of elements ATLASSERT( pEntry->dwFlags & SOAPFLAG_FIXEDARR ); if (hr == S_OK) { if (nElements != AtlSoapGetArrayDims(pEntry->pDims)) { return E_FAIL; } } else { hr = S_OK; nElements = AtlSoapGetArrayDims(pEntry->pDims); } nElementsPush = nElements; } dwFlags |= pEntry->dwFlags; // push element with array flag if (S_OK != PushState(((unsigned char *)state.pvElement)+pEntry->nOffset, state.pMap, pEntry, dwFlags & ~SOAPFLAG_READYSTATE, nAllocSize, nElementsPush)) { return E_OUTOFMEMORY; } m_bChildCheck = true; return S_OK; } void * UpdateArray(ParseState& state, const _soapmapentry *pEntry) { ATLENSURE(pEntry); size_t nSize; void *pVal = NULL; if (pEntry->nVal != SOAPTYPE_UNK) { nSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal); } else { ATLENSURE( pEntry->pChain != NULL ); nSize = pEntry->pChain->nElementSize; } if (state.dwFlags & SOAPFLAG_FIXEDARR) { unsigned char *ppArr = (unsigned char *)state.pvElement; pVal = ppArr+(state.nElement*nSize); } else { ATLASSERT( state.dwFlags & SOAPFLAG_DYNARR ); unsigned char **ppArr = (unsigned char **)state.pvElement; pVal = (*ppArr)+(state.nElement*nSize); if (state.dwFlags & SOAPFLAG_UNKSIZE) { ATLASSERT( IsRpcEncoded() == false ); // need to use the previous state's pvElement to update the size_is value ATLASSUME( m_nState > 0 ); int *pnSizeIs = (int *)(((unsigned char *)m_stateStack[m_nState-1].pvElement)+ (state.pMap->pEntries[pEntry->nSizeIs].nOffset)); // update size_is parameter *pnSizeIs = (int)(state.nElement+1); state.nExpectedElements++; } } state.nElement++; return pVal; } HRESULT ProcessString(const _soapmapentry *pEntry, void *pVal) { ATLENSURE_RETURN( pEntry != NULL ); // set to the string builder class ATLASSERT( GetReader() != NULL ); m_stringBuilder.SetReader(GetReader()); m_stringBuilder.SetParent(this); m_stringBuilder.Clear(); GetReader()->putContentHandler( &m_stringBuilder ); if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags)) { return E_OUTOFMEMORY; } return S_OK; } HRESULT CheckHref( const _soapmapentry *pEntry, void *pVal, ISAXAttributes *pAttributes, DWORD dwIncludeFlags = 0, DWORD dwExcludeFlags = 0) { ATLASSERT( pEntry != NULL ); ATLASSERT( pVal != NULL ); ATLASSERT( pAttributes != NULL ); const wchar_t *wsz = NULL; int cch = 0; HRESULT hr = GetAttribute(pAttributes, L"href", sizeof("href")-1, &wsz, &cch); if ((hr == S_OK) && (wsz != NULL)) { // only allow hrefs on structs and arrays if (((pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))==0) && (pEntry->pChain == NULL || pEntry->pChain->mapType != SOAPMAP_STRUCT)) { ATLTRACE( _T("ATL Server only allows href's on arrays and structs.\r\n") ); return E_FAIL; } ATLASSERT( IsRpcEncoded() == true ); _ATLTRY { if (*wsz == L'#') { wsz++; cch--; } REFSTRING strRef(wsz, cch); if (m_refMap.Lookup(strRef) != NULL) { // ATL Server does not support multi-reference objects ATLASSERT( FALSE ); return E_FAIL; } ParseState& currState = GetState(); if ((currState.pEntry != NULL) && (currState.pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))) { // it is an array item ATLASSERT( currState.nElement != 0 ); // exclude array flags for href'd array elements dwExcludeFlags |= SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR; } ParseState state; state.pvElement = pVal; state.dwFlags = (pEntry->dwFlags | dwIncludeFlags) & ~dwExcludeFlags; state.nExpectedElements = 0; state.nElement = 0; state.pMap = GetState().pMap; state.pEntry = pEntry; if (!m_refMap.SetAt(strRef, state)) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } // make sure there are no child elements m_bNullCheck = true; // push an emtpy state return PushState(); } _ATLCATCHALL() { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } } return S_FALSE; } HRESULT ProcessUDT( const _soapmapentry *pEntry, void *pVal) { ATLENSURE_RETURN( pEntry != NULL ); ATLENSURE_RETURN( pVal != NULL ); ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_ERR ); ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_USERBASE ); // if it is a complex type, get the chain entry // and push the new state on the stack DWORD dwFlags = pEntry->dwFlags; if (pEntry->pChain->mapType != SOAPMAP_ENUM) { // struct dwFlags &= ~(SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR); m_bChildCheck = pEntry->pChain->nElements != 0; } else { // enum dwFlags |= SOAPFLAG_READYSTATE; // enums must not have child elements m_bNullCheck = true; // enums must be specified m_bCharacters = true; } if (S_OK != PushState(pVal, pEntry->pChain, pEntry, dwFlags, 0, pEntry->pChain->nElements)) { return E_OUTOFMEMORY; } return S_OK; } HRESULT ChainEntry( const ParseState& state, const wchar_t *wszNamespaceUri, int cchNamespaceUri, const wchar_t *wszLocalName, int cchLocalName, ISAXAttributes *pAttributes) { ATLENSURE_RETURN( state.pMap != NULL ); // PAD is only supported on the client const _soapmap *pMap = state.pMap; if ((pMap->dwCallFlags & SOAPFLAG_CHAIN)==0) { return S_FALSE; } ATLENSURE_RETURN( pMap->dwCallFlags & SOAPFLAG_PAD ); ATLASSUME( m_bClient == true ); ATLENSURE_RETURN( pMap->nElements == 1 ); const _soapmapentry *pEntries = pMap->pEntries; ATLENSURE_RETURN( pEntries != NULL ); int nIndex; if (pEntries[0].dwFlags & SOAPFLAG_OUT) { nIndex = 0; } else { nIndex = 1; } const _soapmapentry *pEntry = &pEntries[nIndex]; ATLENSURE_RETURN( pEntry->nHash != 0 ); ATLENSURE_RETURN( pEntry->pChain != NULL ); if (S_OK != PushState(state.pvElement, pEntry->pChain, pEntry, pEntry->dwFlags, 0, pEntry->pChain->nElements)) { return E_OUTOFMEMORY; } return ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, pAttributes); } HRESULT IsNullEntry(const _soapmapentry *pEntry, ISAXAttributes *pAttributes) { ATLASSERT( pEntry != NULL ); ATLASSERT( pAttributes != NULL ); HRESULT hr = E_FAIL; bool bNull = false; const wchar_t *wszNull = NULL; int cchNull = 0; hr = GetAttribute(pAttributes, L"nil", sizeof("nil")-1, &wszNull, &cchNull, XSI_NAMESPACEW, sizeof(XSI_NAMESPACEA)-1); if ((hr == S_OK) && (wszNull != NULL)) { hr = AtlGetSAXValue(&bNull, wszNull, cchNull); if (hr == S_OK) { if (bNull != false) { if (pEntry->dwFlags & SOAPFLAG_NULLABLE) { m_bNullCheck = true; // push an emtpy state return PushState(); } // non-nullable element return E_FAIL; } } } return S_FALSE; } HRESULT ProcessParams( const wchar_t *wszNamespaceUri, int cchNamespaceUri, const wchar_t *wszLocalName, int cchLocalName, ISAXAttributes *pAttributes) { (wszNamespaceUri); (cchNamespaceUri); if (m_stateStack.IsEmpty()) { if (m_dwState == SOAP_HEADERS) { return CheckMustUnderstandHeader(pAttributes); } return E_FAIL; } ParseState &state = GetState(); ATLASSERT( state.pvElement != NULL ); HRESULT hr = E_FAIL; const _soapmapentry *pEntry = NULL; // if array element if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) { hr = ValidateArrayEntry(state, wszLocalName, cchLocalName); if (SUCCEEDED(hr)) { pEntry = state.pEntry; } else { return hr; } } else // not an array element { // special-case for PAD with type= hr = ChainEntry(state, wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, pAttributes); if (hr == S_FALSE) { hr = GetElementEntry(state, wszNamespaceUri, wszLocalName, cchLocalName, pAttributes, &pEntry); if (hr != S_OK) { if (hr == S_FALSE) { hr = S_OK; } else if (m_dwState == SOAP_HEADERS) { hr = CheckMustUnderstandHeader(pAttributes); } return hr; } ATLASSERT( pEntry != NULL ); } else { return hr; } } hr = IsNullEntry(pEntry, pAttributes); if (hr != S_FALSE) { return hr; } hr = S_OK; ATLENSURE_RETURN(pEntry); // if is array if (((pEntry->pDims != NULL) || (pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))) && ((state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) == 0)) { // get SOAP section-5 info (if it is there) return GetSection5Info(state, pEntry, pAttributes); } else { // if it is a simple type, push a new (ready) state on the stack void *pVal; if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) { pVal = UpdateArray(state, pEntry); ATLASSERT( pVal != NULL ); } else { pVal = (((unsigned char *)state.pvElement)+pEntry->nOffset); } if (IsRpcEncoded() != false) { // check for href // we ONLY do this for rpc/encoded (required for interop) // NOTE: ATL Server does not support object graphs, so // only single-reference elements are allowed hr = CheckHref(pEntry, pVal, pAttributes); if (hr != S_FALSE) { return hr; } hr = S_OK; } if (pEntry->nVal != SOAPTYPE_UNK) { // simple types should not have child elements m_bNullCheck = true; // if it is a string if ((pEntry->nVal == SOAPTYPE_STRING) || (pEntry->nVal == SOAPTYPE_BASE64BINARY)) { hr = ProcessString(pEntry, pVal); } else { // expect characters for all non-string simple types m_bCharacters = true; // basic simple type if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags)) { hr = E_OUTOFMEMORY; } } } else { hr = ProcessUDT(pEntry, pVal); if (pEntry->dwFlags & (SOAPFLAG_DYNARRWRAPPER)) { // We're moving to the **first** entry in the dynamic array wrapper. // We know it is the first entry because the dynamic array wrapper is created // by sproxy and it guarantees this layouts. ++m_nDepth; ProcessParams (wszNamespaceUri, cchNamespaceUri, pEntry->pChain->pEntries[0].wszField, pEntry->pChain->pEntries[0].cchField, pAttributes); } } } return hr; } size_t GetSizeIsValue(void *pvParam, const _soapmap *pMap, const _soapmapentry *pEntry) { ATLENSURE( pvParam != NULL ); ATLENSURE( pMap != NULL ); ATLENSURE( pEntry != NULL ); int nSizeIs = pEntry->nSizeIs; size_t nOffset = pMap->pEntries[nSizeIs].nOffset; void *pVal = ((unsigned char *)pvParam)+nOffset; __int64 nVal = 0; switch(pMap->pEntries[nSizeIs].nVal) { case SOAPTYPE_INTEGER: case SOAPTYPE_NONPOSITIVEINTEGER: case SOAPTYPE_NEGATIVEINTEGER: case SOAPTYPE_LONG: nVal = *((__int64 *)pVal); break; case SOAPTYPE_INT: nVal = *((int *)pVal); break; case SOAPTYPE_SHORT: nVal = *((short *)pVal); break; case SOAPTYPE_BYTE: nVal = *((char *)pVal); break; case SOAPTYPE_POSITIVEINTEGER: case SOAPTYPE_NONNEGATIVEINTEGER: case SOAPTYPE_UNSIGNEDLONG: unsigned __int64 n; n = *((unsigned __int64 *)pVal); if (n > _I64_MAX) { // come on ... nVal = 0; } else { nVal = (__int64)n; } break; case SOAPTYPE_UNSIGNEDINT: nVal = *((unsigned int *)pVal); break; case SOAPTYPE_UNSIGNEDSHORT: nVal = *((unsigned short *)pVal); break; case SOAPTYPE_UNSIGNEDBYTE: nVal = *((unsigned char *)pVal); break; default: nVal = 0; } if (nVal < 0) { nVal = 0; } return (size_t) nVal; } HRESULT GenerateArrayInfo(const _soapmapentry *pEntry, const int *pDims, IWriteStream *pStream) { ATLENSURE_RETURN( pEntry != NULL ); ATLENSURE_RETURN( pStream != NULL ); HRESULT hr = S_OK; if (pEntry->nVal != SOAPTYPE_UNK) { // xsd type hr = pStream->WriteStream(" soapenc:arrayType=\"xsd:", sizeof(" soapenc:arrayType=\"xsd:")-1, NULL); } else { ATLENSURE_RETURN( pEntry->pChain != NULL ); hr = pStream->WriteStream(" xmlns:q1=\"", sizeof(" xmlns:q1=\"")-1, NULL); if (SUCCEEDED(hr)) { if (pEntry->pChain->szNamespace != NULL) { hr = pStream->WriteStream(pEntry->pChain->szNamespace, pEntry->pChain->cchNamespace, NULL); } else { hr = pStream->WriteStream(GetNamespaceUriA(), -1, NULL); } if (SUCCEEDED(hr)) { hr = pStream->WriteStream("\"", 1, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(" soapenc:arrayType=\"q1:", sizeof(" soapenc:arrayType=\"q1:")-1, NULL); } } } } if (FAILED(hr)) { return hr; } if (pEntry->nVal != SOAPTYPE_UNK) { hr = pStream->WriteStream(s_xsdNames[pEntry->nVal].szName , s_xsdNames[pEntry->nVal].cchName, NULL); } else { ATLASSERT( pEntry->pChain != NULL ); hr = pStream->WriteStream(pEntry->pChain->szName, pEntry->pChain->cchName, NULL); } if (FAILED(hr)) { return hr; } hr = pStream->WriteStream("[", 1, NULL); if (FAILED(hr)) { return hr; } CWriteStreamHelper s( pStream ); for (int i=1; i<=pDims[0]; i++) { if (!s.Write(pDims[i]) || ((i < pDims[0]) && (S_OK != pStream->WriteStream(", ", 2, NULL)))) { return E_FAIL; } } hr = pStream->WriteStream("]\"", 2, NULL); if (FAILED(hr)) { return hr; } return S_OK; } HRESULT GenerateXSDWrapper(bool bStart, int nVal, bool bNull, IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "WriteStream(s_xsdNames[nVal].szName, s_xsdNames[nVal].cchName, NULL); if ((bNull != false) && (SUCCEEDED(hr))) { hr = pStream->WriteStream(" xsi:nil=\"1\"", sizeof(" xsi:nil=\"1\"")-1, NULL); } if (SUCCEEDED(hr)) { hr = pStream->WriteStream(">", 1, NULL); } } return hr; } HRESULT GenerateGenericWrapper(bool bStart, const _soapmap *pMap, IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "WriteStream(pMap->szName, pMap->cchName, NULL); if (SUCCEEDED(hr)) { hr = pStream->WriteStream(">", 1, NULL); } } return hr; } HRESULT GetArrayInformation( IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry, void *pvParam, size_t &nCnt, size_t &nElementSize) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); ATLENSURE_RETURN( pEntry != NULL ); ATLENSURE_RETURN( pvParam != NULL ); const int *pDims = NULL; int arrDims[2] = { 0 }; if (pEntry->dwFlags & SOAPFLAG_FIXEDARR) { pDims = pEntry->pDims; } else { ATLASSERT( pEntry->dwFlags & SOAPFLAG_DYNARR ); nCnt = GetSizeIsValue(pvParam, pMap, pEntry); if (nCnt == 0) { // array size should only be zero if array is NULL // did you forget to set the array size? ATLASSERT( FALSE ); return E_FAIL; } arrDims[0] = 1; arrDims[1] = (int) nCnt; pDims = arrDims; } // output array information HRESULT hr = GenerateArrayInfo(pEntry, pDims, pStream); if (FAILED(hr)) { return hr; } if (SUCCEEDED(hr)) { nCnt = AtlSoapGetArrayDims(pDims); // did you forget to set the size_is value? ATLASSERT( nCnt != 0 ); if (pEntry->nVal != SOAPTYPE_UNK) { nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal); } else { ATLENSURE_RETURN( pEntry->pChain != NULL ); nElementSize = pEntry->pChain->nElementSize; } } return hr; } HRESULT GenerateEnum(IWriteStream *pStream, void *pVal, const _soapmapentry *pEntry, bool bArray) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pVal != NULL ); ATLENSURE_RETURN( pEntry != NULL ); int nVal = *((int *)pVal); const _soapmapentry *pEnumEntries = pEntry->pChain->pEntries; ATLENSURE_RETURN( pEnumEntries != NULL ); size_t j; HRESULT hr = E_FAIL; for (j=0; pEnumEntries[j].nHash != 0; j++) { if (nVal == pEnumEntries[j].nVal) { hr = pStream->WriteStream(pEnumEntries[j].szField, pEnumEntries[j].cchField, NULL); if ((bArray != false) && (SUCCEEDED(hr))) { hr = GenerateGenericWrapper(false, pEntry->pChain, pStream); } break; } } return hr; } HRESULT GenerateHeaders(CResponseGenerator *pGenerator, const _soapmap *pMap, IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); ATLENSURE_RETURN( pMap != NULL ); ATLENSURE_RETURN( pGenerator != NULL ); DWORD dwIncludeFlags = SOAPFLAG_OUT; if (m_bClient != false) { dwIncludeFlags = SOAPFLAG_IN; } size_t nCnt = 0; for (size_t i=0; pMap->pEntries[i].nHash != 0; i++) { if (pMap->pEntries[i].dwFlags & dwIncludeFlags) { nCnt++; } } // no headers to be sent if (nCnt == 0) { return S_OK; } HRESULT hr = pGenerator->StartHeaders(pStream, pMap); if (SUCCEEDED(hr)) { hr = GenerateResponseHelper(pGenerator, pMap, GetHeaderValue(), pStream); if (SUCCEEDED(hr)) { hr = pGenerator->EndHeaders(pStream); } } return hr; } bool IsNullElement(const _soapmapentry *pEntry, void *pVal, DWORD dwExcludeFlags=0) { ATLENSURE( pEntry != NULL ); ATLENSURE( pVal != NULL ); bool bNull = false; DWORD dwFlags = pEntry->dwFlags & ~dwExcludeFlags; if (dwFlags & SOAPFLAG_DYNARR) { unsigned char **ppArr = (unsigned char **)pVal; if (*ppArr == NULL) { bNull = true; } } else if (pEntry->nVal == SOAPTYPE_STRING) { BSTR *pBSTR = (BSTR *)pVal; if (*pBSTR == NULL) { bNull = true; } } else if ((pEntry->nVal == SOAPTYPE_BASE64BINARY) || (pEntry->nVal == SOAPTYPE_HEXBINARY)) { if (((ATLSOAP_BLOB *)pVal)->data == NULL) { bNull = true; } } return bNull; } HRESULT GenerateNull(IWriteStream *pStream) { ATLENSURE_RETURN( pStream != NULL ); return pStream->WriteStream(" xsi:nil=\"1\"/>", sizeof(" xsi:nil=\"1\"/>")-1, NULL); } HRESULT GenerateResponseHelper(CResponseGenerator *pGenerator, const _soapmap *pMap, void *pvParam, IWriteStream *pStream, bool bArrayElement = false) { ATLENSURE_RETURN( pGenerator != NULL ); ATLENSURE_RETURN( pMap != NULL ); ATLENSURE_RETURN( pStream != NULL ); HRESULT hr = S_OK; if ((bArrayElement != false) && ((pMap->dwCallFlags & SOAPFLAG_PAD)==0)) { hr = pGenerator->StartMap(pStream, pMap, m_bClient); if (FAILED(hr)) { return hr; } } ATLENSURE_RETURN( pMap->pEntries != NULL ); const _soapmapentry *pEntries = pMap->pEntries; size_t i; DWORD dwIncludeFlags; DWORD dwExcludeFlags; if (m_bClient != false) { dwIncludeFlags = SOAPFLAG_IN; dwExcludeFlags = SOAPFLAG_OUT; } else { dwIncludeFlags = SOAPFLAG_OUT; dwExcludeFlags = SOAPFLAG_IN; } for (i=0; pEntries[i].nHash != 0; i++) { if (((pEntries[i].dwFlags & dwIncludeFlags) || ((pEntries[i].dwFlags & dwExcludeFlags)==0)) && ((pEntries[i].dwFlags & SOAPFLAG_NOMARSHAL)==0)) { hr = pGenerator->StartEntry(pStream, pMap, &pEntries[i]); if (FAILED(hr)) { return hr; } size_t nElementSize = 0; size_t nCnt = 1; ATLASSERT( pvParam != NULL ); void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset; if (IsNullElement(&pEntries[i], pvCurrent)) { hr = GenerateNull(pStream); if (SUCCEEDED(hr)) { continue; } return hr; } bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0; if (bArray != false) { hr = GetArrayInformation(pStream, pMap, &pEntries[i], pvParam, nCnt, nElementSize); } hr = pStream->WriteStream(">", 1, NULL); if (FAILED(hr)) { return hr; } for (size_t nElement=0; nElementmapType != SOAPMAP_ENUM) { // struct hr = GenerateResponseHelper(pGenerator, pEntries[i].pChain, pVal, pStream, bArray); } else { if (bArray != false) { hr = GenerateGenericWrapper(true, pEntries[i].pChain, pStream); if (FAILED(hr)) { return hr; } } hr = GenerateEnum(pStream, pVal, &pEntries[i], bArray); } } } // output element close if (SUCCEEDED(hr)) { hr = pGenerator->EndEntry(pStream, pMap, &pEntries[i]); } } if (FAILED(hr)) { return hr; } } if ((bArrayElement != false) && ((pMap->dwCallFlags & SOAPFLAG_PAD)==0)) { // output type name hr = pGenerator->EndMap(pStream, pMap, m_bClient); } return hr; } void CleanupHelper(const _soapmap *pMap, void *pvParam) { ATLENSURE( pMap != NULL ); ATLENSURE( pMap->pEntries != NULL ); if (pvParam == NULL) { return; } const _soapmapentry *pEntries = pMap->pEntries; size_t i; for (i=0; pEntries[i].nHash != 0; i++) { if ((m_bClient != false) && ((pEntries[i].dwFlags & SOAPFLAG_OUT)==0)) { // skip in-only headers on the client continue; } void *pvCheck = ((unsigned char *)pvParam)+pEntries[i].nOffset; if (IsNullElement(&pEntries[i], pvCheck)) { continue; } size_t nElementSize = 0; size_t nCnt = 1; const int *pDims = NULL; int arrDims[2] = { 0 }; bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0; if (bArray != false) { if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR) { pDims = pEntries[i].pDims; } else { ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR ); nCnt = GetSizeIsValue(pvParam, pMap, &pEntries[i]); arrDims[0] = 1; arrDims[1] = (int) nCnt; pDims = arrDims; } nCnt = AtlSoapGetArrayDims(pDims); if (pEntries[i].nVal != SOAPTYPE_UNK) { nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntries[i].nVal); } else { ATLENSURE( pEntries[i].pChain != NULL ); nElementSize = pEntries[i].pChain->nElementSize; } } void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset; for (size_t nElement=0; nElementmapType != SOAPMAP_ENUM) { CleanupHelper(pEntries[i].pChain, pVal); } } } if (pEntries[i].dwFlags & SOAPFLAG_DYNARR) { // free it unsigned char **ppArr = (unsigned char **)pvCurrent; ATLENSURE( ppArr != NULL ); if (*ppArr != NULL) { m_pMemMgr->Free(*ppArr); *ppArr = NULL; } } } } const _soapmap * GetSoapMapFromName( const wchar_t * wszName, int cchName = -1, const wchar_t * wszNamespaceUri = NULL, int cchNamespaceUri = -1, int *pnVal = NULL, bool bHeader = false) { (cchNamespaceUri); const _soapmap ** pEntry = NULL; if (bHeader == false) { pEntry = GetFunctionMap(); } else { pEntry = GetHeaderMap(); } if (pEntry == NULL) { return NULL; } if (cchName < 0) { cchName = (int)wcslen(wszName); } if ((cchNamespaceUri < 0) && (wszNamespaceUri != NULL)) { cchNamespaceUri = (int)wcslen(wszNamespaceUri); } ULONG nFunctionHash = AtlSoapHashStr(wszName, cchName); ULONG nNamespaceHash = wszNamespaceUri ? AtlSoapHashStr(wszNamespaceUri, cchNamespaceUri) : 0; int i; for (i=0; pEntry[i] != NULL; i++) { if ((IsEqualStringHash(wszName, cchName, nFunctionHash, pEntry[i]->wszName, pEntry[i]->cchWName, pEntry[i]->nHash) != FALSE) && (!wszNamespaceUri || IsEqualStringHash(wszNamespaceUri, cchNamespaceUri, nNamespaceHash, pEntry[i]->wszNamespace, pEntry[i]->cchNamespace, pEntry[i]->nNamespaceHash) != FALSE)) { break; } } if (pnVal != NULL) { *pnVal = i; } return pEntry[i]; } HRESULT CheckEndElement(const ParseState& state) { // check for all elements if (state.nElement == state.nExpectedElements) { return S_OK; } // error check for fixed arrays if (state.dwFlags & SOAPFLAG_FIXEDARR) { return E_FAIL; } // check for dynamic arrays if (state.dwFlags & SOAPFLAG_DYNARR) { // check for dynamic arrays with known size // (from soap:arrayType attribute) if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0) { return E_FAIL; } } DWORD dwIncludeFlags; DWORD dwExcludeFlags; if (m_bClient != false) { dwIncludeFlags = SOAPFLAG_OUT; dwExcludeFlags = SOAPFLAG_IN; } else { dwIncludeFlags = SOAPFLAG_IN; dwExcludeFlags = SOAPFLAG_OUT; } if (state.pMap != NULL) { // ensure all omitted elements were nullable elements or nomarshal elements const _soapmapentry *pEntries = state.pMap->pEntries; for (size_t i=0; pEntries[i].nHash != 0; i++) { if ((pEntries[i].dwFlags & dwIncludeFlags) || ((pEntries[i].dwFlags & dwExcludeFlags)==0)) { if (state.vec.GetBit(i) == false) { if (((pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0) && (pEntries[i].nVal != SOAPTYPE_UNK)) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckEndElement -- invalid number of elements for parameter/field\r\n") ); return E_FAIL; } } } } } return S_OK; } HRESULT CheckSoapHeaders(const ParseState &state) { DWORD dwIncludeFlags; DWORD dwExcludeFlags; if (m_bClient != false) { dwIncludeFlags = SOAPFLAG_OUT; dwExcludeFlags = SOAPFLAG_IN; } else { dwIncludeFlags = SOAPFLAG_IN; dwExcludeFlags = SOAPFLAG_OUT; } if (state.pMap != NULL) { ATLASSERT( state.pMap->mapType == SOAPMAP_HEADER ); // ensure all omitted elements were nullable elements, nomarshal elements, or non-required elements const _soapmapentry *pEntries = state.pMap->pEntries; for (size_t i=0; pEntries[i].nHash != 0; i++) { if ((pEntries[i].dwFlags & dwIncludeFlags) || ((pEntries[i].dwFlags & dwExcludeFlags)==0)) { if (state.vec.GetBit(i) == false) { bool bNoOmit = (pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0; if ((bNoOmit != false) || ((bNoOmit != false) && (pEntries[i].dwFlags & SOAPFLAG_MUSTUNDERSTAND))) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckSoapHeaders -- missing header\r\n") ); return E_FAIL; } } } } } return S_OK; } HRESULT CheckEndHeaders( const wchar_t * wszNamespaceUri, int cchNamespaceUri, const wchar_t * wszLocalName, int cchLocalName) { if (IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW, sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW, cchLocalName, wszLocalName, cchNamespaceUri, wszNamespaceUri)) { m_dwState = SOAP_HEADERS_DONE; return S_OK; } // some sort of error ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid SOAP message format while processing headers.\r\n" ) ); return E_FAIL; } protected: ISAXXMLReader * SetReader(ISAXXMLReader *pReader) { ISAXXMLReader *pPrevRdr = m_spReader; m_spReader = pReader; return pPrevRdr; } ISAXXMLReader * GetReader() { return m_spReader; } HRESULT SetSoapMapFromName( const wchar_t * wszName, int cchName = -1, const wchar_t * wszNamespaceUri = NULL, int cchNamespaceUri = -1, bool bHeader = false) { ATLENSURE_RETURN( wszName != NULL ); int nVal; const _soapmap *pMap = NULL; if (m_stateStack.GetCount() != 0) { ATLASSUME( m_stateStack[0].pMap != NULL ); nVal = (int) m_stateStack[0].nAllocSize; ATLASSERT( GetFunctionMap() != NULL ); pMap = GetFunctionMap()[nVal]; } else { pMap = GetSoapMapFromName(wszName, cchName, wszNamespaceUri, cchNamespaceUri, &nVal, bHeader); } if (pMap == NULL) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- _soapmap not found for: %.*ws, with namespace %.*ws\r\n"), (int)wcslen(wszName), wszName, wszNamespaceUri ? (int)wcslen(wszNamespaceUri) : 0, wszNamespaceUri ? wszNamespaceUri : L""); return E_FAIL; } HRESULT hr = E_OUTOFMEMORY; // allocate the parameter struct void *pvParam = NULL; if (bHeader != false) { pvParam = GetHeaderValue(); } else { if (m_bClient == false) { m_pvParam = m_pMemMgr->Allocate(pMap->nElementSize); } pvParam = m_pvParam; } if (pvParam != NULL) { if (bHeader == false) { memset(pvParam, 0x00, pMap->nElementSize); } // push initial state if (m_stateStack.GetCount() != 0) { m_stateStack.RemoveAll(); } hr = PushState(pvParam, pMap, NULL, 0, nVal, pMap->nElements); if (FAILED(hr)) { if ((m_bClient == false) && (bHeader == false)) { m_pMemMgr->Free(pvParam); } } } #ifdef _DEBUG if (hr == E_OUTOFMEMORY) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- out of memory.\r\n" ) ); } #endif // _DEBUG return hr; } // implementation virtual const _soapmap ** GetFunctionMap() = 0; virtual const _soapmap ** GetHeaderMap() = 0; virtual const wchar_t * GetNamespaceUri() = 0; virtual const char * GetServiceName() = 0; virtual const char * GetNamespaceUriA() = 0; virtual HRESULT CallFunction( void *pvParam, const wchar_t *wszLocalName, int cchLocalName, size_t nItem) = 0; virtual void * GetHeaderValue() = 0; public: CSoapRootHandler(ISAXXMLReader *pReader = NULL) : m_pMemMgr(&m_crtHeap), m_spReader(pReader), m_bClient(false), m_nState(0), m_pvParam(NULL), m_nDepth(0) { InitHandlerState(); } virtual ~CSoapRootHandler() { m_skipHandler.DetachParent(); } IAtlMemMgr * SetMemMgr(IAtlMemMgr *pMemMgr) { IAtlMemMgr *pPrevMgr = m_pMemMgr; m_pMemMgr = pMemMgr; return pPrevMgr; } IAtlMemMgr * GetMemMgr() { return m_pMemMgr; } // override this function to do SOAP Fault handling virtual HRESULT SoapFault( SOAP_ERROR_CODE /*errCode*/, const wchar_t * /*wszDetail*/, int /*cchDetail*/) { if (m_bClient != false) { return S_OK; } // SOAP servers must implement this function ATLASSERT( FALSE ); return E_FAIL; } // // implementation // void InitHandlerState() { m_bNullCheck = false; m_bCharacters = false; m_bChildCheck = false; m_dwState = SOAP_START; } HRESULT __stdcall startDocument() { InitHandlerState(); return S_OK; } HRESULT __stdcall startElement( const wchar_t *wszNamespaceUri, int cchNamespaceUri, const wchar_t *wszLocalName, int cchLocalName, const wchar_t * wszQName, int cchQName, ISAXAttributes *pAttributes) { if (m_bNullCheck || m_bCharacters) { // make sure elements that aren't supposed to have child elements // do not have child elements, and where we were expecting // characters, we got them return E_FAIL; } m_bChildCheck = false; ++m_nDepth; HRESULT hr = S_OK; switch (m_dwState) { case SOAP_PARAMS: case SOAP_HEADERS: { hr = ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, pAttributes); break; } case SOAP_START: case SOAP_ENVELOPE: case SOAP_HEADERS_DONE: { ULONG nNamespaceHash = AtlSoapHashStr(wszNamespaceUri, cchNamespaceUri); if (nNamespaceHash != SOAP_ENV) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- incorrect SOAP-ENV namespace.\r\n" ) ); return E_FAIL; } ULONG nElementHash = AtlSoapHashStr(wszLocalName, cchLocalName); if (nElementHash == ENVELOPE && IsEqualElement( sizeof(SOAP_ENVELOPEA)-1, SOAP_ENVELOPEW, sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW, cchLocalName, wszLocalName, cchNamespaceUri, wszNamespaceUri)) { // Envelope must be first element in package if (m_dwState != SOAP_START) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Envelope\" in unexpected location.\r\n" ) ); hr = E_FAIL; } m_dwState = SOAP_ENVELOPE; } else if (nElementHash == HEADER && IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW, sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW, cchLocalName, wszLocalName, cchNamespaceUri, wszNamespaceUri)) { if (m_dwState != SOAP_ENVELOPE) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Headers\" in unexpected location.\r\n" ) ); hr = E_FAIL; } m_dwState = SOAP_HEADERS; } else if (nElementHash == BODY && IsEqualElement(sizeof(SOAP_BODYA)-1, SOAP_BODYW, sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW, cchLocalName, wszLocalName, cchNamespaceUri, wszNamespaceUri)) { if (m_dwState == SOAP_START) { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Body\" in unexpected location.\r\n" ) ); hr = E_FAIL; } m_dwState = SOAP_BODY; } break; } case SOAP_BODY: { hr = DispatchSoapCall(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName); m_dwState = SOAP_PARAMS; if (SUCCEEDED(hr)) { if (GetState().pMap->dwCallFlags & SOAPFLAG_PAD) { hr = startElement(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, wszQName, cchQName, pAttributes); } } break; } #ifdef _DEBUG default: { // should never get here -- internal error ATLASSERT( FALSE ); } #endif // _DEBUG } return hr; } HRESULT __stdcall characters( const wchar_t *wszChars, int cchChars) { m_bCharacters = false; // if it is a ready state, get the value if (m_stateStack.IsEmpty() == false) { ParseState& state = GetState(); if ((state.dwFlags & SOAPFLAG_READYSTATE) && ((state.dwFlags & SOAPFLAG_SIZEIS)==0)) // don't marshal struct size_is elements -- should be filled in by array marshaling code { if ((state.pMap == NULL) || (state.pMap->mapType != SOAPMAP_ENUM)) { return AtlSoapGetElementValue(wszChars, cchChars, state.pvElement, (SOAPTYPES)state.pEntry->nVal, GetMemMgr()); } else { // enum ATLASSERT( state.pMap != NULL ); ATLASSERT( state.pMap->pEntries != NULL ); ULONG nHash = AtlSoapHashStr(wszChars, cchChars); const _soapmapentry *pEntries = state.pMap->pEntries; size_t i; for (i=0; pEntries[i].nHash != 0; i++) { if ((nHash == pEntries[i].nHash) && (cchChars == pEntries[i].cchField) && (!wcsncmp(wszChars, pEntries[i].wszField, cchChars))) { break; } } if (pEntries[i].nHash != 0) { *((int *)state.pvElement) = pEntries[i].nVal; state.nElement++; return S_OK; } // no matching enum entry found ATLTRACE( _T("ATLSOAP: CSoapRootHandler::characters -- no matching enum entry found for: %.*ws.\r\n" ), cchChars, wszChars ); return E_FAIL; } } } // otherwise, ignore return S_OK; } HRESULT __stdcall endElement( const wchar_t * wszNamespaceUri, int cchNamespaceUri, const wchar_t * wszLocalName, int cchLocalName, const wchar_t * /*wszQName*/, int /*cchQName*/) { static bool bDynArrWrapper = false; if (m_bCharacters) { return E_FAIL; } m_bNullCheck = false; if (m_stateStack.IsEmpty() != false) { return S_OK; } if (!bDynArrWrapper && (m_nState > 1)) { ParseState prevState = m_stateStack.GetAt(m_nState - 1); ParseState curState = m_stateStack.GetAt(m_nState); if (prevState.dwFlags & SOAPFLAG_DYNARRWRAPPER) { bDynArrWrapper = true; endElement (wszNamespaceUri, cchNamespaceUri, curState.pEntry->wszField, curState.pEntry->cchField, NULL, 0); } } else { bDynArrWrapper = false; } --m_nDepth; const ParseState& state = GetState(); if ((m_dwState == SOAP_HEADERS) && (m_stateStack.GetCount() == 1)) { return CheckEndHeaders(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName); } if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) { if (state.dwFlags & SOAPFLAG_READYSTATE) { PopState(); } const ParseState& currstate = GetState(); ATLENSURE_RETURN( currstate.pEntry != NULL ); if (m_nDepth == (currstate.nDepth-1)) { if (S_OK != CheckEndElement(currstate)) { // invalid number of elements ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid number of array elements for array parameter %.*ws.\r\n"), currstate.pEntry->cchField, currstate.pEntry->wszField ); return E_FAIL; } PopState(); } } else { if (S_OK != CheckEndElement(state)) { return E_FAIL; } PopState(); } return S_OK; } HRESULT SetClientStruct(void *pvParam, int nMapIndex) { ATLENSURE_RETURN( pvParam != NULL ); ATLENSURE_RETURN( nMapIndex >= 0 ); // this is the params struct // store for later use m_pvParam = pvParam; const _soapmap ** pEntries = GetHeaderMap(); ATLENSURE_RETURN( pEntries != NULL ); // push header value return PushState(GetHeaderValue(), pEntries[nMapIndex], NULL, 0, nMapIndex, pEntries[nMapIndex]->nElements); } void ResetClientState(bool bFull = false) { m_stateStack.RemoveAll(); m_nState = 0; if (bFull != false) { m_dwState = SOAP_START; m_pvParam = NULL; } } HRESULT CreateReader() { return m_spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER); } HRESULT InitializeSOAP(IServiceProvider *pProvider) { HRESULT hr = S_OK; if (m_spReader.p == NULL) { hr = E_FAIL; if (pProvider != NULL) { IAtlMemMgr *pMemMgr = NULL; hr = pProvider->QueryService(__uuidof(IAtlMemMgr), __uuidof(IAtlMemMgr), (void **)&pMemMgr); if ((SUCCEEDED(hr)) && (pMemMgr != NULL)) { SetMemMgr(pMemMgr); } hr = pProvider->QueryService(__uuidof(ISAXXMLReader), __uuidof(ISAXXMLReader), (void **)&m_spReader); } if (FAILED(hr)) { hr = CreateReader(); } } if (SUCCEEDED(hr)) { hr = m_spReader->putContentHandler(this); } #ifdef _DEBUG else { ATLTRACE( _T("ATLSOAP: CSoapRootHandler::InitializeSOAP -- failed to get SAXXMLReader.\r\n" ) ); } #endif // _DEBUG return hr; } void UninitializeSOAP() { if (m_spReader.p != NULL) { m_spReader->putContentHandler(NULL); m_spReader.Release(); } } virtual HRESULT DispatchSoapCall(const wchar_t *wszNamespaceUri, int cchNamespaceUri, const wchar_t *wszLocalName, int cchLocalName) { HRESULT hr = S_OK; if (m_stateStack.IsEmpty() == false) { ATLASSUME( m_stateStack[0].pMap != NULL ); // check to see if all required and non-nullable SOAP headers were sent if (m_stateStack[0].pMap->mapType == SOAPMAP_HEADER) { hr = CheckSoapHeaders(m_stateStack[0]); } if (SUCCEEDED(hr)) { hr = SetSoapMapFromName(wszLocalName, cchLocalName, wszNamespaceUri, cchNamespaceUri); } } else { // get the appropriate function map hr = SetSoapMapFromName(wszLocalName, cchLocalName, wszNamespaceUri, cchNamespaceUri); if (SUCCEEDED(hr)) { // set the SOAP Header map for the function ATLASSUME( m_stateStack.IsEmpty() == false ); const _soapmap **ppHeaderMap = GetHeaderMap(); ATLENSURE_RETURN( ppHeaderMap != NULL ); // create a temporary parse state for checking headers ParseState state; state.pMap = ppHeaderMap[m_stateStack[0].nAllocSize]; ATLENSURE_RETURN( state.pMap != NULL ); // check to see if all required and non-nullable SOAP headers were sent hr = CheckSoapHeaders(state); } } return hr; } virtual HRESULT BeginParse(IStream *pStream) { ATLASSERT( pStream != NULL ); CComVariant varStream; varStream = static_cast(pStream); HRESULT hr = m_spReader->parse(varStream); if (SUCCEEDED(hr)) { if (m_refMap.GetCount() != 0) { hr = E_FAIL; } } return hr; } HRESULT CallFunctionInternal() { HRESULT hr = E_FAIL; const ParseState& state = m_stateStack[0]; hr = CallFunction( state.pvElement, state.pMap->wszName, state.pMap->cchWName, state.nAllocSize); return hr; } virtual HRESULT GenerateResponse(IWriteStream *pStream) { ATLASSUME( m_stateStack.IsEmpty() == false ); ATLASSUME( m_stateStack[0].pMap != NULL ); ATLASSUME( m_stateStack[0].pvElement != NULL ); const ParseState& state = m_stateStack[0]; const _soapmap *pHeaderMap = NULL; if (m_bClient == false) { const _soapmap **ppHeaderMap = GetHeaderMap(); if (ppHeaderMap != NULL) { pHeaderMap = ppHeaderMap[state.nAllocSize]; } } else { pHeaderMap = state.pMap; } const _soapmap *pFuncMap = NULL; if (m_bClient == false) { pFuncMap = state.pMap; } else { const _soapmap **ppFuncMap = GetFunctionMap(); ATLENSURE_RETURN( ppFuncMap != NULL ); pFuncMap = ppFuncMap[state.nAllocSize]; } ATLENSURE_RETURN( pFuncMap != NULL ); CRpcEncodedGenerator rpcGen; CPADGenerator padGen; CPIDGenerator pidGen; CResponseGenerator *pGenerator = NULL; if ((pFuncMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) == (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) { pGenerator = &rpcGen; } else if (pFuncMap->dwCallFlags & SOAPFLAG_PID) { ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) ); pGenerator = &pidGen; } else { ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) ); ATLASSERT( pFuncMap->dwCallFlags & SOAPFLAG_PAD ); pGenerator = &padGen; } HRESULT hr = pGenerator->StartEnvelope(pStream); if (SUCCEEDED(hr)) { // generate headers if necessary hr = GenerateHeaders(pGenerator, pHeaderMap, pStream); if (SUCCEEDED(hr)) { hr = pGenerator->StartBody(pStream); if (SUCCEEDED(hr)) { hr = GenerateResponseHelper(pGenerator, pFuncMap, m_pvParam, pStream, true); if (SUCCEEDED(hr)) { hr = pGenerator->EndBody(pStream); if (SUCCEEDED(hr)) { hr = pGenerator->EndEnvelope(pStream); } } } } } return hr; } virtual void Cleanup() { // cleanup headers CleanupHeaders(); if ((m_stateStack.IsEmpty() == false) && (m_pvParam != NULL)) { const _soapmap **ppFuncMap = GetFunctionMap(); ATLENSURE( ppFuncMap != NULL ); const _soapmap *pFuncMap = ppFuncMap[m_stateStack[0].nAllocSize]; ATLENSURE( pFuncMap != NULL ); CleanupHelper(pFuncMap, m_pvParam); if (m_bClient == false) { m_pMemMgr->Free(m_pvParam); m_stateStack.RemoveAll(); } } } virtual void CleanupHeaders() { if (m_stateStack.IsEmpty() == false) { const _soapmap **ppHeaderMap = GetHeaderMap(); ATLENSURE( ppHeaderMap != NULL ); const _soapmap *pHeaderMap = ppHeaderMap[m_stateStack[0].nAllocSize]; ATLENSURE( pHeaderMap != NULL ); CleanupHelper(pHeaderMap, GetHeaderValue()); } } void SetClient(bool bClient) { m_bClient = bClient; } }; // class CSoapRootHandler #define DECLARE_XSD_ENTRY( __name ) \ { L ## __name, __name, sizeof(__name)-1 }, __declspec(selectany) const CSoapRootHandler::XSDEntry CSoapRootHandler::s_xsdNames[] = { DECLARE_XSD_ENTRY("string") DECLARE_XSD_ENTRY("boolean") DECLARE_XSD_ENTRY("float") DECLARE_XSD_ENTRY("double") DECLARE_XSD_ENTRY("decimal") DECLARE_XSD_ENTRY("duration") DECLARE_XSD_ENTRY("hexBinary") DECLARE_XSD_ENTRY("base64Binary") DECLARE_XSD_ENTRY("anyURI") DECLARE_XSD_ENTRY("ID") DECLARE_XSD_ENTRY("IDREF") DECLARE_XSD_ENTRY("ENTITY") DECLARE_XSD_ENTRY("NOTATION") DECLARE_XSD_ENTRY("QName") DECLARE_XSD_ENTRY("normalizedString") DECLARE_XSD_ENTRY("token") DECLARE_XSD_ENTRY("language") DECLARE_XSD_ENTRY("IDREFS") DECLARE_XSD_ENTRY("ENTITIES") DECLARE_XSD_ENTRY("NMTOKEN") DECLARE_XSD_ENTRY("NMTOKENS") DECLARE_XSD_ENTRY("Name") DECLARE_XSD_ENTRY("NCName") DECLARE_XSD_ENTRY("integer") DECLARE_XSD_ENTRY("nonPositiveInteger") DECLARE_XSD_ENTRY("negativeInteger") DECLARE_XSD_ENTRY("long") DECLARE_XSD_ENTRY("int") DECLARE_XSD_ENTRY("short") DECLARE_XSD_ENTRY("byte") DECLARE_XSD_ENTRY("nonNegativeInteger") DECLARE_XSD_ENTRY("unsignedLong") DECLARE_XSD_ENTRY("unsignedInt") DECLARE_XSD_ENTRY("unsignedShort") DECLARE_XSD_ENTRY("unsignedByte") DECLARE_XSD_ENTRY("positiveInteger") DECLARE_XSD_ENTRY("dateTime") DECLARE_XSD_ENTRY("time") DECLARE_XSD_ENTRY("date") DECLARE_XSD_ENTRY("gMonth") DECLARE_XSD_ENTRY("gYearMonth") DECLARE_XSD_ENTRY("gYear") DECLARE_XSD_ENTRY("gMonthDay") DECLARE_XSD_ENTRY("gDay") }; __declspec(selectany) CCRTHeap CSoapRootHandler::m_crtHeap; template class CSoapHandler : public CSoapRootHandler, public CComObjectRootEx, public IRequestHandlerImpl { protected: HTTP_CODE m_hcErr; CHttpResponse *m_pHttpResponse; // heap for SOAP requests CWin32Heap m_heap; // default heap is COM heap (SOAP Servers can double as COM objects) CComHeap m_comHeap; public: BEGIN_COM_MAP(CSoapHandler) COM_INTERFACE_ENTRY(ISAXContentHandler) COM_INTERFACE_ENTRY(IRequestHandler) END_COM_MAP() CSoapHandler() :m_pHttpResponse(NULL), m_hcErr(HTTP_SUCCESS) { SetMemMgr(&m_comHeap); } void SetHttpError(HTTP_CODE hcErr) { m_hcErr = hcErr; } HRESULT SoapFault( SOAP_ERROR_CODE errCode, const wchar_t *wszDetail, int cchDetail) { ATLASSUME( m_pHttpResponse != NULL ); SetHttpError(AtlsHttpError(500, SUBERR_NO_PROCESS)); m_pHttpResponse->ClearHeaders(); m_pHttpResponse->ClearContent(); m_pHttpResponse->SetContentType("text/xml"); m_pHttpResponse->SetStatusCode(500); CSoapFault fault; if (wszDetail != NULL) { if (cchDetail < 0) { cchDetail = (int) wcslen(wszDetail); } _ATLTRY { fault.m_strDetail.SetString(wszDetail, cchDetail); } _ATLCATCHALL() { ATLTRACE( _T("CSoapHandler::SoapFault -- out of memory.\r\n" ) ); return E_OUTOFMEMORY; } } fault.m_soapErrCode = errCode; fault.GenerateFault(m_pHttpResponse); return S_OK; } HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider) { m_hcErr = IRequestHandlerImpl::InitializeHandler(pRequestInfo, pProvider); if (m_hcErr == HTTP_SUCCESS) { HRESULT hr = InitializeSOAP(m_spServiceProvider); if (SUCCEEDED(hr)) { // try to use the per-thread heap CIsapiWorker *pWorker = pRequestInfo->pExtension->GetThreadWorker(); if (pWorker != NULL) { m_heap.Attach(pWorker->m_hHeap, false); SetMemMgr(&m_heap); } return m_hcErr; } } // some initialization failure CHttpResponse HttpResponse(pRequestInfo->pServerContext); m_pHttpResponse = &HttpResponse; SoapFault(SOAP_E_SERVER, NULL, 0); m_pHttpResponse = NULL; return m_hcErr; } HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider * /*pProvider*/) { // SOAPACTION header is required per the SOAP 1.1 // mainly so firewalls can filter on it. char szBuf[ATL_URL_MAX_URL_LENGTH+1]; szBuf[0] = '\0'; DWORD dwLen = ATL_URL_MAX_URL_LENGTH; if ( m_spServerContext->GetServerVariable("HTTP_SOAPACTION", szBuf, &dwLen) != FALSE ) { if ( dwLen >= 2 ) { // drop the last " szBuf[dwLen-2] = '\0'; char *szMethod = strrchr(szBuf, '#'); if (szMethod != NULL) { _ATLTRY { // ignore return code here SetSoapMapFromName(CA2W( szMethod+1 ), -1, GetNamespaceUri(), -1, true); } _ATLCATCHALL() { return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); } } } } else { // SOAP requestion that use the HTTP transport // must have a SOAPACTION header. return HTTP_ERROR(500, ISE_SUBERR_SOAPNOSOAPACTION); } // set the header map CHttpResponse HttpResponse(pRequestInfo->pServerContext); m_pHttpResponse = &HttpResponse; CStreamOnServerContext s(pRequestInfo->pServerContext); #ifdef _DEBUG CSAXSoapErrorHandler err; GetReader()->putErrorHandler(&err); #endif // _DEBUG HRESULT hr = BeginParse(&s); #ifdef _DEBUG // release the error handler GetReader()->putErrorHandler(NULL); #endif // _DEBUG if (FAILED(hr)) { Cleanup(); if (m_hcErr == HTTP_SUCCESS) { SoapFault(SOAP_E_CLIENT, NULL, NULL); } return m_hcErr; } _ATLTRY { hr = CallFunctionInternal(); } _ATLCATCHALL() { // cleanup before propagating user exception Cleanup(); HttpResponse.Detach(); _ATLRETHROW; } if (FAILED(hr)) { Cleanup(); HttpResponse.ClearHeaders(); HttpResponse.ClearContent(); if (m_hcErr != HTTP_SUCCESS) { HttpResponse.SetStatusCode(HTTP_ERROR_CODE(m_hcErr)); return HTTP_SUCCESS_NO_PROCESS; } HttpResponse.SetStatusCode(500); GenerateAppError(&HttpResponse, hr); return AtlsHttpError(500, SUBERR_NO_PROCESS); } HttpResponse.SetContentType("text/xml"); hr = GenerateResponse(&HttpResponse); Cleanup(); if (FAILED(hr)) { SoapFault(SOAP_E_SERVER, NULL, 0); return m_hcErr; } return HTTP_SUCCESS; } virtual ATL_NOINLINE HRESULT GenerateAppError(IWriteStream *pStream, HRESULT hr) { if (pStream == NULL) { return E_INVALIDARG; } LPWSTR pwszMessage = NULL; DWORD dwLen = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, (LPWSTR) &pwszMessage, 0, NULL); if (dwLen == 0) { pwszMessage = L"Application Error"; } hr = SoapFault(SOAP_E_SERVER, pwszMessage, dwLen ? dwLen : -1); if (dwLen != 0) { ::LocalFree(pwszMessage); } return hr; } void UninitializeHandler() { UninitializeSOAP(); } }; // client error states enum SOAPCLIENT_ERROR { SOAPCLIENT_SUCCESS=0, // everything succeeded SOAPCLIENT_INITIALIZE_ERROR, // initialization failed -- most likely an MSXML installation problem SOAPCLIENT_OUTOFMEMORY, // out of memory SOAPCLIENT_GENERATE_ERROR, // failed in generating the response SOAPCLIENT_CONNECT_ERROR, // failed connecting to server SOAPCLIENT_SEND_ERROR, // failed in sending message SOAPCLIENT_SERVER_ERROR, // server error SOAPCLIENT_SOAPFAULT, // a SOAP Fault was returned by the server SOAPCLIENT_PARSEFAULT_ERROR, // failed in parsing SOAP fault SOAPCLIENT_READ_ERROR, // failed in reading response SOAPCLIENT_PARSE_ERROR // failed in parsing response }; template class CSoapSocketClientT { private: CUrl m_url; CWriteStreamOnCString m_writeStream; CReadStreamOnSocket m_readStream; DWORD m_dwTimeout; SOAPCLIENT_ERROR m_errorState; protected: virtual HRESULT GetClientReader(ISAXXMLReader **pReader) { if (pReader == NULL) { return E_POINTER; } *pReader = NULL; CComPtr spReader; HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER); if (SUCCEEDED(hr)) { *pReader = spReader.Detach(); } return hr; } public: // note : not shared across stock client implementations CAtlHttpClientT m_socket; CSoapFault m_fault; // constructor CSoapSocketClientT(LPCTSTR szUrl) : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { TCHAR szTmp[ATL_URL_MAX_URL_LENGTH]; if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE)) m_url.CrackUrl(szTmp); } CSoapSocketClientT(LPCTSTR szServer, LPCTSTR szUri, ATL_URL_PORT nPort=80) : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { ATLASSERT( szServer != NULL ); ATLASSERT( szUri != NULL ); m_url.SetUrlPath(szUri); m_url.SetHostName(szServer); m_url.SetPortNumber(nPort); } ~CSoapSocketClientT() { CleanupClient(); } SOAPCLIENT_ERROR GetClientError() { return m_errorState; } void SetClientError(SOAPCLIENT_ERROR errorState) { m_errorState = errorState; } IWriteStream * GetWriteStream() { return &m_writeStream; } HRESULT GetReadStream(IStream **ppStream) { if (ppStream == NULL) { return E_POINTER; } *ppStream = &m_readStream; return S_OK; } void CleanupClient() { m_writeStream.Cleanup(); m_fault.Clear(); SetClientError(SOAPCLIENT_SUCCESS); } HRESULT SendRequest(LPCTSTR szAction) { HRESULT hr = E_FAIL; _ATLTRY { // create extra headers to send with request CFixedStringT strExtraHeaders(szAction); strExtraHeaders.Append(_T("Accept: text/xml\r\n"), sizeof("Accept: text/xml\r\n")-1); CAtlNavigateData navData; navData.SetMethod(ATL_HTTP_METHOD_POST); navData.SetPort(m_url.GetPortNumber()); navData.SetExtraHeaders(strExtraHeaders); navData.SetPostData((LPBYTE)(LPCSTR) m_writeStream.m_str, m_writeStream.m_str.GetLength(), _T("text/xml; charset=utf-8")); ATLSOAP_TRACE( (LPBYTE)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength() ); if (m_dwTimeout != 0) { navData.SetSocketTimeout(m_dwTimeout); } if (m_socket.Navigate(&m_url, &navData) != false) { if (GetStatusCode() == 200) { hr = (m_readStream.Init(&m_socket) != FALSE ? S_OK : E_FAIL); if (hr != S_OK) { SetClientError(SOAPCLIENT_READ_ERROR); } } else if (GetStatusCode() == 202) { // for one-way methods hr = S_OK; } else { SetClientError(SOAPCLIENT_SERVER_ERROR); } } else if (GetStatusCode() == 500) { SetClientError(SOAPCLIENT_SOAPFAULT); // if returned 500, get the SOAP fault if (m_readStream.Init(&m_socket) != FALSE) { CComPtr spReader; if (SUCCEEDED(GetClientReader(&spReader))) { CComPtr spReadStream; if (SUCCEEDED(GetReadStream(&spReadStream))) { if (FAILED(m_fault.ParseFault(spReadStream, spReader))) { SetClientError(SOAPCLIENT_PARSEFAULT_ERROR); } } } } } else { SetClientError(SOAPCLIENT_SEND_ERROR); } } _ATLCATCHALL() { hr = E_FAIL; } return hr; } HRESULT SetUrl(LPCTSTR szUrl) { TCHAR szTmp[ATL_URL_MAX_URL_LENGTH]; if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE)) { return E_FAIL; } return (m_url.CrackUrl(szTmp) != FALSE) ? S_OK : E_FAIL; } HRESULT GetUrl(__out_ecount_part_z(*pdwLen, *pdwLen) LPTSTR szUrl, __inout LPDWORD pdwLen) { if ((szUrl == NULL) || (pdwLen == NULL)) { return E_INVALIDARG; } return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL; } HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80) { BOOL bRet = m_socket.SetProxy(szProxy, nProxyPort); return (bRet != FALSE) ? S_OK : E_FAIL; } void SetTimeout(DWORD dwTimeout) { m_dwTimeout = dwTimeout; } int GetStatusCode() { return m_socket.GetStatus(); } }; // CSoapSocketClientT #ifndef ATLSOAP_NOWININET class CReadStreamOnInet : public IStreamImpl { public: HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) { if (ppv == NULL) { return E_POINTER; } *ppv = NULL; if (InlineIsEqualGUID(riid, IID_IUnknown) || InlineIsEqualGUID(riid, IID_IStream) || InlineIsEqualGUID(riid, IID_ISequentialStream)) { *ppv = static_cast(this); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return 1; } ULONG __stdcall Release() { return 1; } private: HINTERNET m_hFile; public: CReadStreamOnInet() :m_hFile(NULL) { } void Init(HINTERNET hFile) { m_hFile = hFile; } HRESULT STDMETHODCALLTYPE Read(void *pDest, ULONG dwMaxLen, ULONG *pdwRead) { BOOL bRet = InternetReadFile(m_hFile, pDest, dwMaxLen, pdwRead); return (bRet != FALSE) ? S_OK : E_FAIL; } }; // CStreamOnInet class CSoapWininetClient { private: CUrl m_url; CWriteStreamOnCString m_writeStream; CReadStreamOnInet m_readStream; CString m_strProxy; DWORD m_dwTimeout; CFixedStringT m_strUrl; SOAPCLIENT_ERROR m_errorState; void CloseAll() { if (m_hRequest != NULL) { InternetCloseHandle(m_hRequest); m_hRequest = NULL; } if (m_hConnection != NULL) { InternetCloseHandle(m_hConnection); m_hConnection = NULL; } if (m_hInternet != NULL) { InternetCloseHandle(m_hInternet); m_hInternet = NULL; } } HRESULT ConnectToServer() { if (m_hConnection != NULL) { return S_OK; } m_hInternet = InternetOpen( ATLSOAPINET_CLIENT, m_strProxy.GetLength() ? (INTERNET_OPEN_TYPE_PRECONFIG | INTERNET_OPEN_TYPE_PROXY) : INTERNET_OPEN_TYPE_PRECONFIG, m_strProxy.GetLength() ? (LPCTSTR) m_strProxy : NULL, NULL, 0); if (m_hInternet != NULL) { if (m_dwTimeout != 0) { InternetSetOption(m_hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, &m_dwTimeout, sizeof(m_dwTimeout)); InternetSetOption(m_hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT, &m_dwTimeout, sizeof(m_dwTimeout)); InternetSetOption(m_hInternet, INTERNET_OPTION_SEND_TIMEOUT, &m_dwTimeout, sizeof(m_dwTimeout)); } m_hConnection = InternetConnect(m_hInternet, m_url.GetHostName(), (INTERNET_PORT) m_url.GetPortNumber(), NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL); if (m_hConnection != NULL) { return S_OK; } } CloseAll(); return E_FAIL; } protected: virtual HRESULT GetClientReader(ISAXXMLReader **pReader) { if (pReader == NULL) { return E_POINTER; } *pReader = NULL; CComPtr spReader; HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER); if (SUCCEEDED(hr)) { *pReader = spReader.Detach(); } return hr; } public: // note : not shared across stock client implementations HINTERNET m_hInternet; HINTERNET m_hConnection; HINTERNET m_hRequest; CSoapFault m_fault; CSoapWininetClient(LPCTSTR szUrl) :m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { TCHAR szTmp[ATL_URL_MAX_URL_LENGTH]; if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE)) { if (m_url.CrackUrl(szTmp) != FALSE) { SetProxy(); _ATLTRY { m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength()); m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength()); } _ATLCATCHALL() { } } } } CSoapWininetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80) :m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { if (m_url.SetHostName(szServer) != FALSE) { if (m_url.SetUrlPath(szUri) != FALSE) { if (m_url.SetPortNumber((ATL_URL_PORT) nPort) != FALSE) { _ATLTRY { m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength()); m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength()); } _ATLCATCHALL() { } } } } } virtual ~CSoapWininetClient() { CleanupClient(); CloseAll(); } SOAPCLIENT_ERROR GetClientError() { return m_errorState; } void SetClientError(SOAPCLIENT_ERROR errorState) { m_errorState = errorState; } IWriteStream * GetWriteStream() { return &m_writeStream; } HRESULT GetReadStream(IStream **ppStream) { if (ppStream == NULL) { return E_POINTER; } *ppStream = &m_readStream; return S_OK; } void CleanupClient() { m_writeStream.Cleanup(); if (m_hRequest != NULL) { InternetCloseHandle(m_hRequest); m_hRequest = NULL; } m_fault.Clear(); SetClientError(SOAPCLIENT_SUCCESS); } HRESULT SendRequest(LPCTSTR szAction) { if (ConnectToServer() != S_OK) { SetClientError(SOAPCLIENT_CONNECT_ERROR); return E_FAIL; } CString strHeaders; _ATLTRY { strHeaders.Append(szAction); strHeaders.Append(_T("Content-Type: text/xml; charset=utf-8\r\n")); } _ATLCATCHALL() { return E_OUTOFMEMORY; } static LPCTSTR s_szAcceptTypes[] = { _T("text/*"), NULL }; m_hRequest = HttpOpenRequest(m_hConnection, _T("POST"), m_strUrl, _T("HTTP/1.0"), NULL, s_szAcceptTypes, INTERNET_FLAG_NO_UI | INTERNET_FLAG_KEEP_CONNECTION | ((m_url.GetScheme() == ATL_URL_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0) , NULL); if (m_hRequest != NULL) { if (FALSE != HttpSendRequest(m_hRequest, strHeaders, (DWORD) strHeaders.GetLength(), (void *)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength())) { m_readStream.Init(m_hRequest); if (GetStatusCode() != HTTP_STATUS_SERVER_ERROR) { return S_OK; } else { SetClientError(SOAPCLIENT_SOAPFAULT); CComPtr spReader; if (SUCCEEDED(GetClientReader(&spReader))) { CComPtr spReadStream; if (SUCCEEDED(GetReadStream(&spReadStream))) { if (FAILED(m_fault.ParseFault(spReadStream, spReader))) { SetClientError(SOAPCLIENT_PARSEFAULT_ERROR); } } } } } } else { SetClientError(SOAPCLIENT_SEND_ERROR); } return E_FAIL; } HRESULT SetUrl(LPCTSTR szUrl) { CloseAll(); TCHAR szTmp[ATL_URL_MAX_URL_LENGTH]; if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE)) { return E_FAIL; } if (m_url.CrackUrl(szTmp) != FALSE) { _ATLTRY { m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength()); m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength()); } _ATLCATCHALL() { return E_OUTOFMEMORY; } return S_OK; } return E_FAIL; } HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen) { if ((szUrl == NULL) || (pdwLen == NULL)) { return E_INVALIDARG; } return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL; } HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80) { _ATLTRY { if (szProxy && szProxy[0]) { m_strProxy.Format(_T("http=http://%s:%d https=http://%s:%d"), szProxy, nProxyPort, szProxy, nProxyPort); } else { m_strProxy.Empty(); } } _ATLCATCHALL() { return E_OUTOFMEMORY; } return S_OK; } void SetTimeout(DWORD dwTimeout) { m_dwTimeout = dwTimeout; } int GetStatusCode() { DWORD dwLen = 255; TCHAR szBuf[256]; if (HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE, szBuf, &dwLen, NULL)) { szBuf[dwLen] = '\0'; return _ttoi(szBuf); } return 0; } }; // CSoapWininetClient #endif #ifndef ATLSOAP_NOMSXML_INET class CSoapMSXMLInetClient { private: CUrl m_url; CWriteStreamOnCString m_writeStream; DWORD m_dwTimeout; SOAPCLIENT_ERROR m_errorState; HRESULT ConnectToServer() { TCHAR szURL[ATL_URL_MAX_URL_LENGTH]; DWORD dwLen = ATL_URL_MAX_URL_LENGTH; HRESULT hr = E_FAIL; if (m_spHttpRequest) return S_OK; if (!m_url.CreateUrl(szURL, &dwLen)) return E_FAIL; hr = m_spHttpRequest.CoCreateInstance(__uuidof(ServerXMLHTTP30)); if (hr != S_OK) return hr; CComVariant vEmpty; hr = m_spHttpRequest->open( CComBSTR(L"POST"), CComBSTR(szURL), CComVariant(VARIANT_BOOL(VARIANT_FALSE)), vEmpty, vEmpty ); if (hr != S_OK) { m_spHttpRequest.Release(); return hr; } return S_OK; } protected: virtual HRESULT GetClientReader(ISAXXMLReader **pReader) { if (pReader == NULL) { return E_POINTER; } *pReader = NULL; CComPtr spReader; HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER); if (SUCCEEDED(hr)) { *pReader = spReader.Detach(); } return hr; } public: // note : not shared across stock client implementations CComPtr m_spHttpRequest; CSoapFault m_fault; CSoapMSXMLInetClient(LPCTSTR szUrl) :m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { m_url.CrackUrl(szUrl); } CSoapMSXMLInetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80) : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS) { m_url.SetHostName(szServer); m_url.SetUrlPath(szUri); m_url.SetPortNumber((ATL_URL_PORT) nPort); } virtual ~CSoapMSXMLInetClient() { CleanupClient(); } SOAPCLIENT_ERROR GetClientError() { return m_errorState; } void SetClientError(SOAPCLIENT_ERROR errorState) { m_errorState = errorState; } IWriteStream * GetWriteStream() { return &m_writeStream; } HRESULT GetReadStream(IStream **ppStream) { if (ppStream == NULL) { return E_POINTER; } *ppStream = NULL; HRESULT hr = E_FAIL; if (m_spHttpRequest) { VARIANT vResponseStream; VariantInit(&vResponseStream); hr = m_spHttpRequest->get_responseStream(&vResponseStream); if (S_OK == hr) { hr = E_FAIL; if ((vResponseStream.vt == VT_UNKNOWN) && (vResponseStream.punkVal != NULL)) { // we return the refcount with the pointer! hr = vResponseStream.punkVal->QueryInterface(__uuidof(IStream), (void **)ppStream); } else { SetClientError(SOAPCLIENT_READ_ERROR); } } VariantClear(&vResponseStream); } return hr; } void CleanupClient() { m_writeStream.Cleanup(); m_spHttpRequest.Release(); m_fault.Clear(); SetClientError(SOAPCLIENT_SUCCESS); } HRESULT SendRequest(LPCTSTR szAction) { if (ConnectToServer() != S_OK) { SetClientError(SOAPCLIENT_CONNECT_ERROR); return E_FAIL; } // set the action header LPCTSTR szColon = _tcschr(szAction, _T(':')); if (szColon != NULL) { do { szColon++; } while (_istspace(static_cast(*szColon))); if (FAILED(m_spHttpRequest->setRequestHeader( CComBSTR( L"SOAPAction" ), CComBSTR( szColon )))) { SetClientError(SOAPCLIENT_SEND_ERROR); return E_FAIL; } } // if SOAPAction header not properly formed, attempt to send anyway if (FAILED(m_spHttpRequest->setRequestHeader(CComBSTR( L"Content-Type" ), CComBSTR(L"text/xml; charset=utf-8")))) { SetClientError(SOAPCLIENT_SEND_ERROR); return E_FAIL; } // set timeout if (m_dwTimeout != 0) { long nTimeout = (long) m_dwTimeout; m_spHttpRequest->setTimeouts(nTimeout, nTimeout, nTimeout, nTimeout); // reset timeout m_dwTimeout = 0; } CComVariant vBody(m_writeStream.m_str); HRESULT hr = m_spHttpRequest->send(vBody); if ((SUCCEEDED(hr)) && (GetStatusCode() == 500)) { hr = E_FAIL; CComPtr spReader; if (SUCCEEDED(GetClientReader(&spReader))) { SetClientError(SOAPCLIENT_SOAPFAULT); CComPtr spReadStream; if (SUCCEEDED(GetReadStream(&spReadStream))) { if (FAILED(m_fault.ParseFault(spReadStream, spReader))) { SetClientError(SOAPCLIENT_PARSEFAULT_ERROR); } } } } else if (FAILED(hr)) { SetClientError(SOAPCLIENT_SEND_ERROR); } return hr; } HRESULT SetUrl(LPCTSTR szUrl) { CleanupClient(); return (m_url.CrackUrl(szUrl) != FALSE ? S_OK : E_FAIL); } HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen) { if ((szUrl == NULL) || (pdwLen == NULL)) { return E_INVALIDARG; } return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL; } void SetTimeout(DWORD dwTimeout) { m_dwTimeout = dwTimeout; } int GetStatusCode() { long lStatus; if (m_spHttpRequest->get_status(&lStatus) == S_OK) { return (int) lStatus; } return 0; } HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80) { (szProxy); (nProxyPort); ATLTRACE( _T("CSoapMSXMLInetClient does not support SetProxy") ); return S_OK; } }; // CSoapMSXMLInetClient #endif class _CSDLGenerator : public ITagReplacerImpl<_CSDLGenerator> { private: typedef CAtlMap > WSDLMAP; typedef CAtlMap > HEADERMAP; HRESULT GenerateWSDLHelper(const _soapmap *pMap, WSDLMAP& structMap, WSDLMAP& enumMap) { ATLENSURE_RETURN( pMap != NULL ); const _soapmapentry *pEntries = pMap->pEntries; ATLENSURE_RETURN( pEntries != NULL ); HRESULT hr = S_OK; for (int i=0; pEntries[i].nHash != 0; i++) { if (pEntries[i].nVal == SOAPTYPE_UNK) { ATLENSURE_RETURN( pEntries[i].pChain != NULL ); _ATLTRY { POSITION pos = NULL; CStringA strName(pEntries[i].pChain->szName, pEntries[i].pChain->cchName); if (pEntries[i].pChain->mapType == SOAPMAP_STRUCT) { pos = structMap.SetAt(strName, pEntries[i].pChain); } else if (pEntries[i].pChain->mapType == SOAPMAP_ENUM) { pos = enumMap.SetAt(strName, pEntries[i].pChain); } if (pos == NULL) { hr = E_OUTOFMEMORY; break; } } _ATLCATCHALL() { hr = E_OUTOFMEMORY; break; } hr = GenerateWSDLHelper(pEntries[i].pChain, structMap, enumMap); if (FAILED(hr)) { break; } } } return hr; } HTTP_CODE IsUDT(const _soapmapentry *pEntry) { ATLENSURE( pEntry != NULL ); return (pEntry->nVal != SOAPTYPE_UNK) ? HTTP_S_FALSE : HTTP_SUCCESS; } HTTP_CODE GetSoapDims(const _soapmapentry *pEntry) { ATLENSURE( pEntry != NULL ); if (pEntry->pDims[0] != 0) { if (SUCCEEDED(m_pWriteStream->WriteStream("[", 1, NULL))) { for (int i=1; i<=pEntry->pDims[0]; i++) { if (m_writeHelper.Write(pEntry->pDims[i]) != FALSE) { if (i < pEntry->pDims[0]) { if (FAILED(m_pWriteStream->WriteStream(", ", 2, NULL))) { return HTTP_FAIL; } } } } if (SUCCEEDED(m_pWriteStream->WriteStream("]", 1, NULL))) { return HTTP_SUCCESS; } } } return HTTP_FAIL; } const _soapmap **m_pFuncs; const _soapmap **m_pHeaders; int m_nFunc; int m_nParam; int m_nHeader; WSDLMAP m_structMap; WSDLMAP m_enumMap; POSITION m_currUDTPos; int m_nCurrUDTField; HEADERMAP m_headerMap; POSITION m_currHeaderPos; CWriteStreamHelper m_writeHelper; CStringA m_strServiceName; CStringA m_strNamespaceUri; IWriteStream *m_pWriteStream; CComPtr m_spHttpServerContext; DWORD m_dwCallFlags; protected: void SetWriteStream(IWriteStream *pStream) { m_pWriteStream = pStream; m_writeHelper.Attach(m_pWriteStream); } void SetHttpServerContext(IHttpServerContext *pServerContext) { m_spHttpServerContext = pServerContext; } static HTTP_CODE GetSoapType(int nVal, IWriteStream *pStream) { return (pStream->WriteStream(CSoapRootHandler::s_xsdNames[nVal].szName, CSoapRootHandler::s_xsdNames[nVal].cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HRESULT InitializeSDL(CSoapRootHandler *pHdlr) { m_pFuncs = pHdlr->GetFunctionMap(); if (m_pFuncs == NULL) { return E_FAIL; } ATLASSUME( m_pFuncs[0] != NULL ); m_dwCallFlags = m_pFuncs[0]->dwCallFlags; size_t i; for (i=0; m_pFuncs[i] != NULL; i++) { const _soapmap *pMap = m_pFuncs[i]; HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap); if (FAILED(hr)) { return hr; } } m_pHeaders = pHdlr->GetHeaderMap(); if (m_pHeaders != NULL) { for (i=0; m_pHeaders[i] != NULL; i++) { const _soapmap *pMap = m_pHeaders[i]; HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap); if (FAILED(hr)) { return hr; } } for (i=0; m_pHeaders[i] != NULL; i++) { const _soapmap *pMap = m_pHeaders[i]; for (size_t j=0; pMap->pEntries[j].nHash != 0; j++) { HRESULT hr = S_OK; _ATLTRY { if (m_headerMap.SetAt(pMap->pEntries[j].szField, &pMap->pEntries[j]) == NULL) { hr = E_OUTOFMEMORY; } } _ATLCATCHALL() { hr = E_OUTOFMEMORY; } if (FAILED(hr)) { return hr; } } } } _ATLTRY { m_strServiceName = pHdlr->GetServiceName(); m_strNamespaceUri = pHdlr->GetNamespaceUriA(); } _ATLCATCHALL() { return E_OUTOFMEMORY; } return S_OK; } virtual const char * GetHandlerName() = 0; public: _CSDLGenerator() :m_pFuncs(NULL), m_nFunc(-1), m_nParam(-1), m_currUDTPos(NULL), m_nCurrUDTField(-1), m_pWriteStream(NULL), m_nHeader(-1), m_currHeaderPos(NULL) { } virtual ~_CSDLGenerator() { } HTTP_CODE OnGetURL() { char szURL[ATL_URL_MAX_URL_LENGTH]; DWORD dwUrlSize = sizeof(szURL); char szServer[ATL_URL_MAX_HOST_NAME_LENGTH]; DWORD dwServerSize = sizeof(szServer); char szHttps[16]; DWORD dwHttpsLen = sizeof(szHttps); char szPort[ATL_URL_MAX_PORT_NUMBER_LENGTH+1]; DWORD dwPortLen = sizeof(szPort); if (m_spHttpServerContext->GetServerVariable("URL", szURL, &dwUrlSize) != FALSE) { if (m_spHttpServerContext->GetServerVariable("SERVER_NAME", szServer, &dwServerSize) != FALSE) { bool bHttps = false; if ((m_spHttpServerContext->GetServerVariable("HTTPS", szHttps, &dwHttpsLen) != FALSE) && (!_stricmp(szHttps, "ON"))) { bHttps = true; } if (m_spHttpServerContext->GetServerVariable("SERVER_PORT", szPort, &dwPortLen) != FALSE) { _ATLTRY { CStringA strUrl; strUrl.Format("http%s://%s:%s%s?Handler=%s", bHttps ? "s" : "", szServer, szPort, szURL, GetHandlerName()); CA2W wszUrl(strUrl); wchar_t *pwszUrl = wszUrl; HRESULT hr = AtlGenXMLValue(m_pWriteStream, &pwszUrl); return SUCCEEDED(hr) ? HTTP_SUCCESS : HTTP_FAIL; } _ATLCATCHALL() { return HTTP_FAIL; } } } } return HTTP_FAIL; } HTTP_CODE OnGetNamespace() { return (m_pWriteStream->WriteStream(m_strNamespaceUri, m_strNamespaceUri.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnGetNextFunction() { m_nFunc++; if (m_pFuncs[m_nFunc] == NULL) { m_nFunc = -1; return HTTP_S_FALSE; } return HTTP_SUCCESS; } HTTP_CODE OnGetFunctionName() { return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->szName, m_pFuncs[m_nFunc]->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetNextParameter() { ++m_nParam; if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nHash != 0) { if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_NOMARSHAL) { return OnGetNextParameter(); } return HTTP_SUCCESS; } m_nParam = -1; return HTTP_S_FALSE; } HTTP_CODE OnIsInParameter() { return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_IN) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetParameterName() { HRESULT hr = S_OK; if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_RETVAL) { hr = m_pWriteStream->WriteStream("return", sizeof("return")-1, NULL); } else { hr = m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].szField, m_pFuncs[m_nFunc]->pEntries[m_nParam].cchField, NULL); } return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnNotIsArrayParameter() { return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) ? HTTP_S_FALSE: HTTP_SUCCESS; } HTTP_CODE OnIsParameterUDT() { return IsUDT(&m_pFuncs[m_nFunc]->pEntries[m_nParam]); } HTTP_CODE OnGetParameterSoapType() { if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal != SOAPTYPE_UNK) { return GetSoapType(m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal, m_pWriteStream); } ATLASSUME( m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain != NULL ); return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->szName, m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsParameterDynamicArray() { return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS: HTTP_S_FALSE; } HTTP_CODE OnIsArrayParameter() { return (OnNotIsArrayParameter() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsParameterOneDimensional() { return (m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetParameterArraySize() { return (m_writeHelper.Write(m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[1]) != FALSE) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnGetParameterArraySoapDims() { return GetSoapDims(&m_pFuncs[m_nFunc]->pEntries[m_nParam]); } HTTP_CODE OnIsOutParameter() { return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_OUT) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetNextEnum() { if (m_currUDTPos == NULL) { m_currUDTPos = m_enumMap.GetStartPosition(); } else { m_enumMap.GetNext(m_currUDTPos); } return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetEnumName() { const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos); return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetNextEnumElement() { const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos); ++m_nCurrUDTField; if (pMap->pEntries[m_nCurrUDTField].nHash != 0) { return HTTP_SUCCESS; } m_nCurrUDTField = -1; return HTTP_S_FALSE; } HTTP_CODE OnGetEnumElementName() { const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos); return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField, pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetNextStruct() { if (m_currUDTPos == NULL) { m_currUDTPos = m_structMap.GetStartPosition(); } else { m_structMap.GetNext(m_currUDTPos); } return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetStructName() { const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos); return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetNextStructField() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); ++m_nCurrUDTField; if (pMap->pEntries[m_nCurrUDTField].nHash != 0) { return HTTP_SUCCESS; } m_nCurrUDTField = -1; return HTTP_S_FALSE; } HTTP_CODE OnGetStructFieldName() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField, pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnNotIsArrayField() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return (pMap->pEntries[m_nCurrUDTField].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) ? HTTP_S_FALSE : HTTP_SUCCESS; } HTTP_CODE OnIsFieldUDT() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return IsUDT(&pMap->pEntries[m_nCurrUDTField]); } HTTP_CODE OnGetStructFieldSoapType() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); if (pMap->pEntries[m_nCurrUDTField].nVal != SOAPTYPE_UNK) { return GetSoapType(pMap->pEntries[m_nCurrUDTField].nVal, m_pWriteStream); } ATLASSERT( pMap->pEntries[m_nCurrUDTField].pChain != NULL ); return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].pChain->szName, pMap->pEntries[m_nCurrUDTField].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsArrayField() { return (OnNotIsArrayField() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsFieldDynamicArray() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return (pMap->pEntries[m_nCurrUDTField].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetFieldSizeIsName() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); int nIndex = pMap->pEntries[m_nCurrUDTField].nSizeIs; ATLASSERT( nIndex >= 0 ); return (m_pStream->WriteStream(pMap->pEntries[nIndex].szField, pMap->pEntries[nIndex].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsFieldOneDimensional() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return (pMap->pEntries[m_nCurrUDTField].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetFieldArraySize() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return (m_writeHelper.Write(pMap->pEntries[m_nCurrUDTField].pDims[1]) != FALSE) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetFieldArraySoapDims() { const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos); return GetSoapDims(&pMap->pEntries[m_nCurrUDTField]); } HTTP_CODE OnGetServiceName() { return (m_pWriteStream->WriteStream(m_strServiceName, m_strServiceName.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnGetNextHeader() { if (m_currHeaderPos == NULL) { m_currHeaderPos = m_headerMap.GetStartPosition(); } else { m_headerMap.GetNext(m_currHeaderPos); } return (m_currHeaderPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsInHeader() { return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_IN) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsOutHeader() { return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_OUT) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsRequiredHeader() { return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_MUSTUNDERSTAND) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetHeaderName() { const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos); return (m_pWriteStream->WriteStream(pEntry->szField, pEntry->cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnNotIsArrayHeader() { const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos); return (pEntry->dwFlags & SOAPFLAG_FIXEDARR) ? HTTP_S_FALSE : HTTP_SUCCESS; } HTTP_CODE OnIsHeaderUDT() { return IsUDT(m_headerMap.GetValueAt(m_currHeaderPos)); } HTTP_CODE OnGetHeaderSoapType() { const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos); if (pEntry->nVal != SOAPTYPE_UNK) { return GetSoapType(pEntry->nVal, m_pWriteStream); } ATLENSURE( pEntry->pChain != NULL ); return (m_pWriteStream->WriteStream(pEntry->pChain->szName, pEntry->pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnIsHeaderOneDimensional() { const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos); return (pEntry->pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetHeaderArraySize() { const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos); return (m_writeHelper.Write(pEntry->pDims[1]) != FALSE) ? HTTP_SUCCESS : HTTP_S_FALSE; } HTTP_CODE OnGetHeaderArraySoapDims() { return GetSoapDims(m_headerMap.GetValueAt(m_currHeaderPos)); } HTTP_CODE OnGetNextFunctionHeader() { ++m_nHeader; if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].nHash != 0) { if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_NOMARSHAL) { return OnGetNextHeader(); } return HTTP_SUCCESS; } m_nHeader = -1; return HTTP_S_FALSE; } HTTP_CODE OnGetFunctionHeaderName() { return (m_pWriteStream->WriteStream( m_pHeaders[m_nFunc]->pEntries[m_nHeader].szField, m_pHeaders[m_nFunc]->pEntries[m_nHeader].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL; } HTTP_CODE OnIsArrayHeader() { return (OnNotIsArrayHeader() == HTTP_SUCCESS) ? HTTP_S_FALSE : HTTP_SUCCESS; } HTTP_CODE OnIsDocumentLiteral() { if ((m_dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) { return HTTP_SUCCESS; } return HTTP_S_FALSE; } HTTP_CODE OnIsRpcEncoded() { if ((m_dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) == (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) { return HTTP_SUCCESS; } return HTTP_S_FALSE; } #pragma warning (push) #pragma warning (disable : 4640) // construction of local static object is not thread-safe BEGIN_REPLACEMENT_METHOD_MAP(_CSDLGenerator) REPLACEMENT_METHOD_ENTRY("GetNamespace", OnGetNamespace) REPLACEMENT_METHOD_ENTRY("GetNextFunction", OnGetNextFunction) REPLACEMENT_METHOD_ENTRY("GetFunctionName", OnGetFunctionName) REPLACEMENT_METHOD_ENTRY("GetNextParameter", OnGetNextParameter) REPLACEMENT_METHOD_ENTRY("IsInParameter", OnIsInParameter) REPLACEMENT_METHOD_ENTRY("GetParameterName", OnGetParameterName) REPLACEMENT_METHOD_ENTRY("NotIsArrayParameter", OnNotIsArrayParameter) REPLACEMENT_METHOD_ENTRY("IsParameterUDT", OnIsParameterUDT) REPLACEMENT_METHOD_ENTRY("GetParameterSoapType", OnGetParameterSoapType) REPLACEMENT_METHOD_ENTRY("IsParameterDynamicArray", OnIsParameterDynamicArray) REPLACEMENT_METHOD_ENTRY("IsArrayParameter", OnIsArrayParameter) REPLACEMENT_METHOD_ENTRY("IsParameterOneDimensional", OnIsParameterOneDimensional) REPLACEMENT_METHOD_ENTRY("GetParameterArraySize", OnGetParameterArraySize) REPLACEMENT_METHOD_ENTRY("GetParameterArraySoapDims", OnGetParameterArraySoapDims) REPLACEMENT_METHOD_ENTRY("IsOutParameter", OnIsOutParameter) REPLACEMENT_METHOD_ENTRY("GetNextEnum", OnGetNextEnum) REPLACEMENT_METHOD_ENTRY("GetEnumName", OnGetEnumName) REPLACEMENT_METHOD_ENTRY("GetNextEnumElement", OnGetNextEnumElement) REPLACEMENT_METHOD_ENTRY("GetEnumElementName", OnGetEnumElementName) REPLACEMENT_METHOD_ENTRY("GetNextStruct", OnGetNextStruct) REPLACEMENT_METHOD_ENTRY("GetStructName", OnGetStructName) REPLACEMENT_METHOD_ENTRY("GetNextStructField", OnGetNextStructField) REPLACEMENT_METHOD_ENTRY("GetStructFieldName", OnGetStructFieldName) REPLACEMENT_METHOD_ENTRY("NotIsArrayField", OnNotIsArrayField) REPLACEMENT_METHOD_ENTRY("IsFieldUDT", OnIsFieldUDT) REPLACEMENT_METHOD_ENTRY("GetStructFieldSoapType", OnGetStructFieldSoapType) REPLACEMENT_METHOD_ENTRY("IsArrayField", OnIsArrayField) REPLACEMENT_METHOD_ENTRY("IsFieldOneDimensional", OnIsFieldOneDimensional) REPLACEMENT_METHOD_ENTRY("GetFieldArraySize", OnGetFieldArraySize) REPLACEMENT_METHOD_ENTRY("GetFieldArraySoapDims", OnGetFieldArraySoapDims) REPLACEMENT_METHOD_ENTRY("GetServiceName", OnGetServiceName) REPLACEMENT_METHOD_ENTRY("GetURL", OnGetURL) REPLACEMENT_METHOD_ENTRY("GetNextHeader", OnGetNextHeader) REPLACEMENT_METHOD_ENTRY("GetHeaderName", OnGetHeaderName) REPLACEMENT_METHOD_ENTRY("NotIsArrayHeader", OnNotIsArrayHeader) REPLACEMENT_METHOD_ENTRY("IsArrayHeader", OnIsArrayHeader) REPLACEMENT_METHOD_ENTRY("IsHeaderUDT", OnIsHeaderUDT) REPLACEMENT_METHOD_ENTRY("GetHeaderSoapType", OnGetHeaderSoapType) REPLACEMENT_METHOD_ENTRY("IsHeaderOneDimensional", OnIsHeaderOneDimensional) REPLACEMENT_METHOD_ENTRY("GetHeaderArraySize", OnGetHeaderArraySize) REPLACEMENT_METHOD_ENTRY("GetHeaderArraySoapDims", OnGetHeaderArraySoapDims) REPLACEMENT_METHOD_ENTRY("GetNextFunctionHeader", OnGetNextFunctionHeader) REPLACEMENT_METHOD_ENTRY("GetFunctionHeaderName", OnGetFunctionHeaderName) REPLACEMENT_METHOD_ENTRY("IsInHeader", OnIsInHeader) REPLACEMENT_METHOD_ENTRY("IsOutHeader", OnIsOutHeader) REPLACEMENT_METHOD_ENTRY("IsRequiredHeader", OnIsRequiredHeader) REPLACEMENT_METHOD_ENTRY("IsDocumentLiteral", OnIsDocumentLiteral) REPLACEMENT_METHOD_ENTRY("IsRpcEncoded", OnIsRpcEncoded) REPLACEMENT_METHOD_ENTRY("IsFieldDynamicArray", OnIsFieldDynamicArray) REPLACEMENT_METHOD_ENTRY("GetFieldSizeIsName", OnGetFieldSizeIsName) END_REPLACEMENT_METHOD_MAP() #pragma warning (pop) }; // class _CSDLGenerator template class CSDLGenerator : public _CSDLGenerator, public IRequestHandlerImpl< CSDLGenerator >, public CComObjectRootEx { private: public: typedef CSDLGenerator _sdlGenerator; BEGIN_COM_MAP(_sdlGenerator) COM_INTERFACE_ENTRY(IRequestHandler) COM_INTERFACE_ENTRY(ITagReplacer) END_COM_MAP() HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pServiceProvider) { IRequestHandlerImpl::InitializeHandler(pRequestInfo, pServiceProvider); CComObjectStack handler; if (FAILED(InitializeSDL(&handler))) { return HTTP_FAIL; } CStencil s; HTTP_CODE hcErr = s.LoadFromString(s_szAtlsWSDLSrf, (DWORD) strlen(s_szAtlsWSDLSrf)); if (hcErr == HTTP_SUCCESS) { hcErr = HTTP_FAIL; CHttpResponse HttpResponse(pRequestInfo->pServerContext); HttpResponse.SetContentType("text/xml"); if (s.ParseReplacements(this) != false) { s.FinishParseReplacements(); SetStream(&HttpResponse); SetWriteStream(&HttpResponse); SetHttpServerContext(m_spServerContext); ATLASSERT( s.ParseSuccessful() != false ); hcErr = s.Render(this, &HttpResponse); } } return hcErr; } const char * GetHandlerName() { return szHandlerName; } }; // class CSDLGenerator } // namespace ATL #pragma pack(pop) #pragma warning(pop) #endif // __ATLSOAP_H__