Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1295 lines
35 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. odblogc.cpp
  5. Abstract:
  6. NCSA Logging Format implementation
  7. Author:
  8. Terence Kwan ( terryk ) 18-Sep-1996
  9. Project:
  10. IIS Logging 3.0
  11. --*/
  12. #include "precomp.hxx"
  13. #include "odbcconn.hxx"
  14. #include <ilogobj.hxx>
  15. #include "odblogc.hxx"
  16. #include <iadmw.h>
  17. /************************************************************
  18. * Symbolic Constants and Data
  19. ************************************************************/
  20. # define MAX_SQL_FIELD_NAMES_LEN ( 400)
  21. # define MAX_SQL_FIELD_VALUES_LEN ( 200)
  22. # define MAX_SQL_IDENTIFIER_QUOTE_CHAR ( 50)
  23. # define PSZ_UNKNOWN_FIELD_W _T("-")
  24. # define PSZ_UNKNOWN_FIELD_A _T("-")
  25. # define PSZ_GET_ERROR_FAILED_A _T("ODBC:GetLastError() Failed")
  26. # define LEN_PSZ_GET_ERROR_FAILED_A sizeof(PSZ_GET_ERROR_FAILED_A)
  27. # define PSZ_GET_ERROR_FAILED_W _T("ODBC:GetLastError() Failed")
  28. # define LEN_PSZ_GET_ERROR_FAILED_W sizeof(PSZ_GET_ERROR_FAILED_W)
  29. //
  30. // The template of SQL command has 3 arguments.
  31. // 1. table name
  32. // 2. field names
  33. // 3. field values
  34. // 1,2 and 3 are obained during the first wsprintf
  35. //
  36. static const CHAR sg_rgchSqlInsertCmdTemplate[] =
  37. _T("insert into %s ( %s) values ( %s)");
  38. # define PSZ_SQL_INSERT_CMD_TEMPLATE ( sg_rgchSqlInsertCmdTemplate)
  39. # define LEN_PSZ_SQL_INSERT_CMD_TEMPLATE \
  40. ( lstrlen( PSZ_SQL_INSERT_CMD_TEMPLATE))
  41. //
  42. // Leave %ws so that we can print the service and server name when this
  43. // string is used to generate an SQL statement.
  44. //
  45. static const CHAR sg_rgchStdLogFieldValues[] =
  46. _T(" ?, ?, ?, '%s', '%s', ?, ?, ?, ?, ?, ?, ?, ?, ?");
  47. # define PSZ_INTERNET_STD_LOG_FORMAT_FIELD_NAMES ( sg_rgchStdLogFieldNames)
  48. # define PSZ_INTERNET_STD_LOG_FORMAT_FIELD_VALUES ( sg_rgchStdLogFieldValues)
  49. //
  50. // AllFieldInfo()
  51. // Defines all the fields required for SQL logging of the information
  52. // to the database using ODBC interfaces.
  53. // C arrays are numbered from offset 0.
  54. // SQL columns are numbered from 1.
  55. // field index values start from 0 and we adjust it when we talk of SQL col.
  56. // FieldInfo( symbolic-name, field-name,
  57. // field-index/column-number,
  58. // field-C-type, field-Sql-type,
  59. // field-precision, field-max-size, field-cb-value)
  60. //
  61. # define StringField( symName, fldName, fldIndex, prec) \
  62. FieldInfo( symName, fldName, fldIndex, SQL_C_CHAR, SQL_CHAR, \
  63. (prec), (prec), SQL_NTS)
  64. # define NumericField( symName, fldName, fldIndex) \
  65. FieldInfo( symName, fldName, fldIndex, SQL_C_LONG, SQL_INTEGER, \
  66. 0, sizeof( DWORD), 0)
  67. # define TimeStampField( symName, fldName, fldIndex) \
  68. FieldInfo( symName, fldName, fldIndex, SQL_C_TIMESTAMP, SQL_TIMESTAMP, \
  69. 0, sizeof( TIMESTAMP_STRUCT), 0)
  70. //
  71. // fields that have constant value. we are interested in names of such fields.
  72. // they have negative field indexes.
  73. // These fields need not be generated as parameter markers.
  74. // ( Since they are invariants during lifetime of an INET_SQL_LOG oject)
  75. // Hence the field values will go into the command generated.
  76. // Left here as a documentation aid and field-generation purposes.
  77. //
  78. # define ConstantValueField( synName, fldName) \
  79. FieldInfo( synName, fldName, -1, SQL_C_CHAR, SQL_CHAR, 0, 0, SQL_NTS)
  80. //
  81. // Ideally the "username" field should have MAX_USER_NAME_LEN as max size.
  82. // However, Access 7.0 limits varchar() size to be 255 (8 bits) :-(
  83. // So, we limit the size to be the least of the two ...
  84. //
  85. // FieldNames used are reserved. They are same as the names distributed
  86. // in the template log file. Do not change them at free will.
  87. //
  88. //
  89. # define AllFieldInfo() \
  90. StringField( CLIENT_HOST, _T("ClientHost"), 0, 255) \
  91. StringField( USER_NAME, _T("username"), 1, 255) \
  92. TimeStampField( REQUEST_TIME, _T("LogTime"), 2) \
  93. ConstantValueField( SERVICE_NAME, _T("service")) \
  94. ConstantValueField( SERVER_NAME, _T("machine")) \
  95. StringField( SERVER_IPADDR, _T("serverip"), 3, 50) \
  96. NumericField( PROCESSING_TIME, _T("processingtime"), 4) \
  97. NumericField( BYTES_RECVD, _T("bytesrecvd"), 5) \
  98. NumericField( BYTES_SENT, _T("bytessent"), 6) \
  99. NumericField( SERVICE_STATUS, _T("servicestatus"), 7) \
  100. NumericField( WIN32_STATUS, _T("win32status"), 8) \
  101. StringField( SERVICE_OPERATION, _T("operation"), 9, 255) \
  102. StringField( SERVICE_TARGET, _T("target"), 10, 255) \
  103. StringField( SERVICE_PARAMS, _T("parameters"), 11, 255) \
  104. /************************************************************
  105. * Type Definitions
  106. ************************************************************/
  107. //
  108. // Define the FieldInfo macro to generate a list of enumerations for
  109. // the indexes to be used in the array of field parameters.
  110. //
  111. # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \
  112. i ## symName = (index),
  113. enum LOGGING_VALID_COLUMNS {
  114. // fields run from 0 through iMaxFields
  115. AllFieldInfo()
  116. iMaxFields
  117. }; // enum LOGGING_VALID_COLUMNS
  118. # undef FieldInfo
  119. # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \
  120. fi ## symName,
  121. enum LOGGING_FIELD_INDEXES {
  122. fiMinFields = -1,
  123. // fields run from 0 through fiMaxFields
  124. AllFieldInfo()
  125. fiMaxFields
  126. }; // enum LOGGING_FIELD_INDEXES
  127. # undef FieldInfo
  128. struct FIELD_INFO {
  129. int iParam;
  130. CHAR * pszName;
  131. SWORD paramType;
  132. SWORD cType;
  133. SWORD sqlType;
  134. UDWORD cbColPrecision;
  135. SWORD ibScale;
  136. SDWORD cbMaxSize;
  137. SDWORD cbValue;
  138. }; // struct FIELD_INFO
  139. //
  140. // Define the FieldInfo macro to generate a list of data to be generated
  141. // for entering the data values in an array for parameter information.
  142. // Note the terminating ',' used here.
  143. //
  144. # define FieldInfo(symName, field, index, cType, sqlType, prec, maxSz, cbVal) \
  145. { ((index) + 1), field, SQL_PARAM_INPUT, cType, sqlType, \
  146. ( prec), 0, ( maxSz), ( cbVal) },
  147. /*
  148. The array of Fields: sg_rgFields contain the field information
  149. for logging to SQL database for the log-record of
  150. the services. The values are defined using the macros FieldInfo()
  151. defined above.
  152. If there is any need to add/delete/modify the parameters bound,
  153. one should modify the above table "AllFieldInfo" macro.
  154. */
  155. static FIELD_INFO sg_rgFields[] = {
  156. AllFieldInfo()
  157. //
  158. // The above macro after expansion terminates with a comma.
  159. // Add dummy entry to complete initialization of array.
  160. //
  161. { 0, _T("dummy"), SQL_PARAM_INPUT, 0, 0, 0, 0, 0, 0}
  162. };
  163. # undef FieldInfo
  164. //
  165. // tick minute.
  166. //
  167. #define TICK_MINUTE (60 * 1000)
  168. /************************************************************
  169. * Functions
  170. ************************************************************/
  171. BOOL
  172. GenerateFieldNames(IN PODBC_CONNECTION poc,
  173. OUT CHAR * pchFieldNames,
  174. IN DWORD cchFieldNames);
  175. inline BOOL
  176. IsEmptyStr( IN LPCSTR psz)
  177. { return ( psz == NULL || *psz == _T('\0')); }
  178. BOOL
  179. CODBCLOG::PrepareStatement( VOID)
  180. /*++
  181. This command forms the template SQL command used for insertion
  182. of log records. Then it prepares the SQL command( for later execution)
  183. using ODBC_CONNECTION::PrepareStatement().
  184. It should always be called after locking the INET_SQL_LOG object.
  185. Arguments:
  186. None
  187. Returns:
  188. TRUE on success and FALSE if there is any failure.
  189. Note:
  190. The template for insertion is:
  191. insert into <table name> ( field names ...) values ( ?, ?, ...)
  192. ^^^^
  193. Field values go here
  194. Field names are generated on a per logging format basis.
  195. --*/
  196. {
  197. BOOL fReturn = FALSE;
  198. CHAR rgchFieldNames[ MAX_SQL_FIELD_NAMES_LEN];
  199. CHAR rgchFieldValues[ MAX_SQL_FIELD_VALUES_LEN];
  200. //
  201. // Obtain field names and field values ( template) for various log formats.
  202. // The order of field names should match the order of field values
  203. // generated by FormatLogInformation() for the format specified.
  204. //
  205. rgchFieldNames[ 0] = rgchFieldValues[ 0] = _T('\0');
  206. DWORD cchFields;
  207. fReturn = GenerateFieldNames(m_poc,
  208. rgchFieldNames,
  209. MAX_SQL_FIELD_NAMES_LEN);
  210. if ( !fReturn) {
  211. //DBGPRINTF(( DBG_CONTEXT,
  212. // " Unable to generate field names. Error = %d\n",
  213. // GetLastError()));
  214. //break;
  215. return(fReturn);
  216. }
  217. cchFields = wsprintf( (CHAR *)rgchFieldValues,
  218. PSZ_INTERNET_STD_LOG_FORMAT_FIELD_VALUES,
  219. QueryServiceName(),
  220. QueryServerName());
  221. fReturn = (fReturn && (cchFields < MAX_SQL_FIELD_VALUES_LEN));
  222. //DBG_ASSERT( cchFields < MAX_SQL_FIELD_VALUES_LEN);
  223. fReturn = TRUE;
  224. if ( fReturn) {
  225. CHAR * pwszSqlCommand;
  226. DWORD cchReqd;
  227. //
  228. // The required number of chars include sql insert template command
  229. // and field names and table name.
  230. //
  231. cchReqd = (DWORD)( LEN_PSZ_SQL_INSERT_CMD_TEMPLATE +
  232. strlen( m_rgchTableName) +
  233. strlen( rgchFieldNames) +
  234. strlen( rgchFieldValues) + 20);
  235. pwszSqlCommand = ( CHAR *) LocalAlloc( LPTR, cchReqd * sizeof( CHAR));
  236. m_poStmt = m_poc->AllocStatement();
  237. fReturn = ( pwszSqlCommand != NULL ) && ( m_poStmt != NULL );
  238. if ( fReturn ) {
  239. DWORD cchUsed;
  240. cchUsed = wsprintf( pwszSqlCommand,
  241. PSZ_SQL_INSERT_CMD_TEMPLATE,
  242. m_rgchTableName,
  243. rgchFieldNames,
  244. rgchFieldValues);
  245. //DBG_ASSERT( cchUsed < cchReqd);
  246. //IF_DEBUG(INETLOG) {
  247. // DBGPRINTF( ( DBG_CONTEXT,
  248. // " Sqlcommand generated is: %ws.\n",
  249. // pwszSqlCommand));
  250. //}
  251. fReturn = ((cchUsed < cchReqd) &&
  252. m_poStmt->PrepareStatement( pwszSqlCommand)
  253. );
  254. LocalFree( pwszSqlCommand); // free allocated memory
  255. }
  256. } // valid field names and filed values.
  257. //IF_DEBUG( INETLOG) {
  258. //
  259. // DBGPRINTF( ( DBG_CONTEXT,
  260. // "%s::PrepareStatement() returns %d.",
  261. // QueryClassIdString(), fReturn));
  262. //}
  263. return ( fReturn);
  264. } // INET_SQL_LOG::PrepareStatement()
  265. BOOL
  266. CODBCLOG::PrepareParameters( VOID)
  267. /*++
  268. This function creates an array of ODBC_PARAMETER objects used for binding
  269. parameters to an already prepared statement. These ODBC_PARAMETER objects
  270. are then used for insertion of data values into the table specified,
  271. through ODBC.
  272. This function should always be called after locking the object.
  273. Arguments:
  274. None
  275. Returns:
  276. TRUE on success and FALSE if there is any failure.
  277. --*/
  278. {
  279. BOOL fReturn = FALSE;
  280. PODBC_PARAMETER * prgParams = NULL;
  281. DWORD cParams = 0;
  282. DWORD nParamsSeen = 0;
  283. DWORD i;
  284. //DBG_ASSERT( m_poStmt != NULL && m_poStmt->IsValid() &&
  285. // m_ppParams == NULL && m_cOdbcParams == 0);
  286. //
  287. // create sufficient space for iMaxFields pointers to ODBC objects.
  288. //
  289. prgParams = new PODBC_PARAMETER[ iMaxFields];
  290. if ( prgParams != NULL) {
  291. fReturn = TRUE; // Assume everything will go on fine.
  292. cParams = iMaxFields;
  293. //
  294. // Create all the ODBC parameters.
  295. // Walk through all field indexes and pick up the valid columns
  296. //
  297. for( nParamsSeen = 0, i =0; i < fiMaxFields; i++) {
  298. if ( sg_rgFields[i].iParam > 0) {
  299. WORD colNum = (WORD ) sg_rgFields[i].iParam;
  300. prgParams[nParamsSeen] =
  301. new ODBC_PARAMETER(colNum,
  302. sg_rgFields[i].paramType,
  303. sg_rgFields[i].cType,
  304. sg_rgFields[i].sqlType,
  305. sg_rgFields[i].cbColPrecision
  306. );
  307. if ( prgParams[ nParamsSeen] == NULL) {
  308. fReturn = FALSE;
  309. //DBGPRINTF( ( DBG_CONTEXT,
  310. // " Failed to create Parameter[%d] %s. \n",
  311. // i, sg_rgFields[i].pszName));
  312. break;
  313. }
  314. nParamsSeen++;
  315. //DBG_ASSERT( nParamsSeen <= cParams);
  316. }
  317. } // for creation of all ODBC parameters
  318. if ( fReturn) {
  319. //
  320. // Set buffers for values to be received during insertions.
  321. // Bind parameters to the statement using ODBC_CONNECTION object.
  322. //
  323. //DBG_ASSERT( nParamsSeen == cParams);
  324. for( nParamsSeen = 0, i = 0; i < fiMaxFields; i++) {
  325. if ( sg_rgFields[i].iParam > 0) {
  326. if (!prgParams[nParamsSeen]->
  327. SetValueBuffer(sg_rgFields[i].cbMaxSize,
  328. sg_rgFields[i].cbValue) ||
  329. !m_poStmt->BindParameter( prgParams[nParamsSeen])
  330. ) {
  331. fReturn = FALSE;
  332. //DBGPRINTF( ( DBG_CONTEXT,
  333. // " Binding Parameter [%u] (%08x) failed.\n",
  334. // nParamsSeen, prgParams[nParamsSeen]));
  335. //DBG_CODE( prgParams[ i]->Print());
  336. break;
  337. }
  338. nParamsSeen++;
  339. }
  340. } // for
  341. } // if all ODBC params were created.
  342. if ( !fReturn) {
  343. //
  344. // Free up the space used, since we were unsuccessful.
  345. //
  346. for( i = 0; i < iMaxFields; i++) {
  347. if ( prgParams[ i] != NULL) {
  348. delete ( prgParams[ i]);
  349. prgParams[i] = NULL;
  350. }
  351. } // for
  352. delete [] prgParams;
  353. prgParams = NULL;
  354. cParams = 0;
  355. }
  356. } // if array for pointers to ODBC params created successfully
  357. //
  358. // Set the values. Either invalid or valid ,depending on failure/success
  359. //
  360. m_ppParams = prgParams;
  361. m_cOdbcParams = cParams;
  362. return ( fReturn);
  363. } // INET_SQL_LOG::PrepareParameters()
  364. BOOL
  365. GenerateFieldNames(IN PODBC_CONNECTION poc,
  366. OUT CHAR * pchFieldNames,
  367. IN DWORD cchFieldNames)
  368. /*++
  369. This function generates the field names string from the names of the fields
  370. and identifier quote character for particular ODBC datasource in use.
  371. --*/
  372. {
  373. BOOL fReturn = FALSE;
  374. CHAR rgchQuote[MAX_SQL_IDENTIFIER_QUOTE_CHAR];
  375. DWORD cchQuote;
  376. //DBG_ASSERT( poc != NULL && pchFieldNames != NULL);
  377. pchFieldNames[0] = _T('\0'); // initialize
  378. //
  379. // Inquire and obtain the SQL identifier quote char for ODBC data source.
  380. //
  381. fReturn = poc->GetInfo(SQL_IDENTIFIER_QUOTE_CHAR,
  382. rgchQuote, MAX_SQL_IDENTIFIER_QUOTE_CHAR,
  383. &cchQuote);
  384. if ( !fReturn) {
  385. //DBG_CODE( {
  386. // STR strError;
  387. //
  388. // poc->GetLastErrorText( &strError);
  389. //
  390. // DBGPRINTF(( DBG_CONTEXT,
  391. // " ODBC_CONNECTION(%08x)::GetInfo(QuoteChar) failed."
  392. // " Error = %s\n",
  393. // poc, strError.QueryStr()));
  394. //});
  395. } else {
  396. DWORD i;
  397. DWORD cchUsed = 0;
  398. DWORD cchLen;
  399. //
  400. // ODBC returns " " (blank) if there is no special character
  401. // for quoting identifiers. we need to identify and string the same.
  402. // This needs to be done, other wise ODBC will complain when
  403. // we give unwanted blanks before ","
  404. //
  405. if ( !strcmp( rgchQuote, _T(" "))) {
  406. rgchQuote[0] = _T('\0'); // string the quoted blank.
  407. cchQuote = 0;
  408. } else {
  409. cchQuote = (DWORD)strlen( rgchQuote);
  410. }
  411. // for each column, generate the quoted literal string and concatenate.
  412. for( i = 0; i < fiMaxFields; i++) {
  413. DWORD cchLen1 =
  414. (DWORD)strlen(sg_rgFields[i].pszName) + 2 * cchQuote + 2;
  415. if ( cchUsed + cchLen1 < cchFieldNames) {
  416. // space available for copying the data.
  417. cchLen = wsprintf( pchFieldNames + cchUsed,
  418. _T(" %s%s%s,"),
  419. rgchQuote,
  420. sg_rgFields[i].pszName,
  421. rgchQuote
  422. );
  423. //DBG_ASSERT( cchLen == cchLen1);
  424. }
  425. cchUsed += cchLen1;
  426. } // for
  427. if ( cchUsed >= cchFieldNames) {
  428. // buffer exceeded. return error.
  429. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  430. fReturn = FALSE;
  431. } else {
  432. //
  433. // Reset the last character from being a ","
  434. //
  435. cchLen = (cchUsed > 0) ? (cchUsed - 1) : 0;
  436. pchFieldNames[cchLen] = _T('\0');
  437. fReturn = TRUE;
  438. }
  439. }
  440. //IF_DEBUG( INETLOG) {
  441. //
  442. // DBGPRINTF(( DBG_CONTEXT,
  443. // " GenerateFieldNames() returns %d."
  444. // " Fields = %S\n",
  445. // fReturn, pchFieldNames));
  446. //}
  447. return (fReturn);
  448. } // GenerateFieldNames()
  449. CODBCLOG::CODBCLOG()
  450. {
  451. INITIALIZE_CRITICAL_SECTION( &m_csLock);
  452. m_poc = NULL;
  453. m_poStmt = NULL;
  454. m_ppParams = NULL;
  455. m_fEnableEventLog = true;
  456. m_TickResumeOpen = GetTickCount() + TICK_MINUTE;
  457. }
  458. /////////////////////////////////////////////////////////////////////////////
  459. // CODBCLOG::~CODBCLOG - Destructor
  460. CODBCLOG::~CODBCLOG()
  461. {
  462. TerminateLog( );
  463. DeleteCriticalSection( &m_csLock);
  464. }
  465. STDMETHODIMP
  466. CODBCLOG::InitializeLog(
  467. LPCSTR pszInstanceName,
  468. LPCSTR pszMetabasePath,
  469. CHAR* pMetabase )
  470. {
  471. DWORD dwError = NO_ERROR;
  472. // load ODBC entry point
  473. LoadODBC();
  474. // get the default parameters
  475. DWORD dwL = sizeof(m_rgchServerName);
  476. if ( !GetComputerName( m_rgchServerName, &dwL ) )
  477. {
  478. m_rgchServerName[0] = '\0';
  479. }
  480. strcpy( m_rgchServiceName, pszInstanceName);
  481. //
  482. // nntp (5x) logging sends the private IMDCOM interface while w3svc (6.0)
  483. // logging sends the public IMSAdminBase interface. Find out which it is
  484. //
  485. BOOL fIsPublicInterface = (_strnicmp(pszInstanceName, "w3svc", 5) == 0);
  486. if (fIsPublicInterface)
  487. {
  488. dwError = GetRegParametersFromPublicInterface(pszMetabasePath,
  489. pMetabase);
  490. }
  491. else
  492. {
  493. dwError = GetRegParameters(pszMetabasePath, pMetabase);
  494. }
  495. if (dwError == NO_ERROR )
  496. {
  497. // open database
  498. if ( m_poc == NULL )
  499. {
  500. Lock();
  501. m_poc = new ODBC_CONNECTION();
  502. if ( m_poc == NULL )
  503. {
  504. dwError = ERROR_NOT_ENOUGH_MEMORY;
  505. } else
  506. {
  507. if ( !m_poc->Open( m_rgchDataSource, m_rgchUserName, m_rgchPassword ) ||
  508. !PrepareStatement() ||
  509. !PrepareParameters() )
  510. {
  511. dwError = GetLastError();
  512. }
  513. }
  514. Unlock();
  515. }
  516. }
  517. return (dwError == NO_ERROR) ? S_OK : HRESULT_FROM_WIN32(dwError);
  518. }
  519. STDMETHODIMP
  520. CODBCLOG::LogInformation(
  521. IInetLogInformation * ppvDataObj
  522. )
  523. {
  524. DWORD dwError = ERROR_SUCCESS;
  525. BOOL fReturn;
  526. SYSTEMTIME stNow;
  527. STR strClientHostName;
  528. STR strClientUserName;
  529. STR strServerIpAddress;
  530. STR strOperation;
  531. STR strTarget;
  532. STR strParameters;
  533. PCHAR pTmp;
  534. DWORD cbSize;
  535. DWORD dwBytesSent;
  536. DWORD dwBytesRecvd;
  537. DWORD dwProtocolStatus;
  538. DWORD dwWin32Status;
  539. DWORD dwTimeForProcessing;
  540. if (!(
  541. m_poc != NULL && m_poc->IsValid() &&
  542. m_poStmt != NULL && m_poStmt->IsValid() &&
  543. m_ppParams != NULL
  544. ))
  545. {
  546. //
  547. // Check if it is time to retry
  548. //
  549. DWORD tickCount = GetTickCount( );
  550. if ( (tickCount < m_TickResumeOpen) ||
  551. ((tickCount + TICK_MINUTE) < tickCount ) ) // The Tick counter is about to wrap.
  552. {
  553. return ERROR_INVALID_PARAMETER;
  554. }
  555. }
  556. dwBytesSent = ppvDataObj->GetBytesSent( );
  557. dwBytesRecvd = ppvDataObj->GetBytesRecvd( );
  558. dwTimeForProcessing = ppvDataObj->GetTimeForProcessing( );
  559. dwWin32Status = ppvDataObj->GetWin32Status( );
  560. dwProtocolStatus = ppvDataObj->GetProtocolStatus( );
  561. pTmp = ppvDataObj->GetClientHostName( NULL, &cbSize);
  562. if ( cbSize == 0 ) {
  563. pTmp = "";
  564. }
  565. strClientHostName.Copy(pTmp);
  566. pTmp = ppvDataObj->GetClientUserName( NULL, &cbSize);
  567. if ( cbSize == 0 ) {
  568. pTmp = "";
  569. }
  570. strClientUserName.Copy(pTmp);
  571. pTmp = ppvDataObj->GetServerAddress( NULL, &cbSize);
  572. if ( cbSize == 0 ) {
  573. pTmp = "";
  574. }
  575. strServerIpAddress.Copy(pTmp);
  576. pTmp = ppvDataObj->GetOperation( NULL, &cbSize);
  577. if ( cbSize == 0 ) {
  578. pTmp = "";
  579. }
  580. strOperation.Copy(pTmp);
  581. pTmp = ppvDataObj->GetTarget( NULL, &cbSize);
  582. if ( cbSize == 0 ) {
  583. pTmp = "";
  584. }
  585. strTarget.Copy(pTmp);
  586. pTmp = ppvDataObj->GetParameters( NULL, &cbSize);
  587. if ( cbSize == 0 ) {
  588. pTmp = "";
  589. }
  590. strParameters.Copy(pTmp);
  591. LPCSTR pszUserName = strClientUserName.QueryStr();
  592. LPCSTR pszOperation = strOperation.QueryStr();
  593. LPCSTR pszTarget = strTarget.QueryStr();
  594. LPCSTR pszParameters= strParameters.QueryStr();
  595. LPCSTR pszServerAddr= strServerIpAddress.QueryStr();
  596. SDWORD cbParameters;
  597. SDWORD cbTarget;
  598. cbParameters = (SDWORD)strlen( pszParameters ? pszParameters : "" ) + 1;
  599. cbTarget = (SDWORD)strlen( pszTarget ? pszTarget : "" ) + 1;
  600. //
  601. // Format the Date and Time for logging.
  602. //
  603. GetLocalTime( & stNow);
  604. if ( IsEmptyStr(pszUserName)) { pszUserName = QueryDefaultUserName();}
  605. if ( IsEmptyStr(pszOperation)) { pszOperation = PSZ_UNKNOWN_FIELD_A; }
  606. if ( IsEmptyStr(pszParameters)) { pszParameters= PSZ_UNKNOWN_FIELD_A; }
  607. if ( IsEmptyStr(pszTarget)) { pszTarget = PSZ_UNKNOWN_FIELD_A; }
  608. if ( IsEmptyStr(pszServerAddr)) { pszServerAddr= PSZ_UNKNOWN_FIELD_A; }
  609. Lock();
  610. //
  611. // Reopen if necessary.
  612. //
  613. if (!(
  614. m_poc != NULL && m_poc->IsValid() &&
  615. m_poStmt != NULL && m_poStmt->IsValid() &&
  616. m_ppParams != NULL
  617. ))
  618. {
  619. TerminateLog();
  620. m_TickResumeOpen = GetTickCount( ) + TICK_MINUTE;
  621. m_poc = new ODBC_CONNECTION();
  622. if ( m_poc == NULL )
  623. {
  624. dwError = ERROR_NOT_ENOUGH_MEMORY;
  625. }
  626. else
  627. {
  628. //
  629. // Try to open a new connection but don't log the failure in the eventlog
  630. //
  631. if ( !m_poc->Open( m_rgchDataSource, m_rgchUserName, m_rgchPassword, FALSE) ||
  632. !PrepareStatement() ||
  633. !PrepareParameters() )
  634. {
  635. dwError = GetLastError();
  636. if ( ERROR_SUCCESS == dwError)
  637. {
  638. //
  639. // Last Error wasn't set correctly
  640. //
  641. dwError = ERROR_GEN_FAILURE;
  642. }
  643. }
  644. }
  645. if ( ERROR_SUCCESS != dwError )
  646. {
  647. Unlock();
  648. return dwError;
  649. }
  650. }
  651. DBG_ASSERT(m_poc != NULL && m_poc->IsValid());
  652. DBG_ASSERT(m_poStmt != NULL && m_poStmt->IsValid());
  653. DBG_ASSERT(m_ppParams != NULL );
  654. //
  655. // Truncate the operation, parameters and target fields
  656. //
  657. if ( strOperation.QueryCCH() >= (DWORD)m_ppParams[ iSERVICE_OPERATION]->QueryMaxCbValue() )
  658. {
  659. strOperation.SetLen( (DWORD)m_ppParams[ iSERVICE_OPERATION]->QueryMaxCbValue() - 1 );
  660. }
  661. if ( cbTarget > m_ppParams[ iSERVICE_TARGET]->QueryMaxCbValue() )
  662. {
  663. strTarget.SetLen((DWORD)m_ppParams[ iSERVICE_TARGET]->QueryMaxCbValue()-1);
  664. }
  665. if ( cbParameters > m_ppParams[ iSERVICE_PARAMS]->QueryMaxCbValue() )
  666. {
  667. strParameters.SetLen((DWORD)m_ppParams[ iSERVICE_PARAMS]->QueryMaxCbValue()-1);
  668. }
  669. //
  670. // Copy data values into parameter markers.
  671. // NYI: LARGE_INTEGERS are ignored. Only lowBytes used!
  672. //
  673. fReturn =
  674. (
  675. m_ppParams[ iCLIENT_HOST]->
  676. CopyValue( strClientHostName.QueryStr()) &&
  677. m_ppParams[ iUSER_NAME]->CopyValue( pszUserName) &&
  678. m_ppParams[ iREQUEST_TIME]->CopyValue( &stNow) &&
  679. m_ppParams[ iSERVER_IPADDR]->CopyValue( pszServerAddr) &&
  680. m_ppParams[ iPROCESSING_TIME]->
  681. CopyValue( dwTimeForProcessing) &&
  682. m_ppParams[ iBYTES_RECVD]->
  683. CopyValue( dwBytesRecvd) &&
  684. m_ppParams[ iBYTES_SENT]->
  685. CopyValue( dwBytesSent) &&
  686. m_ppParams[ iSERVICE_STATUS]->
  687. CopyValue( dwProtocolStatus) &&
  688. m_ppParams[ iWIN32_STATUS]->CopyValue( dwWin32Status) &&
  689. m_ppParams[ iSERVICE_OPERATION]->CopyValue( pszOperation) &&
  690. m_ppParams[ iSERVICE_TARGET]->CopyValue( pszTarget) &&
  691. m_ppParams[ iSERVICE_PARAMS]->CopyValue( pszParameters)
  692. );
  693. //
  694. // Execute insertion if parameters got copied properly.
  695. //
  696. if (fReturn)
  697. {
  698. fReturn = m_poStmt->ExecuteStatement();
  699. }
  700. Unlock();
  701. if ( !fReturn )
  702. {
  703. //
  704. // Execution of SQL statement failed.
  705. // Pass the error as genuine failure, indicating ODBC failed
  706. // Obtain and store the error string in the proper return field
  707. //
  708. TerminateLog();
  709. dwError = ERROR_GEN_FAILURE;
  710. if ( true == m_fEnableEventLog )
  711. {
  712. //
  713. // We have not written an event log before. Indicate error
  714. //
  715. if ( g_eventLog != NULL )
  716. {
  717. const CHAR* tmpString[1];
  718. tmpString[0] = m_rgchDataSource;
  719. g_eventLog->LogEvent(
  720. LOG_EVENT_ODBC_LOGGING_ERROR,
  721. 1,
  722. tmpString,
  723. GetLastError()
  724. );
  725. }
  726. Lock();
  727. m_fEnableEventLog = false;
  728. m_TickResumeOpen = GetTickCount() + TICK_MINUTE;
  729. Unlock();
  730. }
  731. }
  732. else
  733. {
  734. //
  735. // Success. Re-enable event logging
  736. //
  737. if (false == m_fEnableEventLog)
  738. {
  739. if ( g_eventLog != NULL )
  740. {
  741. const CHAR* tmpString[1];
  742. tmpString[0] = m_rgchDataSource;
  743. g_eventLog->LogEvent(
  744. LOG_EVENT_ODBC_LOGGING_RESUMED,
  745. 1,
  746. tmpString
  747. );
  748. }
  749. m_fEnableEventLog = true;
  750. }
  751. }
  752. return(dwError);
  753. }
  754. STDMETHODIMP
  755. CODBCLOG::TerminateLog()
  756. {
  757. DWORD dwError = NO_ERROR;
  758. Lock();
  759. if (m_poStmt != NULL )
  760. {
  761. delete m_poStmt;
  762. m_poStmt = NULL;
  763. }
  764. if (m_poc!= NULL)
  765. {
  766. if (!m_poc->Close())
  767. {
  768. dwError = GetLastError();
  769. }
  770. delete m_poc;
  771. m_poc=NULL;
  772. }
  773. if (m_ppParams!=NULL)
  774. {
  775. DWORD i;
  776. for (i=0;i<m_cOdbcParams;i++)
  777. {
  778. if (m_ppParams[i]!=NULL)
  779. {
  780. delete m_ppParams[i];
  781. m_ppParams[i]=NULL;
  782. }
  783. }
  784. delete []m_ppParams;
  785. m_ppParams = NULL;
  786. m_cOdbcParams=0;
  787. }
  788. Unlock();
  789. return(dwError);
  790. }
  791. STDMETHODIMP
  792. CODBCLOG::SetConfig(
  793. DWORD,
  794. BYTE *)
  795. {
  796. return(0L);
  797. }
  798. STDMETHODIMP
  799. CODBCLOG::GetConfig(
  800. DWORD,
  801. BYTE * log)
  802. {
  803. PINETLOG_CONFIGURATIONA pLogConfig = (PINETLOG_CONFIGURATIONA)log;
  804. pLogConfig->inetLogType = INET_LOG_TO_SQL;
  805. strcpy( pLogConfig->u.logSql.rgchDataSource, m_rgchDataSource);
  806. strcpy( pLogConfig->u.logSql.rgchTableName, m_rgchTableName);
  807. strcpy( pLogConfig->u.logSql.rgchUserName, m_rgchUserName);
  808. strcpy( pLogConfig->u.logSql.rgchPassword, m_rgchPassword);
  809. return(0L);
  810. }
  811. DWORD
  812. CODBCLOG::GetRegParameters(
  813. LPCSTR pszRegKey,
  814. LPVOID pvIMDCOM )
  815. {
  816. DWORD err = NO_ERROR;
  817. MB mb( (IMDCOM*) pvIMDCOM );
  818. DWORD cb;
  819. if ( !mb.Open("") )
  820. {
  821. err = GetLastError();
  822. goto Exit;
  823. }
  824. cb = sizeof(m_rgchDataSource);
  825. if ( !mb.GetString( pszRegKey, MD_LOGSQL_DATA_SOURCES, IIS_MD_UT_SERVER, m_rgchDataSource, &cb ) )
  826. {
  827. strcpy(m_rgchDataSource,DEFAULT_LOG_SQL_DATASOURCE);
  828. }
  829. cb = sizeof(m_rgchTableName);
  830. if ( !mb.GetString( pszRegKey, MD_LOGSQL_TABLE_NAME, IIS_MD_UT_SERVER, m_rgchTableName, &cb ) )
  831. {
  832. strcpy(m_rgchTableName,DEFAULT_LOG_SQL_TABLE);
  833. }
  834. cb = sizeof(m_rgchUserName);
  835. if ( !mb.GetString( pszRegKey, MD_LOGSQL_USER_NAME, IIS_MD_UT_SERVER, m_rgchUserName, &cb ) )
  836. {
  837. strcpy(m_rgchUserName,DEFAULT_LOG_SQL_USER_NAME);
  838. }
  839. cb = sizeof(m_rgchPassword);
  840. if ( !mb.GetString( pszRegKey, MD_LOGSQL_PASSWORD, IIS_MD_UT_SERVER, m_rgchPassword, &cb, METADATA_INHERIT|METADATA_SECURE ) )
  841. {
  842. strcpy(m_rgchPassword,DEFAULT_LOG_SQL_PASSWORD);
  843. }
  844. Exit:
  845. return err;
  846. }
  847. inline
  848. VOID
  849. WCopyToA(
  850. const WCHAR * wszSrc,
  851. CHAR * szDest
  852. )
  853. {
  854. while( *wszSrc )
  855. {
  856. *szDest++ = (CHAR) *wszSrc++;
  857. }
  858. *szDest = '\0';
  859. }
  860. inline
  861. VOID
  862. ACopyToW(
  863. const CHAR * szSrc,
  864. WCHAR * wszDest
  865. )
  866. {
  867. while( *szSrc )
  868. {
  869. *wszDest++ = (WCHAR) *szSrc++;
  870. }
  871. *wszDest = L'\0';
  872. }
  873. DWORD
  874. CODBCLOG::GetRegParametersFromPublicInterface(LPCSTR pszRegKey,
  875. LPVOID pMetabase)
  876. {
  877. //
  878. // What I really want is the version of MB in iisutil.dll. But, since I
  879. // cannot link to that and iisrtl.dll, I will just work with the
  880. // IMSAdminBase object directly
  881. //
  882. IMSAdminBase *pAdminBase = (IMSAdminBase *)pMetabase;
  883. METADATA_HANDLE hMBPath = NULL;
  884. DWORD cbRequired;
  885. METADATA_RECORD mdr;
  886. WCHAR pwszBuffer[MAX_PATH];
  887. WCHAR pwszRegKey[MAX_PATH];
  888. HRESULT hr;
  889. ACopyToW(pszRegKey, pwszRegKey);
  890. // MB::MB
  891. pAdminBase->AddRef();
  892. // MB::Open
  893. hr = pAdminBase->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  894. L"",
  895. METADATA_PERMISSION_READ,
  896. MB_TIMEOUT,
  897. &hMBPath);
  898. if (FAILED(hr))
  899. {
  900. goto Exit;
  901. }
  902. // MB::GetString
  903. mdr.dwMDIdentifier = MD_LOGSQL_DATA_SOURCES;
  904. mdr.dwMDAttributes = METADATA_INHERIT;
  905. mdr.dwMDUserType = IIS_MD_UT_SERVER;
  906. mdr.dwMDDataType = STRING_METADATA;
  907. mdr.dwMDDataLen = sizeof pwszBuffer;
  908. mdr.pbMDData = (BYTE *)pwszBuffer;
  909. hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired);
  910. if (FAILED(hr) ||
  911. wcslen(pwszBuffer) >= sizeof m_rgchDataSource)
  912. {
  913. strcpy(m_rgchDataSource, DEFAULT_LOG_SQL_DATASOURCE);
  914. }
  915. else
  916. {
  917. WCopyToA(pwszBuffer, m_rgchDataSource);
  918. }
  919. // MB::GetString
  920. mdr.dwMDIdentifier = MD_LOGSQL_TABLE_NAME;
  921. mdr.dwMDAttributes = METADATA_INHERIT;
  922. mdr.dwMDUserType = IIS_MD_UT_SERVER;
  923. mdr.dwMDDataType = STRING_METADATA;
  924. mdr.dwMDDataLen = sizeof pwszBuffer;
  925. mdr.pbMDData = (BYTE *)pwszBuffer;
  926. hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired);
  927. if (FAILED(hr) ||
  928. wcslen(pwszBuffer) >= sizeof m_rgchTableName)
  929. {
  930. strcpy(m_rgchTableName, DEFAULT_LOG_SQL_TABLE);
  931. }
  932. else
  933. {
  934. WCopyToA(pwszBuffer, m_rgchTableName);
  935. }
  936. // MB::GetString
  937. mdr.dwMDIdentifier = MD_LOGSQL_USER_NAME;
  938. mdr.dwMDAttributes = METADATA_INHERIT;
  939. mdr.dwMDUserType = IIS_MD_UT_SERVER;
  940. mdr.dwMDDataType = STRING_METADATA;
  941. mdr.dwMDDataLen = sizeof pwszBuffer;
  942. mdr.pbMDData = (BYTE *)pwszBuffer;
  943. hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired);
  944. if (FAILED(hr) ||
  945. wcslen(pwszBuffer) >= sizeof m_rgchUserName)
  946. {
  947. strcpy(m_rgchUserName, DEFAULT_LOG_SQL_USER_NAME);
  948. }
  949. else
  950. {
  951. WCopyToA(pwszBuffer, m_rgchUserName);
  952. }
  953. // MB::GetString
  954. mdr.dwMDIdentifier = MD_LOGSQL_PASSWORD;
  955. mdr.dwMDAttributes = METADATA_INHERIT|METADATA_SECURE;
  956. mdr.dwMDUserType = IIS_MD_UT_SERVER;
  957. mdr.dwMDDataType = STRING_METADATA;
  958. mdr.dwMDDataLen = sizeof pwszBuffer;
  959. mdr.pbMDData = (BYTE *)pwszBuffer;
  960. hr = pAdminBase->GetData(hMBPath, pwszRegKey, &mdr, &cbRequired);
  961. if (FAILED(hr) ||
  962. wcslen(pwszBuffer) >= sizeof m_rgchPassword)
  963. {
  964. strcpy(m_rgchPassword, DEFAULT_LOG_SQL_PASSWORD);
  965. }
  966. else
  967. {
  968. WCopyToA(pwszBuffer, m_rgchPassword);
  969. }
  970. hr = S_OK;
  971. Exit:
  972. // MB::Close
  973. if (hMBPath)
  974. {
  975. pAdminBase->CloseKey(hMBPath);
  976. hMBPath = NULL;
  977. }
  978. // MB::~MB
  979. pAdminBase->Release();
  980. if (FAILED(hr))
  981. {
  982. return HRESULTTOWIN32(hr);
  983. }
  984. return NO_ERROR;
  985. }
  986. STDMETHODIMP
  987. CODBCLOG::QueryExtraLoggingFields(
  988. PDWORD pcbSize,
  989. TCHAR *pszFieldsList
  990. )
  991. /*++
  992. Routine Description:
  993. get configuration information
  994. Arguments:
  995. cbSize - size of the data structure
  996. log - log configuration data structure
  997. Return Value:
  998. --*/
  999. {
  1000. *pcbSize = 0;
  1001. *pszFieldsList = '\0';
  1002. return(0L);
  1003. }