⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 odbc.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
	if( queryType == DBMS_QUERY_CHECK )
		return( CRYPT_OK );

	/* Read the data */
	sqlStatus = SQLGetData( hStmt, 1, dataType, data, maxLength, &length );
	if( !sqlStatusOK( sqlStatus ) )
		return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
							  CRYPT_ERROR_READ ) );
	*dataLength = ( int ) length;
	return( CRYPT_OK );
	}

/* Perform a transaction that returns information */

static int performQuery( DBMS_STATE_INFO *dbmsInfo, const char *command,
						 char *data, int *dataLength, const char *boundData,
						 const int boundDataLength, const time_t boundDate,
						 const DBMS_CACHEDQUERY_TYPE queryEntry,
						 const DBMS_QUERY_TYPE queryType )
	{
	/* We have to explicitly set the maximum length indicator because some
	   sources will helpfully zero-pad the data to the maximum indicated size,
	   which is smaller for binary data */
	const int maxLength = dbmsInfo->hasBinaryBlobs ? \
						  MAX_CERT_SIZE : MAX_QUERY_RESULT_SIZE;
	const SQLHSTMT hStmt = dbmsInfo->hStmt[ queryEntry ];
	TIMESTAMP_STRUCT timeStamp;
	SQLINTEGER lengthInfo;
	SQLRETURN sqlStatus;
	int status;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
	assert( ( data == NULL && dataLength == NULL ) || \
			isWritePtr( dataLength, sizeof( int ) ) );
	assert( ( boundData == NULL && boundDataLength == 0 ) || \
			( boundDate == 0 ) );

	/* Clear return value */
	if( dataLength != NULL )
		*dataLength = 0;

	/* If we're starting a new query, handle the query initialisation and
	   parameter binding */
	if( queryType == DBMS_QUERY_START || \
		queryType == DBMS_QUERY_CHECK || \
		queryType == DBMS_QUERY_NORMAL )
		{
		/* Prepare the query for execution if necessary */
		if( queryEntry != DBMS_CACHEDQUERY_NONE && \
			!dbmsInfo->hStmtPrepared[ queryEntry ] )
			{
			char query[ MAX_SQL_QUERY_SIZE ];

			convertQuery( dbmsInfo, query, command );
			sqlStatus = SQLPrepare( hStmt, query, SQL_NTS );
			if( !sqlStatusOK( sqlStatus ) )
				return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
									  CRYPT_ERROR_READ ) );
			dbmsInfo->hStmtPrepared[ queryEntry ] = TRUE;
			}

		/* Bind in any query parameters that may be required */
		status = bindParameters( hStmt, boundData, boundDataLength,
								 boundDate, &timeStamp, &lengthInfo,
								 dbmsInfo, TRUE );
		if( cryptStatusError( status ) )
			return( status );
		}

	switch( queryType )
		{
		case DBMS_QUERY_START:
			assert( boundDate == 0 );

			/* Execute the query */
			if( queryEntry == DBMS_CACHEDQUERY_NONE )
				{
				char query[ MAX_SQL_QUERY_SIZE ];

				convertQuery( dbmsInfo, query, command );
				sqlStatus = SQLExecDirect( hStmt, query, SQL_NTS );
				}
			else
				sqlStatus = SQLExecute( hStmt );
			if( !sqlStatusOK( sqlStatus ) )
				return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
									  CRYPT_ERROR_READ ) );

			/* If we're starting an ongoing query with results to be fetched
			   later, we're done */
			if( data == NULL )
				return( CRYPT_OK );

			/* Drop through to fetch the first set of results */

		case DBMS_QUERY_CONTINUE:
			assert( maxLength > 16 && isWritePtr( data, maxLength ) );

			/* We're in the middle of a continuing query, fetch the next set
			   of results.  If we've run out of results (indicated by a not-
			   found status) we explicitly signal to the caller that the
			   query has completed */
			status = fetchData( dbmsInfo->hStmt[ queryEntry ], data,
								dataLength, maxLength, DBMS_QUERY_CONTINUE,
								dbmsInfo );
			return( cryptStatusOK( status ) ? CRYPT_OK : \
					( status == CRYPT_ERROR_NOTFOUND ) ? \
					CRYPT_ERROR_COMPLETE : status );

		case DBMS_QUERY_CANCEL:
			/* Cancel any outstanding requests to clear the hStmt ready for
			   re-use */
			SQLCloseCursor( dbmsInfo->hStmt[ queryEntry ] );
			return( CRYPT_OK );

		case DBMS_QUERY_CHECK:
		case DBMS_QUERY_NORMAL:
			/* Only return a maximum of a single row in response to a point
			   query.  This is a simple optimisation to ensure that the
			   database client doesn't start sucking across huge amounts of
			   data when it's not necessary */
			SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS, ( SQLPOINTER ) 1,
							SQL_IS_INTEGER );

			/* Execute the SQL statement and fetch the results */
			if( queryEntry == DBMS_CACHEDQUERY_NONE )
				{
				char query[ MAX_SQL_QUERY_SIZE ];

				convertQuery( dbmsInfo, query, command );
				sqlStatus = SQLExecDirect( hStmt, query, SQL_NTS );
				}
			else
				sqlStatus = SQLExecute( hStmt );
			if( sqlStatusOK( sqlStatus ) )
				{
				status = fetchData( hStmt, data, dataLength, maxLength,
									queryType, dbmsInfo );
				SQLCloseCursor( hStmt );
				}
			else
				status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
									   CRYPT_ERROR_READ );

			/* Reset the statement handle's multi-row result handling */
			SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS, ( SQLPOINTER ) 0,
							SQL_IS_INTEGER );
			return( status );

		default:
			assert( NOTREACHED );
			return( CRYPT_ERROR_NOTAVAIL );
		}

	assert( NOTREACHED );
	return( CRYPT_ERROR );
	}

