--- ../snort-1.9.0/src/output-plugins/spo_database.c	Fri Sep 20 13:41:49 2002
+++ src/output-plugins/spo_database.c	Mon Nov 11 11:28:44 2002
@@ -150,11 +150,11 @@
     MYSQL_ROW m_row;
 #endif
 #ifdef ENABLE_ODBC
-    SQLHENV u_handle;
+    SQLHENV u_environment;
     SQLHDBC u_connection;
     SQLHSTMT u_statement;
     SQLINTEGER  u_col;
-    SQLINTEGER  u_rows;
+    SQLCHAR u_quote_char[2];
 #endif
 #ifdef ENABLE_ORACLE
     OCIEnv *o_environment;
@@ -222,6 +222,9 @@
 static int instances = 0;
 
 /******** Database Specific Extras  ************************************/
+#ifdef ENABLE_ODBC
+static void odbc_errors(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt);
+#endif /* ENABLE_ODBC */
 
 /* The following is for supporting Microsoft SQL Server */
 #ifdef ENABLE_MSSQL
@@ -808,14 +811,37 @@
     unsigned int ref_id, class_id=0;
     ClassType *class_ptr;
     ReferenceNode *refNode;
-
+#ifdef ENABLE_DB_TRANSACTIONS
+# ifdef ENABLE_ODBC
+    /*
+     *  For ODBC you should not really be using BEGIN, ROLLBACK, COMMIT
+     *  and in any case not all DBs are SQL92 conformant (which need
+     *  BEGIN TRANSACTION, not BEGIN). A much better method is to turn off
+     *  autocommit behavior and commit or rollback the transaction with
+     *  SQLTransact/SQLEndTran.
+     *
+     *  transactions_disabled set if we are unable to turn off autocommit
+     *  and hence cannot do the SQLTransact() later.
+     */
+    int transactions_disabled = 0;
+# endif
+#endif
+    
     query = NewQueryNode(NULL, 0);
     root = query;
 
 #ifdef ENABLE_DB_TRANSACTIONS
+# ifdef ENABLE_ODBC
+    if (SQL_SUCCESS != SQLSetConnectOption(data->u_connection,
+                                           SQL_AUTOCOMMIT,
+                                           SQL_AUTOCOMMIT_OFF)) {
+        odbc_errors(NULL, data->u_connection, NULL);
+        transactions_disabled = 1;
+    }
+# else
     Insert("BEGIN", data);
+# endif
 #endif
-    
     if(msg == NULL)
         msg = "";
 
@@ -825,10 +851,11 @@
     else
         tmp = GetCurrentTimestamp();
 
-#ifdef ENABLE_MSSQL
-    if(data->shared->dbtype_id == DB_MSSQL)
+#if defined(ENABLE_MSSQL) || defined(ODBC)
+    if((data->shared->dbtype_id == DB_MSSQL) ||
+       (data->shared->dbtype_id == DB_ODBC))
     {
-        /* SQL Server uses a date format which is slightly
+        /* SQL Server (and ODBC) uses a date format which is slightly
          * different from the ISO-8601 standard generated
          * by GetTimestamp() and GetCurrentTimestamp().  We
          * need to convert from the ISO-8601 format of:
@@ -1331,7 +1358,16 @@
         if ( Insert(query->val,data) == 0 )
         {
 #ifdef ENABLE_DB_TRANSACTIONS
+# ifdef ENABLE_ODBC
+            if (!transactions_disabled) {
+                SQLTransact(SQL_NULL_HANDLE, data->u_connection, SQL_ROLLBACK);
+                SQLSetConnectOption(data->u_connection,
+                                    SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
+                transactions_disabled = 1;      /* prevent SQLTransact again below */
+            }
+# else
            Insert("ROLLBACK", data);
+# endif
 #endif
            break;
         }
@@ -1344,7 +1380,17 @@
     data->shared->cid++;
 
 #ifdef ENABLE_DB_TRANSACTIONS
+# ifdef ENABLE_ODBC
+    if (!transactions_disabled) {
+        if (SQL_SUCCESS != SQLTransact(SQL_NULL_HANDLE,
+                                       data->u_connection, SQL_COMMIT))
+            odbc_errors(NULL, data->u_connection, NULL);
+        SQLSetConnectOption(data->u_connection,
+                            SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
+    }
+# else
     Insert("COMMIT", data);
+# endif
 #endif
     
     /* A Unixodbc bugfix */
@@ -1406,6 +1452,25 @@
       }
     }
     else
