📄 dbxodbc.c
字号:
/****************************************************************************
* *
* cryptlib ODBC Mapping Routines *
* Copyright Peter Gutmann 1996-2003 *
* *
****************************************************************************/
#include <stdio.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "keyset.h"
#include "dbxdbx.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "keyset.h"
#include "dbxdbx.h"
#else
#include "crypt.h"
#include "keysets/keyset.h"
#include "keyset/dbxdbx.h"
#endif /* Compiler-specific includes */
/* SQLError() returns error information at various levels and is rather
unstable in its handling of input parameters, for example with some Win16
drivers if you pass it a valid hstmt then it may GPF after some calls so
you need to force a NULL hstmt. The following values define the levels
of handle that we pass in in order for SQLError() to work as advertised */
#define SQL_ERRLVL_0 0
#define SQL_ERRLVL_1 1
#define SQL_ERRLVL_2 2
#ifdef USE_ODBC
/****************************************************************************
* *
* Init/Shutdown Routines *
* *
****************************************************************************/
#ifdef DYNAMIC_LOAD
/* Global function pointers. These are necessary because the functions need
to be dynamically linked since not all systems contain the necessary
DLL's. Explicitly linking to them will make cryptlib unloadable on some
systems.
MSDN updates from late 2000 defined SQLROWCOUNT themselves (which could be
fixed by undefining it), however after late 2002 the value was typedef'd,
requring all sorts of extra trickery to handle the different cases.
Because of this this particular function is typedef'd with a _FN suffix
to reduce problems */
#define NULL_HINSTANCE ( HINSTANCE ) NULL
static HINSTANCE hODBC = NULL_HINSTANCE;
typedef RETCODE ( SQL_API *SQLALLOCENV )( HENV FAR *phenv );
typedef RETCODE ( SQL_API *SQLALLOCCONNECT )( HENV henv, HDBC FAR *phdbc );
typedef RETCODE ( SQL_API *SQLALLOCSTMT )( HDBC hdbc, HSTMT FAR *phstmt );
typedef RETCODE ( SQL_API *SQLBINDPARAMETER )( HSTMT hstmt, UWORD ipar,
SWORD fParamType, SWORD fCType, SWORD fSqlType,
UDWORD cbColDef, SWORD ibScale, PTR rgbValue,
SDWORD cbValueMax, SDWORD FAR *pcbValue );
typedef RETCODE ( SQL_API *SQLCANCEL )( HSTMT hstmt );
typedef RETCODE ( SQL_API *SQLCONNECT )( HDBC hdbc, UCHAR FAR *szDSN,
SWORD cdDSN, UCHAR FAR *szUID, SWORD cbUID,
UCHAR FAR *szAuthStr, SWORD cbAuthStr );
typedef RETCODE ( SQL_API *SQLDISCONNECT )( HDBC hdbc );
typedef RETCODE ( SQL_API *SQLERROR )( HENV henv, HDBC hdbc, HSTMT hstmt,
UCHAR FAR *szSqlState, SDWORD FAR *pfNativeError,
UCHAR FAR *szErrorMsg, SWORD cbErrorMsgMax,
SWORD FAR *pcbErrorMsg );
typedef RETCODE ( SQL_API *SQLEXECDIRECT )( HSTMT hstmt, UCHAR FAR *szSqlStr,
SDWORD cbSqlStr );
typedef RETCODE ( SQL_API *SQLEXECUTE )( HSTMT hstmt );
typedef RETCODE ( SQL_API *SQLFETCH )( HSTMT hstmt );
typedef RETCODE ( SQL_API *SQLFREECONNECT )( HDBC hdbc );
typedef RETCODE ( SQL_API *SQLFREEENV )( HENV henv );
typedef RETCODE ( SQL_API *SQLFREESTMT )( HSTMT hstmt, UWORD fOption );
typedef RETCODE ( SQL_API *SQLGETDATA )( HSTMT hstmt, UWORD icol,
SWORD fCType, PTR rgbValue, SDWORD cbValueMax,
SDWORD FAR *pcbValue );
typedef RETCODE ( SQL_API *SQLGETINFO )( HDBC hdbc, UWORD fInfoType,
PTR rgbInfoValue, SWORD cbInfoValueMax,
SWORD FAR *pcbInfoValue );
typedef RETCODE ( SQL_API *SQLGETTYPEINFO )( HSTMT hstmt, SWORD fSqlType );
typedef RETCODE ( SQL_API *SQLPARAMDATA )( HSTMT hstmt, PTR FAR *prgbValue );
typedef RETCODE ( SQL_API *SQLPREPARE )( HSTMT hstmt, UCHAR FAR *szSqlStr,
SDWORD cbSqlStr );
typedef RETCODE ( SQL_API *SQLPUTDATA )( HSTMT hstmt, PTR rgbValue,
SDWORD cbValue );
typedef RETCODE ( SQL_API *SQLROWCOUNT_FN )( HSTMT hstmt, SDWORD *cbRowCount );
typedef RETCODE ( SQL_API *SQLSETCONNECTOPTION )( HDBC hdbc, UWORD fOption,
UDWORD vParam );
typedef RETCODE ( SQL_API *SQLSETSTMTOPTION )( HSTMT hstmt, UWORD fOption,
UDWORD vParam );
typedef RETCODE ( SQL_API *SQLTRANSACT )( HENV henv, HDBC hdbc, UWORD fType );
static SQLALLOCCONNECT pSQLAllocConnect = NULL;
static SQLALLOCENV pSQLAllocEnv = NULL;
static SQLALLOCSTMT pSQLAllocStmt = NULL;
static SQLBINDPARAMETER pSQLBindParameter = NULL;
static SQLCANCEL pSQLCancel = NULL;
static SQLCONNECT pSQLConnect = NULL;
static SQLDISCONNECT pSQLDisconnect = NULL;
static SQLERROR pSQLError = NULL;
static SQLEXECDIRECT pSQLExecDirect = NULL;
static SQLEXECUTE pSQLExecute = NULL;
static SQLFETCH pSQLFetch = NULL;
static SQLFREECONNECT pSQLFreeConnect = NULL;
static SQLFREEENV pSQLFreeEnv = NULL;
static SQLFREESTMT pSQLFreeStmt = NULL;
static SQLGETDATA pSQLGetData = NULL;
static SQLGETINFO pSQLGetInfo = NULL;
static SQLGETTYPEINFO pSQLGetTypeInfo = NULL;
static SQLPARAMDATA pSQLParamData = NULL;
static SQLPREPARE pSQLPrepare = NULL;
static SQLPUTDATA pSQLPutData = NULL;
static SQLROWCOUNT_FN pSQLRowCount = NULL;
static SQLSETCONNECTOPTION pSQLSetConnectOption = NULL;
static SQLSETSTMTOPTION pSQLSetStmtOption = NULL;
static SQLTRANSACT pSQLTransact = NULL;
/* The use of dynamically bound function pointers vs statically linked
functions requires a bit of sleight of hand since we can't give the
pointers the same names as prototyped functions. To get around this we
redefine the actual function names to the names of the pointers */
#define SQLAllocConnect pSQLAllocConnect
#define SQLAllocEnv pSQLAllocEnv
#define SQLAllocStmt pSQLAllocStmt
#define SQLBindParameter pSQLBindParameter
#define SQLCancel pSQLCancel
#define SQLConnect pSQLConnect
#define SQLDisconnect pSQLDisconnect
#define SQLError pSQLError
#define SQLExecDirect pSQLExecDirect
#define SQLExecute pSQLExecute
#define SQLFetch pSQLFetch
#define SQLFreeConnect pSQLFreeConnect
#define SQLFreeEnv pSQLFreeEnv
#define SQLFreeStmt pSQLFreeStmt
#define SQLGetData pSQLGetData
#define SQLGetInfo pSQLGetInfo
#define SQLGetTypeInfo pSQLGetTypeInfo
#define SQLParamData pSQLParamData
#define SQLPrepare pSQLPrepare
#define SQLPutData pSQLPutData
#define SQLRowCount pSQLRowCount
#define SQLSetConnectOption pSQLSetConnectOption
#define SQLSetStmtOption pSQLSetStmtOption
#define SQLTransact pSQLTransact
/* Depending on whether we're running under Win16 or Win32 we load the ODBC
driver under a different name */
#ifdef __WIN16__
#define ODBC_LIBNAME "ODBC.DLL"
#else
#define ODBC_LIBNAME "ODBC32.DLL"
#endif /* __WIN16__ */
/* Dynamically load and unload any necessary DBMS libraries */
int dbxInitODBC( void )
{
#ifdef __WIN16__
UINT errorMode;
#endif /* __WIN16__ */
/* If the ODBC module is already linked in, don't do anything */
if( hODBC != NULL_HINSTANCE )
return( CRYPT_OK );
/* Obtain a handle to the module containing the ODBC functions */
#ifdef __WIN16__
errorMode = SetErrorMode( SEM_NOOPENFILEERRORBOX );
hODBC = LoadLibrary( ODBC_LIBNAME );
SetErrorMode( errorMode );
if( hODBC < HINSTANCE_ERROR )
{
hODBC = NULL_HINSTANCE;
return( CRYPT_ERROR );
}
#else
if( ( hODBC = LoadLibrary( ODBC_LIBNAME ) ) == NULL_HINSTANCE )
return( CRYPT_ERROR );
#endif /* __WIN32__ */
/* Now get pointers to the functions */
pSQLAllocConnect = ( SQLALLOCCONNECT ) GetProcAddress( hODBC, "SQLAllocConnect" );
pSQLAllocEnv = ( SQLALLOCENV ) GetProcAddress( hODBC, "SQLAllocEnv" );
pSQLAllocStmt = ( SQLALLOCSTMT ) GetProcAddress( hODBC, "SQLAllocStmt" );
pSQLBindParameter = ( SQLBINDPARAMETER ) GetProcAddress( hODBC, "SQLBindParameter" );
pSQLCancel = ( SQLCANCEL ) GetProcAddress( hODBC, "SQLCancel" );
pSQLConnect = ( SQLCONNECT ) GetProcAddress( hODBC, "SQLConnect" );
pSQLDisconnect = ( SQLDISCONNECT ) GetProcAddress( hODBC, "SQLDisconnect" );
pSQLError = ( SQLERROR ) GetProcAddress( hODBC, "SQLError" );
pSQLExecDirect = ( SQLEXECDIRECT ) GetProcAddress( hODBC, "SQLExecDirect" );
pSQLExecute = ( SQLEXECUTE ) GetProcAddress( hODBC, "SQLExecute" );
pSQLFetch = ( SQLFETCH ) GetProcAddress( hODBC, "SQLFetch" );
pSQLFreeConnect = ( SQLFREECONNECT ) GetProcAddress( hODBC, "SQLFreeConnect" );
pSQLFreeEnv = ( SQLFREEENV ) GetProcAddress( hODBC, "SQLFreeEnv" );
pSQLFreeStmt = ( SQLFREESTMT ) GetProcAddress( hODBC, "SQLFreeStmt" );
pSQLGetData = ( SQLGETDATA ) GetProcAddress( hODBC, "SQLGetData" );
pSQLGetInfo = ( SQLGETINFO ) GetProcAddress( hODBC, "SQLGetInfo" );
pSQLGetTypeInfo = ( SQLGETTYPEINFO ) GetProcAddress( hODBC, "SQLGetTypeInfo" );
pSQLParamData = ( SQLPARAMDATA ) GetProcAddress( hODBC, "SQLParamData" );
pSQLPrepare = ( SQLPREPARE ) GetProcAddress( hODBC, "SQLPrepare" );
pSQLPutData = ( SQLPUTDATA ) GetProcAddress( hODBC, "SQLPutData" );
pSQLRowCount = ( SQLROWCOUNT_FN ) GetProcAddress( hODBC, "SQLRowCount" );
pSQLSetConnectOption = ( SQLSETCONNECTOPTION ) GetProcAddress( hODBC, "SQLSetConnectOption" );
pSQLSetStmtOption = ( SQLSETSTMTOPTION ) GetProcAddress( hODBC, "SQLSetStmtOption" );
pSQLTransact = ( SQLTRANSACT ) GetProcAddress( hODBC, "SQLTransact" );
/* Make sure that we got valid pointers for every ODBC function */
if( pSQLAllocConnect == NULL || pSQLAllocEnv == NULL ||
pSQLAllocStmt == NULL || pSQLBindParameter == NULL ||
pSQLCancel == NULL || pSQLConnect == NULL ||
pSQLDisconnect == NULL || pSQLError == NULL ||
pSQLExecDirect == NULL || pSQLExecute == NULL ||
pSQLFetch == NULL || pSQLFreeConnect == NULL ||
pSQLFreeEnv == NULL || pSQLFreeStmt == NULL ||
pSQLGetData == NULL || pSQLGetInfo == NULL ||
pSQLGetTypeInfo == NULL || pSQLParamData == NULL ||
pSQLPrepare == NULL || pSQLPutData == NULL ||
pSQLSetConnectOption == NULL || pSQLSetStmtOption == NULL ||
pSQLTransact == NULL )
{
/* Free the library reference and reset the handle */
FreeLibrary( hODBC );
hODBC = NULL_HINSTANCE;
return( CRYPT_ERROR );
}
return( CRYPT_OK );
}
void dbxEndODBC( void )
{
if( hODBC != NULL_HINSTANCE )
FreeLibrary( hODBC );
hODBC = NULL_HINSTANCE;
}
#else
int dbxInitODBC( void )
{
return( CRYPT_OK );
}
void dbxEndODBC( void )
{
}
#endif /* DYNAMIC_LOAD */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Get information on an ODBC error */
static int getErrorInfo( DBMS_STATE_INFO *dbmsInfo, const int errorLevel,
const int defaultStatus )
{
HDBC hdbc = ( errorLevel < 1 ) ? SQL_NULL_HDBC : dbmsInfo->hDbc;
HDBC hstmt = ( errorLevel < 2 ) ? SQL_NULL_HSTMT : dbmsInfo->hStmt;
char altErrorMessage[ MAX_ERRMSG_SIZE ];
char szSqlState[ SQL_SQLSTATE_SIZE ], szAltSqlState[ SQL_SQLSTATE_SIZE ];
SDWORD dwNativeError = 0, dwAltNativeError = 0;
SWORD dummy;
RETCODE retCode;
/* Get the initial ODBC error info. We pre-set the native error codes
to set because they sometimes aren't set by SQLError() */
retCode = SQLError( dbmsInfo->hEnv, hdbc, hstmt, szSqlState,
&dwNativeError, dbmsInfo->errorMessage,
MAX_ERRMSG_SIZE - 1, &dummy );
dbmsInfo->errorCode = ( int ) dwNativeError; /* Usually 0 */
/* Work around a bug in ODBC 2.0 drivers (still present on older NT 4
machines) in which the primary error is some bizarre nonsense value
and the actual error is present at the second level */
retCode = SQLError( dbmsInfo->hEnv, hdbc, hstmt, szAltSqlState,
&dwAltNativeError, altErrorMessage,
MAX_ERRMSG_SIZE - 1, &dummy );
if( !strncmp( szSqlState, "01004", 5 ) )
{
memcpy( szSqlState, szAltSqlState, SQL_SQLSTATE_SIZE );
strcpy( dbmsInfo->errorMessage, altErrorMessage );
}
/* Some of the information returned by SQLError() is pretty odd. It
usually returns an ANSI SQL2 error state in SQLSTATE, but also returns
a native error code in NativeError. However the NativeError codes
aren't documented anywhere, so we rely on SQLSTATE having a useful
value. We can also get SQL_NO_DATA_FOUND with SQLSTATE set to
"00000" and the error message string empty */
if( !strncmp( szSqlState, "S0002", 5 ) || /* ODBC 2.x */
!strncmp( szSqlState, "42S02", 5 ) || /* ODBC 3.x */
( !strncmp( szSqlState, "00000", 5 ) && retCode == SQL_NO_DATA_FOUND ) )
{
/* Make sure that the caller gets a sensible error message if they
try to examine the extended error information */
if( !*dbmsInfo->errorMessage )
strcpy( dbmsInfo->errorMessage, "No data found." );
return( CRYPT_ERROR_NOTFOUND );
}
/* When we're trying to create a new keyset, there may already be one
present giving an S0001 (table already exists) or S0011 (index
already exists) error . We could check for the table by doing a
dummy read, but it's easier to just try the update anyway and convert
the error code to the correct value here if there's a problem */
if( !strncmp( szSqlState, "S0001", 5 ) ||
!strncmp( szSqlState, "S0011", 5 ) || /* ODBC 2.x */
!strncmp( szSqlState, "42S01", 5 ) ||
!strncmp( szSqlState, "42S11", 5 ) ) /* ODBX 3.x */
return( CRYPT_ERROR_DUPLICATE );
/* This one is a bit odd: An integrity constraint violation occurred,
which means (among other things) that an attempt was made to write a
duplicate value to a column constrained to contain unique values. It
can also include things like writing a NULL value to a column
constrained to be NOT NULL, but this wouldn't normally happen so we
can convert this one to a duplicate data error */
if( !strncmp( szSqlState, "23000", 5 ) )
return( CRYPT_ERROR_DUPLICATE );
return( defaultStatus );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -