2016-04-12 23:43:46 +02:00
//---------------------------------------------------------------------------------------------------------------------------------
// File: pdo_parser.cpp
//
// Contents: Implements a parser to parse the PDO DSN.
//
// Copyright Microsoft Corporation
//
2021-09-08 02:38:17 +02:00
// Microsoft Drivers 5.10 for PHP for SQL Server
2016-04-12 23:43:46 +02:00
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
2018-12-20 20:49:06 +01:00
extern " C " {
# include "php_pdo_sqlsrv.h"
}
# include "php_pdo_sqlsrv_int.h"
2016-04-12 23:43:46 +02:00
// Constructor
2017-06-22 23:04:34 +02:00
conn_string_parser : : conn_string_parser ( _In_ sqlsrv_context & ctx , _In_ const char * dsn , _In_ int len , _In_ HashTable * conn_options_ht )
2016-04-12 23:43:46 +02:00
{
2017-04-21 02:17:05 +02:00
this - > orig_str = dsn ;
2016-04-12 23:43:46 +02:00
this - > len = len ;
2017-04-21 02:17:05 +02:00
this - > element_ht = conn_options_ht ;
2016-04-12 23:43:46 +02:00
this - > pos = - 1 ;
this - > ctx = & ctx ;
2017-01-19 20:53:21 +01:00
this - > current_key = 0 ;
this - > current_key_name = NULL ;
2016-04-12 23:43:46 +02:00
}
2017-06-22 23:04:34 +02:00
sql_string_parser : : sql_string_parser ( _In_ sqlsrv_context & ctx , _In_ const char * sql_str , _In_ int len , _In_ HashTable * placeholders_ht )
2017-04-21 02:17:05 +02:00
{
this - > orig_str = sql_str ;
this - > len = len ;
this - > element_ht = placeholders_ht ;
this - > pos = - 1 ;
this - > ctx = & ctx ;
this - > current_key = 0 ;
}
2016-04-12 23:43:46 +02:00
// Move to the next character
2017-04-21 02:17:05 +02:00
inline bool string_parser : : next ( void )
2016-04-12 23:43:46 +02:00
{
// if already at the end then return false
if ( this - > is_eos ( ) ) {
return false ;
}
SQLSRV_ASSERT ( this - > pos < len , " Unexpected cursor position in conn_string_parser::next " ) ;
this - > pos + + ;
if ( this - > is_eos ( ) ) {
return false ;
}
return true ;
}
// Check for end of string.
2017-04-21 02:17:05 +02:00
inline bool string_parser : : is_eos ( void )
2016-04-12 23:43:46 +02:00
{
if ( this - > pos = = len )
{
return true ; // EOS
}
SQLSRV_ASSERT ( this - > pos < len , " Unexpected cursor position in conn_string_parser::is_eos " ) ;
return false ;
}
// Check for white space.
2017-06-22 23:04:34 +02:00
inline bool string_parser : : is_white_space ( _In_ char c )
2016-04-12 23:43:46 +02:00
{
if ( c = = ' ' | | c = = ' \r ' | | c = = ' \n ' | | c = = ' \t ' ) {
return true ;
}
return false ;
}
// Discard any trailing white spaces.
2017-06-22 23:04:34 +02:00
int conn_string_parser : : discard_trailing_white_spaces ( _In_reads_ ( len ) const char * str , _Inout_ int len )
2016-04-12 23:43:46 +02:00
{
const char * end = str + ( len - 1 ) ;
while ( ( this - > is_white_space ( * end ) ) & & ( len > 0 ) ) {
len - - ;
end - - ;
}
return len ;
}
// Discard white spaces.
2017-04-21 23:57:57 +02:00
bool string_parser : : discard_white_spaces ( )
2016-04-12 23:43:46 +02:00
{
if ( this - > is_eos ( ) ) {
return false ;
}
2018-08-01 02:22:56 +02:00
while ( this - > is_white_space ( this - > orig_str [ pos ] ) ) {
2016-04-12 23:43:46 +02:00
if ( ! next ( ) )
return false ;
}
return true ;
}
2017-04-21 02:17:05 +02:00
// Add a key-value pair to the hashtable
2020-04-21 00:17:21 +02:00
void string_parser : : add_key_value_pair ( _In_reads_ ( len ) const char * value , _In_ int len )
2016-04-12 23:43:46 +02:00
{
zval value_z ;
ZVAL_UNDEF ( & value_z ) ;
if ( len = = 0 ) {
ZVAL_STRINGL ( & value_z , " " , 0 ) ;
}
else {
ZVAL_STRINGL ( & value_z , const_cast < char * > ( value ) , len ) ;
}
2020-04-21 00:17:21 +02:00
core : : sqlsrv_zend_hash_index_update ( * ctx , this - > element_ht , this - > current_key , & value_z ) ;
2017-04-21 02:17:05 +02:00
}
// Add a key-value pair to the hashtable with int value
2020-04-21 00:17:21 +02:00
void sql_string_parser : : add_key_int_value_pair ( _In_ unsigned int value ) {
2017-04-21 02:17:05 +02:00
zval value_z ;
ZVAL_LONG ( & value_z , value ) ;
2017-05-06 01:39:51 +02:00
2020-04-21 00:17:21 +02:00
core : : sqlsrv_zend_hash_index_update ( * ctx , this - > element_ht , this - > current_key , & value_z ) ;
2016-04-12 23:43:46 +02:00
}
// Validate a given DSN keyword.
2020-04-21 00:17:21 +02:00
void conn_string_parser : : validate_key ( _In_reads_ ( key_len ) const char * key , _Inout_ int key_len )
2016-04-12 23:43:46 +02:00
{
int new_len = discard_trailing_white_spaces ( key , key_len ) ;
2018-08-01 02:22:56 +02:00
for ( int i = 0 ; PDO_CONN_OPTS [ i ] . conn_option_key ! = SQLSRV_CONN_OPTION_INVALID ; + + i )
2016-04-12 23:43:46 +02:00
{
// discard the null terminator.
2018-08-01 02:22:56 +02:00
if ( new_len = = ( PDO_CONN_OPTS [ i ] . sqlsrv_len - 1 ) & & ! strncasecmp ( key , PDO_CONN_OPTS [ i ] . sqlsrv_name , new_len ) ) {
2016-04-12 23:43:46 +02:00
2018-08-01 02:22:56 +02:00
this - > current_key = PDO_CONN_OPTS [ i ] . conn_option_key ;
this - > current_key_name = PDO_CONN_OPTS [ i ] . sqlsrv_name ;
2016-04-12 23:43:46 +02:00
return ;
}
}
// encountered an invalid key, throw error.
sqlsrv_malloc_auto_ptr < char > key_name ;
key_name = static_cast < char * > ( sqlsrv_malloc ( new_len + 1 ) ) ;
2016-07-06 20:47:05 +02:00
memcpy_s ( key_name , new_len + 1 , key , new_len ) ;
2017-01-19 20:53:21 +01:00
2018-08-01 02:22:56 +02:00
key_name [ new_len ] = ' \0 ' ;
2016-04-12 23:43:46 +02:00
2017-01-26 02:21:10 +01:00
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_INVALID_DSN_KEY , static_cast < char * > ( key_name ) ) ;
2016-04-12 23:43:46 +02:00
}
2017-04-21 02:17:05 +02:00
inline bool sql_string_parser : : is_placeholder_char ( char c )
{
2017-04-24 20:33:28 +02:00
// placeholder only accepts numbers, upper and lower case alphabets and underscore
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | ( c > = ' A ' & & c < = ' Z ' ) | | ( c > = ' a ' & & c < = ' z ' ) | | c = = ' _ ' ) {
return true ;
2017-04-21 02:17:05 +02:00
}
2017-04-24 20:33:28 +02:00
return false ;
2017-04-21 02:17:05 +02:00
}
2016-04-12 23:43:46 +02:00
// Primary function which parses the connection string/DSN.
2020-04-21 00:17:21 +02:00
void conn_string_parser : : parse_conn_string ( void )
2016-04-12 23:43:46 +02:00
{
States state = FirstKeyValuePair ; // starting state
int start_pos = - 1 ;
try {
while ( ! this - > is_eos ( ) ) {
switch ( state ) {
case FirstKeyValuePair :
{
// discard leading spaces
if ( ! next ( ) | | ! discard_white_spaces ( ) ) {
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_INVALID_DSN_STRING ) ; //EOS
}
state = Key ;
break ;
}
case Key :
{
start_pos = this - > pos ;
// read the key name
2018-08-01 02:22:56 +02:00
while ( this - > orig_str [ pos ] ! = ' = ' ) {
2016-04-12 23:43:46 +02:00
if ( ! next ( ) ) {
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY ) ; //EOS
}
}
2020-04-21 00:17:21 +02:00
this - > validate_key ( & ( this - > orig_str [ start_pos ] ) , ( pos - start_pos ) ) ;
2016-04-12 23:43:46 +02:00
state = Value ;
break ;
}
case Value :
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( ( this - > orig_str [ pos ] = = ' = ' ) , " conn_string_parser:: parse_conn_string: "
2016-04-12 23:43:46 +02:00
" Equal was expected " ) ;
next ( ) ; // skip "="
// if EOS encountered after 0 or more spaces OR semi-colon encountered.
2018-08-01 02:22:56 +02:00
if ( ! discard_white_spaces ( ) | | this - > orig_str [ pos ] = = ' ; ' ) {
2016-04-12 23:43:46 +02:00
2020-04-21 00:17:21 +02:00
add_key_value_pair ( NULL , 0 ) ;
2016-04-12 23:43:46 +02:00
if ( this - > is_eos ( ) ) {
break ; // EOS
}
else {
2018-08-01 02:22:56 +02:00
// this->orig_str[pos] == ';'
2016-04-12 23:43:46 +02:00
state = NextKeyValuePair ;
}
}
// if LCB
2018-08-01 02:22:56 +02:00
else if ( this - > orig_str [ pos ] = = ' { ' ) {
2016-04-12 23:43:46 +02:00
start_pos = this - > pos ; // starting character is LCB
state = ValueContent1 ;
}
// If NonSP-LCB-SC
else {
start_pos = this - > pos ;
state = ValueContent2 ;
}
break ;
}
case ValueContent1 :
{
2018-08-01 02:22:56 +02:00
while ( this - > orig_str [ pos ] ! = ' } ' ) {
2016-04-12 23:43:46 +02:00
if ( ! next ( ) ) {
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE , this - > current_key_name ) ;
}
}
// If we reached here than RCB encountered
state = RCBEncountered ;
break ;
}
case ValueContent2 :
{
2018-08-01 02:22:56 +02:00
while ( this - > orig_str [ pos ] ! = ' ; ' ) {
2016-04-12 23:43:46 +02:00
if ( ! next ( ) ) {
break ; //EOS
}
}
2018-08-01 02:22:56 +02:00
if ( ! this - > is_eos ( ) & & this - > orig_str [ pos ] = = ' ; ' ) {
2016-04-12 23:43:46 +02:00
// semi-colon encountered, so go to next key-value pair
state = NextKeyValuePair ;
}
2020-04-21 00:17:21 +02:00
add_key_value_pair ( & ( this - > orig_str [ start_pos ] ) , this - > pos - start_pos ) ;
2016-04-12 23:43:46 +02:00
SQLSRV_ASSERT ( ( ( state = = NextKeyValuePair ) | | ( this - > is_eos ( ) ) ) ,
" conn_string_parser::parse_conn_string: Invalid state encountered " ) ;
break ;
}
case RCBEncountered :
{
// Read the next character after RCB.
if ( ! next ( ) ) {
// EOS
2020-04-21 00:17:21 +02:00
add_key_value_pair ( & ( this - > orig_str [ start_pos ] ) , this - > pos - start_pos ) ;
2016-04-12 23:43:46 +02:00
break ;
}
SQLSRV_ASSERT ( ! this - > is_eos ( ) , " conn_string_parser::parse_conn_string: Unexpected EOS encountered " ) ;
// if second RCB encountered than go back to ValueContent1
2018-08-01 02:22:56 +02:00
if ( this - > orig_str [ pos ] = = ' } ' ) {
2016-04-12 23:43:46 +02:00
if ( ! next ( ) ) {
// EOS after a second RCB is error
THROW_PDO_ERROR ( this - > ctx , SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN , this - > current_key_name ) ;
}
state = ValueContent1 ;
break ;
}
int end_pos = this - > pos ;
// discard any trailing white-spaces.
2018-08-01 02:22:56 +02:00
if ( this - > is_white_space ( this - > orig_str [ pos ] ) ) {
2016-04-12 23:43:46 +02:00
if ( ! this - > discard_white_spaces ( ) ) {
//EOS
2020-04-21 00:17:21 +02:00
add_key_value_pair ( & ( this - > orig_str [ start_pos ] ) , end_pos - start_pos ) ;
2016-04-12 23:43:46 +02:00
break ;
}
}
// if semi-colon than go to next key-value pair
2018-08-01 02:22:56 +02:00
if ( this - > orig_str [ pos ] = = ' ; ' ) {
2016-04-12 23:43:46 +02:00
2020-04-21 00:17:21 +02:00
add_key_value_pair ( & ( this - > orig_str [ start_pos ] ) , end_pos - start_pos ) ;
2016-04-12 23:43:46 +02:00
state = NextKeyValuePair ;
break ;
}
// Non - (RCB, SP*, SC, EOS) character. Any other character after an RCB is an error.
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_INVALID_DSN_VALUE , this - > current_key_name ) ;
break ;
}
case NextKeyValuePair :
{
2018-08-01 02:22:56 +02:00
SQLSRV_ASSERT ( ( this - > orig_str [ pos ] = = ' ; ' ) ,
2016-04-12 23:43:46 +02:00
" conn_string_parser::parse_conn_string: semi-colon was expected. " ) ;
// Call next() to skip the semi-colon.
if ( ! next ( ) | | ! this - > discard_white_spaces ( ) ) {
// EOS
break ;
}
2018-08-01 02:22:56 +02:00
if ( this - > orig_str [ pos ] = = ' ; ' ) {
2016-04-12 23:43:46 +02:00
// a second semi-colon is error case.
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING , this - > pos ) ;
}
else {
// any other character leads to the next key
state = Key ;
break ;
}
} //case NextKeyValuePair
} // switch
} //while
}
catch ( pdo : : PDOException & ) {
throw ;
}
}
2017-04-21 02:17:05 +02:00
// Primary function which parses out the named placeholders from a sql string.
2020-04-21 00:17:21 +02:00
void sql_string_parser : : parse_sql_string ( void ) {
2017-04-21 02:17:05 +02:00
try {
2017-05-13 01:02:34 +02:00
int start_pos = - 1 ;
2017-04-21 02:17:05 +02:00
while ( ! this - > is_eos ( ) ) {
2017-05-13 01:02:34 +02:00
// if pos is -1, then reading from a string is an initialized read
2017-05-12 19:56:26 +02:00
if ( pos = = - 1 ) {
next ( ) ;
}
2017-04-21 02:17:05 +02:00
// skip until a '"', '\'', ':' or '?'
char sym ;
while ( this - > orig_str [ pos ] ! = ' " ' & & this - > orig_str [ pos ] ! = ' \' ' & & this - > orig_str [ pos ] ! = ' : ' & & this - > orig_str [ pos ] ! = ' ? ' & & ! this - > is_eos ( ) ) {
next ( ) ;
}
sym = this - > orig_str [ pos ] ;
// if '"' or '\'', skip until the next '"' or '\'' respectively
if ( sym = = ' " ' | | sym = = ' \' ' ) {
next ( ) ;
while ( this - > orig_str [ pos ] ! = sym & & ! this - > is_eos ( ) ) {
next ( ) ;
}
}
2017-05-02 00:24:32 +02:00
// if ':', store string placeholder in the placeholders hashtable
2017-04-21 02:17:05 +02:00
else if ( sym = = ' : ' ) {
start_pos = this - > pos ;
next ( ) ;
// keep going until the next space or line break
2017-05-06 01:39:51 +02:00
// while (!is_white_space(this->orig_str[pos]) && !this->is_eos()) {
2017-04-21 02:17:05 +02:00
while ( is_placeholder_char ( this - > orig_str [ pos ] ) ) {
next ( ) ;
}
2020-04-21 00:17:21 +02:00
add_key_value_pair ( & ( this - > orig_str [ start_pos ] ) , this - > pos - start_pos ) ;
2017-04-21 23:57:57 +02:00
discard_white_spaces ( ) ;
// if an '=' is right after a placeholder, it means the placeholder is for output parameters
// and emulate prepare does not support output parameters
if ( this - > orig_str [ pos ] = = ' = ' ) {
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED ) ;
}
2017-04-21 02:17:05 +02:00
this - > current_key + + ;
}
2017-05-02 00:24:32 +02:00
// if '?', store long placeholder into the placeholders hashtable
2017-04-21 02:17:05 +02:00
else if ( sym = = ' ? ' ) {
next ( ) ;
// add dummy value to placeholders ht to keep count of the number of placeholders
add_key_int_value_pair ( this - > current_key ) ;
2017-04-21 23:57:57 +02:00
discard_white_spaces ( ) ;
if ( this - > orig_str [ pos ] = = ' = ' ) {
THROW_PDO_ERROR ( this - > ctx , PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED ) ;
}
2017-04-21 02:17:05 +02:00
this - > current_key + + ;
}
}
}
catch ( pdo : : PDOException & ) {
throw ;
}
}