/*++ Copyright (c) 1995-1996 Microsoft Corporation Module Name : odblogc.cpp Abstract: NCSA Logging Format implementation Author: Terence Kwan ( terryk ) 18-Sep-1996 Project: IIS Logging 3.0 --*/ #include "precomp.hxx" #include "odbcconn.hxx" #include #include "odblogc.hxx" #include /************************************************************ * Symbolic Constants and Data ************************************************************/ # define MAX_SQL_FIELD_NAMES_LEN ( 400) # define MAX_SQL_FIELD_VALUES_LEN ( 200) # define MAX_SQL_IDENTIFIER_QUOTE_CHAR ( 50) # define PSZ_UNKNOWN_FIELD_W _T("-") # define PSZ_UNKNOWN_FIELD_A _T("-") # define PSZ_GET_ERROR_FAILED_A _T("ODBC:GetLastError() Failed") # define LEN_PSZ_GET_ERROR_FAILED_A sizeof(PSZ_GET_ERROR_FAILED_A) # define PSZ_GET_ERROR_FAILED_W _T("ODBC:GetLastError() Failed") # define LEN_PSZ_GET_ERROR_FAILED_W sizeof(PSZ_GET_ERROR_FAILED_W) // // The template of SQL command has 3 arguments. // 1. table name // 2. field names // 3. field values // 1,2 and 3 are obained during the first wsprintf // static const CHAR sg_rgchSqlInsertCmdTemplate[] = _T("insert into %s ( %s) values ( %s)"); # define PSZ_SQL_INSERT_CMD_TEMPLATE ( sg_rgchSqlInsertCmdTemplate) # define LEN_PSZ_SQL_INSERT_CMD_TEMPLATE \ ( lstrlen( PSZ_SQL_INSERT_CMD_TEMPLATE)) // // Leave %ws so that we can print the service and server name when this // string is used to generate an SQL statement. // static const CHAR sg_rgchStdLogFieldValues[] = _T(" ?, ?, ?, '%s', '%s', ?, ?, ?, ?, ?, ?, ?, ?, ?"); # define PSZ_INTERNET_STD_LOG_FORMAT_FIELD_NAMES ( sg_rgchStdLogFieldNames) # define PSZ_INTERNET_STD_LOG_FORMAT_FIELD_VALUES ( sg_rgchStdLogFieldValues) // // AllFieldInfo() // Defines all the fields required for SQL logging of the information // to the database using ODBC interfaces. // C arrays are numbered from offset 0. // SQL columns are numbered from 1. // field index values start from 0 and we adjust it when we talk of SQL col. // FieldInfo( symbolic-name, field-name, // field-index/column-number, // field-C-type, field-Sql-type, // field-precision, field-max-size, field-cb-value) // # define StringField( symName, fldName, fldIndex, prec) \ FieldInfo( symName, fldName, fldIndex, SQL_C_CHAR, SQL_CHAR, \ (prec), (prec), SQL_NTS) # define NumericField( symName, fldName, fldIndex) \ FieldInfo( symName, fldName, fldIndex, SQL_C_LONG, SQL_INTEGER, \ 0, sizeof( DWORD), 0) # define TimeStampField( symName, fldName, fldIndex) \ FieldInfo( symName, fldName, fldIndex, SQL_C_TIMESTAMP, SQL_TIMESTAMP, \ 0, sizeof( TIMESTAMP_STRUCT), 0) // // fields that have constant value. we are interested in names of such fields. // they have negative field indexes. // These fields need not be generated as parameter markers. // ( Since they are invariants during lifetime of an INET_SQL_LOG oject) // Hence the field values will go into the command generated. // Left here as a documentation aid and field-generation purposes. // # define ConstantValueField( synName, fldName) \ FieldInfo( synName, fldName, -1, SQL_C_CHAR, SQL_CHAR, 0, 0, SQL_NTS) // // Ideally the "username" field should have MAX_USER_NAME_LEN as max size. // However, Access 7.0 limits varchar() size to be 255 (8 bits) :-( // So, we limit the size to be the least of the two ... // // FieldNames used are reserved. They are same as the names distributed // in the template log file. Do not change them at free will. // // # define AllFieldInfo() \ StringField( CLIENT_HOST, _T("ClientHost"), 0, 255) \ StringField( USER_NAME, _T("username"), 1, 255) \ TimeStampField( REQUEST_TIME, _T("LogTime"), 2) \ ConstantValueField( SERVICE_NAME, _T("service")) \ ConstantValueField( SERVER_NAME, _T("machine")) \ StringField( SERVER_IPADDR, _T("serverip"), 3, 50) \ NumericField( PROCESSING_TIME, _T("processingtime"), 4) \ NumericField( BYTES_RECVD, _T("bytesrecvd"), 5) \ NumericField( BYTES_SENT, _T("bytessent"), 6) \ NumericField( SERVICE_STATUS, _T("servicestatus"), 7) \ NumericField( WIN32_STATUS, _T("win32status"), 8) \ StringField( SERVICE_OPERATION, _T("operation"), 9, 255) \ StringField( SERVICE_TARGET, _T("target"), 10, 255) \ StringField( SERVICE_PARAMS, _T("parameters"), 11, 255) \ /************************************************************ * Type Definitions ************************************************************/ // // Define the FieldInfo macro to generate a list of enumerations for // the indexes to be used in the array of field parameters. // # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \ i ## symName = (index), enum LOGGING_VALID_COLUMNS { // fields run from 0 through iMaxFields AllFieldInfo() iMaxFields }; // enum LOGGING_VALID_COLUMNS # undef FieldInfo # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \ fi ## symName, enum LOGGING_FIELD_INDEXES { fiMinFields = -1, // fields run from 0 through fiMaxFields AllFieldInfo() fiMaxFields }; // enum LOGGING_FIELD_INDEXES # undef FieldInfo struct FIELD_INFO { int iParam; CHAR * pszName; SWORD paramType; SWORD cType; SWORD sqlType; UDWORD cbColPrecision; SWORD ibScale; SDWORD cbMaxSize; SDWORD cbValue; }; // struct FIELD_INFO // // Define the FieldInfo macro to generate a list of data to be generated // for entering the data values in an array for parameter information. // Note the terminating ',' used here. // # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \ { ((index) + 1), field, SQL_PARAM_INPUT, cType, sqlType, \ ( prec), 0, ( maxSz), ( cbVal) }, /* The array of Fields: sg_rgFields contain the field information for logging to SQL database for the log-record of the services. The values are defined using the macros FieldInfo() defined above. If there is any need to add/delete/modify the parameters bound, one should modify the above table "AllFieldInfo" macro. */ static FIELD_INFO sg_rgFields[] = { AllFieldInfo() // // The above macro after expansion terminates with a comma. // Add dummy entry to complete initialization of array. // { 0, _T("dummy"), SQL_PARAM_INPUT, 0, 0, 0, 0, 0, 0} }; # undef FieldInfo // // tick minute. // #define TICK_MINUTE (60 * 1000) /************************************************************ * Functions ************************************************************/ BOOL GenerateFieldNames(IN PODBC_CONNECTION poc, OUT CHAR * pchFieldNames, IN DWORD cchFieldNames); inline BOOL IsEmptyStr( IN LPCSTR psz) { return ( psz == NULL || *psz == _T('\0')); } BOOL CODBCLOG::PrepareStatement( VOID) /*++ This command forms the template SQL command used for insertion of log records. Then it prepares the SQL command( for later execution) using ODBC_CONNECTION::PrepareStatement(). It should always be called after locking the INET_SQL_LOG object. Arguments: None Returns: TRUE on success and FALSE if there is any failure. Note: The template for insertion is: insert into ( field names ...) values ( ?, ?, ...) ^^^^ Field values go here Field names are generated on a per logging format basis. --*/ { BOOL fReturn = FALSE; CHAR rgchFieldNames[ MAX_SQL_FIELD_NAMES_LEN]; CHAR rgchFieldValues[ MAX_SQL_FIELD_VALUES_LEN]; // // Obtain field names and field values ( template) for various log formats. // The order of field names should match the order of field values // generated by FormatLogInformation() for the format specified. // rgchFieldNames[ 0] = rgchFieldValues[ 0] = _T('\0'); DWORD cchFields; fReturn = GenerateFieldNames(m_poc, rgchFieldNames, MAX_SQL_FIELD_NAMES_LEN); if ( !fReturn) { //DBGPRINTF(( DBG_CONTEXT, // " Unable to generate field names. Error = %d\n", // GetLastError())); //break; return(fReturn); } cchFields = wsprintf( (CHAR *)rgchFieldValues, PSZ_INTERNET_STD_LOG_FORMAT_FIELD_VALUES, QueryServiceName(), QueryServerName()); fReturn = (fReturn && (cchFields < MAX_SQL_FIELD_VALUES_LEN)); //DBG_ASSERT( cchFields < MAX_SQL_FIELD_VALUES_LEN); fReturn = TRUE; if ( fReturn) { CHAR * pwszSqlCommand; DWORD cchReqd; // // The required number of chars include sql insert template command // and field names and table name. // cchReqd = (DWORD)( LEN_PSZ_SQL_INSERT_CMD_TEMPLATE + strlen( m_rgchTableName) + strlen( rgchFieldNames) + strlen( rgchFieldValues) + 20); pwszSqlCommand = ( CHAR *) LocalAlloc( LPTR, cchReqd * sizeof( CHAR)); m_poStmt = m_poc->AllocStatement(); fReturn = ( pwszSqlCommand != NULL ) && ( m_poStmt != NULL ); if ( fReturn ) { DWORD cchUsed; cchUsed = wsprintf( pwszSqlCommand, PSZ_SQL_INSERT_CMD_TEMPLATE, m_rgchTableName, rgchFieldNames, rgchFieldValues); //DBG_ASSERT( cchUsed < cchReqd); //IF_DEBUG(INETLOG) { // DBGPRINTF( ( DBG_CONTEXT, // " Sqlcommand generated is: %ws.\n", // pwszSqlCommand)); //} fReturn = ((cchUsed < cchReqd) && m_poStmt->PrepareStatement( pwszSqlCommand) ); LocalFree( pwszSqlCommand); // free allocated memory } } // valid field names and filed values. //IF_DEBUG( INETLOG) { // // DBGPRINTF( ( DBG_CONTEXT, // "%s::PrepareStatement() returns %d.", // QueryClassIdString(), fReturn)); //} return ( fReturn); } // INET_SQL_LOG::PrepareStatement() BOOL CODBCLOG::PrepareParameters( VOID) /*++ This function creates an array of ODBC_PARAMETER objects used for binding parameters to an already prepared statement. These ODBC_PARAMETER objects are then used for insertion of data values into the table specified, through ODBC. This function should always be called after locking the object. Arguments: None Returns: TRUE on success and FALSE if there is any failure. --*/ { BOOL fReturn = FALSE; PODBC_PARAMETER * prgParams = NULL; DWORD cParams = 0; DWORD nParamsSeen = 0; DWORD i; //DBG_ASSERT( m_poStmt != NULL && m_poStmt->IsValid() && // m_ppParams == NULL && m_cOdbcParams == 0); // // create sufficient space for iMaxFields pointers to ODBC objects. // prgParams = new PODBC_PARAMETER[ iMaxFields]; if ( prgParams != NULL) { fReturn = TRUE; // Assume everything will go on fine. cParams = iMaxFields; // // Create all the ODBC parameters. // Walk through all field indexes and pick up the valid columns // for( nParamsSeen = 0, i =0; i < fiMaxFields; i++) { if ( sg_rgFields[i].iParam > 0) { WORD colNum = (WORD ) sg_rgFields[i].iParam; prgParams[nParamsSeen] = new ODBC_PARAMETER(colNum, sg_rgFields[i].paramType, sg_rgFields[i].cType, sg_rgFields[i].sqlType, sg_rgFields[i].cbColPrecision ); if ( prgParams[ nParamsSeen] == NULL) { fReturn = FALSE; //DBGPRINTF( ( DBG_CONTEXT, // " Failed to create Parameter[%d] %s. \n", // i, sg_rgFields[i].pszName)); break; } nParamsSeen++; //DBG_ASSERT( nParamsSeen <= cParams); } } // for creation of all ODBC parameters if ( fReturn) { // // Set buffers for values to be received during insertions. // Bind parameters to the statement using ODBC_CONNECTION object. // //DBG_ASSERT( nParamsSeen == cParams); for( nParamsSeen = 0, i = 0; i < fiMaxFields; i++) { if ( sg_rgFields[i].iParam > 0) { if (!prgParams[nParamsSeen]-> SetValueBuffer(sg_rgFields[i].cbMaxSize, sg_rgFields[i].cbValue) || !m_poStmt->BindParameter( prgParams[nParamsSeen]) ) { fReturn = FALSE; //DBGPRINTF( ( DBG_CONTEXT, // " Binding Parameter [%u] (%08x) failed.\n", // nParamsSeen, prgParams[nParamsSeen])); //DBG_CODE( prgParams[ i]->Print()); break; } nParamsSeen++; } } // for } // if all ODBC params were created. if ( !fReturn) { // // Free up the space used, since we were unsuccessful. // for( i = 0; i < iMaxFields; i++) { if ( prgParams[ i] != NULL) { delete ( prgParams[ i]); prgParams[i] = NULL; } } // for delete [] prgParams; prgParams = NULL; cParams = 0; } } // if array for pointers to ODBC params created successfully // // Set the values. Either invalid or valid ,depending on failure/success // m_ppParams = prgParams; m_cOdbcParams = cParams; return ( fReturn); } // INET_SQL_LOG::PrepareParameters() BOOL GenerateFieldNames(IN PODBC_CONNECTION poc, OUT CHAR * pchFieldNames, IN DWORD cchFieldNames) /*++ This function generates the field names string from the names of the fields and identifier quote character for particular ODBC datasource in use. --*/ { BOOL fReturn = FALSE; CHAR rgchQuote[MAX_SQL_IDENTIFIER_QUOTE_CHAR]; DWORD cchQuote; //DBG_ASSERT( poc != NULL && pchFieldNames != NULL); pchFieldNames[0] = _T('\0'); // initialize // // Inquire and obtain the SQL identifier quote char for ODBC data source. // fReturn = poc->GetInfo(SQL_IDENTIFIER_QUOTE_CHAR, rgchQuote, MAX_SQL_IDENTIFIER_QUOTE_CHAR, &cchQuote); if ( !fReturn) { //DBG_CODE( { // STR strError; // // poc->GetLastErrorText( &strError); // // DBGPRINTF(( DBG_CONTEXT, // " ODBC_CONNECTION(%08x)::GetInfo(QuoteChar) failed." // " Error = %s\n", // poc, strError.QueryStr())); //}); } else { DWORD i; DWORD cchUsed = 0; DWORD cchLen; // // ODBC returns " " (blank) if there is no special character // for quoting identifiers. we need to identify and string the same. // This needs to be done, other wise ODBC will complain when // we give unwanted blanks before "," // if ( !strcmp( rgchQuote, _T(" "))) { rgchQuote[0] = _T('\0'); // string the quoted blank. cchQuote = 0; } else { cchQuote = (DWORD)strlen( rgchQuote); } // for each column, generate the quoted literal string and concatenate. for( i = 0; i < fiMaxFields; i++) { DWORD cchLen1 = (DWORD)strlen(sg_rgFields[i].pszName) + 2 * cchQuote + 2; if ( cchUsed + cchLen1 < cchFieldNames) { // space available for copying the data. cchLen = wsprintf( pchFieldNames + cchUsed, _T(" %s%s%s,"), rgchQuote, sg_rgFields[i].pszName, rgchQuote ); //DBG_ASSERT( cchLen == cchLen1); } cchUsed += cchLen1; } // for if ( cchUsed >= cchFieldNames) { // buffer exceeded. return error. SetLastError( ERROR_INSUFFICIENT_BUFFER); fReturn = FALSE; } else { // // Reset the last character from being a "," // cchLen = (cchUsed > 0) ? (cchUsed - 1) : 0; pchFieldNames[cchLen] = _T('\0'); fReturn = TRUE; } } //IF_DEBUG( INETLOG) { // // DBGPRINTF(( DBG_CONTEXT, // " GenerateFieldNames() returns %d." // " Fields = %S\n", // fReturn, pchFieldNames)); //} return (fReturn); } // GenerateFieldNames() CODBCLOG::CODBCLOG() { INITIALIZE_CRITICAL_SECTION( &m_csLock); m_poc = NULL; m_poStmt = NULL; m_ppParams = NULL; m_fEnableEventLog = true; m_TickResumeOpen = GetTickCount() + TICK_MINUTE; } ///////////////////////////////////////////////////////////////////////////// // CODBCLOG::~CODBCLOG - Destructor CODBCLOG::~CODBCLOG() { TerminateLog( ); DeleteCriticalSection( &m_csLock); } STDMETHODIMP CODBCLOG::InitializeLog( LPCSTR pszInstanceName, LPCSTR pszMetabasePath, CHAR* pMetabase ) { DWORD dwError = NO_ERROR; // load ODBC entry point LoadODBC(); // get the default parameters DWORD dwL = sizeof(m_rgchServerName); if ( !GetComputerName( m_rgchServerName, &dwL ) ) { m_rgchServerName[0] = '\0'; } strcpy( m_rgchServiceName, pszInstanceName); // // nntp (5x) logging sends the private IMDCOM interface while w3svc (6.0) // logging sends the public IMSAdminBase interface. Find out which it is // BOOL fIsPublicInterface = (_strnicmp(pszInstanceName, "w3svc", 5) == 0); if (fIsPublicInterface) { dwError = GetRegParametersFromPublicInterface(pszMetabasePath, pMetabase); } else { dwError = GetRegParameters(pszMetabasePath, pMetabase); } if (dwError == NO_ERROR ) { // open database if ( m_poc == NULL ) { Lock(); m_poc = new ODBC_CONNECTION(); if ( m_poc == NULL ) { dwError = ERROR_NOT_ENOUGH_MEMORY; } else { if ( !m_poc->Open( m_rgchDataSource, m_rgchUserName, m_rgchPassword ) || !PrepareStatement() || !PrepareParameters() ) { dwError = GetLastError(); } } Unlock(); } } return (dwError == NO_ERROR) ? S_OK : HRESULT_FROM_WIN32(dwError); } STDMETHODIMP CODBCLOG::LogInformation( IInetLogInformation * ppvDataObj ) { DWORD dwError = ERROR_SUCCESS; BOOL fReturn; SYSTEMTIME stNow; STR strClientHostName; STR strClientUserName; STR strServerIpAddress; STR strOperation; STR strTarget; STR strParameters; PCHAR pTmp; DWORD cbSize; DWORD dwBytesSent; DWORD dwBytesRecvd; DWORD dwProtocolStatus; DWORD dwWin32Status; DWORD dwTimeForProcessing; if (!( m_poc != NULL && m_poc->IsValid() && m_poStmt != NULL && m_poStmt->IsValid() && m_ppParams != NULL )) { // // Check if it is time to retry // DWORD tickCount = GetTickCount( ); if ( (tickCount < m_TickResumeOpen) || ((tickCount + TICK_MINUTE) < tickCount ) ) // The Tick counter is about to wrap. { return ERROR_INVALID_PARAMETER; } } dwBytesSent = ppvDataObj->GetBytesSent( ); dwBytesRecvd = ppvDataObj->GetBytesRecvd( ); dwTimeForProcessing = ppvDataObj->GetTimeForProcessing( ); dwWin32Status = ppvDataObj->GetWin32Status( ); dwProtocolStatus = ppvDataObj->GetProtocolStatus( ); pTmp = ppvDataObj->GetClientHostName( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strClientHostName.Copy(pTmp); pTmp = ppvDataObj->GetClientUserName( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strClientUserName.Copy(pTmp); pTmp = ppvDataObj->GetServerAddress( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strServerIpAddress.Copy(pTmp); pTmp = ppvDataObj->GetOperation( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strOperation.Copy(pTmp); pTmp = ppvDataObj->GetTarget( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strTarget.Copy(pTmp); pTmp = ppvDataObj->GetParameters( NULL, &cbSize); if ( cbSize == 0 ) { pTmp = ""; } strParameters.Copy(pTmp); LPCSTR pszUserName = strClientUserName.QueryStr(); LPCSTR pszOperation = strOperation.QueryStr(); LPCSTR pszTarget = strTarget.QueryStr(); LPCSTR pszParameters= strParameters.QueryStr(); LPCSTR pszServerAddr= strServerIpAddress.QueryStr(); SDWORD cbParameters; SDWORD cbTarget; cbParameters = (SDWORD)strlen( pszParameters ? pszParameters : "" ) + 1; cbTarget = (SDWORD)strlen( pszTarget ? pszTarget : "" ) + 1; // // Format the Date and Time for logging. // GetLocalTime( & stNow); if ( IsEmptyStr(pszUserName)) { pszUserName = QueryDefaultUserName();} if ( IsEmptyStr(pszOperation)) { pszOperation = PSZ_UNKNOWN_FIELD_A; } if ( IsEmptyStr(pszParameters)) { pszParameters= PSZ_UNKNOWN_FIELD_A; } if ( IsEmptyStr(pszTarget)) { pszTarget = PSZ_UNKNOWN_FIELD_A; } if ( IsEmptyStr(pszServerAddr)) { pszServerAddr= PSZ_UNKNOWN_FIELD_A; } Lock(); // // Reopen if necessary. // if (!( m_poc != NULL && m_poc->IsValid() && m_poStmt != NULL && m_poStmt->IsValid() && m_ppParams != NULL )) { TerminateLog(); m_TickResumeOpen = GetTickCount( ) + TICK_MINUTE; m_poc = new ODBC_CONNECTION(); if ( m_poc == NULL ) { dwError = ERROR_NOT_ENOUGH_MEMORY; } else { // // Try to open a new connection but don't log the failure in the eventlog // if ( !m_poc->Open( m_rgchDataSource, m_rgchUserName, m_rgchPassword, FALSE) || !PrepareStatement() || !PrepareParameters() ) { dwError = GetLastError(); if ( ERROR_SUCCESS == dwError) { // // Last Error wasn't set correctly // dwError = ERROR_GEN_FAILURE; } } } if ( ERROR_SUCCESS != dwError ) { Unlock(); return dwError; } } DBG_ASSERT(m_poc != NULL && m_poc->IsValid()); DBG_ASSERT(m_poStmt != NULL && m_poStmt->IsValid()); DBG_ASSERT(m_ppParams != NULL ); // // Truncate the operation, parameters and target fields // if ( strOperation.QueryCCH() >= (DWORD)m_ppParams[ iSERVICE_OPERATION]->QueryMaxCbValue() ) { strOperation.SetLen( (DWORD)m_ppParams[ iSERVICE_OPERATION]->QueryMaxCbValue() - 1 ); } if ( cbTarget > m_ppParams[ iSERVICE_TARGET]->QueryMaxCbValue() ) { strTarget.SetLen((DWORD)m_ppParams[ iSERVICE_TARGET]->QueryMaxCbValue()-1); } if ( cbParameters > m_ppParams[ iSERVICE_PARAMS]->QueryMaxCbValue() ) { strParameters.SetLen((DWORD)m_ppParams[ iSERVICE_PARAMS]->QueryMaxCbValue()-1); } // // Copy data values into parameter markers. // NYI: LARGE_INTEGERS are ignored. Only lowBytes used! // fReturn = ( m_ppParams[ iCLIENT_HOST]-> CopyValue( strClientHostName.QueryStr()) && m_ppParams[ iUSER_NAME]->CopyValue( pszUserName) && m_ppParams[ iREQUEST_TIME]->CopyValue( &stNow) && m_ppParams[ iSERVER_IPADDR]->CopyValue( pszServerAddr) && m_ppParams[ iPROCESSING_TIME]-> CopyValue( dwTimeForProcessing) && m_ppParams[ iBYTES_RECVD]-> CopyValue( dwBytesRecvd) && m_ppParams[ iBYTES_SENT]-> CopyValue( dwBytesSent) && m_ppParams[ iSERVICE_STATUS]-> CopyValue( dwProtocolStatus) && m_ppParams[ iWIN32_STATUS]->CopyValue( dwWin32Status) && m_ppParams[ iSERVICE_OPERATION]->CopyValue( pszOperation) && m_ppParams[ iSERVICE_TARGET]->CopyValue( pszTarget) && m_ppParams[ iSERVICE_PARAMS]->CopyValue( pszParameters) ); // // Execute insertion if parameters got copied properly. // if (fReturn) { fReturn = m_poStmt->ExecuteStatement(); } Unlock(); if ( !fReturn ) { // // Execution of SQL statement failed. // Pass the error as genuine failure, indicating ODBC failed // Obtain and store the error string in the proper return field // TerminateLog(); dwError = ERROR_GEN_FAILURE; if ( true == m_fEnableEventLog ) { // // We have not written an event log before. Indicate error // if ( g_eventLog != NULL ) { const CHAR* tmpString[1]; tmpString[0] = m_rgchDataSource; g_eventLog->LogEvent( LOG_EVENT_ODBC_LOGGING_ERROR, 1, tmpString, GetLastError() ); } Lock(); m_fEnableEventLog = false; m_TickResumeOpen = GetTickCount() + TICK_MINUTE; Unlock(); } } else { // // Success. Re-enable event logging // if (false == m_fEnableEventLog) { if ( g_eventLog != NULL ) { const CHAR* tmpString[1]; tmpString[0] = m_rgchDataSource; g_eventLog->LogEvent( LOG_EVENT_ODBC_LOGGING_RESUMED, 1, tmpString ); } m_fEnableEventLog = true; } } return(dwError); } STDMETHODIMP CODBCLOG::TerminateLog() { DWORD dwError = NO_ERROR; Lock(); if (m_poStmt != NULL ) { delete m_poStmt; m_poStmt = NULL; } if (m_poc!= NULL) { if (!m_poc->Close()) { dwError = GetLastError(); } delete m_poc; m_poc=NULL; } if (m_ppParams!=NULL) { DWORD i; for (i=0;iinetLogType = INET_LOG_TO_SQL; strcpy( pLogConfig->u.logSql.rgchDataSource, m_rgchDataSource); strcpy( pLogConfig->u.logSql.rgchTableName, m_rgchTableName); strcpy( pLogConfig->u.logSql.rgchUserName, m_rgchUserName); strcpy( pLogConfig->u.logSql.rgchPassword, m_rgchPassword); return(0L); } DWORD CODBCLOG::GetRegParameters( LPCSTR pszRegKey, LPVOID pvIMDCOM ) { DWORD err = NO_ERROR; MB mb( (IMDCOM*) pvIMDCOM ); DWORD cb; if ( !mb.Open("") ) { err = GetLastError(); goto Exit; } cb = sizeof(m_rgchDataSource); if ( !mb.GetString( pszRegKey, MD_LOGSQL_DATA_SOURCES, IIS_MD_UT_SERVER, m_rgchDataSource, &cb ) ) { strcpy(m_rgchDataSource,DEFAULT_LOG_SQL_DATASOURCE); } cb = sizeof(m_rgchTableName); if ( !mb.GetString( pszRegKey, MD_LOGSQL_TABLE_NAME, IIS_MD_UT_SERVER, m_rgchTableName, &cb ) ) { strcpy(m_rgchTableName,DEFAULT_LOG_SQL_TABLE); } cb = sizeof(m_rgchUserName); if ( !mb.GetString( pszRegKey, MD_LOGSQL_USER_NAME, IIS_MD_UT_SERVER, m_rgchUserName, &cb ) ) { strcpy(m_rgchUserName,DEFAULT_LOG_SQL_USER_NAME); } cb = sizeof(m_rgchPassword); if ( !mb.GetString( pszRegKey, MD_LOGSQL_PASSWORD, IIS_MD_UT_SERVER, m_rgchPassword, &cb, METADATA_INHERIT|METADATA_SECURE ) ) { strcpy(m_rgchPassword,DEFAULT_LOG_SQL_PASSWORD); } Exit: return err; } inline VOID WCopyToA( const WCHAR * wszSrc, CHAR * szDest ) { while( *wszSrc ) { *szDest++ = (CHAR) *wszSrc++; } *szDest = '\0'; } inline VOID ACopyToW( const CHAR * szSrc, WCHAR * wszDest ) { while( *szSrc ) { *wszDest++ = (WCHAR) *szSrc++; } *wszDest = L'\0'; } DWORD CODBCLOG::GetRegParametersFromPublicInterface(LPCSTR pszRegKey, LPVOID pMetabase) { // // What I really want is the version of MB in iisutil.dll. But, since I // cannot link to that and iisrtl.dll, I will just work with the // IMSAdminBase object directly // IMSAdminBase *pAdminBase = (IMSAdminBase *)pMetabase; METADATA_HANDLE hMBPath = NULL; DWORD cbRequired; METADATA_RECORD mdr; WCHAR pwszBuffer[MAX_PATH]; WCHAR pwszRegKey[MAX_PATH]; HRESULT hr; ACopyToW(pszRegKey, pwszRegKey); // MB::MB pAdminBase->AddRef(); // MB::Open hr = pAdminBase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"", METADATA_PERMISSION_READ, MB_TIMEOUT, &hMBPath); if (FAILED(hr)) { goto Exit; } // MB::GetString mdr.dwMDIdentifier = MD_LOGSQL_DATA_SOURCES; mdr.dwMDAttributes = METADATA_INHERIT; mdr.dwMDUserType = IIS_MD_UT_SERVER; mdr.dwMDDataType = STRING_METADATA; mdr.dwMDDataLen = sizeof pwszBuffer; mdr.pbMDData = (BYTE *)pwszBuffer; hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired); if (FAILED(hr) || wcslen(pwszBuffer) >= sizeof m_rgchDataSource) { strcpy(m_rgchDataSource, DEFAULT_LOG_SQL_DATASOURCE); } else { WCopyToA(pwszBuffer, m_rgchDataSource); } // MB::GetString mdr.dwMDIdentifier = MD_LOGSQL_TABLE_NAME; mdr.dwMDAttributes = METADATA_INHERIT; mdr.dwMDUserType = IIS_MD_UT_SERVER; mdr.dwMDDataType = STRING_METADATA; mdr.dwMDDataLen = sizeof pwszBuffer; mdr.pbMDData = (BYTE *)pwszBuffer; hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired); if (FAILED(hr) || wcslen(pwszBuffer) >= sizeof m_rgchTableName) { strcpy(m_rgchTableName, DEFAULT_LOG_SQL_TABLE); } else { WCopyToA(pwszBuffer, m_rgchTableName); } // MB::GetString mdr.dwMDIdentifier = MD_LOGSQL_USER_NAME; mdr.dwMDAttributes = METADATA_INHERIT; mdr.dwMDUserType = IIS_MD_UT_SERVER; mdr.dwMDDataType = STRING_METADATA; mdr.dwMDDataLen = sizeof pwszBuffer; mdr.pbMDData = (BYTE *)pwszBuffer; hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired); if (FAILED(hr) || wcslen(pwszBuffer) >= sizeof m_rgchUserName) { strcpy(m_rgchUserName, DEFAULT_LOG_SQL_USER_NAME); } else { WCopyToA(pwszBuffer, m_rgchUserName); } // MB::GetString mdr.dwMDIdentifier = MD_LOGSQL_PASSWORD; mdr.dwMDAttributes = METADATA_INHERIT|METADATA_SECURE; mdr.dwMDUserType = IIS_MD_UT_SERVER; mdr.dwMDDataType = STRING_METADATA; mdr.dwMDDataLen = sizeof pwszBuffer; mdr.pbMDData = (BYTE *)pwszBuffer; hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired); if (FAILED(hr) || wcslen(pwszBuffer) >= sizeof m_rgchPassword) { strcpy(m_rgchPassword, DEFAULT_LOG_SQL_PASSWORD); } else { WCopyToA(pwszBuffer, m_rgchPassword); } hr = S_OK; Exit: // MB::Close if (hMBPath) { pAdminBase->CloseKey(hMBPath); hMBPath = NULL; } // MB::~MB pAdminBase->Release(); if (FAILED(hr)) { return HRESULTTOWIN32(hr); } return NO_ERROR; } STDMETHODIMP CODBCLOG::QueryExtraLoggingFields( PDWORD pcbSize, TCHAR *pszFieldsList ) /*++ Routine Description: get configuration information Arguments: cbSize - size of the data structure log - log configuration data structure Return Value: --*/ { *pcbSize = 0; *pszFieldsList = '\0'; return(0L); }