Coverage Report

Created: 2026-06-02 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/quot_print.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
   | Author: Kirill Maximov <kir@actimind.com>                            |
12
   +----------------------------------------------------------------------+
13
 */
14
15
#include <stdlib.h>
16
17
#ifdef HAVE_UNISTD_H
18
#include <unistd.h>
19
#endif
20
#include <string.h>
21
#include <errno.h>
22
23
#include "php.h"
24
#include "quot_print.h"
25
26
#include <stdio.h>
27
28
/*
29
*  Converting HEX char to INT value
30
*/
31
static char php_hex2int(int c) /* {{{ */
32
0
{
33
0
  if (isdigit(c)) {
34
0
    return c - '0';
35
0
  } else if (c >= 'A' && c <= 'F') {
36
0
    return c - 'A' + 10;
37
0
  } else {
38
0
    ZEND_ASSERT(c >= 'a' && c <= 'f');
39
0
    return c - 'a' + 10;
40
0
  }
41
0
}
42
/* }}} */
43
44
PHPAPI zend_string *php_quot_print_decode(const unsigned char *str, size_t length, int replace_us_by_ws) /* {{{ */
45
0
{
46
0
  size_t i;
47
0
  unsigned const char *p1;
48
0
  unsigned char *p2;
49
0
  unsigned int h_nbl, l_nbl;
50
51
0
  size_t decoded_len, buf_size;
52
0
  zend_string *retval;
53
54
0
  static unsigned int hexval_tbl[256] = {
55
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 32, 16, 64, 64, 16, 64, 64,
56
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
57
0
    32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
58
0
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 64, 64, 64, 64, 64, 64,
59
0
    64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64,
60
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
61
0
    64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64,
62
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
63
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
65
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
66
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
67
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
68
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
69
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
70
0
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
71
0
  };
72
73
0
  if (replace_us_by_ws) {
74
0
    replace_us_by_ws = '_';
75
0
  }
76
77
0
  i = length, p1 = str; buf_size = length;
78
79
0
  while (i > 1 && *p1 != '\0') {
80
0
    if (*p1 == '=') {
81
0
      buf_size -= 2;
82
0
      p1++;
83
0
      i--;
84
0
    }
85
0
    p1++;
86
0
    i--;
87
0
  }
88
89
0
  retval = zend_string_alloc(buf_size, 0);
90
0
  i = length; p1 = str; p2 = (unsigned char*)ZSTR_VAL(retval);
91
0
  decoded_len = 0;
92
93
0
  while (i > 0 && *p1 != '\0') {
94
0
    if (*p1 == '=') {
95
0
      i--, p1++;
96
0
      if (i == 0 || *p1 == '\0') {
97
0
        break;
98
0
      }
99
0
      h_nbl = hexval_tbl[*p1];
100
0
      if (h_nbl < 16) {
101
        /* next char should be a hexadecimal digit */
102
0
        if ((--i) == 0 || (l_nbl = hexval_tbl[*(++p1)]) >= 16) {
103
0
          efree(retval);
104
0
          return NULL;
105
0
        }
106
0
        *(p2++) = (h_nbl << 4) | l_nbl, decoded_len++;
107
0
        i--, p1++;
108
0
      } else if (h_nbl < 64) {
109
        /* soft line break */
110
0
        while (h_nbl == 32) {
111
0
          if (--i == 0 || (h_nbl = hexval_tbl[*(++p1)]) == 64) {
112
0
            efree(retval);
113
0
            return NULL;
114
0
          }
115
0
        }
116
0
        if (p1[0] == '\r' && i >= 2 && p1[1] == '\n') {
117
0
          i--, p1++;
118
0
        }
119
0
        i--, p1++;
120
0
      } else {
121
0
        efree(retval);
122
0
        return NULL;
123
0
      }
124
0
    } else {
125
0
      *(p2++) = (replace_us_by_ws == *p1 ? '\x20': *p1);
126
0
      i--, p1++, decoded_len++;
127
0
    }
128
0
  }
129
130
0
  *p2 = '\0';
131
0
  ZSTR_LEN(retval) = decoded_len;
132
0
  return retval;
133
0
}
134
/* }}} */
135
136
20.6k
#define PHP_QPRINT_MAXL 75
137
138
PHPAPI zend_string *php_quot_print_encode(const unsigned char *str, size_t length) /* {{{ */
139
27
{
140
27
  zend_ulong lp = 0;
141
27
  unsigned char c, *d;
142
27
  const char *hex = "0123456789ABCDEF";
143
27
  zend_string *ret;
144
145
27
  ret = zend_string_safe_alloc(3, (length + (((3 * length)/(PHP_QPRINT_MAXL-9)) + 1)), 0, 0);
146
27
  d = (unsigned char*)ZSTR_VAL(ret);
147
148
18.7k
  while (length--) {
149
18.7k
    if (((c = *str++) == '\015') && (*str == '\012') && length > 0) {
150
24
      *d++ = '\015';
151
24
      *d++ = *str++;
152
24
      length--;
153
24
      lp = 0;
154
18.6k
    } else {
155
18.6k
      if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') || ((c == ' ') && (*str == '\015'))) {
156
9.99k
        if ((((lp+= 3) > PHP_QPRINT_MAXL) && (c <= 0x7f))
157
9.81k
            || ((c > 0x7f) && (c <= 0xdf) && ((lp + 3) > PHP_QPRINT_MAXL))
158
9.72k
            || ((c > 0xdf) && (c <= 0xef) && ((lp + 6) > PHP_QPRINT_MAXL))
159
9.72k
            || ((c > 0xef) && (c <= 0xf4) && ((lp + 9) > PHP_QPRINT_MAXL))) {
160
300
          *d++ = '=';
161
300
          *d++ = '\015';
162
300
          *d++ = '\012';
163
300
          lp = 3;
164
300
        }
165
9.99k
        *d++ = '=';
166
9.99k
        *d++ = hex[c >> 4];
167
9.99k
        *d++ = hex[c & 0xf];
168
9.99k
      } else {
169
8.68k
        if ((++lp) > PHP_QPRINT_MAXL) {
170
132
          *d++ = '=';
171
132
          *d++ = '\015';
172
132
          *d++ = '\012';
173
132
          lp = 1;
174
132
        }
175
8.68k
        *d++ = c;
176
8.68k
      }
177
18.6k
    }
178
18.7k
  }
