/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 picoSqlNet - picoSQL client library

 Copyright (C) Picosoft s.r.l. 1994-2002

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
static char rcsid[] = "$Id$";
# ifdef WIN32
# include <windows.h>
# include <sql.h>
# include <sqlext.h>
static char User[9 + 1];
static char Password[9 + 1];
extern "C" HINSTANCE ghInstance;
# else
extern "C" {
# include "sqltype.h"
# include "ctype.h"
}
# endif
# include "pstring.h"
# include "envirini.h"
# include "dberror.h"
# include "sqlhndls.h"
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "method.h"
#include "log.h"
#include "except.h"
}

const int DEFAULT_PORT = 6789;


extern "C" {

extern CorbaMethod methods[];
void i_SQLError (void ** argv) {}
void i_SQLTables (void ** argv) {}
void i_SQLPrepare (void ** argv) {}
void i_SQLExecute (void ** argv) {}
void i_SQLExecDirect (void ** argv) {}
void i_SQLNumResultCols (void ** argv) {}
void i_SQLRowCount (void ** argv) {}
void i_SQLDescribeCol (void ** argv) {}
void i_SQLFetch (void ** argv) {}
void i_SQLFetchBindCol (void ** argv) {}
void i_SQLExtendedFetch (void ** argv) {}
void i_SQLExtendedFetchBindCol (void ** argv) {}
void i_SQLGetData (void ** argv) {}
void i_SQLFreeStmt (void ** argv) {}
void i_SQLAllocEnv (void ** argv) {}
void i_SQLAllocConnect (void ** argv) {}
void i_SQLConnect (void ** argv) {}
void i_SQLAllocStmt (void ** argv) {}
void i_SQLDisconnect (void ** argv) {}
void i_SQLFreeConnect (void ** argv) {}
void i_SQLFreeEnv (void ** argv) {}
void i_SQLCancel (void ** argv) {}
void i_SQLColAttributes (void ** argv) {}
void i_SQLColumns (void ** argv) {}
void i_SQLBind (void ** argv) {}
void i_SQLGetConnectOption (void ** argv) {}
void i_SQLSetConnectOption (void ** argv) {}
void i_SQLGetStmtOption (void ** argv) {}
void i_SQLSetStmtOption (void ** argv) {}
void i_SQLBindParameter (void ** argv) {}
void i_SQLExecuteParams (void ** argv) {}
void i_SQLExecDirectParams (void ** argv) {}
void i_SQLTransact (void ** argv) {}
void i_SQLStatistics (void ** argv) {}
void i_SQLSpecialColumns (void ** argv) {}
void i_SQLGetTypeInfo (void ** argv) {}
void i_SQLGetCursorName (void ** argv) {}
void i_SQLSetCursorName (void ** argv) {}
void i_SQLNumParams (void ** argv) {}
void i_SQLPutData (void ** argv) {}
void i_SQLGetAllData (void ** argv) {}
void i_SQLTablePrivileges (void ** argv) {}
void i_SQLProcedures (void ** argv) {}
void i_SQLProcedureColumns (void ** argv) {}
void i_SQLPrimaryKeys (void ** argv) {}
void i_SQLMoreResults (void ** argv) {}
void i_SQLForeignKeys (void ** argv) {}
void i_SQLDescribeParam (void ** argv) {}
void i_SQLColumnPrivileges (void ** argv) {}

// dummy function implemented by the DM

RETCODE SQL_API SQLDataSources(HENV henv,
                               UWORD fDirection,
                               UCHAR FAR *szDSN,
                               SWORD cbDSNMax, 
                               SWORD FAR *pcbDSN,
                               UCHAR FAR *szDescription,
                               SWORD cbDescriptionMax,
                               SWORD FAR *pcbDescription)
{
   return SQL_NO_DATA_FOUND;
}

//   Allocate an environment (ENV) block.


RETCODE SQL_API SQLAllocEnv(
   HENV FAR * phenv)
{
   ClntHenv *henv =  new ClntHenv;
   *phenv = (HENV) henv;
   CORBA_init();
   return SQL_SUCCESS;
}
//   Allocate a DBC block.

RETCODE   SQL_API SQLAllocConnect(
   HENV   henv,
   HDBC FAR * phdbc)
{
   ClntHdbc *hdbc;
   ClntHenv *myHenv = (ClntHenv *) henv;

   if (!myHenv || !myHenv->IsA(ClntHenv::Class)) 
      return SQL_INVALID_HANDLE;
   
   hdbc = new ClntHdbc (myHenv);

   hdbc->obj = IiopObj_new(0, (CORBA_Octet*) "key", 4);
   hdbc->call = Invoke_new();

   *phdbc = (HDBC) hdbc;
   return SQL_SUCCESS;
}


RETCODE SQL_API SQLConnect(
   HDBC   hdbc,
   UCHAR FAR *szDSN,
   SWORD   cbDSN,
   UCHAR FAR *szUID,
   SWORD   cbUID,
   UCHAR FAR *szAuthStr,
   SWORD   cbAuthStr)
{
   ClntHdbc *myHdbc = (ClntHdbc *) hdbc;
   RETCODE Return = SQL_ERROR;
   char profString[128];
   char *nomeDB = 0;
   unsigned char *user = 0;
   unsigned char *password = 0;
   int sql_nts = SQL_NTS;
   EnvirIni odbcEnv;

   if (!myHdbc || !myHdbc->IsA(ClntHdbc::Class)) 
      return SQL_INVALID_HANDLE;

   DelError (0, 0, 0);
   nomeDB = (char *) toCorbaString (szDSN, cbDSN);
   user = toCorbaString (szUID, cbUID);
   password = toCorbaString (szAuthStr, cbAuthStr);

   if (odbcEnv.Read(nomeDB, "Server", profString, sizeof(profString)) > 0) {
      if (myHdbc->obj->host)
         myFree (myHdbc->obj->host);
      myHdbc->obj->host = (char *)myMalloc (strlen (profString) + 1);
      strcpy (myHdbc->obj->host, profString);
   } else {
      AddError (0, myHdbc, 0, "08000", 0, "Missing host in configuration!");
      return SQL_ERROR;
   }
   if (odbcEnv.Read(nomeDB, "Port", profString, sizeof(profString)) > 0)
      myHdbc->obj->port = atoi (profString);
   if (odbcEnv.Read(nomeDB, "Name", profString, sizeof(profString)) > 0)
      myHdbc->name = profString;
   else
      myHdbc->name = nomeDB;
   if (odbcEnv.Read(nomeDB, "NetLog", profString, sizeof(profString)) > 0)
      SetLogFile (profString);
   if (myHdbc->obj->port == 0)
      myHdbc->obj->port = DEFAULT_PORT;

   Exception *ex = Exception_new();
   if (!Invoke_init (myHdbc->call, ex, myHdbc->obj, methods)) {
      char stat[6];
      sprintf (stat,"08%03d", ex->code);
      AddError (0, myHdbc, 0, stat, ex->code, ex->description);
      Exception_log(ex);
   } else {
      void *argv[8];
      argv[0] = &Return;
      argv[1] = &myHdbc->henv->srvHenv;
      if (!Invoke_invoke (myHdbc->call, ex, "SQLAllocEnv", argv)) {
         char stat[6];
         sprintf (stat,"08%03d", ex->code);
         AddError (myHdbc->henv, 0, 0, stat, ex->code, ex->description);
         Exception_log(ex);
      } else {
         argv[0] = &Return;
         argv[1] = &myHdbc->henv->srvHenv;
         argv[2] = &myHdbc->srvHdbc;
         if (!Invoke_invoke (myHdbc->call, ex, "SQLAllocConnect", argv)) {
            char stat[6];
            sprintf (stat,"08%03d", ex->code);
            AddError (0, myHdbc, 0, stat, ex->code, ex->description);
            Exception_log(ex);
         } else {
            if (Return == SQL_SUCCESS) {
               argv[1] = &myHdbc->srvHdbc;
               argv[2] = myHdbc->name.gets();
               argv[3] = &sql_nts;
               argv[4] = user;
               argv[5] = &cbUID;
               argv[6] = password;
               argv[7] = &cbAuthStr;
               if (!Invoke_invoke (myHdbc->call, ex, "SQLConnect", argv)) {
                  char stat[6];
                  sprintf (stat,"08%03d", ex->code);
                  AddError (0, myHdbc, 0, stat, ex->code, ex->description);
                  Exception_log(ex);
               }
            }
         }
      }
   }
   Exception_delete (ex);
   if (nomeDB != (char *)szDSN)
      delete nomeDB;
   if (user != szUID)
      delete user;
   if (password != szAuthStr)
      delete password;
   return Return;
}

# ifdef WIN32

int FAR PASCAL FDriverConnectProc(
   HWND   hdlg,
   UINT   wMsg,
   WPARAM   wParam,
   LPARAM   lParam)
{
   switch (wMsg) {
   case WM_INITDIALOG:
      return 1;

   case WM_COMMAND:
      switch (wParam) {
      case IDOK:
         GetDlgItemText(hdlg, 1000, User, sizeof(User));
         GetDlgItemText(hdlg, 1001, Password, sizeof(Password));
         EndDialog(hdlg, 1);
         return PTrue;

      case IDCANCEL:
         EndDialog(hdlg, 0);
         return PTrue;
      }
   }
   return PFalse;
}
# endif // WIN32

//   This function as its "normal" behavior is supposed to bring up a
//   dialog box if it isn't given enough information via "szConnStrIn".  If
//   it is given enough information, it's supposed to use "szConnStrIn" to
//   establish a database connection.  In either case, it returns a
//   string to the user that is the string that was eventually used to
//   establish the connection.

RETCODE   SQL_API SQLDriverConnect(
   HDBC   hdbc,
   HWND   hwnd,
   UCHAR FAR *szConnStrIn,
   SWORD   cbConnStrIn,
   UCHAR FAR *szConnStrOut,
   SWORD   cbConnStrOutMax,
   SWORD FAR *pcbConnStrOut,
   UWORD fDriverCompletion)
{
   RETCODE Return = SQL_SUCCESS;
   PString nomeDB;
   PString nomeUID;
   PString nomePWD;
   PBool   fPrompt = PFalse;
   char *puntdsn = (char *)0;
   char *semicolon = (char *)0;
   int iRet;
   char ProfString[10]; 
   char ProfStringUPPER[10];
   PBool PasswordRequest = PFalse;
   EnvirIni odbcEnv;
   ClntHdbc *myHdbc = (ClntHdbc *) hdbc;

   if (!myHdbc || !myHdbc->IsA(ClntHdbc::Class)) 
      return SQL_INVALID_HANDLE;

   if ((cbConnStrIn < 0 && cbConnStrIn != SQL_NTS) ||
        cbConnStrOutMax < 0) {
      AddError (0, myHdbc, 0, "S1090", 0, "Invalid string or buffer length");
      Return = SQL_ERROR;
   } else {
      if ( ! (fDriverCompletion == SQL_DRIVER_PROMPT ||
              fDriverCompletion == SQL_DRIVER_COMPLETE ||
              fDriverCompletion == SQL_DRIVER_COMPLETE_REQUIRED ||
              fDriverCompletion == SQL_DRIVER_NOPROMPT) ) {
         AddError (0, myHdbc, 0, "S1110", 0, "Invalid driver completion");
         Return = SQL_ERROR;
      } else {
         if ((puntdsn = strstr((char *) szConnStrIn, "DSN=")) != (char *)0) {
            if ((semicolon = strchr(puntdsn, ';')) != 0) {
               *semicolon = 0;
               nomeDB = puntdsn + strlen("DSN=");
               *semicolon = ';';
            } else
               nomeDB = puntdsn + strlen("DSN=");
            if (odbcEnv.Read(nomeDB.gets(), "PasswordRequest", (char *) ProfString, sizeof(ProfString)) != 0) {
               strcpy(ProfStringUPPER, ProfString);
               puntdsn = ProfStringUPPER;
               while (*puntdsn)
                  *puntdsn++ = toupper(*puntdsn);
               if (!strcmp (ProfStringUPPER, "YES"))
                  PasswordRequest = PTrue; 
               else if (strcmp (ProfStringUPPER, "NO")) 
                  nomePWD = ProfString; 
            }
         } else if ((puntdsn = strstr((char *) szConnStrIn, "DRIVER=")) != (char *)0) {
            AddError (0, myHdbc, 0, "IM001", 0, "Driver does not support this function");
            return Return = SQL_ERROR;
         } else {
            AddError (0, myHdbc, 0, "IM007", 0, "No data source or driver specified");
            return Return = SQL_ERROR;
         }
         if ((puntdsn = strstr((char *) szConnStrIn, "UID=")) != (char *)0) {
            if ((semicolon = strchr(puntdsn, ';')) != 0) {
               *semicolon = 0;
               nomeUID = puntdsn + strlen("UID=");
               *semicolon = ';';
            } else
               nomeUID = puntdsn + strlen("UID=");
         } 
         if ((puntdsn = strstr((char *) szConnStrIn, "PWD=")) != (char *)0) {
            if ((semicolon = strchr(puntdsn, ';')) != 0) {
               *semicolon = 0;
               nomePWD = puntdsn + strlen("PWD=");
               *semicolon = ';';
            } else
               nomePWD = puntdsn + strlen("PWD=");
         } 

         if ((szConnStrIn == NULL) || (!cbConnStrIn) ||
             ((cbConnStrIn == SQL_NTS) && (!szConnStrIn[0])))
            fPrompt = PTrue;
         else {
        //  Check connection string for completeness
            if (fDriverCompletion == SQL_DRIVER_PROMPT)
              fPrompt = PTrue;
            else if ((fDriverCompletion == SQL_DRIVER_COMPLETE ||
                      fDriverCompletion == SQL_DRIVER_COMPLETE_REQUIRED) &&
                      nomePWD.size() == 0 && PasswordRequest)
              fPrompt = PTrue;
         }
# ifdef WIN32         
         if (fPrompt) {
        //   It is not necessary to call "MakeProcInstance" if you
        //   generate a dialog box from a DLL.
        //
           iRet = DialogBox(ghInstance, "EDRIVERCONNECT", hwnd, (DLGPROC) FDriverConnectProc);
           if ((!iRet) || (iRet == -1))
              return SQL_NO_DATA_FOUND;
           else {
              nomeUID = User;
              nomePWD = Password;
           }   
         }
# endif         
         Return = SQLConnect(hdbc,
                            (UCHAR *) nomeDB.gets(), (SWORD) nomeDB.size(),
                            (UCHAR *) nomeUID.gets(), (SWORD) nomeUID.size(),
                            (UCHAR *) nomePWD.gets(), (SWORD) nomePWD.size());
         if (szConnStrOut != (UCHAR FAR *) 0) {
            sprintf ((char *) szConnStrOut, "DSN=%s", nomeDB.gets());
            puntdsn = (char *) (szConnStrOut + strlen((char *) szConnStrOut));
            if (nomeUID.size() > 0) {
               sprintf ((char *) puntdsn, ";UID=%s", nomeUID.gets());
               puntdsn = (char *) (szConnStrOut + strlen((char *) szConnStrOut));
            }
            if (nomePWD.size() > 0)
               sprintf ((char *) puntdsn, ";PWD=%s", nomePWD.gets());
         }
         if (pcbConnStrOut != (SWORD FAR *) 0)
            *pcbConnStrOut = strlen((char *) szConnStrOut);
      }
   }
   return Return;
}


RETCODE SQL_API SQLDisconnect(
   HDBC   hdbc)
{
   ClntHdbc *myHdbc = (ClntHdbc *) hdbc;
   RETCODE Return = SQL_ERROR;

   void *argv[2];
   argv[0] = &Return;
   argv[1] = &myHdbc->srvHdbc;
   Exception *ex = Exception_new();
/* Ci sarebbe anche da interrompere la connessione !! */
   if (!Invoke_invoke (myHdbc->call, ex, "SQLDisconnect", argv)) {
      char stat[6];
      sprintf (stat,"08%03d", ex->code);
      AddError (0, myHdbc, 0, stat, ex->code, ex->description);
      Exception_log(ex);
   }
   Exception_delete (ex);
   return Return;
}

RETCODE   SQL_API SQLFreeConnect(
   HDBC   hdbc)
{
   ClntHdbc *myHdbc = (ClntHdbc *) hdbc;
   RETCODE Return = SQL_ERROR;

   void *argv[2];
   argv[0] = &Return;
   argv[1] = &myHdbc->srvHdbc;
   Exception *ex = Exception_new();
   if (!Invoke_invoke (myHdbc->call, ex, "SQLFreeConnect", argv)) {
      char stat[6];
      sprintf (stat,"08%03d", ex->code);
      AddError (0, myHdbc, 0, stat, ex->code, ex->description);
      Exception_log(ex);
   } else {
      if (Return == SQL_SUCCESS) {
         IiopObj_delete (myHdbc->obj);
         Invoke_delete (myHdbc->call);
         delete myHdbc;
      }
   }
   Exception_delete (ex);
   return Return;
}
 

RETCODE SQL_API SQLFreeEnv(
   HENV   henv)
{
   ClntHenv *myHenv = (ClntHenv *) henv;
   if (!myHenv || !myHenv->IsA(ClntHenv::Class)) 
      return SQL_INVALID_HANDLE;
   delete myHenv;
   return SQL_SUCCESS;
}

/*
RETCODE   SQL_API SQLBrowseConnect(
   HDBC   hdbc,
   UCHAR FAR *szConnStrIn,
   SWORD   cbConnStrIn,
   UCHAR FAR *szConnStrOut,
   SWORD   cbConnStrOutMax,
   SWORD FAR *pcbConnStrOut)
{   
   if (Trace.isSet)
      Trace.Write ("SQLBrowseConnect\n"); 
    return SQL_SUCCESS;
}
*/
};
