/* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.

   The MySQL Connector/ODBC is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
   MySQL Connectors. There are special exceptions to the terms and
   conditions of the GPLv2 as it is applied to this software, see the
   FLOSS License Exception
   <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
   
   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; version 2 of the License.
   
   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.,
   51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */

#include "installer.h"
#include "MYODBCUtil.h"


static const char* OPTION_NAMES[]= { "FOUND_ROWS",        /*FLAG_FOUND_ROWS*/
                                     "BIG_PACKETS",       /*FLAG_BIG_PACKETS*/
                                     "NO_PROMPT",         /*FLAG_NO_PROMPT*/
                                     "DYNAMIC_CURSOR",    /*FLAG_DYNAMIC_CURSOR*/
                                     "NO_SCHEMA",         /*FLAG_NO_SCHEMA*/
                                     "NO_DEFAULT_CURSOR",
                                     "NO_LOCALE",
                                     "PAD_SPACE",
                                     "FULL_COLUMN_NAMES",
                                     "COMPRESSED_PROTO",
                                     "IGNORE_SPACE",
                                     "NO_BIGINT",
                                     "NO_CATALOG",
                                     "USE_MYCNF",
                                     "SAFE",
                                     "NO_TRANSACTIONS",
                                     "LOG_QUERY",
                                     "NO_CACHE",
                                     "FORWARD_CURSOR",
                                     "AUTO_RECONNECT",
                                     "AUTO_IS_NULL",
                                     "ZERO_DATE_TO_MIN",
                                     "MIN_DATE_TO_ZERO",
                                     "MULTI_STATEMENTS",
                                     "COLUMN_SIZE_S32",
                                     "NO_BINARY_RESULT",
                                     "NO_I_S"               /*FLAG_NO_INFORMATION_SCHEMA*/
                                    };

static const ulong OPTION_FLAGS[]= { FLAG_FOUND_ROWS,
                                     FLAG_BIG_PACKETS,
                                     FLAG_NO_PROMPT,
                                     FLAG_DYNAMIC_CURSOR,
                                     FLAG_NO_SCHEMA,
                                     FLAG_NO_DEFAULT_CURSOR,
                                     FLAG_NO_LOCALE,
                                     FLAG_PAD_SPACE,
                                     FLAG_FULL_COLUMN_NAMES,
                                     FLAG_COMPRESSED_PROTO,
                                     FLAG_IGNORE_SPACE,
                                     FLAG_NO_BIGINT,
                                     FLAG_NO_CATALOG,
                                     FLAG_USE_MYCNF,
                                     FLAG_SAFE,
                                     FLAG_NO_TRANSACTIONS,
                                     FLAG_LOG_QUERY,
                                     FLAG_NO_CACHE,
                                     FLAG_FORWARD_CURSOR,
                                     FLAG_AUTO_RECONNECT,
                                     FLAG_AUTO_IS_NULL,
                                     FLAG_ZERO_DATE_TO_MIN,
                                     FLAG_MIN_DATE_TO_ZERO,
                                     FLAG_MULTI_STATEMENTS,
                                     FLAG_COLUMN_SIZE_S32,
                                     FLAG_NO_BINARY_RESULT,
                                     FLAG_NO_INFORMATION_SCHEMA

};

ulong NamedAttributeToFlag(const char* name)
{
  int i;

  for (i= 0; i < sizeof(OPTION_NAMES)/sizeof(char*);++i)
  {
    if (strcasecmp( name, OPTION_NAMES[i] ) == 0)
    {
      return OPTION_FLAGS[i];
    }
  }

  return 0;
}


