/*!
  @file           vcn85.cpp
  @author         Bernd Vorsprach - bernd.vorsprach@sap.com
  @author         Martin Reinecke - martin.reinecke@sap.com
  @brief          kernel connect/release/execute

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

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

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/

/* ------------------------------------------------------------------
 * includes
 * ------------------------------------------------------------------
 */
#include "hcn85.h"

#include "SAPDB/DBM/Srv/KernelAccess/DBMSrvKnl_Session.hpp"

#include "hcn50.h"
#include "hcn53.h"
#include "hcn80.h"
#include "hcn90.h"

#define CONTINUE_TXT   "CONTINUE\n"
#define END_TXT        "END     \n"
#define EMPTY_TXT      "        \n"

#define STRINGMARK_CHAR '\''
#define FIELDSEP_CHAR   ';'

static tcn00_Error cn85_ReadUserByType
  (const char           * args,
   char                 * replyData,
   int                  * replyLen,
   bool                 & bUserTypeFound,
   cn50DBMUser          & oUser)
{
  char         szUserType[PARAMETER_MAXLEN_CN90];
  int          nUser       = 0;
  const char * pValue;
  tcn00_Error	 nFuncReturn = OK_CN00;

  bUserTypeFound = false;

  // read parameters 
  cn90GetToken(args, szUserType, 1, PARAMETER_MAXLEN_CN90);
  if (strncmp(szUserType, USER_TYPE_CN50, strlen(USER_TYPE_CN50)) == 0) {
    bUserTypeFound = true;
    pValue = strchr(szUserType, ASSIGN_CHAR_CN50);
    if (pValue != NULL) {
      pValue++;
      if (strcmp(pValue, USERTYPE_DBM_CN50) == 0) {
        nUser = FIRSTDBM_CN50;
      } else if (strcmp(pValue, USERTYPE_DBA_CN50) == 0) {
        nUser = SYSDBA_CN50;
      } else if (strcmp(pValue, USERTYPE_SAP_CN50) == 0) {
        nUser = SAPUSR_CN50;
      } else if (strcmp(pValue, USERTYPE_DOM_CN50) == 0) {
        nUser = DOMAIN_CN50;
      } else {
        nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00, 0, "unknown usertype");
      } // end if
    } else {
      nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00, 0, "missing usertype");
    } // end if
  } // end if

  if (bUserTypeFound && (nFuncReturn == OK_CN00)) {
    oUser.setUserName( oUser.getUser(nUser));
    if (strlen(oUser.getUserName()) > 0) {
      if (oUser.reload() != OK_CN00) {
        nFuncReturn = cn90AnswerEvent(replyData, replyLen, oUser.lastEvent());
      } // end if
    } else {
      nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00, 0, "user for usertype not found");
    } // end if
  } // end if
 
  return nFuncReturn;
} // end cn85_ReadUserByType

/*! @brief cn85_KernelConnect */
static tcn00_Error cn85_KernelConnect (
      char                 * replyData,
      int                  * replyLen,
      DBMSrvKnl_Session & oSession,
      const char           * szDatabase,
      const char           * szUsername,
      const char           * szPassword,
      bool                   bWithFallback,
      bool                   bEvent)
{
  tcn00_Error	         nFuncReturn = OK_CN00;

  cn90AnswerOK(replyData, replyLen, NULL);

  // check database state
  if ((cn90DBState(szDatabase) != STATE_ONLINE_CN00   ) &&
      (cn90DBState(szDatabase) != STATE_STANDBY_CN00) &&
      (cn90DBState(szDatabase) != STATE_ADMIN_CN00  )    ) {
    nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_DBNORUN_CN00);
  } // end if

  if (nFuncReturn == OK_CN00) {
    if (bEvent) {
      if (!oSession.EventConnect(szDatabase)) {
        nFuncReturn = cn90AnswerMessage(replyData, replyLen, oSession.LastMessage());
      } // end if
    } else {
      if (!oSession.NormalConnect(szDatabase, szUsername, szPassword)) {
        if ((oSession.LastMessage().ID() == ERR_LOGFULL_CN00) && bWithFallback) {
          if (!oSession.FallbackConnect(szDatabase, szUsername, szPassword)) {
            nFuncReturn = cn90AnswerMessage(replyData, replyLen, oSession.LastMessage());
          } // end if
        } else {
          nFuncReturn = cn90AnswerMessage(replyData, replyLen, oSession.LastMessage());
        } // end if
      } // end if
    } // end if
  } // end if

  return nFuncReturn;
} /* end cn85_Connect */

/*! @brief cn85GetKernelSession */
tcn00_Error cn85GetKernelSession
  ( VControlDataT *          vcontrol, 
    char          *          replyData,
    int           *          replyLen,
    char          *          szUser,
    char          *          szPwd )
{
  tcn00_Error     nFuncReturn = OK_CN00;

  DBMSrvKnl_Session * pSession = new DBMSrvKnl_Session;
  if (pSession == NULL) {
    nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_MEM_CN00);
  } else {
    vcontrol->pKernelSession =  pSession;
  } // end if

  if (nFuncReturn == OK_CN00) {
    nFuncReturn = cn85_KernelConnect(replyData, replyLen, *pSession, vcontrol->dbname.asCharp(),
                                     szUser, szPwd, true, false);

  } // end if

  if (nFuncReturn == OK_CN00) {
    cn90AnswerOK(replyData, replyLen, NULL);
  } else {
    cn85FreeKernelSession(vcontrol); 
  } // end if

  return nFuncReturn;
} // end cn85GetKernelSession

/*! @brief cn85GetKernelSession */
tcn00_Error cn85GetKernelSession
  ( VControlDataT *          vcontrol, 
    char          *          replyData,
    int           *          replyLen,
    char          *          szUserPwd )
{
  tcn00_Error     nFuncReturn = OK_CN00;
  tcn00_UserNamec szUser;
  tsp00_Namec     szPassword;

  char * pPassword = strchr(szUserPwd, PWD_SEPARATOR_CN00);
  if (pPassword != NULL) {
    *pPassword = CHAR_STRINGTERM_CN90;
    pPassword++;
    cn90Uncrypt(pPassword, false);
    szUser.rawAssign(szUserPwd);
    szPassword.rawAssign(pPassword);
  } else {
    nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00, 1, "user name or password not found");
  } // end if

  if (nFuncReturn == OK_CN00) {
    nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, szUser.asCharp(), szPassword.asCharp());
  } // end if
  
  return nFuncReturn;
} // end cn85GetKernelSession

/*! @brief cn85GetKernelSession */
tcn00_Error cn85GetKernelSession
  ( VControlDataT *          vcontrol, 
    char          *          replyData,
    int           *          replyLen )
{
  tcn00_Error     nFuncReturn = OK_CN00;
  tcn00_UserNamec szUser;
  tsp00_Namec     szPassword;

  cn50DBMUser  usrDBM(vcontrol->dbname, cn50DBMUser::getColdUser(vcontrol->dbname));
  if (usrDBM.existsOnDisk() ) {
    szUser     = usrDBM.getUserName();
    szPassword = usrDBM.getClearMasterPwd();
  } else {
    nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_USRREAD_CN00);
  } // end if

  if (nFuncReturn == OK_CN00) {
    nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, szUser.asCharp(), szPassword.asCharp());
  } // end if

  return nFuncReturn;
} // end cn85GetKernelSession

