// Message.cpp : Implementation of cMessage #include "stdafx.h" #include "Inject.h" #include "Message.h" #include "InjectApi.h" #include "MessageLoaders.h" #include "MessageParsers.h" // m_pParent noahb cMessage::cFieldList::iterator cMessage::cLoadContext::lookupField( cMessage::cMessageElement *pElement ) { for( cLoadContext *pContext = this; pContext != NULL; pContext = pContext->m_pParent ) { for( cFieldList::iterator i = m_pMessage->m_fields.begin() + m_dwOffset; i != m_pMessage->m_fields.end(); i += i->m_nOwns ) { if( i->m_pSchema == pElement ) return i; } } return m_pMessage->m_fields.end(); } void cMessage::cMessageSchema::loadSchema( DWORD dwSchema ) { TCHAR szQuery[ 255 ]; ::_stprintf( szQuery, _T( "/schema/messages/message[@type='%04X']" ), dwSchema ); MSXML::IXMLDOMElementPtr pMessage = (*g_pXML)->selectSingleNode( szQuery ); if( pMessage.GetInterfacePtr() == NULL ) // Nothing here, so we create a valid but empty message return; cElementParser::cContext c( &m_members ); c.parseChildren( pMessage ); } MSXML::IXMLDOMDocumentPtr * cMessage::g_pXML; cMessage::cMessageSchemaMap cMessage::g_schema; void cMessage::init() { // Load the schema TCHAR szPath[ MAX_PATH ]; ::InjectMapPath( eInjectPathAgent, _T( "messages.xml" ), szPath ); g_pXML = new MSXML::IXMLDOMDocumentPtr; (*g_pXML).CreateInstance( __uuidof( MSXML::DOMDocument ) ); (*g_pXML)->load( szPath ); // Initialize our schema helper objects cFieldLoader::init(); cElementParser::init(); #ifdef _DEBUG // Load all of the message type USES_CONVERSION; MSXML::IXMLDOMNodeListPtr pAllMessages = (*g_pXML)->selectNodes( _T( "/schema/messages/message" ) ); for( MSXML::IXMLDOMElementPtr pMessage = pAllMessages->nextNode(); pMessage.GetInterfacePtr() != NULL; pMessage = pAllMessages->nextNode() ) { _variant_t vType = pMessage->getAttribute( _T( "type" ) ); if( vType.vt != VT_BSTR ) { _ASSERTE( FALSE ); continue; } // Get the ID from the message long nID; if( ::_stscanf( OLE2T( vType.bstrVal ), _T( "%X" ), &nID ) != 1 ) { _ASSERTE( FALSE ); continue; } cMessageSchema ms; ms.loadSchema( nID ); } #endif } void cMessage::term() { g_schema.clear(); cElementParser::term(); cFieldLoader::term(); if( (*g_pXML).GetInterfacePtr() != NULL ) { (*g_pXML).Release(); delete g_pXML; } } ///////////////////////////////////////////////////////////////////////////// // cMessage cMessage::cMessage() : m_nType( 0 ), m_pStartCrack( NULL ), m_pEndCrack( NULL ), m_pEndData( NULL ), m_pSchema( NULL ) { } cMessage::cFieldList::iterator cMessage::getFieldFromElement( cMessageElement *pElement ) { for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); i += i->m_nOwns ) { cMessageElement *pSchema = i->m_pSchema; if( pSchema == pElement ) break; } return i; } void cMessage::crackMessage( BYTE *pBody, DWORD dwSize ) { m_pStartCrack = pBody; m_pEndData = pBody + dwSize + sizeof( DWORD ); m_nType = *reinterpret_cast< long * >( pBody ); m_fields.clear(); m_pEndCrack = m_pStartCrack + sizeof( long ); // First look up the message to see if it's already decoded cMessageSchemaMap::iterator i_schema = g_schema.find( m_nType ); if( i_schema == g_schema.end() ) { // Make a new one m_pSchema = new cMessageSchema; m_pSchema->loadSchema( m_nType ); g_schema.insert( cMessageSchemaMap::value_type( m_nType, std::auto_ptr< cMessageSchema >( m_pSchema ) ) ); } else m_pSchema = i_schema->second.get(); // At this point we have "a" schema of some quality // set up the cursors // Crack all fields right awway - testing mostly cLoadContext context( this ); m_icracked = m_pSchema->m_members.begin(); while( m_icracked != m_pSchema->m_members.end() ) { if( !m_icracked->get()->load( context ) ) { // Failure to load the data, abort m_icracked = m_pSchema->m_members.end(); break; } // Carry on to the next field ++ m_icracked; } } void cMessage::crackAll() { cLoadContext context( this ); while( m_icracked != m_pSchema->m_members.end() ) { if( !m_icracked->get()->load( context ) ) { // Failure to load the data, abort m_icracked = m_pSchema->m_members.end(); break; } // Carry on to the next field ++ m_icracked; } } bool cMessage::crackMember( cFieldList::iterator i ) { if( i != m_fields.end() ) // We aren't at the end yet return true; if( m_icracked == m_pSchema->m_members.end() ) // At the end now return false; // Attempt to crack the next field cLoadContext context( this ); while( m_icracked != m_pSchema->m_members.end() ) { if( !m_icracked->get()->load( context ) ) { // Failure to load the data, abort m_icracked = m_pSchema->m_members.end(); break; } // Carry on to the next field ++ m_icracked; if( i != m_fields.end() ) return true; } return false; } STDMETHODIMP cMessage::get_Type(long *pVal) { _ASSERTE( pVal != NULL ); *pVal = *reinterpret_cast< long * >( m_pStartCrack ); return S_OK; } STDMETHODIMP cMessage::get_Data(VARIANT *pVal) { long nSize = m_pEndData - m_pStartCrack; if( nSize == 0 ) { // Special case, this message is entirely cracked - return NULL pVal->vt = VT_NULL; pVal->intVal = 0; return S_OK; } // We've got some data to share SAFEARRAYBOUND sab = { nSize, 0 }; SAFEARRAY *pArray = ::SafeArrayCreate( VT_UI1, 1, &sab ); ::SafeArrayAllocData( pArray ); LPVOID pvData; ::SafeArrayAccessData( pArray, &pvData ); ::memcpy( pvData, m_pStartCrack, nSize ); ::SafeArrayUnaccessData( pArray ); pVal->vt = VT_ARRAY | VT_UI1; pVal->parray = pArray; return S_OK; } STDMETHODIMP cMessage::get_Member(VARIANT vName, VARIANT *pVal) { ::VariantInit (pVal); if( vName.vt == VT_BSTR ) { _bstr_t bstrName = vName; // Iterate over the fields and return our match - in this loop we'll do incremental // cracking, so it's a little messy. When we hit the end, we look to see if there are // more uncracked fields int nFieldCount = m_fields.size(); for( int nField = 0; nField != nFieldCount; nField += m_fields[ nField ].m_nOwns ) { if( m_fields[ nField ].m_pSchema->m_strName == bstrName ) { m_fields[ nField ].m_pSchema->getValue( this, m_fields.begin() + nField, pVal ); return S_OK; } } pVal->vt = VT_EMPTY; return S_OK; } // Attempt to convert it into an index HRESULT hRes = ::VariantChangeType( &vName, &vName, 0, VT_I4 ); if( FAILED( hRes ) ) { _ASSERTE( FALSE ); return hRes; } // Check if the value is in range long nIndex = vName.lVal; if( nIndex < 0 ) { _ASSERTE( nIndex >= 0 ); return E_INVALIDARG; } // Now, one problem is we aren't exactly sure how big the array is, so we have to walk // through the fields, skipping appropriately for( cFieldList::iterator i = m_fields.begin(); crackMember( i ); i += i->m_nOwns, -- nIndex ) { if( nIndex == 0 ) { // We've found the index - extract the value i->m_pSchema->getValue( this, i, pVal ); return S_OK; } } // The index was too high _ASSERTE( FALSE ); return E_INVALIDARG; } STDMETHODIMP cMessage::get_MemberName(long nIndex, BSTR *pVal) { _ASSERTE( nIndex >= 0 ); _ASSERTE( pVal != NULL ); USES_CONVERSION; for( cFieldList::iterator i = m_fields.begin(); crackMember( i ); i += i->m_nOwns, -- nIndex ) { if( nIndex == 0 ) { // We've found the index - extract the value *pVal = OLE2BSTR( i->m_pSchema->m_strName ); return S_OK; } } // The index was too high return E_INVALIDARG; } STDMETHODIMP cMessage::get_Count(long *pVal) { _ASSERTE( pVal != NULL ); // We must crack them all to get a count crackAll(); *pVal = 0; for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); i += i->m_nOwns, ++ ( *pVal ) ); return S_OK; }