+#else if ENABLE_ODBC
+    /*
+     *  In ODBC ' is used to delimit strings and a ' inside a string is
+     *  doubled up not escaped. There should not need to be any other
+     *  translations.
+     */
+    if (data->shared->dbtype_id == DB_ODBC)
+    {
+        for (end=from+from_length; from != end; from++)
+        {
+            if (*from == '\'') {
+                *to++ = '\'';
+                *to++ = '\'';
+            }
+            else
+                *to++= *from; 
+        }
+    }
+    else
 #endif
     {
       for(end=from+from_length; from != end; from++)
@@ -1516,7 +1581,7 @@
 
    select0 = (char *) malloc (MAX_QUERY_LENGTH+1);
 
-#ifndef ENABLE_MSSQL
+#ifdef ENABLE_MSSQL
    if ( data->shared->dbtype_id == DB_MSSQL )
       /* "schema" is a keyword in SQL Server, so use square brackets
        *  to indicate that we are referring to the table
@@ -1524,6 +1589,13 @@
       snprintf(select0, MAX_QUERY_LENGTH, "SELECT vseq FROM [schema]");
    else
 #endif
+#ifdef ENABLE_ODBC
+       if (data->shared->dbtype_id == DB_ODBC)
+           snprintf(select0, MAX_QUERY_LENGTH,
+                    "SELECT vseq FROM %sschema%s",
+                    data->u_quote_char, data->u_quote_char);
+       else
+#endif
       snprintf(select0, MAX_QUERY_LENGTH, "SELECT vseq FROM schema");
 
    schema_version = Select(select0,data);
@@ -1601,10 +1673,16 @@
 #ifdef ENABLE_ODBC
     if(data->shared->dbtype_id == DB_ODBC)
     {
-        if(SQLAllocStmt(data->u_connection, &data->u_statement) == SQL_SUCCESS)
-            if(SQLPrepare(data->u_statement, query, SQL_NTS) == SQL_SUCCESS)
-                if(SQLExecute(data->u_statement) == SQL_SUCCESS)
-                    result = 1;
+        SQLRETURN       osts;
+        
+        osts = SQLExecDirect(data->u_statement, query, SQL_NTS);
+        if (!SQL_SUCCEEDED(osts)) {
+            odbc_errors(NULL, NULL, data->u_statement);
+        }
+        else {
+            result = 1;
+        }
+        SQLFreeStmt(data->u_statement, SQL_CLOSE);
     }
 #endif
 
@@ -1737,25 +1815,46 @@
 #ifdef ENABLE_ODBC
     if(data->shared->dbtype_id == DB_ODBC)
     {
-        if(SQLAllocStmt(data->u_connection, &data->u_statement) == SQL_SUCCESS)
-            if(SQLPrepare(data->u_statement, query, SQL_NTS) == SQL_SUCCESS)
-                if(SQLExecute(data->u_statement) == SQL_SUCCESS)
-                    if(SQLRowCount(data->u_statement, &data->u_rows) == SQL_SUCCESS)
-                        if(data->u_rows)
-                        {
-                            if(data->u_rows > 1)
-                            {
-                                ErrorMessage("database: warning (%s) returned more than one result\n", query);
-                                result = 0;
-                            }
-                            else
-                            {
-                                if(SQLFetch(data->u_statement) == SQL_SUCCESS)
-                                    if(SQLGetData(data->u_statement,1,SQL_INTEGER,&data->u_col,
-                                                  sizeof(data->u_col), NULL) == SQL_SUCCESS)
-                                        result = (int)data->u_col;
-                            }
-                        }
+        SQLRETURN       osts;
+
+        osts = SQLExecDirect(data->u_statement, query, SQL_NTS);
+        if (!SQL_SUCCEEDED(osts)) {
+            odbc_errors(NULL, NULL, data->u_statement);
+        }
+        else {
+            SQLSMALLINT         cols;
+            
+            /*
+             *  This code used to use SQLRowCount which hardly ever returns
+             *  anything other than -1 in ODBC drivers for select and was
+             *  just working by accident.
+             */
+            osts = SQLNumResultCols(data->u_statement, &cols);
+            if (!SQL_SUCCEEDED(osts)) {
+                odbc_errors(NULL, NULL, data->u_statement);
+            }
+            else if (cols != 1) {
+                ErrorMessage("database: warning (%s) returned more than "
+                             "one column\n", query);
+                result = 0;
+            }
+            else {
+                if (SQLFetch(data->u_statement) != SQL_SUCCESS) {
+                    odbc_errors(NULL, NULL, data->u_statement);
+                }
+                else {
+                    if (SQLGetData(data->u_statement,1,SQL_INTEGER,
+                                   &data->u_col, sizeof(data->u_col),
+                                   NULL) != SQL_SUCCESS) {
+                        odbc_errors(NULL, NULL, data->u_statement);
+                    }
+                    else {
+                        result = (int)data->u_col;
+                    }
+                }
+            }
+            SQLFreeStmt(data->u_statement, SQL_CLOSE);
+        }
     }
 #endif
 
