Coverage Report

Created: 2026-06-02 06:39

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] == '}' && str[i + 1] != '\0') {
50
      /* If not at the end, not quoted */
51
0
      return false;
52
0
    }
53
0
  }
54
0
  return true;
55
0
}
56
57
/**
58
 * Determines if a value for a connection string should be quoted.
59
 *
60
 * The ODBC specification mentions:
61
 * "Because of connection string and initialization file grammar, keywords and
62
 * attribute values that contain the characters []{}(),;?*=!@ not enclosed
63
 * with braces should be avoided."
64
 *
65
 * Note that it assumes that the string is *not* already quoted. You should
66
 * check beforehand.
67
 */
68
PHPAPI bool php_odbc_connstr_should_quote(const char *str)
69
0
{
70
0
  return strpbrk(str, "[]{}(),;?*=!@") != NULL;
71
0
}
72
73
/**
74
 * Gets the length of a string after it has been quoted.
75
 */
76
PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str)
77
0
{
78
  /* Start with including the quotes ({}) and the null terminator */
79
0
  size_t size = 3;
80
  /* Scan the string like strlen, doubling each } character. */
81
0
  while (*in_str) {
82
0
    size++;
83
0
    if (*in_str++ == '}') {
84
0
      size++;
85
0
    }
86
0
  }
87
0
  return size;
88
0
}
89
90
/**
91
 * Quotes a string with ODBC rules.
92
 *
93
 * Some characters (curly braces, semicolons) are special and must be quoted.
94
 * In the case of '}' in a quoted string, they must be escaped SQL style; that
95
 * is, repeated.
96
 */
97
PHPAPI size_t php_odbc_connstr_quote(char *out_str, const char *in_str, size_t out_str_size)
98
0
{
99
0
  *out_str++ = '{';
100
0
  out_str_size--;
101
0
  while (out_str_size > 2) {
102
0
    if (*in_str == '\0') {
103
0
      break;
104
0
    } else if (*in_str == '}' && out_str_size - 1 > 2) {
105
      /* enough room to append */
106
0
      *out_str++ = '}';
107
0
      *out_str++ = *in_str++;
108
0
      out_str_size -= 2;
109
0
    } else if (*in_str == '}') {
110
      /* not enough, truncate here */
111
0
      break;
112
0
    } else {
113
0
      *out_str++ = *in_str++;
114
0
      out_str_size--;
115
0
    }
116
0
  }
117
  /* append termination */
118
0
  *out_str++ = '}';
119
0
  *out_str++ = '\0';
120
0
  out_str_size -= 2;
121
  /* return how many characters were left */
122
0
  return strlen(in_str);
123
0
}