Coverage Report

Created: 2026-08-13 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lighttpd1.4/src/burl.c
Line
Count
Source
1
/*
2
 * burl - buffer URL normalization
3
 *
4
 * Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
5
 * License: BSD 3-clause (same as lighttpd)
6
 */
7
#include "first.h"
8
#include "burl.h"
9
10
#include <string.h>
11
12
#include "buffer.h"
13
#include "base64.h"
14
15
static const char hex_chars_uc[] = "0123456789ABCDEF";
16
17
/* everything except: ! $ & ' ( ) * + , - . / 0-9 : ; = ? @ A-Z _ a-z ~ */
18
static const char encoded_chars_http_uri_reqd[] = {
19
  /*
20
  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
21
  */
22
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  00 -  0F control chars */
23
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  10 -  1F */
24
  1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  20 -  2F space " # % */
25
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  30 -  3F < > */
26
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  40 -  4F */
27
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  50 -  5F [ \ ] ^ */
28
  1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  60 -  6F ` */
29
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  70 -  7F { | } DEL */
30
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  80 -  8F */
31
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  90 -  9F */
32
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  A0 -  AF */
33
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  B0 -  BF */
34
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  C0 -  CF */
35
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  D0 -  DF */
36
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  E0 -  EF */
37
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /*  F0 -  FF */
38
};
39
40
41
/* c (char) and n (nibble) MUST be unsigned integer types */
42
#define li_cton(c,n) \
43
58.8M
  (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0))
44
45
/* b (byte) MUST be unsigned integer type
46
 * https://en.wikipedia.org/wiki/UTF-8
47
 * detect invalid UTF-8 byte and byte in overlong encoding of 7-bit ASCII
48
 * (but does not detect other invalid/overlong multibyte encoding sequences) */
49
58.5M
#define li_utf8_invalid_byte(b) light_utf8_invalid_byte(b)
50
51
52
static int burl_is_unreserved (const int c)
53
19.8k
{
54
19.8k
    return (light_isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~');
55
19.8k
}
56
57
58
__attribute_cold__
59
static int burl_normalize_basic_unreserved_fix (buffer *b, buffer *t, int i, int qs)
60
596
{
61
596
    int j = i;
62
596
    const int used = (int)buffer_clen(b);
63
596
    const unsigned char * const s = (unsigned char *)b->ptr;
64
596
    unsigned char * const p =
65
596
      (unsigned char *)buffer_string_prepare_copy(t,i+(used-i)*3+1);
66
596
    unsigned int n1, n2;
67
596
    int invalid_utf8 = 0;
68
596
    memcpy(p, s, (size_t)i);
69
26.5M
    for (; i < used; ++i, ++j) {
70
26.5M
        if (!encoded_chars_http_uri_reqd[s[i]]) {
71
967k
            p[j] = s[i];
72
967k
            if (__builtin_expect( (s[i] == '?'), 0) && -1 == qs) qs = j;
73
967k
        }
74
25.5M
        else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)) {
75
17.6k
            const unsigned int x = (n1 << 4) | n2;
76
17.6k
            if (burl_is_unreserved(x)) {
77
1.58k
                p[j] = x;
78
1.58k
            }
79
16.0k
            else {
80
16.0k
                p[j]   = '%';
81
16.0k
                p[++j] = hex_chars_uc[n1]; /*(s[i+1] & 0xdf)*/
82
16.0k
                p[++j] = hex_chars_uc[n2]; /*(s[i+2] & 0xdf)*/
83
16.0k
                invalid_utf8 |= li_utf8_invalid_byte(x);
84
16.0k
            }
85
17.6k
            i+=2;
86
17.6k
        }
87
25.5M
        else if (s[i] == '#') break; /* ignore fragment */
88
25.5M
        else {
89
25.5M
            p[j]   = '%';
90
25.5M
            p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF];
91
25.5M
            p[++j] = hex_chars_uc[s[i] & 0xF];
92
25.5M
            invalid_utf8 |= li_utf8_invalid_byte(s[i]);
93
25.5M
        }
94
26.5M
    }
95
596
    buffer_copy_string_len(b, (char *)p, (size_t)j);
96
596
    return !invalid_utf8 ? qs : -2;
97
596
}
98
99
100
static int burl_normalize_basic_unreserved (buffer *b, buffer *t)
101
780
{
102
780
    const unsigned char * const s = (unsigned char *)b->ptr;
103
780
    const int used = (int)buffer_clen(b);
104
780
    unsigned int n1, n2, x;
105
780
    int qs = -1;
106
780
    int invalid_utf8 = 0;
107
108
613k
    for (int i = 0; i < used; ++i) {
109
613k
        if (!encoded_chars_http_uri_reqd[s[i]]) {
110
610k
            if (__builtin_expect( (s[i] == '?'), 0) && -1 == qs) qs = i;
111
610k
        }
112
2.73k
        else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)
113
2.19k
                 && !burl_is_unreserved((x = (n1 << 4) | n2))) {
114
2.13k
            invalid_utf8 |= li_utf8_invalid_byte(x);
115
2.13k
            if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */
116
2.13k
            if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */
117
2.13k
            i+=2;
118
2.13k
        }
119
597
        else if (s[i] == '#') { /* ignore fragment */
120
1
            buffer_truncate(b, (size_t)i);
121
1
            break;
122
1
        }
123
596
        else {
124
596
            qs = burl_normalize_basic_unreserved_fix(b, t, i, qs);
125
596
            break;
126
596
        }
127
613k
    }
128
129
780
    return !invalid_utf8 ? qs : -2;
130
780
}
131
132
133
__attribute_cold__
134
static int burl_normalize_basic_required_fix (buffer *b, buffer *t, int i, int qs)
135
750
{
136
750
    int j = i;
137
750
    const int used = (int)buffer_clen(b);
138
750
    const unsigned char * const s = (unsigned char *)b->ptr;
139
750
    unsigned char * const p =
140
750
      (unsigned char *)buffer_string_prepare_copy(t,i+(used-i)*3+1);
141
750
    unsigned int n1, n2;
142
750
    int invalid_utf8 = 0;
143
750
    memcpy(p, s, (size_t)i);
144
34.7M
    for (; i < used; ++i, ++j) {
145
34.7M
        if (!encoded_chars_http_uri_reqd[s[i]]) {
146
1.63M
            p[j] = s[i];
147
1.63M
            if (__builtin_expect( (s[i] == '?'), 0) && -1 == qs) qs = j;
148
1.63M
        }
149
33.0M
        else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)) {
150
68.6k
            const unsigned int x = (n1 << 4) | n2;
151
68.6k
            if (!encoded_chars_http_uri_reqd[x]
152
59.6k
                && (qs < 0
153
59.6k
                    ? (x != '/' && x != '?')
154
59.6k
                    : (x != '&' && x != '=' && x != ';' && x != '+'))) {
155
39.7k
                p[j] = x;
156
39.7k
            }
157
28.9k
            else {
158
28.9k
                p[j]   = '%';
159
28.9k
                p[++j] = hex_chars_uc[n1]; /*(s[i+1] & 0xdf)*/
160
28.9k
                p[++j] = hex_chars_uc[n2]; /*(s[i+2] & 0xdf)*/
161
28.9k
                invalid_utf8 |= li_utf8_invalid_byte(x);
162
28.9k
            }
163
68.6k
            i+=2;
164
68.6k
        }
165
33.0M
        else if (s[i] == '#') break; /* ignore fragment */
166
33.0M
        else {
167
33.0M
            p[j]   = '%';
168
33.0M
            p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF];
169
33.0M
            p[++j] = hex_chars_uc[s[i] & 0xF];
170
33.0M
            invalid_utf8 |= li_utf8_invalid_byte(s[i]);
171
33.0M
        }
172
34.7M
    }
173
750
    buffer_copy_string_len(b, (char *)p, (size_t)j);
174
750
    return !invalid_utf8 ? qs : -2;
175
750
}
176
177
178
static int burl_normalize_basic_required (buffer *b, buffer *t)
179
959
{
180
959
    const unsigned char * const s = (unsigned char *)b->ptr;
181
959
    const int used = (int)buffer_clen(b);
182
959
    unsigned int n1, n2, x;
183
959
    int qs = -1;
184
959
    int invalid_utf8 = 0;
185
186
358k
    for (int i = 0; i < used; ++i) {
187
357k
        if (!encoded_chars_http_uri_reqd[s[i]]) {
188
347k
            if (__builtin_expect( (s[i] == '?'), 0) && -1 == qs) qs = i;
189
347k
        }
190
10.1k
        else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)
191
9.43k
                 && (encoded_chars_http_uri_reqd[(x = (n1 << 4) | n2)]
192
4.64k
                     || (qs < 0
193
4.64k
                         ? (x == '/' || x == '?')
194
9.35k
                         : (x == '&' || x == '=' || x == ';' || x == '+')))) {
195
9.35k
            invalid_utf8 |= li_utf8_invalid_byte(x);
196
9.35k
            if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */
197
9.35k
            if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */
198
9.35k
            i+=2;
199
9.35k
        }
200
751
        else if (s[i] == '#') { /* ignore fragment */
201
1
            buffer_truncate(b, (size_t)i);
202
1
            break;
203
1
        }
204
750
        else {
205
750
            qs = burl_normalize_basic_required_fix(b, t, i, qs);
206
750
            break;
207
750
        }
208
357k
    }
209
210
959
    return !invalid_utf8 ? qs : -2;
211
959
}
212
213
214
static int burl_contains_ctrls (const buffer *b)
215
341
{
216
341
    const char * const s = b->ptr;
217
341
    const int used = (int)buffer_clen(b);
218
11.3M
    for (int i = 0; i < used; ++i) {
219
11.3M
        if (s[i] == '%' && (s[i+1] < '2' || (s[i+1] == '7' && s[i+2] == 'F')))
220
97
            return 1;
221
11.3M
    }
222
244
    return 0;
223
341
}
224
225
226
__attribute_cold__
227
static void burl_normalize_qs20_to_plus_fix (buffer *b, int i)
228
137
{
229
137
    char * const s = b->ptr;
230
137
    const int used = (int)buffer_clen(b);
231
137
    int j = i;
232
14.4M
    for (; i < used; ++i, ++j) {
233
14.4M
        s[j] = s[i];
234
14.4M
        if (s[i] == '%' && s[i+1] == '2' && s[i+2] == '0') {
235
2.41k
            s[j] = '+';
236
2.41k
            i+=2;
237
2.41k
        }
238
14.4M
    }
239
137
    buffer_truncate(b, j);
240
137
}
241
242
243
static void burl_normalize_qs20_to_plus (buffer *b, int qs)
244
447
{
245
447
    const char * const s = b->ptr;
246
447
    const int used = qs < 0 ? 0 : (int)buffer_clen(b);
247
447
    int i;
248
447
    if (qs < 0) return;
249
8.09M
    for (i = qs+1; i < used; ++i) {
250
8.09M
        if (s[i] == '%' && s[i+1] == '2' && s[i+2] == '0') break;
251
8.09M
    }
252
447
    if (i != used) burl_normalize_qs20_to_plus_fix(b, i);
253
447
}
254
255
256
__attribute_cold__
257
static int burl_normalize_2F_to_slash_fix (buffer *b, int qs, int i)
258
158
{
259
158
    char * const s = b->ptr;
260
158
    const int blen = (int)buffer_clen(b);
261
158
    const int used = qs < 0 ? blen : qs;
262
158
    int j = i;
263
8.77M
    for (; i < used; ++i, ++j) {
264
8.77M
        s[j] = s[i];
265
8.77M
        if (s[i] == '%' && s[i+1] == '2' && s[i+2] == 'F') {
266
1.22k
            s[j] = '/';
267
1.22k
            i+=2;
268
1.22k
        }
269
8.77M
    }
270
158
    if (qs >= 0) {
271
69
        const int qslen = blen - qs;
272
69
        memmove(s+j, s+qs, (size_t)qslen);
273
69
        qs = j;
274
69
        j += qslen;
275
69
    }
276
158
    buffer_truncate(b, j);
277
158
    return qs;
278
158
}
279
280
281
static int burl_normalize_2F_to_slash (buffer *b, int qs, int flags)
282
1.27k
{
283
    /*("%2F" must already have been uppercased during normalization)*/
284
1.27k
    const char * const s = b->ptr;
285
1.27k
    const int used = qs < 0 ? (int)buffer_clen(b) : qs;
286
77.2M
    for (int i = 0; i < used; ++i) {
287
77.2M
        if (s[i] == '%' && s[i+1] == '2' && s[i+2] == 'F') {
288
178
            return !(flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)
289
178
              ? burl_normalize_2F_to_slash_fix(b, qs, i)     /* _DECODE */
290
178
              : -2; /*(flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)*/
291
178
        }
292
77.2M
    }
293
1.09k
    return qs;
294
1.27k
}
295
296
297
static int burl_normalize_path (buffer *b, buffer *t, int qs, int flags)
298
1.20k
{
299
1.20k
    const unsigned char * const s = (unsigned char *)b->ptr;
300
1.20k
    const int used = (int)buffer_clen(b);
301
1.20k
    int path_simplify = 0;
302
6.19k
    for (int i = 0, len = qs < 0 ? used : qs; i < len; ++i) {
303
5.38k
        if (s[i] == '.' && (s[i+1] != '.' || ++i)
304
3.00k
            && (s[i+1] == '/' || s[i+1] == '?' || s[i+1] == '\0')) {
305
232
            path_simplify = 1;
306
232
            break;
307
232
        }
308
68.2M
        while (i < len && s[i] != '/') ++i;
309
5.15k
        if (s[i] == '/' && s[i+1] == '/') { /*(s[len] != '/')*/
310
169
            path_simplify = 1;
311
169
            break;
312
169
        }
313
5.15k
    }
314
315
1.20k
    if (path_simplify) {
316
401
        if (flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT) return -2;
317
390
        if (qs >= 0) {
318
145
            buffer_copy_string_len(t, b->ptr+qs, used - qs);
319
145
            buffer_truncate(b, qs);
320
145
        }
321
322
390
        buffer_path_simplify(b);
323
324
390
        if (qs >= 0) {
325
145
            qs = (int)buffer_clen(b);
326
145
            buffer_append_string_len(b, BUF_PTR_LEN(t));
327
145
        }
328
390
    }
329
330
1.19k
    return qs;
331
1.20k
}
332
333
334
__attribute_cold__
335
__attribute_noinline__
336
__attribute_pure__
337
421
static int burl_scan_qmark (const buffer * const b) {
338
421
    const char * const qmark = strchr(b->ptr, '?');
339
421
    return qmark ? (int)(qmark - b->ptr) : -1;
340
421
}
341
342
343
int burl_normalize (buffer *b, buffer *t, int flags)
344
1.73k
{
345
1.73k
    int qs;
346
347
  #if defined(_WIN32) || defined(__CYGWIN__)
348
    /* Windows and Cygwin treat '\\' as '/' if '\\' is present in path;
349
     * convert to '/' for consistency before percent-encoding
350
     * normalization which will convert '\\' to "%5C" in the URL.
351
     * (Clients still should not be sending '\\' unencoded in requests.) */
352
    if (flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS) {
353
        for (char *p = b->ptr; *p != '?' && *p != '\0'; ++p) {
354
            if (*p == '\\') *p = '/';
355
            if (p[0] == '%' && p[1] == '5' && (p[2] | 0x20) == 'c') {
356
                p[1] = '2';
357
                p[2] = 'F';
358
                p += 2;
359
            }
360
        }
361
    }
362
  #endif
363
364
1.73k
    qs = (flags & HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED)
365
1.73k
      ? burl_normalize_basic_required(b, t)
366
1.73k
      : burl_normalize_basic_unreserved(b, t);
367
1.73k
    if (-2 == qs) {
368
551
        if (flags & HTTP_PARSEOPT_URL_NORMALIZE_INVALID_UTF8_REJECT) return -2;
369
421
        qs = burl_scan_qmark(b);
370
421
    }
371
372
1.60k
    if (flags & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT) {
373
341
        if (burl_contains_ctrls(b)) return -2;
374
341
    }
375
376
1.51k
    if (flags & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
377
1.51k
                |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) {
378
1.27k
        qs = burl_normalize_2F_to_slash(b, qs, flags);
379
1.27k
        if (-2 == qs) return -2;
380
1.27k
    }
381
382
1.49k
    if (flags & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
383
1.49k
                |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) {
384
1.20k
        qs = burl_normalize_path(b, t, qs, flags);
385
1.20k
        if (-2 == qs) return -2;
386
1.20k
    }
387
388
1.48k
    if (flags & HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS) {
389
924
        if (qs >= 0) burl_normalize_qs20_to_plus(b, qs);
390
924
    }
391
392
1.48k
    return qs;
393
1.49k
}
394
395
396
static void burl_append_encode_nde (buffer * const b, const char * const str, const size_t len)
397
0
{
398
    /* percent-encodes everything except unreserved  - . 0-9 A-Z _ a-z ~
399
     * unless already percent-encoded (does not double-encode) */
400
    /* Note: not checking for invalid UTF-8 */
401
0
    char * const p = buffer_string_prepare_append(b, len*3);
402
0
    unsigned int n1, n2;
403
0
    int j = 0;
404
0
    for (unsigned int i = 0; i < len; ++i, ++j) {
405
0
        if (str[i]=='%' && li_cton(str[i+1], n1) && li_cton(str[i+2], n2)) {
406
0
            const unsigned int x = (n1 << 4) | n2;
407
0
            if (burl_is_unreserved((int)x)) {
408
0
                p[j] = (char)x;
409
0
            }
410
0
            else { /* leave UTF-8, control chars, and required chars encoded */
411
0
                p[j]   = '%';
412
0
                p[++j] = str[i+1];
413
0
                p[++j] = str[i+2];
414
0
            }
415
0
            i+=2;
416
0
        }
417
0
        else if (burl_is_unreserved(str[i])) {
418
0
            p[j] = str[i];
419
0
        }
420
0
        else {
421
0
            p[j]   = '%';
422
0
            p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF];
423
0
            p[++j] = hex_chars_uc[str[i] & 0xF];
424
0
        }
425
0
    }
426
0
    buffer_commit(b, j);
427
0
}
428
429
430
static void burl_append_encode_psnde (buffer * const b, const char * const str, const size_t len)
431
0
{
432
    /* percent-encodes everything except unreserved  - . 0-9 A-Z _ a-z ~ plus /
433
     * unless already percent-encoded (does not double-encode) */
434
    /* Note: not checking for invalid UTF-8 */
435
0
    char * const p = buffer_string_prepare_append(b, len*3);
436
0
    unsigned int n1, n2;
437
0
    int j = 0;
438
0
    for (unsigned int i = 0; i < len; ++i, ++j) {
439
0
        if (str[i]=='%' && li_cton(str[i+1], n1) && li_cton(str[i+2], n2)) {
440
0
            const unsigned int x = (n1 << 4) | n2;
441
0
            if (burl_is_unreserved((int)x)) {
442
0
                p[j] = (char)x;
443
0
            }
444
0
            else { /* leave UTF-8, control chars, and required chars encoded */
445
0
                p[j]   = '%';
446
0
                p[++j] = str[i+1];
447
0
                p[++j] = str[i+2];
448
0
            }
449
0
            i+=2;
450
0
        }
451
0
        else if (burl_is_unreserved(str[i]) || str[i] == '/') {
452
0
            p[j] = str[i];
453
0
        }
454
0
        else {
455
0
            p[j]   = '%';
456
0
            p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF];
457
0
            p[++j] = hex_chars_uc[str[i] & 0xF];
458
0
        }
459
0
    }
460
0
    buffer_commit(b, j);
461
0
}
462
463
464
static void burl_append_encode_all (buffer * const b, const char * const str, const size_t len)
465
0
{
466
    /* percent-encodes everything except unreserved  - . 0-9 A-Z _ a-z ~
467
     * Note: double-encodes any existing '%') */
468
    /* Note: not checking for invalid UTF-8 */
469
0
    char * const p = buffer_string_prepare_append(b, len*3);
470
0
    int j = 0;
471
0
    for (unsigned int i = 0; i < len; ++i, ++j) {
472
0
        if (burl_is_unreserved(str[i])) {
473
0
            p[j] = str[i];
474
0
        }
475
0
        else {
476
0
            p[j]   = '%';
477
0
            p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF];
478
0
            p[++j] = hex_chars_uc[str[i] & 0xF];
479
0
        }
480
0
    }
481
0
    buffer_commit(b, j);
482
0
}
483
484
485
static void burl_offset_tolower (buffer * const b, const size_t off)
486
0
{
487
    /*(skips over all percent-encodings, including encoding of alpha chars)*/
488
0
    for (char *p = b->ptr+off; p[0]; ++p) {
489
0
        if (light_isupper(p[0])) p[0] |= 0x20;
490
0
        else if (p[0]=='%' && light_isxdigit(p[1]) && light_isxdigit(p[2]))
491
0
            p+=2;
492
0
    }
493
0
}
494
495
496
static void burl_offset_toupper (buffer * const b, const size_t off)
497
0
{
498
    /*(skips over all percent-encodings, including encoding of alpha chars)*/
499
0
    for (char *p = b->ptr+off; p[0]; ++p) {
500
0
        if (light_islower(p[0])) p[0] &= 0xdf;
501
0
        else if (p[0]=='%' && light_isxdigit(p[1]) && light_isxdigit(p[2]))
502
0
            p+=2;
503
0
    }
504
0
}
505
506
507
void burl_append (buffer * const b, const char * const str, const size_t len, const int flags)
508
0
{
509
0
    size_t off = 0;
510
511
0
    if (0 == len) return;
512
513
0
    if (0 == flags) {
514
0
        buffer_append_string_len(b, str, len);
515
0
        return;
516
0
    }
517
518
0
    if (flags & (BURL_TOUPPER|BURL_TOLOWER)) off = buffer_clen(b);
519
520
0
    if (flags & BURL_ENCODE_NONE) {
521
0
        buffer_append_string_len(b, str, len);
522
0
    }
523
0
    else if (flags & BURL_ENCODE_ALL) {
524
0
        burl_append_encode_all(b, str, len);
525
0
    }
526
0
    else if (flags & BURL_ENCODE_NDE) {
527
0
        burl_append_encode_nde(b, str, len);
528
0
    }
529
0
    else if (flags & BURL_ENCODE_PSNDE) {
530
0
        burl_append_encode_psnde(b, str, len);
531
0
    }
532
0
    else if (flags & BURL_ENCODE_B64U) {
533
0
        const unsigned char *s = (const unsigned char *)str;
534
0
        buffer_append_base64_encode_no_padding(b, s, len, BASE64_URL);
535
0
    }
536
0
    else if (flags & BURL_DECODE_B64U) {
537
0
        buffer_append_base64_decode(b, str, len, BASE64_URL);
538
0
    }
539
540
    /* note: not normalizing str, which could come from arbitrary header,
541
     * so it is possible that alpha chars are percent-encoded upper/lowercase */
542
0
    if (flags & (BURL_TOLOWER|BURL_TOUPPER)) {
543
0
        (flags & BURL_TOLOWER)
544
0
          ? burl_offset_tolower(b, off)  /*(flags & BURL_TOLOWER)*/
545
0
          : burl_offset_toupper(b, off); /*(flags & BURL_TOUPPER)*/
546
0
    }
547
0
}