1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* escape/unescape functions.
18 *
19 * These functions perform various escaping operations, and are provided in
20 * pairs, a function to query the length of and escape existing buffers, as
21 * well as companion functions to perform the same process to memory
22 * allocated from a pool.
23 *
24 * The API is designed to have the smallest possible RAM footprint, and so
25 * will only allocate the exact amount of RAM needed for each conversion.
26 */
27
28#include "apr_escape.h"
29#include "apr_escape_test_char.h"
30#include "apr_encode_private.h"
31#include "apr_lib.h"
32#include "apr_strings.h"
33
34/* we assume the folks using this ensure 0 <= c < 256... which means
35 * you need a cast to (unsigned char) first, you can't just plug a
36 * char in here and get it to work, because if char is signed then it
37 * will first be sign extended.
38 */
39#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
40
41APR_DECLARE(apr_status_t) apr_escape_shell(char *escaped, const char *str,
42        apr_ssize_t slen, apr_size_t *len)
43{
44    unsigned char *d;
45    const unsigned char *s;
46    apr_size_t size = 1;
47    int found = 0;
48
49    d = (unsigned char *) escaped;
50    s = (const unsigned char *) str;
51
52    if (s) {
53        if (d) {
54            for (; *s && slen; ++s, slen--) {
55#if defined(OS2) || defined(WIN32)
56                /*
57                 * Newlines to Win32/OS2 CreateProcess() are ill advised.
58                 * Convert them to spaces since they are effectively white
59                 * space to most applications
60                 */
61                if (*s == '\r' || *s == '\n') {
62                    if (d) {
63                        *d++ = ' ';
64                        found = 1;
65                    }
66                    continue;
67                }
68#endif
69                if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
70                    *d++ = '\\';
71                    size++;
72                    found = 1;
73                }
74                *d++ = *s;
75                size++;
76            }
77            *d = '\0';
78        }
79        else {
80            for (; *s && slen; ++s, slen--) {
81                if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
82                    size++;
83                    found = 1;
84                }
85                size++;
86            }
87        }
88    }
89
90    if (len) {
91        *len = size;
92    }
93    if (!found) {
94        return APR_NOTFOUND;
95    }
96
97    return APR_SUCCESS;
98}
99
100APR_DECLARE(const char *) apr_pescape_shell(apr_pool_t *p, const char *str)
101{
102    apr_size_t len;
103
104    switch (apr_escape_shell(NULL, str, APR_ESCAPE_STRING, &len)) {
105    case APR_SUCCESS: {
106        char *cmd = apr_palloc(p, len);
107        apr_escape_shell(cmd, str, APR_ESCAPE_STRING, NULL);
108        return cmd;
109    }
110    case APR_NOTFOUND: {
111        break;
112    }
113    }
114
115    return str;
116}
117
118static char x2c(const char *what)
119{
120    register char digit;
121
122#if !APR_CHARSET_EBCDIC
123    digit =
124            ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
125    digit *= 16;
126    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
127#else /*APR_CHARSET_EBCDIC*/
128    char xstr[5];
129    xstr[0]='0';
130    xstr[1]='x';
131    xstr[2]=what[0];
132    xstr[3]=what[1];
133    xstr[4]='\0';
134    digit = ENCODE_TO_NATIVE[0xFF & strtol(xstr, NULL, 16)];
135#endif /*APR_CHARSET_EBCDIC*/
136    return (digit);
137}
138
139APR_DECLARE(apr_status_t) apr_unescape_url(char *escaped, const char *url,
140        apr_ssize_t slen, const char *forbid, const char *reserved, int plus,
141        apr_size_t *len)
142{
143    apr_size_t size = 1;
144    int found = 0;
145    const char *s = (const char *) url;
146    char *d = (char *) escaped;
147    register int badesc, badpath;
148
149    if (!url) {
150        return APR_NOTFOUND;
151    }
152
153    badesc = 0;
154    badpath = 0;
155    if (s) {
156        if (d) {
157            for (; *s && slen; ++s, d++, slen--) {
158                if (plus && *s == '+') {
159                    *d = ' ';
160                    found = 1;
161                }
162                else if (*s != '%') {
163                    *d = *s;
164                }
165                else {
166                    if (!apr_isxdigit(*(s + 1)) || !apr_isxdigit(*(s + 2))) {
167                        badesc = 1;
168                        *d = '%';
169                    }
170                    else {
171                        char decoded;
172                        decoded = x2c(s + 1);
173                        if ((decoded == '\0')
174                                || (forbid && strchr(forbid, decoded))) {
175                            badpath = 1;
176                            *d = decoded;
177                            s += 2;
178                            slen -= 2;
179                        }
180                        else if (reserved && strchr(reserved, decoded)) {
181                            *d++ = *s++;
182                            *d++ = *s++;
183                            *d = *s;
184                            size += 2;
185                        }
186                        else {
187                            *d = decoded;
188                            s += 2;
189                            slen -= 2;
190                            found = 1;
191                        }
192                    }
193                }
194                size++;
195            }
196            *d = '\0';
197        }
198        else {
199            for (; *s && slen; ++s, slen--) {
200                if (plus && *s == '+') {
201                    found = 1;
202                }
203                else if (*s != '%') {
204                    /* character unchanged */
205                }
206                else {
207                    if (!apr_isxdigit(*(s + 1)) || !apr_isxdigit(*(s + 2))) {
208                        badesc = 1;
209                    }
210                    else {
211                        char decoded;
212                        decoded = x2c(s + 1);
213                        if ((decoded == '\0')
214                                || (forbid && strchr(forbid, decoded))) {
215                            badpath = 1;
216                            s += 2;
217                            slen -= 2;
218                        }
219                        else if (reserved && strchr(reserved, decoded)) {
220                            s += 2;
221                            slen -= 2;
222                            size += 2;
223                        }
224                        else {
225                            s += 2;
226                            slen -= 2;
227                            found = 1;
228                        }
229                    }
230                }
231                size++;
232            }
233        }
234    }
235
236    if (len) {
237        *len = size;
238    }
239    if (badesc) {
240        return APR_EINVAL;
241    }
242    else if (badpath) {
243        return APR_BADCH;
244    }
245    else if (!found) {
246        return APR_NOTFOUND;
247    }
248
249    return APR_SUCCESS;
250}
251
252APR_DECLARE(const char *) apr_punescape_url(apr_pool_t *p, const char *url,
253        const char *forbid, const char *reserved, int plus)
254{
255    apr_size_t len;
256
257    switch (apr_unescape_url(NULL, url, APR_ESCAPE_STRING, forbid, reserved,
258            plus, &len)) {
259    case APR_SUCCESS: {
260        char *buf = apr_palloc(p, len);
261        apr_unescape_url(buf, url, APR_ESCAPE_STRING, forbid, reserved, plus,
262                NULL);
263        return buf;
264    }
265    case APR_EINVAL:
266    case APR_BADCH: {
267        return NULL;
268    }
269    case APR_NOTFOUND: {
270        break;
271    }
272    }
273
274    return url;
275}
276
277/* c2x takes an unsigned, and expects the caller has guaranteed that
278 * 0 <= what < 256... which usually means that you have to cast to
279 * unsigned char first, because (unsigned)(char)(x) first goes through
280 * signed extension to an int before the unsigned cast.
281 *
282 * The reason for this assumption is to assist gcc code generation --
283 * the unsigned char -> unsigned extension is already done earlier in
284 * both uses of this code, so there's no need to waste time doing it
285 * again.
286 */
287static const char c2x_table[] = "0123456789abcdef";
288
289static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
290        unsigned char *where)
291{
292#if APR_CHARSET_EBCDIC
293    what = convert_e2a[(unsigned char)what];
294#endif /*APR_CHARSET_EBCDIC*/
295    *where++ = prefix;
296    *where++ = c2x_table[what >> 4];
297    *where++ = c2x_table[what & 0xf];
298    return where;
299}
300
301APR_DECLARE(apr_status_t) apr_escape_path_segment(char *escaped,
302        const char *str, apr_ssize_t slen, apr_size_t *len)
303{
304    apr_size_t size = 1;
305    int found = 0;
306    const unsigned char *s = (const unsigned char *) str;
307    unsigned char *d = (unsigned char *) escaped;
308    unsigned c;
309
310    if (s) {
311        if (d) {
312            while ((c = *s) && slen) {
313                if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
314                    d = c2x(c, '%', d);
315                    size += 2;
316                    found = 1;
317                }
318                else {
319                    *d++ = c;
320                }
321                ++s;
322                size++;
323                slen--;
324            }
325            *d = '\0';
326        }
327        else {
328            while ((c = *s) && slen) {
329                if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
330                    size += 2;
331                    found = 1;
332                }
333                ++s;
334                size++;
335                slen--;
336            }
337        }
338    }
339
340    if (len) {
341        *len = size;
342    }
343    if (!found) {
344        return APR_NOTFOUND;
345    }
346
347    return APR_SUCCESS;
348}
349
350APR_DECLARE(const char *) apr_pescape_path_segment(apr_pool_t *p,
351        const char *str)
352{
353    apr_size_t len;
354
355    switch (apr_escape_path_segment(NULL, str, APR_ESCAPE_STRING, &len)) {
356    case APR_SUCCESS: {
357        char *cmd = apr_palloc(p, len);
358        apr_escape_path_segment(cmd, str, APR_ESCAPE_STRING, NULL);
359        return cmd;
360    }
361    case APR_NOTFOUND: {
362        break;
363    }
364    }
365
366    return str;
367}
368
369APR_DECLARE(apr_status_t) apr_escape_path(char *escaped, const char *path,
370        apr_ssize_t slen, int partial, apr_size_t *len)
371{
372    apr_size_t size = 1;
373    int found = 0;
374    const unsigned char *s = (const unsigned char *) path;
375    unsigned char *d = (unsigned char *) escaped;
376    unsigned c;
377
378    if (!path) {
379        return APR_NOTFOUND;
380    }
381
382    if (!partial) {
383        const char *colon = strchr(path, ':');
384        const char *slash = strchr(path, '/');
385
386        if (colon && (!slash || colon < slash)) {
387            if (d) {
388                *d++ = '.';
389                *d++ = '/';
390            }
391            size += 2;
392            found = 1;
393        }
394    }
395    if (d) {
396        while ((c = *s) && slen) {
397            if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
398                d = c2x(c, '%', d);
399                size += 2;
400                found = 1;
401            }
402            else {
403                *d++ = c;
404            }
405            ++s;
406            size++;
407            slen--;
408        }
409        *d = '\0';
410    }
411    else {
412        while ((c = *s) && slen) {
413            if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
414                size += 2;
415                found = 1;
416            }
417            ++s;
418            size++;
419            slen--;
420        }
421    }
422
423    if (len) {
424        *len = size;
425    }
426    if (!found) {
427        return APR_NOTFOUND;
428    }
429
430    return APR_SUCCESS;
431}
432
433APR_DECLARE(const char *) apr_pescape_path(apr_pool_t *p, const char *str,
434        int partial)
435{
436    apr_size_t len;
437
438    switch (apr_escape_path(NULL, str, APR_ESCAPE_STRING, partial, &len)) {
439    case APR_SUCCESS: {
440        char *path = apr_palloc(p, len);
441        apr_escape_path(path, str, APR_ESCAPE_STRING, partial, NULL);
442        return path;
443    }
444    case APR_NOTFOUND: {
445        break;
446    }
447    }
448
449    return str;
450}
451
452APR_DECLARE(apr_status_t) apr_escape_urlencoded(char *escaped, const char *str,
453        apr_ssize_t slen, apr_size_t *len)
454{
455    apr_size_t size = 1;
456    int found = 0;
457    const unsigned char *s = (const unsigned char *) str;
458    unsigned char *d = (unsigned char *) escaped;
459    unsigned c;
460
461    if (s) {
462        if (d) {
463            while ((c = *s) && slen) {
464                if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
465                    d = c2x(c, '%', d);
466                    size += 2;
467                    found = 1;
468                }
469                else if (c == ' ') {
470                    *d++ = '+';
471                    found = 1;
472                }
473                else {
474                    *d++ = c;
475                }
476                ++s;
477                size++;
478                slen--;
479            }
480            *d = '\0';
481        }
482        else {
483            while ((c = *s) && slen) {
484                if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
485                    size += 2;
486                    found = 1;
487                }
488                else if (c == ' ') {
489                    found = 1;
490                }
491                ++s;
492                size++;
493                slen--;
494            }
495        }
496    }
497
498    if (len) {
499        *len = size;
500    }
501    if (!found) {
502        return APR_NOTFOUND;
503    }
504
505    return APR_SUCCESS;
506}
507
508APR_DECLARE(const char *) apr_pescape_urlencoded(apr_pool_t *p, const char *str)
509{
510    apr_size_t len;
511
512    switch (apr_escape_urlencoded(NULL, str, APR_ESCAPE_STRING, &len)) {
513    case APR_SUCCESS: {
514        char *encoded = apr_palloc(p, len);
515        apr_escape_urlencoded(encoded, str, APR_ESCAPE_STRING, NULL);
516        return encoded;
517    }
518    case APR_NOTFOUND: {
519        break;
520    }
521    }
522
523    return str;
524}
525
526APR_DECLARE(apr_status_t) apr_escape_entity(char *escaped, const char *str,
527        apr_ssize_t slen, int toasc, apr_size_t *len)
528{
529    apr_size_t size = 1;
530    int found = 0;
531    const unsigned char *s = (const unsigned char *) str;
532    unsigned char *d = (unsigned char *) escaped;
533    unsigned c;
534
535    if (s) {
536        if (d) {
537            while ((c = *s) && slen) {
538                if (TEST_CHAR(c, T_ESCAPE_XML)) {
539                    switch (c) {
540                    case '>': {
541                        memcpy(d, "&gt;", 4);
542                        size += 4;
543                        d += 4;
544                        break;
545                    }
546                    case '<': {
547                        memcpy(d, "&lt;", 4);
548                        size += 4;
549                        d += 4;
550                        break;
551                    }
552                    case '&': {
553                        memcpy(d, "&amp;", 5);
554                        size += 5;
555                        d += 5;
556                        break;
557                    }
558                    case '\"': {
559                        memcpy(d, "&quot;", 6);
560                        size += 6;
561                        d += 6;
562                        break;
563                    }
564                    case '\'': {
565                        memcpy(d, "&apos;", 6);
566                        size += 6;
567                        d += 6;
568                        break;
569                    }
570                    }
571                    found = 1;
572                }
573                else if (toasc && !apr_isascii(c)) {
574                    int offset = apr_snprintf((char *) d, 6, "&#%3.3d;", c);
575                    size += offset;
576                    d += offset;
577                    found = 1;
578                }
579                else {
580                    *d++ = c;
581                    size++;
582                }
583                ++s;
584                slen--;
585            }
586            *d = '\0';
587        }
588        else {
589            while ((c = *s) && slen) {
590                if (TEST_CHAR(c, T_ESCAPE_XML)) {
591                    switch (c) {
592                    case '>': {
593                        size += 4;
594                        break;
595                    }
596                    case '<': {
597                        size += 4;
598                        break;
599                    }
600                    case '&': {
601                        size += 5;
602                        break;
603                    }
604                    case '\"': {
605                        size += 6;
606                        break;
607                    }
608                    case '\'': {
609                        size += 6;
610                        break;
611                    }
612                    }
613                    found = 1;
614                }
615                else if (toasc && !apr_isascii(c)) {
616                    char buf[8];
617                    size += apr_snprintf(buf, 6, "&#%3.3d;", c);
618                    found = 1;
619                }
620                else {
621                    size++;
622                }
623                ++s;
624                slen--;
625            }
626        }
627    }
628
629    if (len) {
630        *len = size;
631    }
632    if (!found) {
633        return APR_NOTFOUND;
634    }
635
636    return APR_SUCCESS;
637}
638
639APR_DECLARE(const char *) apr_pescape_entity(apr_pool_t *p, const char *str,
640        int toasc)
641{
642    apr_size_t len;
643
644    switch (apr_escape_entity(NULL, str, APR_ESCAPE_STRING, toasc, &len)) {
645    case APR_SUCCESS: {
646        char *cmd = apr_palloc(p, len);
647        apr_escape_entity(cmd, str, APR_ESCAPE_STRING, toasc, NULL);
648        return cmd;
649    }
650    case APR_NOTFOUND: {
651        break;
652    }
653    }
654
655    return str;
656}
657
658/* maximum length of any ISO-LATIN-1 HTML entity name. */
659#define MAXENTLEN (6)
660
661APR_DECLARE(apr_status_t) apr_unescape_entity(char *unescaped, const char *str,
662        apr_ssize_t slen, apr_size_t *len)
663{
664    int found = 0;
665    apr_size_t size = 1;
666    int val, i, j;
667    char *d = unescaped;
668    const char *s = str;
669    const char *ents;
670    static const char * const entlist[MAXENTLEN + 1] =
671    {
672            NULL, /* 0 */
673            NULL, /* 1 */
674            "lt\074gt\076", /* 2 */
675            "amp\046ETH\320eth\360", /* 3 */
676            "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
677            "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
678            "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
679            "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
680            "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
681            "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
682            "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
683            "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
684            "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
685            "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
686            "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
687    };
688
689    if (s) {
690        if (d) {
691            for (; *s != '\0' && slen; s++, d++, size++, slen--) {
692                if (*s != '&') {
693                    *d = *s;
694                    continue;
695                }
696                /* find end of entity */
697                for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0;
698                        i++) {
699                    continue;
700                }
701
702                if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */
703                    *d = *s;
704                    continue;
705                }
706
707                /* is it numeric ? */
708                if (s[1] == '#') {
709                    for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
710                        val = val * 10 + s[j] - '0';
711                    }
712                    s += i;
713                    if (j < i || val <= 8 || (val >= 11 && val <= 31)
714                            || (val >= 127 && val <= 160) || val >= 256) {
715                        d--; /* no data to output */
716                        size--;
717                    }
718                    else {
719                        *d = ENCODE_TO_ASCII(val);
720                        found = 1;
721                    }
722                }
723                else {
724                    j = i - 1;
725                    if (j > MAXENTLEN || entlist[j] == NULL) {
726                        /* wrong length */
727                        *d = '&';
728                        continue; /* skip it */
729                    }
730                    for (ents = entlist[j]; *ents != '\0'; ents += i) {
731                        if (strncmp(s + 1, ents, j) == 0) {
732                            break;
733                        }
734                    }
735
736                    if (*ents == '\0') {
737                        *d = '&'; /* unknown */
738                    }
739                    else {
740                        *d = ENCODE_TO_ASCII(((const unsigned char *) ents)[j]);
741                        s += i;
742                        slen -= i;
743                        found = 1;
744                    }
745                }
746            }
747            *d = '\0';
748        }
749        else {
750            for (; *s != '\0' && slen; s++, size++, slen--) {
751                if (*s != '&') {
752                    continue;
753                }
754                /* find end of entity */
755                for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0;
756                        i++) {
757                    continue;
758                }
759
760                if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */
761                    continue;
762                }
763
764                /* is it numeric ? */
765                if (s[1] == '#') {
766                    for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
767                        val = val * 10 + s[j] - '0';
768                    }
769                    s += i;
770                    if (j < i || val <= 8 || (val >= 11 && val <= 31)
771                            || (val >= 127 && val <= 160) || val >= 256) {
772                        /* no data to output */
773                        size--;
774                    }
775                    else {
776                        found = 1;
777                    }
778                }
779                else {
780                    j = i - 1;
781                    if (j > MAXENTLEN || entlist[j] == NULL) {
782                        /* wrong length */
783                        continue; /* skip it */
784                    }
785                    for (ents = entlist[j]; *ents != '\0'; ents += i) {
786                        if (strncmp(s + 1, ents, j) == 0) {
787                            break;
788                        }
789                    }
790
791                    if (*ents == '\0') {
792                        /* unknown */
793                    }
794                    else {
795                        s += i;
796                        slen -= i;
797                        found = 1;
798                    }
799                }
800            }
801        }
802    }
803
804    if (len) {
805        *len = size;
806    }
807    if (!found) {
808        return APR_NOTFOUND;
809    }
810
811    return APR_SUCCESS;
812}
813
814APR_DECLARE(const char *) apr_punescape_entity(apr_pool_t *p, const char *str)
815{
816    apr_size_t len;
817
818    switch (apr_unescape_entity(NULL, str, APR_ESCAPE_STRING, &len)) {
819    case APR_SUCCESS: {
820        char *cmd = apr_palloc(p, len);
821        apr_unescape_entity(cmd, str, APR_ESCAPE_STRING, NULL);
822        return cmd;
823    }
824    case APR_NOTFOUND: {
825        break;
826    }
827    }
828
829    return str;
830}
831
832APR_DECLARE(apr_status_t) apr_escape_echo(char *escaped, const char *str,
833        apr_ssize_t slen, int quote, apr_size_t *len)
834{
835    apr_size_t size = 1;
836    int found = 0;
837    const unsigned char *s = (const unsigned char *) str;
838    unsigned char *d = (unsigned char *) escaped;
839    unsigned c;
840
841    if (s) {
842        if (d) {
843            while ((c = *s) && slen) {
844                if (TEST_CHAR(c, T_ESCAPE_ECHO)) {
845                    *d++ = '\\';
846                    size++;
847                    switch (c) {
848                    case '\a':
849                        *d++ = 'a';
850                        size++;
851                        found = 1;
852                        break;
853                    case '\b':
854                        *d++ = 'b';
855                        size++;
856                        found = 1;
857                        break;
858                    case '\f':
859                        *d++ = 'f';
860                        size++;
861                        found = 1;
862                        break;
863                    case '\n':
864                        *d++ = 'n';
865                        size++;
866                        found = 1;
867                        break;
868                    case '\r':
869                        *d++ = 'r';
870                        size++;
871                        found = 1;
872                        break;
873                    case '\t':
874                        *d++ = 't';
875                        size++;
876                        found = 1;
877                        break;
878                    case '\v':
879                        *d++ = 'v';
880                        size++;
881                        found = 1;
882                        break;
883                    case '\\':
884                        *d++ = '\\';
885                        size++;
886                        found = 1;
887                        break;
888                    case '"':
889                        if (quote) {
890                            *d++ = c;
891                            size++;
892                            found = 1;
893                        }
894                        else {
895                            d[-1] = c;
896                        }
897                        break;
898                    default:
899                        c2x(c, 'x', d);
900                        d += 3;
901                        size += 3;
902                        found = 1;
903                        break;
904                    }
905                }
906                else {
907                    *d++ = c;
908                    size++;
909                }
910                ++s;
911                slen--;
912            }
913            *d = '\0';
914        }
915        else {
916            while ((c = *s) && slen) {
917                if (TEST_CHAR(c, T_ESCAPE_ECHO)) {
918                    size++;
919                    switch (c) {
920                    case '\a':
921                    case '\b':
922                    case '\f':
923                    case '\n':
924                    case '\r':
925                    case '\t':
926                    case '\v':
927                    case '\\':
928                        size++;
929                        found = 1;
930                        break;
931                    case '"':
932                        if (quote) {
933                            size++;
934                            found = 1;
935                        }
936                        break;
937                    default:
938                        size += 3;
939                        found = 1;
940                        break;
941                    }
942                }
943                else {
944                    size++;
945                }
946                ++s;
947                slen--;
948            }
949        }
950    }
951
952    if (len) {
953        *len = size;
954    }
955    if (!found) {
956        return APR_NOTFOUND;
957    }
958
959    return APR_SUCCESS;
960}
961
962APR_DECLARE(const char *) apr_pescape_echo(apr_pool_t *p, const char *str,
963        int quote)
964{
965    apr_size_t len;
966
967    switch (apr_escape_echo(NULL, str, APR_ESCAPE_STRING, quote, &len)) {
968    case APR_SUCCESS: {
969        char *cmd = apr_palloc(p, len);
970        apr_escape_echo(cmd, str, APR_ESCAPE_STRING, quote, NULL);
971        return cmd;
972    }
973    case APR_NOTFOUND: {
974        break;
975    }
976    }
977
978    return str;
979}
980
981APR_DECLARE(apr_status_t) apr_escape_hex(char *dest, const void *src,
982        apr_size_t srclen, int colon, apr_size_t *len)
983{
984    const unsigned char *in = src;
985    apr_size_t size;
986
987    if (!src) {
988        return APR_NOTFOUND;
989    }
990
991    if (dest) {
992        for (size = 0; size < srclen; size++) {
993            if (colon && size) {
994                *dest++ = ':';
995            }
996            *dest++ = c2x_table[in[size] >> 4];
997            *dest++ = c2x_table[in[size] & 0xf];
998        }
999        *dest = '\0';
1000    }
1001
1002    if (len) {
1003        if (colon && srclen) {
1004            *len = srclen * 3;
1005        }
1006        else {
1007            *len = srclen * 2 + 1;
1008        }
1009    }
1010
1011    return APR_SUCCESS;
1012}
1013
1014APR_DECLARE(const char *) apr_pescape_hex(apr_pool_t *p, const void *src,
1015        apr_size_t srclen, int colon)
1016{
1017    apr_size_t len;
1018
1019    switch (apr_escape_hex(NULL, src, srclen, colon, &len)) {
1020    case APR_SUCCESS: {
1021        char *cmd = apr_palloc(p, len);
1022        apr_escape_hex(cmd, src, srclen, colon, NULL);
1023        return cmd;
1024    }
1025    case APR_NOTFOUND: {
1026        break;
1027    }
1028    }
1029
1030    return src;
1031}
1032
1033APR_DECLARE(apr_status_t) apr_unescape_hex(void *dest, const char *str,
1034        apr_ssize_t slen, int colon, apr_size_t *len)
1035{
1036    apr_size_t size = 0;
1037    int flip = 0;
1038    const unsigned char *s = (const unsigned char *) str;
1039    unsigned char *d = (unsigned char *) dest;
1040    unsigned c;
1041    unsigned char u = 0;
1042
1043    if (s) {
1044        if (d) {
1045            while ((c = *s) && slen) {
1046
1047                if (!flip) {
1048                    u = 0;
1049                }
1050
1051                if (colon && c == ':' && !flip) {
1052                    ++s;
1053                    slen--;
1054                    continue;
1055                }
1056                else if (apr_isdigit(c)) {
1057                    u |= c - '0';
1058                }
1059                else if (apr_isupper(c) && c <= 'F') {
1060                    u |= c - ('A' - 10);
1061                }
1062                else if (apr_islower(c) && c <= 'f') {
1063                    u |= c - ('a' - 10);
1064                }
1065                else {
1066                    return APR_BADCH;
1067                }
1068
1069                if (flip) {
1070                    *d++ = u;
1071                    size++;
1072                }
1073                else {
1074                    u <<= 4;
1075                    *d = u;
1076                }
1077                flip = !flip;
1078
1079                ++s;
1080                slen--;
1081            }
1082        }
1083        else {
1084            while ((c = *s) && slen) {
1085
1086                if (colon && c == ':' && !flip) {
1087                    ++s;
1088                    slen--;
1089                    continue;
1090                }
1091                else if (apr_isdigit(c)) {
1092                    /* valid */
1093                }
1094                else if (apr_isupper(c) && c <= 'F') {
1095                    /* valid */
1096                }
1097                else if (apr_islower(c) && c <= 'f') {
1098                    /* valid */
1099                }
1100                else {
1101                    return APR_BADCH;
1102                }
1103
1104                if (flip) {
1105                    size++;
1106                }
1107                flip = !flip;
1108
1109                ++s;
1110                slen--;
1111            }
1112        }
1113    }
1114
1115    if (len) {
1116        *len = size;
1117    }
1118    if (!s) {
1119        return APR_NOTFOUND;
1120    }
1121
1122    return APR_SUCCESS;
1123}
1124
1125APR_DECLARE(const void *) apr_punescape_hex(apr_pool_t *p, const char *str,
1126        int colon, apr_size_t *len)
1127{
1128    apr_size_t size;
1129
1130    switch (apr_unescape_hex(NULL, str, APR_ESCAPE_STRING, colon, &size)) {
1131    case APR_SUCCESS: {
1132        void *cmd = apr_palloc(p, size);
1133        apr_unescape_hex(cmd, str, APR_ESCAPE_STRING, colon, len);
1134        return cmd;
1135    }
1136    case APR_BADCH:
1137    case APR_NOTFOUND: {
1138        break;
1139    }
1140    }
1141
1142    return NULL;
1143}
1144
1145APR_DECLARE(apr_status_t) apr_escape_ldap(char *escaped, const void *str,
1146        apr_ssize_t slen, int flags, apr_size_t *len)
1147{
1148    apr_size_t size = 1;
1149    int found = 0;
1150    const unsigned char *s = (const unsigned char *) str;
1151    unsigned char *d = (unsigned char *) escaped;
1152    unsigned c;
1153
1154    if (s) {
1155        if (d) {
1156            while (((c = *s) && slen) || (slen > 0)) {
1157                if (((flags & APR_ESCAPE_LDAP_DN) && TEST_CHAR(c, T_ESCAPE_LDAP_DN))
1158                     || ((flags & APR_ESCAPE_LDAP_FILTER) && TEST_CHAR(c, T_ESCAPE_LDAP_FILTER))) {
1159                    d = c2x(c, '\\', d);
1160                    size += 2;
1161                    found = 1;
1162                }
1163                else {
1164                    *d++ = c;
1165                }
1166                ++s;
1167                size++;
1168                slen--;
1169            }
1170            *d = '\0';
1171        }
1172        else {
1173            while (((c = *s) && slen) || (slen > 0)) {
1174                if (((flags & APR_ESCAPE_LDAP_DN) && TEST_CHAR(c, T_ESCAPE_LDAP_DN))
1175                     || ((flags & APR_ESCAPE_LDAP_FILTER) && TEST_CHAR(c, T_ESCAPE_LDAP_FILTER))) {
1176                    size += 2;
1177                    found = 1;
1178                }
1179                ++s;
1180                size++;
1181                slen--;
1182            }
1183        }
1184    }
1185
1186    if (len) {
1187        *len = size;
1188    }
1189    if (!found) {
1190        return APR_NOTFOUND;
1191    }
1192
1193    return APR_SUCCESS;
1194}
1195
1196APR_DECLARE(const char *) apr_pescape_ldap(apr_pool_t *p, const void *src,
1197        apr_ssize_t srclen, int flags)
1198{
1199    apr_size_t len;
1200
1201    switch (apr_escape_ldap(NULL, src, srclen, flags, &len)) {
1202    case APR_SUCCESS: {
1203        char *encoded = apr_palloc(p, len);
1204        apr_escape_ldap(encoded, src, srclen, flags, NULL);
1205        return encoded;
1206    }
1207    case APR_NOTFOUND: {
1208        break;
1209    }
1210    }
1211
1212    return src;
1213}
1214
1215