/* Fetch extended error information from the database state info */

static void performErrorQuery( DBMS_STATE_INFO *dbmsInfo, int *errorCode,
							   char *errorMessage )
	{
	assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
	assert( isWritePtr( errorCode, sizeof( int ) ) );
	assert( isWritePtr( errorMessage, MAX_ERRMSG_SIZE ) );

	*errorCode = dbmsInfo->errorCode;
	strcpy( errorMessage, dbmsInfo->errorMessage );
	}

/****************************************************************************
*																			*
*						 	Database Write Routines							*
*																			*
****************************************************************************/

/* Perform a transaction that updates the database without returning any
   data */

static int performUpdate( DBMS_STATE_INFO *dbmsInfo, const char *command,
						  const void *boundData, const int boundDataLength,
						  const time_t boundDate,
						  const DBMS_UPDATE_TYPE updateType )
	{
	TIMESTAMP_STRUCT timeStamp;
	const SQLHSTMT hStmt = dbmsInfo->hStmt[ 0 ];
	SQLINTEGER lengthInfo;
	SQLRETURN sqlStatus;
	int status = CRYPT_OK;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );

	/* If we're aborting a transaction, roll it back, re-enable autocommit,
	   and clean up */
	if( updateType == DBMS_UPDATE_ABORT )
		{
		SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc, SQL_ROLLBACK );
		SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
						   ( SQLPOINTER ) SQL_AUTOCOMMIT_ON,
						   SQL_IS_UINTEGER );
		return( CRYPT_OK );
		}

	/* If it's the start of a transaction, turn autocommit off */
	if( updateType == DBMS_UPDATE_BEGIN )
		SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
						   ( SQLPOINTER ) SQL_AUTOCOMMIT_OFF,
						   SQL_IS_UINTEGER );

	/* Bind in any necessary parameters to the hStmt.  For the older (and
	   often somewhat flaky) Win16 ODBC 1.x/2.x drivers the binding process
	   was unlike the behaviour mentioned in the ODBC documentation, which
	   claimed that SQLExecDirect() would return SQL_NEED_DATA if it found a
	   parameter marker.  Instead, we have to bind the parameters before
	   calling SQLExecDirect() and it reads them from the bound location as
	   required.  In addition an older version of the ODBC spec required
	   that the cbColDef value never exceed SQL_MAX_MESSAGE_LENGTH, however
	   this was defined to be 512 bytes which meant that we couldn't add
	   most certs of any real complexity or with keys > 1K bits.  The
	   workaround was to pass in the actual data length here instead.  This
	   worked for all ODBC drivers tested.

	   For any newer Win32 ODBC 3.x drivers this isn't a problem any more,
	   so we use the mechanism described in the docs, leaving the older
	   alternative as an option if it's ever needed */
	status = bindParameters( hStmt, boundData, boundDataLength,
							 boundDate, &timeStamp, &lengthInfo,
							 dbmsInfo, FALSE );
	if( cryptStatusError( status ) )
		return( status );
#ifdef ODBC1x
	if( boundData != NULL )
		{
		dbmsInfo->cbBlobLength = SQL_LEN_DATA_AT_EXEC( boundDataLength );
		SQLBindParameter( hStmt, paramNo++, SQL_PARAM_INPUT,
						  dbmsInfo->hasBinaryBlobs ? SQL_C_BINARY : SQL_C_CHAR,
						  dbmsInfo->blobType, boundDataLength, 0,
						  ( SQLPOINTER ) 6, 0, &dbmsInfo->cbBlobLength );
		}