/*! @brief cn85GetKernelSession */
tcn00_Error cn85GetKernelSession
  ( VControlDataT *          vcontrol, 
    char          *          replyData,
    int           *          replyLen, 
    bool                   & bTempConnect )
{
  tcn00_Error     nFuncReturn = OK_CN00;

  if (vcontrol->pKernelSession != NULL) {
    bTempConnect = false;
  } // end if

  if (vcontrol->pKernelSession == NULL) {
    if (bTempConnect) {
      nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen);
    } else {
      nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_NOUTILSESSION_CN00);
    } // end if
  } // end if

  bTempConnect = (nFuncReturn != OK_CN00) ? false : bTempConnect;
  
  return nFuncReturn;
} // end cn85GetKernelSession

/*! @brief cn85_DBExecute */
static tcn00_Error cn85_DBExecute
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax,
      bool            bNice)
{
  tcn00_Error nFuncReturn = OK_CN00;
  bool        bTemporary  = true;
  DBMSrvKnl_Session::ResultType oResultType = DBMSrvKnl_Session::resultUnknown;

  char        szToken   [PARAMETER_MAXLEN_CN90];
  bool        bUserTypeFound = false;
  cn50DBMUser oUser(vcontrol->dbname);

  // DBA history begin
  const char *     pAction = "";
  bool             bVerify     = false;
  tsp00_Timestampc szTimeStamp;
  if (cn80CheckVerify(command->args, pAction)) {
    bVerify = true;
    cn90Timestamp(szTimeStamp);
    tcn53_DBAAction::start(vcontrol->dbname, szTimeStamp, pAction, DBA_OBJ_DB_CN53);
    tcn53_DBAAction::writeDetail(DBADTL_CDBCMD_CN53, command->args);
  } // end if
  // DBA history end

  cn90GetToken(command->args, szToken, 1, PARAMETER_MAXLEN_CN90);

  nFuncReturn = cn85_ReadUserByType(command->args, replyData, replyLen,bUserTypeFound, oUser);
  if (nFuncReturn == OK_CN00) {
    if (bUserTypeFound) {
      bTemporary  = false;
      nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, bTemporary);
      if (nFuncReturn == ERR_NOUTILSESSION_CN00) {
        bTemporary  = true;
        nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, oUser.getUserName().asCharp(), oUser.getClearMasterPwd().asCharp());
        if (nFuncReturn == OK_CN00) {
          command->setArgs(command->args + strlen(szToken));
        } // end if
      } else {
        nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00, 0, "kernel session already exists");
      } // end if
    } else {
      bTemporary  = true;
      nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, bTemporary);
    } // end if
  } // end if
  
  if (nFuncReturn == OK_CN00) {
    if (!vcontrol->pKernelSession->Execute(command->args, oResultType)) {
      nFuncReturn = cn90AnswerMessage(replyData, replyLen, vcontrol->pKernelSession->LastMessage());
    } else {
      switch (oResultType) {
        case DBMSrvKnl_Session::resultNothing:
          cn90AnswerOK(replyData, replyLen, NULL);
          break;
        case DBMSrvKnl_Session::resultMessage:
          {
            SAPDBErr_MessageList oDBM(DBMSrv_ExtendedError(OK_CN00), cn00_1_ErrTxt(OK_CN00_1), 0);
            oDBM.AppendNewMessage(vcontrol->pKernelSession->KernelMessage());
            cn90AnswerMessage(replyData, replyLen, oDBM);
          } // end block
          break;
        case DBMSrvKnl_Session::resultSQL:
          if (bNice) {
            nFuncReturn = cn85DBFetchNice(vcontrol, command, replyData, replyLen, replyLenMax);
          } else {
            nFuncReturn = cn85DBFetch(vcontrol, command, replyData, replyLen, replyLenMax);
          } // end if
          break;
        case DBMSrvKnl_Session::resultUtility:
          {
            DBMSrv_SQLRecord oRecord(*(vcontrol->pKernelSession));
            tsp00_Int4 i = 0;
            cn90AnswerOK(replyData, replyLen, NULL);

            char * pCurrent = replyData + strlen(replyData);

            for (i = 1; i <= oRecord.fields(); ++i) {
              sprintf(pCurrent, "%-30s = %s%s", oRecord.fieldName(i).CharPtr(), oRecord.fieldAsChar(i, 0).CharPtr(), LINE_SEPSTRING_CN00);
              pCurrent = pCurrent + strlen(pCurrent);
            } // end if
            *replyLen = (int) strlen(replyData);
          } // end block
          break;
        default:
          nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_SQL_CN00, -1, "unknown kernel answer");
          break;
      } // end switch
    } // end if
  } // end if

  if (bTemporary) {
    cn85FreeKernelSession ( vcontrol );
  } // end if

  // DBA history begin
  if (bVerify) {
    tcn53_DBAAction::writeDetail(DBADTL_CDBRESULT_CN53, replyData);
    tcn53_DBAAction::finish(cn90Timestamp(szTimeStamp), (nFuncReturn == OK_CN00));
  } // end if
  // DBA history end

  return nFuncReturn;
} // end cn85_DBExecute

/*! @brief cn85DBConnect */
tcn00_Error cn85DBConnect
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  tcn00_Error nFuncReturn = OK_CN00;
  char        szToken   [PARAMETER_MAXLEN_CN90];
  bool        bUserTypeFound = false;
  cn50DBMUser oUser(vcontrol->dbname);
  /**/

  cn85FreeKernelSession(vcontrol); 

  if (cn90GetToken(command->args, szToken, 1, PARAMETER_MAXLEN_CN90)) {
    nFuncReturn = cn85_ReadUserByType(command->args, replyData, replyLen,bUserTypeFound, oUser);
    if (nFuncReturn == OK_CN00) {
      if (bUserTypeFound) {
        nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, oUser.getUserName().asCharp(), oUser.getClearMasterPwd().asCharp());
      } else {
        nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, szToken);
      } // end if
    } // end if
  } else {
    nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen);
  } // end if

  if (nFuncReturn == OK_CN00) {
    cn90AnswerOK(replyData, replyLen, NULL);
  } // end if

  return nFuncReturn;
} // end cn85DBConnect

/*! @brief cn85DBRelease */
tcn00_Error cn85DBRelease
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  cn85FreeKernelSession ( vcontrol );
  return cn90AnswerOK(replyData, replyLen, NULL);
} // end cn85DBRelease

/*! @brief cn85DBExecute */
tcn00_Error cn85DBExecute 
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  return cn85_DBExecute(vcontrol, command, replyData, replyLen, replyLenMax, false);
} // end cn85DBExecute

/*! @brief cn85DBExecuteSQL */
tcn00_Error cn85DBExecuteSQL 
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  return cn85_DBExecute(vcontrol, command, replyData, replyLen, replyLenMax, false);
} // end cn85DBExecuteSQL

/*! @brief cn85DBExecuteNice */
tcn00_Error cn85DBExecuteNice 
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  return cn85_DBExecute(vcontrol, command, replyData, replyLen, replyLenMax, true);
} // end cn85DBExecuteNice

/*! @brief cn85DBFetch */
tcn00_Error cn85DBFetch
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  tcn00_Error nFuncReturn = OK_CN00;
  bool        bTemporary  = false;

  nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, bTemporary);

  if (nFuncReturn == OK_CN00) {

    DBMSrv_SQLRecord oRecord(*(vcontrol->pKernelSession));
    bool             bEnd      = false;
    bool             bContinue = false;

    cn90AnswerOK(replyData, replyLen, NULL);
    tsp00_Int4 i = 0;

    char * pCurrent  = replyData + strlen(replyData);
    char * pContinue = pCurrent;
    SAPDB_strcpy(pCurrent, CONTINUE_TXT);
    pCurrent  = pCurrent + strlen(pCurrent);
    char * pRecord  = pCurrent;

    Tools_DynamicUTF8String sField;

    do {
      if (bEnd) {
        break; 
      } // end if
      
      pRecord  = pCurrent;

      // read record
      for (i = 1; i <= oRecord.fields(); ++i) {
        sField = oRecord.fieldAsChar(i, STRINGMARK_CHAR);
        if ((pCurrent + sField.Length() + 1) - replyData < replyLenMax) {
          sprintf(pCurrent, "%s%c", sField.CharPtr(), FIELDSEP_CHAR);
          pCurrent = pCurrent + strlen(pCurrent);
        } else {
          bContinue = true;
          break;
        } // end if
      } // end for

      if (bContinue) {
        *pRecord = CHAR_STRINGTERM_CN90;
        break;
      } else {
        *(pCurrent - 1) = '\n';
      } // end if

    } while (oRecord.getNextRecord(bEnd));

    if (!vcontrol->pKernelSession->LastMessage().IsEmpty()) {
      nFuncReturn = cn90AnswerMessage(replyData, replyLen, vcontrol->pKernelSession->LastMessage());
    } else {
      if (bEnd) {
        strncpy(pContinue, END_TXT, strlen(END_TXT));
      } else {
        // prepare next command 
        vcontrol->szNextCommand.rawAssign("db_fetch");
        vcontrol->nNextCommandSkip = 1;
      } // end if
    } // end if

    *replyLen = (int) strlen(replyData);

  } // end if

  return nFuncReturn;
} // end cn85DBFetch

/*! @brief cn85DBFetchNice */
tcn00_Error cn85DBFetchNice
    ( VControlDataT * vcontrol,
      CommandT      * command,
      char          * replyData,
      int           * replyLen,
      int             replyLenMax )
{
  tcn00_Error nFuncReturn = OK_CN00;
  bool        bTemporary  = false;

  nFuncReturn = cn85GetKernelSession(vcontrol, replyData, replyLen, bTemporary);

  if (nFuncReturn == OK_CN00) {

    DBMSrv_SQLRecord oRecord(*(vcontrol->pKernelSession));
    bool             bEnd      = false;
    bool             bContinue = false;

    cn90AnswerOK(replyData, replyLen, NULL);
    tsp00_Int4 i = 0;

    char * pCurrent  = replyData + strlen(replyData);
    char * pContinue = pCurrent;
    SAPDB_strcpy(pCurrent, CONTINUE_TXT);
    pCurrent  = pCurrent + strlen(pCurrent);


    for (i = 1; i <= oRecord.fields(); ++i) {
      sprintf(pCurrent, "%-30s = %s%s", oRecord.fieldName(i).CharPtr(), oRecord.fieldAsChar(i, STRINGMARK_CHAR).CharPtr(), LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + strlen(pCurrent);
    } // end if

    *replyLen = (int) strlen(replyData);

    oRecord.getNextRecord(bEnd);

    if (!vcontrol->pKernelSession->LastMessage().IsEmpty()) {
      nFuncReturn = cn90AnswerMessage(replyData, replyLen, vcontrol->pKernelSession->LastMessage());
    } else {
      if (bEnd) {
        strncpy(pContinue, EMPTY_TXT, strlen(EMPTY_TXT));
      } else {
        // prepare next command 
        vcontrol->szNextCommand.rawAssign("db_fetchnice");
        vcontrol->nNextCommandSkip = 1;
      } // end if
    } // end if

    *replyLen = (int) strlen(replyData);

  } // end if

  return nFuncReturn;
} // end cn85DBFetchNice

/*! @brief cn85FreeKernelSession */
void cn85FreeKernelSession
    ( VControlDataT * vcontrol )
{
  DBMSrvKnl_Session * pSession;

  if (vcontrol != NULL) {
    pSession =  (DBMSrvKnl_Session *) vcontrol->pKernelSession;
    vcontrol->pKernelSession = NULL;
  } // end if

  if (pSession != NULL) {
    pSession->Release();
    delete pSession;
  } // end if

} // end cn85FreeKernelSession