@@ -1860,19 +1959,35 @@
 #ifdef ENABLE_ODBC
     if(data->shared->dbtype_id == DB_ODBC)
     {
-        if(!(SQLAllocEnv(&data->u_handle) == SQL_SUCCESS))
+        if (SQLAllocEnv(&data->u_environment) != SQL_SUCCESS)
         {
             FatalError("database: unable to allocate ODBC environment\n");
         }
-        if(!(SQLAllocConnect(data->u_handle, &data->u_connection) ==
-             SQL_SUCCESS))
+        if (SQLAllocConnect(data->u_environment, &data->u_connection) !=
+            SQL_SUCCESS)
         {
+            odbc_errors(data->u_environment, NULL, NULL);
             FatalError("database: unable to allocate ODBC connection handle\n");
         }
-        if(!(SQLConnect(data->u_connection, data->shared->dbname, SQL_NTS, data->user, SQL_NTS, data->password, SQL_NTS) == SQL_SUCCESS))
+        if(!SQL_SUCCEEDED(SQLConnect(data->u_connection,
+                                     data->shared->dbname, SQL_NTS,
+                                     data->user, SQL_NTS,
+                                     data->password, SQL_NTS)))
         {
+            odbc_errors(NULL, data->u_connection, NULL);
             FatalError("database: ODBC unable to connect\n");
         }
+        if (!SQL_SUCCEEDED(SQLGetInfo(data->u_connection,
+                                      SQL_IDENTIFIER_QUOTE_CHAR,
+                                      &data->u_quote_char,
+                                      sizeof(data->u_quote_char), NULL )))
+        {
+            strcpy( data->u_quote_char, "" );
+        }
+        if (SQL_SUCCESS != SQLAllocStmt(data->u_connection, &data->u_statement))        {
+            odbc_errors(NULL, data->u_connection, NULL);
+            FatalError("database: Unable to allocate statement\n");
+        }
     }
 #endif
 
@@ -2007,10 +2122,14 @@
 #ifdef ENABLE_ODBC
         if(data->shared->dbtype_id == DB_ODBC)
         {
-            if(data->u_handle)
+            if(data->u_environment)
             {
-                SQLDisconnect(data->u_connection); 
-                SQLFreeHandle(SQL_HANDLE_ENV, data->u_handle); 
+                SQLFreeStmt(data->u_statement, SQL_DROP);
+                /*  Just in cas we get here with an outstanding transaction */
+                SQLEndTran(SQL_HANDLE_DBC, data->u_connection, SQL_ROLLBACK);
+                SQLDisconnect(data->u_connection);
+                SQLFreeConnect(data->u_connection);
+                SQLFreeEnv(data->u_environment);
             }
         }
 #endif
@@ -2121,6 +2240,28 @@
        free(current);
    }
 }
+#ifdef ENABLE_ODBC
+static void odbc_errors(
+    SQLHENV     henv,
+    SQLHDBC     hdbc,
+    SQLHSTMT    hstmt)
+{
+    SQLRETURN   ests;
+    SQLCHAR     errmsg[512];
+    SQLCHAR     state[7];
+    SQLINTEGER  native;
+    SQLSMALLINT errmsg_len;
+    
+    do {
+        ests = SQLError(henv, hdbc, hstmt, state, &native, errmsg,
+                        sizeof(errmsg), &errmsg_len);
+        if (SQL_SUCCEEDED(ests)) {
+            ErrorMessage("database: ODBC state:%s, native:%ld,%s\n",
+                         state, native, errmsg);
+        }
+    } while (SQL_SUCCEEDED(ests));
+}
+#endif /* ENABLE_ODBC */
 
 #ifdef ENABLE_MSSQL
 /*