/*!
    \internal
    \brief      Parse a string of "name=value" pairs.

                SQLDriverConnect receives connection information 
                as a list of attributes in the form of keyword-value pairs. Each 
                pair is terminated with a ';', and the entire list is 
                terminated with a '\0'.

    \note       This will not replace existing values in pDataSource.
*/  
BOOL MYODBCUtilReadConnectStr( MYODBCUTIL_DATASOURCE *pDataSource, LPCSTR pszStr )
{
    MYODBCUTIL_ATTR_PARSE_STATE nState      = MYODBCUTIL_ATTR_PARSE_STATE_NAME_START;
    char *                      pAnchorChar = (char *)pszStr;
    char *                      pScanChar   = (char *)pszStr;
    char *                      pszName     = 0; 

    ulong nOption= 0, nPositives=0, nNegatives= 0, flag= 0;

    /* short circuit if we have not been given stuff to parse */
    if ( !pszStr || !(*pszStr) )
        return FALSE;

    /* scan the input */
    while ( 1 )
    {
        switch ( nState )
        {
            case MYODBCUTIL_ATTR_PARSE_STATE_NAME_START:
                {
                    if ( isalpha( *pScanChar ) || *pScanChar == '{' )
                    {
                        pAnchorChar = pScanChar;
                        nState = MYODBCUTIL_ATTR_PARSE_STATE_NAME; 
                    }
                }
                break;
            case MYODBCUTIL_ATTR_PARSE_STATE_NAME:
                {
                    if ( (!isalpha( *pScanChar ) && !isdigit( *pScanChar ) && *pScanChar != '}') || *pScanChar == '=' )
                    {
                      /* To prevent a memory leak when use such connection strings UID=root;PWD=;SERVER=localhost;... */
                        if( pszName )
                            _global_free( pszName );
                        pszName = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );

                        if ( *pScanChar == '=' )
                            nState = MYODBCUTIL_ATTR_PARSE_STATE_VALUE_START;
                        else
                            nState = MYODBCUTIL_ATTR_PARSE_STATE_EQUAL;       
                    }
                }
                break;
            case MYODBCUTIL_ATTR_PARSE_STATE_EQUAL:
                {
                    if ( *pScanChar == '=' )
                        nState = MYODBCUTIL_ATTR_PARSE_STATE_VALUE_START;
                }
                break;
            case MYODBCUTIL_ATTR_PARSE_STATE_VALUE_START:
                {
                    if ( !isspace( *pScanChar ) )
                    {
                        pAnchorChar = pScanChar;
                        nState = MYODBCUTIL_ATTR_PARSE_STATE_VALUE;
                    }
                }
                break;
            case MYODBCUTIL_ATTR_PARSE_STATE_VALUE:
                {
                    /*
                        ODBC RULE

                        If any keywords are repeated in the connection string, the driver uses the value 
                        associated with the first occurrence of the keyword. If the DSN and DRIVER keywords 
                        are included in the same connection string, the Driver Manager and the driver use 
                        whichever keyword appears first.
                    */

                    /* ';' terminates name/value but we may not find one for last */
                    if ( *pScanChar == ';' || *pScanChar == '\0' )
                    {
                        if ( strcasecmp( pszName, "DATABASE" ) == 0 || strcasecmp( pszName, "DB" ) == 0 )
                        {
                            if ( !pDataSource->pszDATABASE )
                                pDataSource->pszDATABASE = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar ) ;
                        }
                        else if ( strcasecmp( pszName, "DESCRIPTION" ) == 0 || strcasecmp( pszName, "DESC" ) == 0 )
                        {
                            if ( !pDataSource->pszDESCRIPTION )
                                pDataSource->pszDESCRIPTION = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "DRIVER" ) == 0 )
                        {
                            if ( !pDataSource->pszDRIVER && !pDataSource->pszDSN ) /* we use one or other - whichever comes 1st */
                            {
                                pDataSource->pszDRIVER = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar ); /* friendly name such as; "MySQL ODBC 3.51 Driver" */
                                pDataSource->nConnect = MYODBCUTIL_DATASOURCE_CONNECT_DRIVER;
                            }
                        }
                        else if ( strcasecmp( pszName, "DSN" ) == 0 )
                        {
                            if ( !pDataSource->pszDSN && !pDataSource->pszDRIVER ) /* we use one or other - whichever comes 1st */
                            {
                                pDataSource->pszDSN = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                                pDataSource->nConnect = MYODBCUTIL_DATASOURCE_CONNECT_DSN;
                            }
                        }
                        else if ( strcasecmp( pszName, "OPTION" ) == 0 )
                        {    
                            if ( !pDataSource->pszOPTION )
                            {
                                pDataSource->pszOPTION = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                                nOption= strtoul(pDataSource->pszOPTION, NULL, 10);
                            }
                        }
                        else if( strcasecmp( pszName, "INTERACTIVE" ) == 0 )
                        {
                          char tmp= *pScanChar;
                          *pScanChar= '\0';
                          pDataSource->bINTERACTIVE= (atol(pAnchorChar) != 0);
                          *pScanChar= tmp;
                        }
                        /*
                            MYODBC RULE

                            We will 'read' variants on the standard keywords (but only write standard version).
                        */    
                        else if ( strcasecmp( pszName, "PWD" ) == 0 || strcasecmp( pszName, "PASSWORD" ) == 0 )
                        {    
                            if ( !pDataSource->pszPASSWORD )
                                pDataSource->pszPASSWORD = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "PORT" ) == 0 )
                        {    
                            if ( !pDataSource->pszPORT )
                                pDataSource->pszPORT = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SERVER" ) == 0 )
                        {    
                            if ( !pDataSource->pszSERVER )
                                pDataSource->pszSERVER = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SOCKET" ) == 0 )
                        {    
                            if ( !pDataSource->pszSOCKET )
                                pDataSource->pszSOCKET = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        /* We have INITSTMT */
                        else if ( strcasecmp( pszName, "STMT" ) == 0 || strcasecmp( pszName, "INITSTMT" ) == 0)
                        {    
                            if ( !pDataSource->pszSTMT )
                                pDataSource->pszSTMT = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        /*
                            MYODBC RULE

                            We will 'read' variants on the standard keywords (but only write standard version).
                        */    
                        else if ( strcasecmp( pszName, "UID" ) == 0 || strcasecmp( pszName, "USER" ) == 0 )
                        {    
                            if ( !pDataSource->pszUSER )
                                pDataSource->pszUSER = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLCA" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLCA )
                                pDataSource->pszSSLCA = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLCAPATH" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLCAPATH )
                                pDataSource->pszSSLCAPATH = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLCERT" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLCERT )
                                pDataSource->pszSSLCERT = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLCIPHER" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLCIPHER )
                                pDataSource->pszSSLCIPHER = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLKEY" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLKEY )
                                pDataSource->pszSSLKEY = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if ( strcasecmp( pszName, "SSLVERIFY" ) == 0 )
                        {    
                            if ( !pDataSource->pszSSLVERIFY )
                                pDataSource->pszSSLVERIFY = (char *)_global_strndup( pAnchorChar, pScanChar - pAnchorChar );
                        }
                        else if (strcasecmp(pszName, "CHARSET") == 0)
                        {
                          if (!pDataSource->pszCHARSET)
                            pDataSource->pszCHARSET=
                              (char *)_global_strndup(pAnchorChar,
                                                      pScanChar - pAnchorChar);
                        }
                        else if (strcasecmp(pszName, "READTIMEOUT") == 0)
                        {
                          if (!pDataSource->pszREADTIMEOUT)
                            pDataSource->pszREADTIMEOUT=
                              (char *)_global_strndup(pAnchorChar,
                                                      pScanChar - pAnchorChar);
                        }
                        else if (strcasecmp(pszName, "WRITETIMEOUT") == 0)
                        {
                          if (!pDataSource->pszWRITETIMEOUT)
                            pDataSource->pszWRITETIMEOUT=
                              (char *)_global_strndup(pAnchorChar,
                                                      pScanChar - pAnchorChar);
                        }
                        else if ( strcasecmp( pszName, "SAVEFILE" ) == 0 )
                        {
                          pDataSource->bSaveFileDSN = TRUE;
                        }
                        else if ((flag= NamedAttributeToFlag(pszName)) != 0)
                        {
                          /* Check if we did not have that option yet */
                          if ((flag & (nPositives | nNegatives)) == 0)
                          {
                            if (atol(pAnchorChar) != 0)
                            {
                              nPositives |= flag;
                            }
                            else
                            {
                              nNegatives |= flag;
                            }
                          }
                        }
                        else
                        {
                            /* What the ? */
                        }

                        if ( pszName )
                        {
                            _global_free( pszName );
                            pszName = 0;
                        }
                        
                    }
                }
                break;
            default:
                fprintf( stderr, "[%s][%d][ERROR] Unhandled state.\n", __FILE__, __LINE__ );
                return FALSE;
        }

        /* ';' is used to terminate a name/value pair */
        if ( *pScanChar == ';' )
            nState = MYODBCUTIL_ATTR_PARSE_STATE_NAME_START;

        /* null terminates */
        if ( pScanChar[0] == '\0' )
            break;

        ++pScanChar;

    } /* while scan */

    if ((nNegatives|nPositives) != 0)
    {
      char tmp[24];

      nOption= nOption & ~nNegatives;
      nOption= nOption | nPositives;

      sprintf(tmp, "%lu", nOption);

      if (pDataSource->pszOPTION != NULL)
      {
        _global_free(pDataSource->pszOPTION);
      }

      pDataSource->pszOPTION= (char *)_global_strdup(tmp);
    }

    if ( pszName )
    {
        _global_free( pszName );
    }

    return TRUE;
}