#endif /* ODBC1x */

	/* Execute the command/hStmt as appropriate */
	if( command == NULL )
		sqlStatus = SQLExecute( hStmt );
	else
		{
		char query[ MAX_SQL_QUERY_SIZE ];

		convertQuery( dbmsInfo, query, command );
		sqlStatus = SQLExecDirect( hStmt, query, SQL_NTS );
		}
#ifdef ODBC1x
	if( sqlStatus == SQL_NEED_DATA )
		{
		SQLPOINTER pToken;

		/* Add the key data and perform a dummy SQLParamData() call to tell
		   the ODBC driver that we've finished with the operation */
		SQLParamData( hStmt, &pToken );
		sqlStatus = SQLPutData( hStmt, ( SQLPOINTER ) boundData,
								boundDataLength );
		if( sqlStatusOK( sqlStatus ) )
			sqlStatus = SQLParamData( hStmt, &pToken );
		}
#endif /* ODBC1x */
	if( !sqlStatusOK( sqlStatus ) )
		{
		/* The return status from a delete operation can be reported in
		   several ways at the whim of the driver.  Some drivers always
		   report success even though nothing was found to delete (more
		   common in ODBC 2.x drivers, see the code further on for the
		   handling for this).  Others report a failure to delete anything
		   with an SQL_NO_DATA status (more common in ODBC 3.x drivers).
		   For this case we convert the overall status to a
		   CRYPT_ERROR_NOTFOUND and update the sqlStatus as required if we
		   need to continue */
		if( sqlStatus == SQL_NO_DATA && command != NULL && \
			!strCompare( command, "DELETE", 6 ) )
			{
			status = CRYPT_ERROR_NOTFOUND;
			if( updateType != DBMS_UPDATE_COMMIT )
				return( status );
			}
		else
			{
			/* If we hit an error at this point we can only exit if we're
			   not finishing a transaction.  If we are, the commit turns
			   into an abort further down */
			status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
								   CRYPT_ERROR_WRITE );
			if( updateType != DBMS_UPDATE_COMMIT )
				return( status );
			}
		}
	else
		/* If we're performing a delete, the operation will succeed even
		   though nothing was found to delete,  so we make sure that we
		   actually changed something */
		if( command != NULL && !strCompare( command, "DELETE", 6 ) )
			{
			SQLUINTEGER rowCount;

			SQLRowCount( hStmt, &rowCount );
			if( rowCount <= 0 )
				status = CRYPT_ERROR_NOTFOUND;
			}

	/* If it's the end of a transaction, commit the transaction and turn
	   autocommit on again */
	if( updateType == DBMS_UPDATE_COMMIT )
		{
		SQLRETURN sqlStatus;

		/* If we've had a failure before this point, abort, otherwise
		   commit.  The SQLSMALLINT cast is necessary in some development
		   environments (although spurious) */
		sqlStatus = SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc,
								( SQLSMALLINT  ) \
								( cryptStatusError( status ) ? \
								  SQL_ROLLBACK : SQL_COMMIT ) );
		if( dbmsInfo->transactIsDestructive )
			{
			int i;

			/* If transactions are destructive for this back-end, invalidate
			   all prepared statements */
			for( i = 0; i < NO_CACHED_QUERIES; i++ )
				dbmsInfo->hStmtPrepared[ i ] = FALSE;
			}
		SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
						   ( SQLPOINTER ) SQL_AUTOCOMMIT_ON,
						   SQL_IS_UINTEGER );
		if( cryptStatusOK( status ) && !sqlStatusOK( sqlStatus ) )
			status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
								   CRYPT_ERROR_WRITE );
		}

	return( status );
	}

#ifndef USE_RPCAPI

int initDispatchODBC( DBMS_INFO *dbmsInfo )
	{
	dbmsInfo->openDatabaseBackend = openDatabase;
	dbmsInfo->closeDatabaseBackend = closeDatabase;
	dbmsInfo->performUpdateBackend = performUpdate;
	dbmsInfo->performQueryBackend = performQuery;
	dbmsInfo->performErrorQueryBackend = performErrorQuery;

	return( CRYPT_OK );
	}
#else

/* Pull in the shared database RPC routines, renaming the generic dispatch
   function to the ODBC-specific one which is called directly by the
   marshalling code */

#define processCommand( stateInfo, buffer ) \
		odbcProcessCommand( stateInfo, buffer )
#include "dbx_rpc.c"

#endif /* !USE_RPCAPI */

#endif /* USE_ODBC */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -