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

📄 dbxodbc.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:

/* Some MS database engines uses nonstandard SQL for primary keys.  Instead
   of allowing a simple PRIMARY KEY qualifier, they require that the use of
   a primary key to be given as constraint on a column, which also involves
   creating an index on that column.  In theory we could rewrite the primary
   key qualifier to create a fixed-name index using the constraint notation,
   however Access and SQL Server go even further and create an implied unique
   index for the key, making it both useless for its intended purpose
   (forcing clustering of identical entries) as well as rendering the table
   unusable (since it'll only allow a single value to be added).  Because of
   this we have to remove the PRIMARY KEY qualifier entirely.

   Detecting when this is necessary is tricky, it's required for Access, and
   SQL Server but not for Foxpro or (probably) any non-MS products, so we
   check for a DBMS name of "Access" or "SQL Server" and remove it if we find
   either.  In addition if we find Access we fix up some other problems it
   has as well */

static void convertQuery( DBMS_STATE_INFO *dbmsInfo, char *query,
						  const char *command )
	{
	RETCODE retCode;
	SWORD bufLen;
	char *keywordPtr, buffer[ 128 ];

	assert( command != NULL );
	strcpy( query, command );

	/* If it's a CREATE TABLE command, rewrite the blob and date types to
	   the appropriate values for the database backend */
	if( !strncmp( command, "CREATE TABLE", 12 ) )
		{
		char *placeholderPtr;

		if( ( placeholderPtr = strstr( query, " BLOB" ) ) != NULL )
			{
			const int nameLen = strlen( dbmsInfo->blobName );

			/* Open up a gap and replace the blob name placeholder with the
			   actual blob name */
			memmove( placeholderPtr + 1 + nameLen, placeholderPtr + 5,
					 strlen( placeholderPtr + 5 ) + 1 );
			memcpy( placeholderPtr + 1, dbmsInfo->blobName, nameLen );
			}
		if( ( placeholderPtr = strstr( query, " DATETIME" ) ) != NULL )
			{
			const int nameLen = strlen( dbmsInfo->dateTimeName );

			/* Open up a gap and replace the date name placeholder with the
			   actual date name */
			memmove( placeholderPtr + 1 + nameLen, placeholderPtr + 9,
					 strlen( placeholderPtr + 9 ) + 1 );
			memcpy( placeholderPtr + 1, dbmsInfo->dateTimeName, nameLen );
			}
		}

	/* If it's not a CREATE TABLE command with a primary key or a
	   SELECT/DELETE with wildcards used, there's nothing to do */
	if( ( strncmp( query, "CREATE TABLE", 12 ) || \
		  ( keywordPtr = strstr( query, " PRIMARY KEY" ) ) == NULL ) && \
		( ( strncmp( query, "SELECT", 6 ) && strncmp( query, "DELETE", 6 ) ) || \
		  strstr( query, " LIKE " ) == NULL ) )
		return;

	/* It's a potential problem command, check for the presence of Access or
	   SQL Server */
	retCode = SQLGetInfo( dbmsInfo->hDbc, SQL_DBMS_NAME, buffer,
						  sizeof( buffer ), &bufLen );
	if( ( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO ) && \
		strCompare( buffer, "Access", 6 ) && \
		strCompare( buffer, "SQL Server", 10 ) )
		return;
	if( query[ 0 ] == 'C' )
		{
#if 0
		/* Rewrite the PRIMARY KEY qualifier as a constraint on the column.
		   We use the name 'PrimaryKey' since this is what Access uses by
		   default */
		memmove( keywordPtr + 33, keywordPtr + 12,
				 ( strlen( keywordPtr ) - 12 ) + 1 );
		memcpy( keywordPtr, "CONSTRAINT PrimaryKey PRIMARY KEY", 33 );
#else
		/* Remove the PRIMARY KEY qualifier (the constraint mechanism is too
		   awkward to handle cleanly, see the comment at the start of this
		   function) */
		memmove( keywordPtr, keywordPtr + 12, strlen( keywordPtr + 12 ) + 1 );
#endif /* 0 */
		}
	else
		{
		/* Unlike everything else in the known universe, Access uses * and ?
		   instead of the standard SQL wildcards so if we find a LIKE ... %
		   we rewrite the % as a * */
		if( buffer[ 0 ] == 'A' && \
			( keywordPtr = strstr( query, " LIKE " ) ) != NULL )
			{
			int i;

			/* Search up to 5 characters ahead for a wildcard and replace it
			   with the one needed by Access if we find it */
			for( i = 7; i < 11 && keywordPtr[ i ]; i++ )
				if( keywordPtr[ i ] == '%' )
					keywordPtr[ i ] = '*';
			}
		}
	}

/* Get the name of the blob and date data type for this data source */

static int getDatatypeInfo( DBMS_STATE_INFO *dbmsInfo )
	{
	RETCODE retCode;
	SDWORD length;
	SWORD bufLen;
	char buffer[ 8 ];
	long count;

	SQLAllocStmt( dbmsInfo->hDbc, &dbmsInfo->hStmt );

	/* First we see whether the database supports long binary strings.  Most
	   of the newer ones that are likely to be used do */
	retCode = SQLGetTypeInfo( dbmsInfo->hStmt, SQL_LONGVARBINARY );
	if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
		{
		/* Get the results of the transaction.  If the database doesn't
		   support this, we'll get SQL_NO_DATA_FOUND (status 100) returned */
		retCode = SQLFetch( dbmsInfo->hStmt );
		if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
			{
			/* Get the type name and maximum possible field length.  We only
			   check the second return code since they both apply to the same
			   row */
			SQLGetData( dbmsInfo->hStmt, 1, SQL_C_CHAR,
						dbmsInfo->blobName, 64, &length );
			retCode = SQLGetData( dbmsInfo->hStmt, 3, SQL_C_LONG,
								  &count, sizeof( long ), &length );
			if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
				{
				dbmsInfo->hasBinaryBlobs = TRUE;
				dbmsInfo->blobType = SQL_LONGVARBINARY;
				}
			}
		else
			{
			/* The backend doesn't support binary blobs, get the name of the 
			   long char type for this data source */
			SQLFreeStmt( dbmsInfo->hStmt, SQL_CLOSE );
			retCode = SQLGetTypeInfo( dbmsInfo->hStmt, SQL_LONGVARCHAR );
			if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
				{
				/* Get the results of the transaction */
				retCode = SQLFetch( dbmsInfo->hStmt );
				if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
					{
					/* Get the type name and maximum possible field length.
					   We only check the second return code since they both
					   apply to the same row */
					SQLGetData( dbmsInfo->hStmt, 1, SQL_C_CHAR,
								dbmsInfo->blobName, 64, &length );
					retCode = SQLGetData( dbmsInfo->hStmt, 3, SQL_C_LONG,
										  &count, sizeof( long ), &length );
					dbmsInfo->blobType = SQL_LONGVARCHAR;
					}
				}
			}
		}

	/* If we couldn't get a blob type or the type is too short to use,
	   report it back as a database open failure */
	if( ( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO ) || \
		count < 4096 )
		{
		if( count >= 4096 )
			/* There was a problem, get more details */
			getErrorInfo( dbmsInfo, SQL_ERRLVL_0, CRYPT_ERROR_OPEN );
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		return( CRYPT_ERROR_OPEN );
		}

	/* Now do the same thing for the date+time data type.  This changed from
	   SQL_TIMESTAMP in ODBC 2.x to SQL_TYPE_TIMESTAMP in ODBC 3.x, since 3.x
	   will be more common we try the 3.x version first and if that fails
	   fall back to 2.x */
	SQLFreeStmt( dbmsInfo->hStmt, SQL_CLOSE );
	retCode = SQLGetTypeInfo( dbmsInfo->hStmt, SQL_TYPE_TIMESTAMP );
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		retCode = SQLGetTypeInfo( dbmsInfo->hStmt, SQL_TIMESTAMP );
	if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
		{
		/* Fetch the results of the transaction and get the type name */
		retCode = SQLFetch( dbmsInfo->hStmt );
		if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
			retCode = SQLGetData( dbmsInfo->hStmt, 1, SQL_C_CHAR,
								  dbmsInfo->dateTimeName, 64, &length );
		}
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		{
		getErrorInfo( dbmsInfo, SQL_ERRLVL_0, CRYPT_ERROR_OPEN );
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		return( CRYPT_ERROR_OPEN );
		}

#if 0	/* Not needed since we always supply the length */
	retCode = SQLGetInfo( dbmsInfo->hDbc, SQL_NEED_LONG_DATA_LEN,
						  buffer, sizeof( buffer ), &bufLen );
	if( retCode != SQL_SUCCESS )
		dbmsInfo->needLongLength = TRUE;	/* Make a paranoid guess */
	else
		dbmsInfo->needLongLength = ( *buffer == 'Y' ) ? TRUE : FALSE;
#endif /* 0 */


	/* Finally, determine the escape char being used.  This is usually '\',
	   but it may have been changed for some reason */
	retCode = SQLGetInfo( dbmsInfo->hDbc, SQL_SEARCH_PATTERN_ESCAPE,
						  buffer, sizeof( buffer ), &bufLen );
	dbmsInfo->escapeChar = ( retCode == SQL_SUCCESS ) ? buffer[ 0 ] : '\\';

	SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
	dbmsInfo->hStmt = NULL;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Database Open/Close Routines					*
*																			*
****************************************************************************/

/* Close a previously-opened ODBC connection.  We have to have this before
   openDatabase() since it may be called by openDatabase() if the open
   process fails.  This is necessary because the complex ODBC open may
   require a fairly extensive cleanup afterwards */

static void closeDatabase( DBMS_STATE_INFO *dbmsInfo )
	{
	assert( isWritePtr( dbmsInfo, DBMS_STATE_INFO ) );

	/* Commit the transaction.  The default transaction mode for drivers 
	   that support SQLSetConnectOption() is auto-commit so the 
	   SQLTransact() call isn't strictly necessary, but we play it safe 
	   anyway */
	if( dbmsInfo->needsUpdate )
		{
		SQLTransact( dbmsInfo->hEnv, dbmsInfo->hDbc, SQL_COMMIT );
		dbmsInfo->needsUpdate = FALSE;
		}

	/* Clean up */
	SQLDisconnect( dbmsInfo->hDbc );
	SQLFreeConnect( dbmsInfo->hDbc );
	SQLFreeEnv( dbmsInfo->hEnv );
	dbmsInfo->hStmt = NULL;
	dbmsInfo->hDbc = NULL;
	dbmsInfo->hEnv = NULL;
	}

/* Open a connection to a data source using ODBC.  We don't check the return
   codes for many of the functions since the worst that can happen if they
   fail is that performance will be somewhat suboptimal.  In addition we
   don't allocate statement handles at this point since these are handled in
   various strange and peculiar ways by different ODBC drivers.  The main
   problem is that some drivers don't support mode than one hstmt per hdbc,
   some support only one active hstmt (an hstmt with results pending) per
   hdbc, and some support multiple active hstmt's per hdbc.  For this reason
   we use a strategy of allocating an hstmt, performing a transaction, and
   then immediately freeing it again afterwards */

static int openDatabase( DBMS_STATE_INFO *dbmsInfo, const char *name,
						 const int options, int *featureFlags )
	{
	DBMS_NAME_INFO nameInfo;
	SWORD userLen = 0, passwordLen = 0;
	RETCODE retCode;
	int status;

	assert( isWritePtr( dbmsInfo, DBMS_STATE_INFO ) );
	assert( isReadPtr( name, 2 ) );
	assert( isWritePtr( featureFlags, sizeof( int ) ) );

	/* Make sure that the driver is bound in */
	if( hODBC == NULL_HINSTANCE )
		return( CRYPT_ERROR_OPEN );

	/* Parse the data source into its individual components */
	status = dbmsParseName( &nameInfo, name, SQL_NTS );
	if( cryptStatusError( status ) )
		return( status );

	/* Allocate environment and connection handles */
	SQLAllocEnv( &dbmsInfo->hEnv );
	SQLAllocConnect( dbmsInfo->hEnv, &dbmsInfo->hDbc );

	/* Set the access mode to read-only if we can.  The default is R/W, but
	   setting it to read-only optimises transaction management */
	if( options == CRYPT_KEYOPT_READONLY )
		SQLSetConnectOption( dbmsInfo->hDbc, SQL_ACCESS_MODE,
							 SQL_MODE_READ_ONLY );

	/* Set the cursor type to forward-only (which should be the default).
	   Note that we're passing an SQLSetStmtOption() arg.to
	   SQLSetConnectOption(), which causes all stmt's allocated for this
	   connection to have the specified behaviour */
	SQLSetConnectOption( dbmsInfo->hDbc, SQL_CURSOR_TYPE,
						 SQL_CURSOR_FORWARD_ONLY );

	/* Turn off scanning for escape clauses in the SQL strings, which lets
	   the driver pass the string directly to the data source.  See the
	   comment for the previous call about the arg.being passed */
	SQLSetConnectOption( dbmsInfo->hDbc, SQL_NOSCAN, SQL_NOSCAN_ON );

	/* Once everything is set up the way we want it, try to connect to a data
	   source and allocate a statement handle */
	retCode = SQLConnect( dbmsInfo->hDbc, 
						  nameInfo.name, ( SQLSMALLINT ) nameInfo.nameLen,
						  nameInfo.user, ( SQLSMALLINT ) nameInfo.userLen,
						  nameInfo.password, ( SQLSMALLINT ) nameInfo.passwordLen );
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		{
		getErrorInfo( dbmsInfo, SQL_ERRLVL_0, CRYPT_ERROR_OPEN );
		SQLFreeConnect( dbmsInfo->hDbc );
		SQLFreeEnv( dbmsInfo->hEnv );
		return( CRYPT_ERROR_OPEN );
		}

	/* Get various driver and source-specific information that we may need
	   later on */
	status = getDatatypeInfo( dbmsInfo );
	if( cryptStatusError( status ) )
		{
		closeDatabase( dbmsInfo );
		return( status );
		}

⌨️ 快捷键说明

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