Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/php_odbc_utils.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Calvin Buckley <calvin@cmpct.info>                          |
12
   +----------------------------------------------------------------------+
13
*/
14
15
#ifdef HAVE_CONFIG_H
16
#include <config.h>
17
#endif
18
19
#include "php.h"
20
21
/*
22
 * This files contains functions shared between ext/pdo_odbc and ext/odbc,
23
 * relating to i.e. connection string quoting rules.
24
 *
25
 * The declarations are PHPAPI due to being available for shared/static
26
 * versions.
27
 */
28
29
/**
30
 * Determines if a string matches the ODBC quoting rules.
31
 *
32
 * A valid quoted string begins with a '{', ends with a '}', and has no '}'
33
 * inside of the string that aren't repeated (as to be escaped).
34
 *
35
 * These rules are what .NET also follows.
36
 */
37
PHPAPI bool php_odbc_connstr_is_quoted(const char *str)
38
0
{
39
  /* ODBC quotes are curly braces */
40
0
  if (str[0] != '{') {
41
0
    return false;
42
0
  }
43
  /* Check for } that aren't doubled up or at the end of the string */
44
0
  size_t length = strlen(str);
45
0
  for (size_t i = 0; i < length; i++) {
46
0
    if (str[i] == '}' && str[i + 1] == '}') {
47
      /* Skip over so we don't count it again */
48
0
      i++;
49
0
    } else if (str[i] == '}') {
50
      /* Only quoted if at the end */
51
0
      return str[i + 1] == '\0';
52
0
    }
53
0
  }
54
  /* No closing brace */
55
0
  return false;
56
0
}
57
58
/**
59
 * Determines if a value for a connection string should be quoted.
60
 *
61
 * The ODBC specification mentions:
62
 * "Because of connection string and initialization file grammar, keywords and
63
 * attribute values that contain the characters []{}(),;?*=!@ not enclosed
64
 * with braces should be avoided."
65
 *
66
 * We also add spaces too, as driver connection string parameter parsing isn't
67
 * standardized and can be quite sloppy; it can lead to significant whitespace
68
 * not being parsed or confusing parsing of different sections. Note that this
69
 * lacks security impact as we already quote the ';=' characters when parsing.
70
 *
71
 * Note that it assumes that the string is *not* already quoted. You should
72
 * check beforehand.
73
 */
74
PHPAPI bool php_odbc_connstr_should_quote(const char *str)
75
0
{
76
0
  return strpbrk(str, "[]{}(),;?*=!@ ") != NULL;
77
0
}
78
79
/**
80
 * Gets the length of a string after it has been quoted.
81
 */
82
PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str)
83
0
{
84
  /* Start with including the quotes ({}) and the null terminator */
85
0
  size_t size = 3;
86
  /* Scan the string like strlen, doubling each } character. */
87
0
  while (*in_str) {
88
0
    size++;
89
0
    if (*in_str++ == '}') {
90
0
      size++;
91
0
    }
92
0
  }
93
0
  return size;
94
0
}
95
96
/**
97
 * Quotes a string with ODBC rules.
98
 *
99
 * Some characters (curly braces, semicolons) are special and must be quoted.
100
 * In the case of '}' in a quoted string, they must be escaped SQL style; that
101
 * is, repeated.
102
 */
103
PHPAPI size_t php_odbc_connstr_quote(char *out_str, const char *in_str, size_t out_str_size)
104
0
{
105
0
  *out_str++ = '{';
106
0
  out_str_size--;
107
0
  while (out_str_size > 2) {
108
0
    if (*in_str == '\0') {
109
0
      break;
110
0
    } else if (*in_str == '}' && out_str_size - 1 > 2) {
111
      /* enough room to append */
112
0
      *out_str++ = '}';
113
0
      *out_str++ = *in_str++;
114
0
      out_str_size -= 2;
115
0
    } else if (*in_str == '}') {
116
      /* not enough, truncate here */
117
0
      break;
118
0
    } else {
119
0
      *out_str++ = *in_str++;
120
0
      out_str_size--;
121
0
    }
122
0
  }
123
  /* append termination */
124
0
  *out_str++ = '}';
125
0
  *out_str++ = '\0';
126
0
  out_str_size -= 2;
127
  /* return how many characters were left */
128
0
  return strlen(in_str);
129
0
}