/*++ Copyright (c) 1998 Microsoft Corporation Module Name : pe_dispi.cxx Abstract: This module provides the implementation for the protocol event dispatchers Author: Keith Lau (KeithLau) 6/24/98 Project: SMTP Server DLL Revision History: KeithLau Created --*/ #define INCL_INETSRV_INCS #include "smtpinc.h" #include "initguid.h" // // ATL includes // #define _ATL_NO_DEBUG_CRT #define _ASSERTE _ASSERT #define _WINDLL #include "atlbase.h" extern CComModule _Module; #include "atlcom.h" #undef _WINDLL // // SEO includes // #include "seo.h" #include "seolib.h" #include #include "smtpcli.hxx" #include "smtpout.hxx" #include // // Dispatcher implementation // #include "pe_dispi.hxx" // ================================================================= // Outbound command generation dispatcher // // // These are the commands that the default handlers hook // char *COutboundDispatcher::s_rgszDefaultCommand[PE_STATE_MAX_STATES] = { "ehlo", "mail", "rcpt", NULL, "quit" }; // // This is what we use to map an event type to an internal index // const GUID *COutboundDispatcher::s_rgrguidEventTypes[PE_STATE_MAX_STATES] = { &CATID_SMTP_ON_SESSION_START, &CATID_SMTP_ON_MESSAGE_START, &CATID_SMTP_ON_PER_RECIPIENT, &CATID_SMTP_ON_BEFORE_DATA, &CATID_SMTP_ON_SESSION_END }; // // Generic dispatcher methods // HRESULT CGenericProtoclEventDispatcher::GetLowerAnsiStringFromVariant( CComVariant &vString, LPSTR pszString, DWORD *pdwLength ) { HRESULT hr = S_OK; DWORD dwInLength; if (!pszString) return(E_POINTER); if (!pdwLength) return(E_INVALIDARG); // Default to NULL *pszString = NULL; if (vString.vt == VT_BSTR) { DWORD dwLength = lstrlenW(vString.bstrVal) + 1; // Set the size anyway dwInLength = *pdwLength; *pdwLength = dwLength; if (dwLength > dwInLength) return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); // copy the rule into an ascii string if (WideCharToMultiByte(CP_ACP, 0, vString.bstrVal, -1, pszString, dwLength, NULL, NULL) <= 0) return(HRESULT_FROM_WIN32(GetLastError())); // Convert to lower case once and for all to avoid strcmpi while (*pszString) { *pszString = (CHAR)tolower(*pszString); pszString++; } } else hr = E_INVALIDARG; return(hr); } HRESULT CGenericProtoclEventDispatcher::InsertBinding( LPPE_COMMAND_NODE *ppHeadNode, LPPE_BINDING_NODE pBinding, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize ) { DWORD dwPriority; BOOL fReorder = FALSE; LPPE_COMMAND_NODE pNode; TraceFunctEnter("CGenericProtoclEventDispatcher::InsertBinding"); if (!ppHeadNode || !pBinding || !pszCommandKeyword) return(E_POINTER); pNode = *ppHeadNode; while (pNode) { // All strings come in lower case if (!strcmp(pszCommandKeyword, pNode->pszCommandKeyword)) { DebugTrace((LPARAM)0, "Found command %s", pszCommandKeyword); // Now we determine if we have changed the max priority // if we increased the priority (i.e. lower value), then // we will have to reposition out node dwPriority = pBinding->dwPriority; if (pBinding->dwPriority < dwPriority) { pBinding->dwPriority = dwPriority; fReorder = TRUE; DebugTrace((LPARAM)0, "Reordering commands"); } // Now insert the binding in the right order LPPE_BINDING_NODE pWalk = pNode->pFirstBinding; LPPE_BINDING_NODE pPrev = (LPPE_BINDING_NODE)&(pNode->pFirstBinding); if (!pWalk) { pBinding->pNext = pNode->pFirstBinding; pNode->pFirstBinding = pBinding; } else while (pWalk) { if (pWalk->dwPriority > dwPriority) break; pPrev = pWalk; pWalk = pWalk->pNext; } pBinding->pNext = pWalk; pPrev->pNext = pBinding; if (!fReorder) return(S_OK); else break; } // Next! pNode = pNode->pNext; } if (!fReorder) { char *pTemp; DWORD dwSize = sizeof(PE_COMMAND_NODE); // Not found, create a new command node, plus a buffer // for the command keyword dwSize += dwCommandKeywordSize; pTemp = new char [dwSize]; if (!pTemp) return(E_OUTOFMEMORY); pNode = (LPPE_COMMAND_NODE)pTemp; pTemp = (char *)(pNode + 1); DebugTrace((LPARAM)0, "Creating node for command %s", pszCommandKeyword); strcpy(pTemp, pszCommandKeyword); dwPriority = pBinding->dwPriority; pBinding->pNext = NULL; pNode->pszCommandKeyword = pTemp; pNode->dwHighestPriority = dwPriority; pNode->pFirstBinding = pBinding; } // Insert based on top priority LPPE_COMMAND_NODE pWalk = *ppHeadNode; LPPE_COMMAND_NODE pPrev = (LPPE_COMMAND_NODE)ppHeadNode; if (!pWalk) { pNode->pNext = NULL; *ppHeadNode = pNode; return(S_OK); } while (pWalk) { if (pWalk->dwHighestPriority > dwPriority) break; pPrev = pWalk; pWalk = pWalk->pNext; } pNode->pNext = pWalk; pPrev->pNext = pNode; TraceFunctLeave(); return(S_OK); } HRESULT CGenericProtoclEventDispatcher::InsertBindingWithHash( LPPE_COMMAND_NODE *rgpHeadNodes, DWORD dwHashSize, LPPE_BINDING_NODE pBinding, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize ) { TraceFunctEnter("CGenericProtoclEventDispatcher::InsertBindingWithHash"); if (!rgpHeadNodes || !pBinding || !pszCommandKeyword) return(E_POINTER); DWORD dwHash = GetHashValue( dwHashSize, pszCommandKeyword, dwCommandKeywordSize); HRESULT hr = InsertBinding( &(rgpHeadNodes[dwHash]), pBinding, pszCommandKeyword, dwCommandKeywordSize); TraceFunctLeave(); return(hr); } HRESULT CGenericProtoclEventDispatcher::FindCommandFromHash( LPPE_COMMAND_NODE *rgpHeadNodes, DWORD dwHashSize, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize, LPPE_COMMAND_NODE *ppCommandNode ) { TraceFunctEnter("CGenericProtoclEventDispatcher::FindCommandFromHash"); if (!rgpHeadNodes || !pszCommandKeyword || !ppCommandNode) return(E_POINTER); DWORD dwHash = GetHashValue( dwHashSize, pszCommandKeyword, dwCommandKeywordSize); LPPE_COMMAND_NODE pNode = rgpHeadNodes[dwHash]; while (pNode) { if (!strcmp(pszCommandKeyword, pNode->pszCommandKeyword)) { *ppCommandNode = pNode; TraceFunctLeave(); return(S_OK); } pNode = pNode->pNext; } TraceFunctLeave(); return(S_FALSE); } HRESULT CGenericProtoclEventDispatcher::CleanupCommandNodes( LPPE_COMMAND_NODE pHeadNode, LPPE_COMMAND_NODE pSkipNode ) { LPPE_COMMAND_NODE pNode; while (pHeadNode) { pNode = pHeadNode->pNext; if (pHeadNode != pSkipNode) { char *pTemp = (char *)pHeadNode; delete [] pTemp; } pHeadNode = pNode; } return(S_OK); } // // Inbound dispatcher methods // HRESULT CInboundDispatcher::AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding ) { if (ppNewBinding) *ppNewBinding = NULL; if (!piBinding || !ppNewBinding) return(E_POINTER); *ppNewBinding = new CInboundBinding(this); if (*ppNewBinding == NULL) return(E_OUTOFMEMORY); return(S_OK); } HRESULT STDMETHODCALLTYPE CInboundDispatcher::ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpInCommandContext *pContext, DWORD dwStopAtPriority, LPPE_COMMAND_NODE pCommandNode, LPPE_BINDING_NODE *ppResumeFrom ) { HRESULT hr = S_OK; LPPE_BINDING_NODE pBinding = NULL; TraceFunctEnterEx((LPARAM)this, "CInboundDispatcher::ChainSinks"); // These are the essential pointers that CANNOT be NULL if (!pContext || !pCommandNode || !ppResumeFrom) return(E_POINTER); // What we do is strictly determined by the ppPreviousCommand // and ppResumeFrom pointers. The logic is as follows: // // pCmdNode ppResumeFrom Action // NULL X Error (E_POINTER) // !NULL NULL Error (ERROR_NO_MORE_ITEMS) // !NULL !NULL Start from the exact binding specified in // *ppResumeFrom // // On exit, these pointers will be updated to the following: // // *ppResumeFrom - This will be set to the next binding to resume from // This will be set to NULL if there are no more bindings pBinding = *ppResumeFrom; if (!pBinding) { _ASSERT(FALSE); ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!"); return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); } // We know exactly where to start chaining ... Do it. // Break when: // 1) The default handler binding is encountered, or // 2) Sinks results are S_OK // 3) No more bindings left CInboundBinding *pInboundBinding; IUnknown *pUnkSink = NULL; ISmtpInCommandSink *pSink; hr = S_OK; *ppResumeFrom = pBinding; while (pBinding && (hr == S_OK)) { // One of the exiting conditions if (pBinding->dwPriority > dwStopAtPriority) break; // Get the containing binding class pInboundBinding = CONTAINING_RECORD( pBinding, CInboundBinding, m_bnInfo); // Call the sink hr = m_piEventManager->CreateSink( pInboundBinding->m_piBinding, NULL, &pUnkSink); if (SUCCEEDED(hr)) { hr = pUnkSink->QueryInterface( IID_ISmtpInCommandSink, (LPVOID *)&pSink); pUnkSink->Release(); pUnkSink = NULL; if (SUCCEEDED(hr)) { hr = pSink->OnSmtpInCommand( pServer, pSession, pMsg, pContext); pSink->Release(); if (hr == MAILTRANSPORT_S_PENDING) break; } else hr = S_OK; } else hr = S_OK; // Next pBinding = pBinding->pNext; *ppResumeFrom = pBinding; } TraceFunctLeaveEx((LPARAM)this); return(hr); } // // CInboundDispatcher::CInboundBinding methods // HRESULT CInboundDispatcher::CInboundBinding::Init( IEventBinding *piBinding ) { HRESULT hr; CComPtr piEventProperties; CComVariant vRule; CHAR szCommandKeyword[256]; DWORD cchCommandKeyword = 0; TraceFunctEnterEx((LPARAM)this, "CInboundDispatcher::CInboundBinding::Init"); if (!piBinding || !m_pDispatcher) return(E_POINTER); // get the parent initialized hr = CBinding::Init(piBinding); if (FAILED(hr)) return hr; // Store the priority m_bnInfo.dwPriority = CBinding::m_dwPriority; m_bnInfo.dwFlags = 0; // get the binding database hr = m_piBinding->get_SourceProperties(&piEventProperties); if (FAILED(hr)) return hr; // get the rule from the binding database hr = piEventProperties->Item(&CComVariant("Rule"), &vRule); if (FAILED(hr)) return hr; // Process the VARIANT to obtain a lower-case ANSI string if (hr == S_OK) { cchCommandKeyword = sizeof(szCommandKeyword); hr = GetLowerAnsiStringFromVariant( vRule, szCommandKeyword, &cchCommandKeyword); } // We cannot proceed without a rule, so we discard this binding if (FAILED(hr)) { ErrorTrace((LPARAM)this, "Failed to get keyword, error %08x", hr); return(hr); } if (!cchCommandKeyword || !(*szCommandKeyword)) return(E_INVALIDARG); DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword); // Call the dispatcher to insert the node into the command list hr = CGenericProtoclEventDispatcher::InsertBindingWithHash( m_pDispatcher->m_rgpCommandList, m_pDispatcher->m_dwHashSize, &m_bnInfo, szCommandKeyword, cchCommandKeyword); if (SUCCEEDED(hr)) m_pDispatcher->m_fSinksInstalled = TRUE; TraceFunctLeaveEx((LPARAM)this); return(hr); } // // Outbound dispatcher methods // void COutboundDispatcher::InitializeDefaultCommandBindings() { for (DWORD i = 0; i < PE_STATE_MAX_STATES; i++) { if (s_rgszDefaultCommand[i]) { // Set up the list head m_rgpCommandList[i] = &(m_rgcnDefaultCommand[i]); // Set up the command node m_rgpCommandList[i]->pNext = NULL; m_rgpCommandList[i]->pszCommandKeyword = s_rgszDefaultCommand[i]; m_rgpCommandList[i]->dwHighestPriority = PRIO_DEFAULT; m_rgpCommandList[i]->pFirstBinding = &(m_rgbnDefaultCommand[i]); // Set up the binding node m_rgbnDefaultCommand[i].pNext = NULL; m_rgbnDefaultCommand[i].dwPriority = PRIO_DEFAULT; m_rgbnDefaultCommand[i].dwFlags = PEBN_DEFAULT; } else m_rgpCommandList[i] = NULL; } } HRESULT COutboundDispatcher::AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding ) { if (ppNewBinding) *ppNewBinding = NULL; if (!piBinding || !ppNewBinding) return(E_POINTER); *ppNewBinding = new COutboundBinding(this, rguidEventType); if (*ppNewBinding == NULL) return(E_OUTOFMEMORY); return(S_OK); } HRESULT STDMETHODCALLTYPE COutboundDispatcher::ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpOutCommandContext *pContext, DWORD dwEventType, LPPE_COMMAND_NODE *ppPreviousCommand, LPPE_BINDING_NODE *ppResumeFrom ) { HRESULT hr = S_OK; LPPE_COMMAND_NODE pCommand = NULL; LPPE_BINDING_NODE pBinding = NULL; _ASSERT(dwEventType < PE_OET_INVALID_EVENT_TYPE); TraceFunctEnterEx((LPARAM)this, "COutboundDispatcher::ChainSinks"); // These are the essential pointers that CANNOT be NULL if (!pContext || !ppPreviousCommand || !ppResumeFrom) return(E_POINTER); // If we encounter a bad event type, just gracefully return // without doing anythig if (dwEventType >= PE_OET_INVALID_EVENT_TYPE) { *ppResumeFrom = NULL; ErrorTrace((LPARAM)this, "Skipping event due to bad event type %u", dwEventType); return(S_OK); } // What we do is strictly determined by the ppPreviousCommand // and ppResumeFrom pointers. The logic is as follows: // // ppPrevCmd ppResumeFrom Action // NULL NULL Start from the first command for this event type // NULL !NULL Start from the first command for this event type // !NULL NULL Start from the first binding for the command // that follows *ppPreviousCommand // !NULL !NULL Start from the exact binding specified in // *ppResumeFrom // // On exit, these pointers will be updated to the following: // // *ppPreviousCommand - This will point to the command node of the command // that was just processed // *ppResumeFrom - This will be set to the next binding to resume from // This will be set to NULL if there are no more bindings if (!*ppPreviousCommand) { pCommand = m_rgpCommandList[dwEventType]; if (!pCommand) { // Ooooops! We peeked with SinksInstalled() but now we have no sinks // This is an error but we will recover gracefully _ASSERT(pCommand); ErrorTrace((LPARAM)this, "ERROR! Empty command chain!!"); return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); } // We check this downstream pBinding = pCommand->pFirstBinding; } else if (*ppResumeFrom) { // We resume from the exact binding as specified pCommand = *ppPreviousCommand; pBinding = *ppResumeFrom; } else { // We start with the next command in the list pCommand = (*ppPreviousCommand)->pNext; if (!pCommand) { DebugTrace((LPARAM)this, "No more commands to chain"); *ppPreviousCommand = NULL; *ppResumeFrom = NULL; return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); } // We check this downstream pBinding = pCommand->pFirstBinding; } // DEBUG Check: we want the retail perf to be as high as possible // and we want it to be fail safe as well. We will do an internal // check here to make sure that any command node has at least one // binding, and that we have bindings when we expect them. #ifdef DEBUG if (!pBinding) { // Ooooops! We have a command node without a binding. This is a blatant // error but we recover gracefully _ASSERT(pBinding); ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!"); return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); } #endif // We know exactly where to start chaining ... Do it. // Break when: // 1) The default handler binding is encountered, or // 2) Sinks results are S_OK // 3) No more bindings left COutboundBinding *pOutboundBinding; IUnknown *pUnkSink = NULL; ISmtpOutCommandSink *pSink; COutboundContext *pCContext; // Initialize pContext->m_pCurrentCommandContext pCContext = (COutboundContext *)pContext; pCContext->m_pCurrentCommandContext = pCommand; hr = S_OK; while (pBinding && (hr == S_OK)) { // One of the exiting conditions if (pBinding->dwFlags & PEBN_DEFAULT) break; // Get the containing binding class pOutboundBinding = CONTAINING_RECORD( pBinding, COutboundBinding, m_bnInfo); // Call the sink hr = m_piEventManager->CreateSink( pOutboundBinding->m_piBinding, NULL, &pUnkSink); if (SUCCEEDED(hr)) { hr = pUnkSink->QueryInterface( IID_ISmtpOutCommandSink, (LPVOID *)&pSink); pUnkSink->Release(); pUnkSink = NULL; if (SUCCEEDED(hr)) { // Pre-fill in the next binding to avoid // race conditions in the async case *ppResumeFrom = pBinding->pNext; hr = pSink->OnSmtpOutCommand( pServer, pSession, pMsg, pContext); pSink->Release(); if (hr == MAILTRANSPORT_S_PENDING) hr = S_OK; // break; } else hr = S_OK; } else hr = S_OK; // Next pBinding = pBinding->pNext; } // Return where to resume from ... *ppPreviousCommand = pCommand; *ppResumeFrom = pBinding; TraceFunctLeaveEx((LPARAM)this); return(hr); } // // COutboundDispatcher::COutboundBinding methods // COutboundDispatcher::COutboundBinding::COutboundBinding( COutboundDispatcher *pDispatcher, REFGUID rguidEventType ) { _ASSERT(pDispatcher); m_pDispatcher = pDispatcher; // Based on the event type GUID, we can calculate which // event type id this goes to for (m_dwEventType = 0; m_dwEventType < PE_OET_INVALID_EVENT_TYPE; m_dwEventType++) if (rguidEventType == *(s_rgrguidEventTypes[m_dwEventType])) break; // If the GUID is not recognized, then the final value of // m_dwEventType will be PE_OET_INVALID_EVENT_TYPE, which // we will not process in this dispatcher } HRESULT COutboundDispatcher::COutboundBinding::Init( IEventBinding *piBinding ) { HRESULT hr; CComPtr piEventProperties; CComVariant vRule; CHAR szCommandKeyword[256]; DWORD cchCommandKeyword = 0; TraceFunctEnterEx((LPARAM)this, "COutboundDispatcher::COutboundBinding::Init"); if (!piBinding || !m_pDispatcher) return(E_POINTER); // If the event type GUID is unknown, invalidate this binding if (m_dwEventType >= PE_OET_INVALID_EVENT_TYPE) return(E_INVALIDARG); // get the parent initialized hr = CBinding::Init(piBinding); if (FAILED(hr)) return hr; // Store the priority m_bnInfo.dwPriority = CBinding::m_dwPriority; m_bnInfo.dwFlags = 0; // get the binding database hr = m_piBinding->get_SourceProperties(&piEventProperties); if (FAILED(hr)) return hr; // get the rule from the binding database hr = piEventProperties->Item(&CComVariant("Rule"), &vRule); if (FAILED(hr)) return hr; // Process the VARIANT to obtain a lower-case ANSI string if (hr == S_OK) { cchCommandKeyword = sizeof(szCommandKeyword); hr = GetLowerAnsiStringFromVariant( vRule, szCommandKeyword, &cchCommandKeyword); } else if(hr == S_FALSE) { // // Treat no rule as an empty string rule // szCommandKeyword[0] = '\0'; cchCommandKeyword = 1; } // We cannot proceed without a rule, so we discard this binding if (FAILED(hr)) { ErrorTrace((LPARAM)this, "Failed to get keyword, error %08x", hr); return(hr); } if (!cchCommandKeyword) return(E_INVALIDARG); DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword); // Call the dispatcher to insert the node into the command list hr = CGenericProtoclEventDispatcher::InsertBinding( &((m_pDispatcher->m_rgpCommandList)[m_dwEventType]), &m_bnInfo, szCommandKeyword, cchCommandKeyword); if (SUCCEEDED(hr)) m_pDispatcher->m_fSinksInstalled = TRUE; TraceFunctLeaveEx((LPARAM)this); return(hr); } // // CResponseDispatcher mathods // HRESULT CResponseDispatcher::AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding ) { if (ppNewBinding) *ppNewBinding = NULL; if (!piBinding || !ppNewBinding) return(E_POINTER); *ppNewBinding = new CResponseBinding(this); if (*ppNewBinding == NULL) return(E_OUTOFMEMORY); return(S_OK); } HRESULT STDMETHODCALLTYPE CResponseDispatcher::ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpServerResponseContext *pContext, DWORD dwStopAtPriority, LPPE_COMMAND_NODE pCommandNode, LPPE_BINDING_NODE *ppResumeFrom ) { HRESULT hr = S_OK; LPPE_BINDING_NODE pBinding = NULL; TraceFunctEnterEx((LPARAM)this, "CResponseDispatcher::ChainSinks"); // These are the essential pointers that CANNOT be NULL if (!pContext || !pCommandNode || !ppResumeFrom) return(E_POINTER); // What we do is strictly determined by the ppPreviousCommand // and ppResumeFrom pointers. The logic is as follows: // // pCmdNode ppResumeFrom Action // NULL X Error (E_POINTER) // !NULL NULL Error (ERROR_NO_MORE_ITEMS) // !NULL !NULL Start from the exact binding specified in // *ppResumeFrom // // On exit, these pointers will be updated to the following: // // *ppResumeFrom - This will be set to the next binding to resume from // This will be set to NULL if there are no more bindings pBinding = *ppResumeFrom; if (!pBinding) { _ASSERT(FALSE); ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!"); return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); } // We know exactly where to start chaining ... Do it. // Break when: // 1) The default handler binding is encountered, or // 2) Sinks results are S_OK // 3) No more bindings left CResponseBinding *pResponseBinding; IUnknown *pUnkSink = NULL; ISmtpServerResponseSink *pSink; hr = S_OK; *ppResumeFrom = pBinding; while (pBinding && (hr == S_OK)) { // One of the exiting conditions if (pBinding->dwPriority > dwStopAtPriority) break; // Get the containing binding class pResponseBinding = CONTAINING_RECORD( pBinding, CResponseBinding, m_bnInfo); // Call the sink hr = m_piEventManager->CreateSink( pResponseBinding->m_piBinding, NULL, &pUnkSink); if (SUCCEEDED(hr)) { hr = pUnkSink->QueryInterface( IID_ISmtpServerResponseSink, (LPVOID *)&pSink); pUnkSink->Release(); pUnkSink = NULL; if (SUCCEEDED(hr)) { // Pre-fill in the next binding to avoid // race conditions in the async case hr = pSink->OnSmtpServerResponse( pServer, pSession, pMsg, pContext); pSink->Release(); if (hr == MAILTRANSPORT_S_PENDING) hr = S_OK; // break; } else hr = S_OK; } else hr = S_OK; // Next pBinding = pBinding->pNext; *ppResumeFrom = pBinding; } TraceFunctLeaveEx((LPARAM)this); return(hr); } // // CResponseDispatcher::CResponseBinding methods // HRESULT CResponseDispatcher::CResponseBinding::Init( IEventBinding *piBinding ) { HRESULT hr; CComPtr piEventProperties; CComVariant vRule; CHAR szCommandKeyword[256]; DWORD cchCommandKeyword = 0; TraceFunctEnterEx((LPARAM)this, "CResponseDispatcher::CResponseBinding::Init"); if (!piBinding || !m_pDispatcher) return(E_POINTER); // get the parent initialized hr = CBinding::Init(piBinding); if (FAILED(hr)) return hr; // Store the priority m_bnInfo.dwPriority = CBinding::m_dwPriority; m_bnInfo.dwFlags = 0; // get the binding database hr = m_piBinding->get_SourceProperties(&piEventProperties); if (FAILED(hr)) return hr; // get the rule from the binding database hr = piEventProperties->Item(&CComVariant("Rule"), &vRule); if (FAILED(hr)) return hr; // Process the VARIANT to obtain a lower-case ANSI string if (hr == S_OK) { cchCommandKeyword = sizeof(szCommandKeyword); hr = GetLowerAnsiStringFromVariant( vRule, szCommandKeyword, &cchCommandKeyword); } // We cannot proceed without a rule, so we discard this binding if (FAILED(hr)) { ErrorTrace((LPARAM)this, "Failed to get keyword, error %08x", hr); return(hr); } if (!cchCommandKeyword || !(*szCommandKeyword)) return(E_INVALIDARG); DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword); // Call the dispatcher to insert the node into the command list hr = CGenericProtoclEventDispatcher::InsertBindingWithHash( m_pDispatcher->m_rgpCommandList, m_pDispatcher->m_dwHashSize, &m_bnInfo, szCommandKeyword, cchCommandKeyword); if (SUCCEEDED(hr)) m_pDispatcher->m_fSinksInstalled = TRUE; TraceFunctLeaveEx((LPARAM)this); return(hr); }