179
27
  *d = '\0';
180
27
  ret = zend_string_truncate(ret, d - (unsigned char*)ZSTR_VAL(ret), 0);
181
27
  return ret;
182
27
}
183
/* }}} */
184
185
/*
186
*
187
* Decoding  Quoted-printable string.
188
*
189
*/
190
/* {{{ Convert a quoted-printable string to an 8 bit string */
191
PHP_FUNCTION(quoted_printable_decode)
192
0
{
193
0
  zend_string *arg1;
194
0
  char *str_in;
195
0
  zend_string *str_out;
196
0
  size_t i = 0, j = 0, k;
197
198
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
199
0
    Z_PARAM_STR(arg1)
200
0
  ZEND_PARSE_PARAMETERS_END();
201
202
0
  if (ZSTR_LEN(arg1) == 0) {
203
    /* shortcut */
204
0
    RETURN_EMPTY_STRING();
205
0
  }
206
207
0
  str_in = ZSTR_VAL(arg1);
208
0
  str_out = zend_string_alloc(ZSTR_LEN(arg1), 0);
209
0
  while (str_in[i]) {
210
0
    switch (str_in[i]) {
211
0
    case '=':
212
0
      if (str_in[i + 1] && str_in[i + 2] &&
213
0
        isxdigit((unsigned char)str_in[i + 1]) &&
214
0
        isxdigit((unsigned char)str_in[i + 2]))
215
0
      {
216
0
        ZSTR_VAL(str_out)[j++] = (php_hex2int((unsigned char)str_in[i + 1]) << 4)
217
0
            + php_hex2int((unsigned char)str_in[i + 2]);
218
0
        i += 3;
219
0
      } else  /* check for soft line break according to RFC 2045*/ {
220
0
        k = 1;
221
0
        while (str_in[i + k] && ((str_in[i + k] == 32) || (str_in[i + k] == 9))) {
222
          /* Possibly, skip spaces/tabs at the end of line */
223
0
          k++;
224
0
        }
225
0
        if (!str_in[i + k]) {
226
          /* End of line reached */
227
0
          i += k;
228
0
        }
229
0
        else if ((str_in[i + k] == 13) && (str_in[i + k + 1] == 10)) {
230
          /* CRLF */
231
0
          i += k + 2;
232
0
        }
233
0
        else if ((str_in[i + k] == 13) || (str_in[i + k] == 10)) {
234
          /* CR or LF */
235
0
          i += k + 1;
236
0
        }
237
0
        else {
238
0
          ZSTR_VAL(str_out)[j++] = str_in[i++];
239
0
        }
240
0
      }
241
0
      break;
242
0
    default:
243
0
      ZSTR_VAL(str_out)[j++] = str_in[i++];
244
0
    }
245
0
  }
246
0
  ZSTR_VAL(str_out)[j] = '\0';
247
0
  ZSTR_LEN(str_out) = j;
248
249
0
  RETVAL_NEW_STR(str_out);
250
0
}
251
/* }}} */
252
253
/* {{{ */
254
PHP_FUNCTION(quoted_printable_encode)
255
27
{
256
27
  zend_string *str;
257
27
  zend_string *new_str;
258
259
81
  ZEND_PARSE_PARAMETERS_START(1, 1)
260
108
    Z_PARAM_STR(str)
261
27
  ZEND_PARSE_PARAMETERS_END();
262
263
27
  if (!ZSTR_LEN(str)) {
264
0
    RETURN_EMPTY_STRING();
265
0
  }
266
267
27
  new_str = php_quot_print_encode((unsigned char *)ZSTR_VAL(str), ZSTR_LEN(str));
268
27
  RETURN_STR(new_str);
269
27
}
270
/* }}} */