]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - encoding/apr_escape.c
Vendor import apr-1.7.0
[FreeBSD/FreeBSD.git] / encoding / apr_escape.c
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
41 APR_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
100 APR_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
118 static 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
139 APR_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
252 APR_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  */
287 static const char c2x_table[] = "0123456789abcdef";
288
289 static 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
301 APR_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
350 APR_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
369 APR_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
433 APR_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
452 APR_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
508 APR_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
526 APR_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
639 APR_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
661 APR_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
814 APR_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
832 APR_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
962 APR_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
981 APR_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
1014 APR_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
1033 APR_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
1125 APR_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
1145 APR_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
1196 APR_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