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_lib.h"
31 #include "apr_strings.h"
32
33 #if APR_CHARSET_EBCDIC
34 static int convert_a2e[256] = {
35 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
36 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F,
37 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
38 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
39 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
40 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
41 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
42 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
43 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x06, 0x17, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B,
44 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xFF,
45 0x41, 0xAA, 0x4A, 0xB1, 0x9F, 0xB2, 0x6A, 0xB5, 0xBB, 0xB4, 0x9A, 0x8A, 0xB0, 0xCA, 0xAF, 0xBC,
46 0x90, 0x8F, 0xEA, 0xFA, 0xBE, 0xA0, 0xB6, 0xB3, 0x9D, 0xDA, 0x9B, 0x8B, 0xB7, 0xB8, 0xB9, 0xAB,
47 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9E, 0x68, 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77,
48 0xAC, 0x69, 0xED, 0xEE, 0xEB, 0xEF, 0xEC, 0xBF, 0x80, 0xFD, 0xFE, 0xFB, 0xFC, 0xBA, 0xAE, 0x59,
49 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9C, 0x48, 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57,
50 0x8C, 0x49, 0xCD, 0xCE, 0xCB, 0xCF, 0xCC, 0xE1, 0x70, 0xDD, 0xDE, 0xDB, 0xDC, 0x8D, 0x8E, 0xDF };
51
52 static int convert_e2a[256] = {
53 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
54 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F,
55 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x17, 0x1B, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07,
56 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A,
57 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
58 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
59 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
60 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
61 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1,
62 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4,
63 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE,
64 0xAC, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, 0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7,
65 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5,
66 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF,
67 0x5C, 0xF7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5,
68 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F };
69 #define RAW_ASCII_CHAR(ch) convert_e2a[(unsigned char)ch]
70 #else /* APR_CHARSET_EBCDIC */
71 #define RAW_ASCII_CHAR(ch) (ch)
72 #endif /* !APR_CHARSET_EBCDIC */
73
74 /* we assume the folks using this ensure 0 <= c < 256... which means
75 * you need a cast to (unsigned char) first, you can't just plug a
76 * char in here and get it to work, because if char is signed then it
77 * will first be sign extended.
78 */
79 #define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
80
apr_escape_shell(char * escaped,const char * str,apr_ssize_t slen,apr_size_t * len)81 APR_DECLARE(apr_status_t) apr_escape_shell(char *escaped, const char *str,
82 apr_ssize_t slen, apr_size_t *len)
83 {
84 unsigned char *d;
85 const unsigned char *s;
86 apr_size_t size = 1;
87 int found = 0;
88
89 d = (unsigned char *) escaped;
90 s = (const unsigned char *) str;
91
92 if (s) {
93 if (d) {
94 for (; *s && slen; ++s, slen--) {
95 #if defined(OS2) || defined(WIN32)
96 /*
97 * Newlines to Win32/OS2 CreateProcess() are ill advised.
98 * Convert them to spaces since they are effectively white
99 * space to most applications
100 */
101 if (*s == '\r' || *s == '\n') {
102 if (d) {
103 *d++ = ' ';
104 found = 1;
105 }
106 continue;
107 }
108 #endif
109 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
110 *d++ = '\\';
111 size++;
112 found = 1;
113 }
114 *d++ = *s;
115 size++;
116 }
117 *d = '\0';
118 }
119 else {
120 for (; *s && slen; ++s, slen--) {
121 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
122 size++;
123 found = 1;
124 }
125 size++;
126 }
127 }
128 }
129
130 if (len) {
131 *len = size;
132 }
133 if (!found) {
134 return APR_NOTFOUND;
135 }
136
137 return APR_SUCCESS;
138 }
139
apr_pescape_shell(apr_pool_t * p,const char * str)140 APR_DECLARE(const char *) apr_pescape_shell(apr_pool_t *p, const char *str)
141 {
142 apr_size_t len;
143
144 switch (apr_escape_shell(NULL, str, APR_ESCAPE_STRING, &len)) {
145 case APR_SUCCESS: {
146 char *cmd = apr_palloc(p, len);
147 apr_escape_shell(cmd, str, APR_ESCAPE_STRING, NULL);
148 return cmd;
149 }
150 case APR_NOTFOUND: {
151 break;
152 }
153 }
154
155 return str;
156 }
157
x2c(const char * what)158 static char x2c(const char *what)
159 {
160 register char digit;
161
162 #if !APR_CHARSET_EBCDIC
163 digit =
164 ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
165 digit *= 16;
166 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
167 #else /*APR_CHARSET_EBCDIC*/
168 char xstr[5];
169 xstr[0]='0';
170 xstr[1]='x';
171 xstr[2]=what[0];
172 xstr[3]=what[1];
173 xstr[4]='\0';
174 digit = convert_a2e[0xFF & strtol(xstr, NULL, 16)];
175 #endif /*APR_CHARSET_EBCDIC*/
176 return (digit);
177 }
178
apr_unescape_url(char * escaped,const char * url,apr_ssize_t slen,const char * forbid,const char * reserved,int plus,apr_size_t * len)179 APR_DECLARE(apr_status_t) apr_unescape_url(char *escaped, const char *url,
180 apr_ssize_t slen, const char *forbid, const char *reserved, int plus,
181 apr_size_t *len)
182 {
183 apr_size_t size = 1;
184 int found = 0;
185 const char *s = (const char *) url;
186 char *d = (char *) escaped;
187 register int badesc, badpath;
188
189 if (!url) {
190 return APR_NOTFOUND;
191 }
192
193 badesc = 0;
194 badpath = 0;
195 if (s) {
196 if (d) {
197 for (; *s && slen; ++s, d++, slen--) {
198 if (plus && *s == '+') {
199 *d = ' ';
200 found = 1;
201 }
202 else if (*s != '%') {
203 *d = *s;
204 }
205 else {
206 if (!apr_isxdigit(*(s + 1)) || !apr_isxdigit(*(s + 2))) {
207 badesc = 1;
208 *d = '%';
209 }
210 else {
211 char decoded;
212 decoded = x2c(s + 1);
213 if ((decoded == '\0')
214 || (forbid && strchr(forbid, decoded))) {
215 badpath = 1;
216 *d = decoded;
217 s += 2;
218 slen -= 2;
219 }
220 else if (reserved && strchr(reserved, decoded)) {
221 *d++ = *s++;
222 *d++ = *s++;
223 *d = *s;
224 size += 2;
225 }
226 else {
227 *d = decoded;
228 s += 2;
229 slen -= 2;
230 found = 1;
231 }
232 }
233 }
234 size++;
235 }
236 *d = '\0';
237 }
238 else {
239 for (; *s && slen; ++s, slen--) {
240 if (plus && *s == '+') {
241 found = 1;
242 }
243 else if (*s != '%') {
244 /* character unchanged */
245 }
246 else {
247 if (!apr_isxdigit(*(s + 1)) || !apr_isxdigit(*(s + 2))) {
248 badesc = 1;
249 }
250 else {
251 char decoded;
252 decoded = x2c(s + 1);
253 if ((decoded == '\0')
254 || (forbid && strchr(forbid, decoded))) {
255 badpath = 1;
256 s += 2;
257 slen -= 2;
258 }
259 else if (reserved && strchr(reserved, decoded)) {
260 s += 2;
261 slen -= 2;
262 size += 2;
263 }
264 else {
265 s += 2;
266 slen -= 2;
267 found = 1;
268 }
269 }
270 }
271 size++;
272 }
273 }
274 }
275
276 if (len) {
277 *len = size;
278 }
279 if (badesc) {
280 return APR_EINVAL;
281 }
282 else if (badpath) {
283 return APR_BADCH;
284 }
285 else if (!found) {
286 return APR_NOTFOUND;
287 }
288
289 return APR_SUCCESS;
290 }
291
apr_punescape_url(apr_pool_t * p,const char * url,const char * forbid,const char * reserved,int plus)292 APR_DECLARE(const char *) apr_punescape_url(apr_pool_t *p, const char *url,
293 const char *forbid, const char *reserved, int plus)
294 {
295 apr_size_t len;
296
297 switch (apr_unescape_url(NULL, url, APR_ESCAPE_STRING, forbid, reserved,
298 plus, &len)) {
299 case APR_SUCCESS: {
300 char *buf = apr_palloc(p, len);
301 apr_unescape_url(buf, url, APR_ESCAPE_STRING, forbid, reserved, plus,
302 NULL);
303 return buf;
304 }
305 case APR_EINVAL:
306 case APR_BADCH: {
307 return NULL;
308 }
309 case APR_NOTFOUND: {
310 break;
311 }
312 }
313
314 return url;
315 }
316
317 /* c2x takes an unsigned, and expects the caller has guaranteed that
318 * 0 <= what < 256... which usually means that you have to cast to
319 * unsigned char first, because (unsigned)(char)(x) first goes through
320 * signed extension to an int before the unsigned cast.
321 *
322 * The reason for this assumption is to assist gcc code generation --
323 * the unsigned char -> unsigned extension is already done earlier in
324 * both uses of this code, so there's no need to waste time doing it
325 * again.
326 */
327 static const char c2x_table[] = "0123456789abcdef";
328
c2x(unsigned what,unsigned char prefix,unsigned char * where)329 static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
330 unsigned char *where)
331 {
332 #if APR_CHARSET_EBCDIC
333 what = convert_e2a[(unsigned char)what];
334 #endif /*APR_CHARSET_EBCDIC*/
335 *where++ = prefix;
336 *where++ = c2x_table[what >> 4];
337 *where++ = c2x_table[what & 0xf];
338 return where;
339 }
340
apr_escape_path_segment(char * escaped,const char * str,apr_ssize_t slen,apr_size_t * len)341 APR_DECLARE(apr_status_t) apr_escape_path_segment(char *escaped,
342 const char *str, apr_ssize_t slen, apr_size_t *len)
343 {
344 apr_size_t size = 1;
345 int found = 0;
346 const unsigned char *s = (const unsigned char *) str;
347 unsigned char *d = (unsigned char *) escaped;
348 unsigned c;
349
350 if (s) {
351 if (d) {
352 while ((c = *s) && slen) {
353 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
354 d = c2x(c, '%', d);
355 size += 2;
356 found = 1;
357 }
358 else {
359 *d++ = c;
360 }
361 ++s;
362 size++;
363 slen--;
364 }
365 *d = '\0';
366 }
367 else {
368 while ((c = *s) && slen) {
369 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
370 size += 2;
371 found = 1;
372 }
373 ++s;
374 size++;
375 slen--;
376 }
377 }
378 }
379
380 if (len) {
381 *len = size;
382 }
383 if (!found) {
384 return APR_NOTFOUND;
385 }
386
387 return APR_SUCCESS;
388 }
389
apr_pescape_path_segment(apr_pool_t * p,const char * str)390 APR_DECLARE(const char *) apr_pescape_path_segment(apr_pool_t *p,
391 const char *str)
392 {
393 apr_size_t len;
394
395 switch (apr_escape_path_segment(NULL, str, APR_ESCAPE_STRING, &len)) {
396 case APR_SUCCESS: {
397 char *cmd = apr_palloc(p, len);
398 apr_escape_path_segment(cmd, str, APR_ESCAPE_STRING, NULL);
399 return cmd;
400 }
401 case APR_NOTFOUND: {
402 break;
403 }
404 }
405
406 return str;
407 }
408
apr_escape_path(char * escaped,const char * path,apr_ssize_t slen,int partial,apr_size_t * len)409 APR_DECLARE(apr_status_t) apr_escape_path(char *escaped, const char *path,
410 apr_ssize_t slen, int partial, apr_size_t *len)
411 {
412 apr_size_t size = 1;
413 int found = 0;
414 const unsigned char *s = (const unsigned char *) path;
415 unsigned char *d = (unsigned char *) escaped;
416 unsigned c;
417
418 if (!path) {
419 return APR_NOTFOUND;
420 }
421
422 if (!partial) {
423 const char *colon = strchr(path, ':');
424 const char *slash = strchr(path, '/');
425
426 if (colon && (!slash || colon < slash)) {
427 if (d) {
428 *d++ = '.';
429 *d++ = '/';
430 }
431 size += 2;
432 found = 1;
433 }
434 }
435 if (d) {
436 while ((c = *s) && slen) {
437 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
438 d = c2x(c, '%', d);
439 size += 2;
440 found = 1;
441 }
442 else {
443 *d++ = c;
444 }
445 ++s;
446 size++;
447 slen--;
448 }
449 *d = '\0';
450 }
451 else {
452 while ((c = *s) && slen) {
453 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
454 size += 2;
455 found = 1;
456 }
457 ++s;
458 size++;
459 slen--;
460 }
461 }
462
463 if (len) {
464 *len = size;
465 }
466 if (!found) {
467 return APR_NOTFOUND;
468 }
469
470 return APR_SUCCESS;
471 }
472
apr_pescape_path(apr_pool_t * p,const char * str,int partial)473 APR_DECLARE(const char *) apr_pescape_path(apr_pool_t *p, const char *str,
474 int partial)
475 {
476 apr_size_t len;
477
478 switch (apr_escape_path(NULL, str, APR_ESCAPE_STRING, partial, &len)) {
479 case APR_SUCCESS: {
480 char *path = apr_palloc(p, len);
481 apr_escape_path(path, str, APR_ESCAPE_STRING, partial, NULL);
482 return path;
483 }
484 case APR_NOTFOUND: {
485 break;
486 }
487 }
488
489 return str;
490 }
491
apr_escape_urlencoded(char * escaped,const char * str,apr_ssize_t slen,apr_size_t * len)492 APR_DECLARE(apr_status_t) apr_escape_urlencoded(char *escaped, const char *str,
493 apr_ssize_t slen, apr_size_t *len)
494 {
495 apr_size_t size = 1;
496 int found = 0;
497 const unsigned char *s = (const unsigned char *) str;
498 unsigned char *d = (unsigned char *) escaped;
499 unsigned c;
500
501 if (s) {
502 if (d) {
503 while ((c = *s) && slen) {
504 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
505 d = c2x(c, '%', d);
506 size += 2;
507 found = 1;
508 }
509 else if (c == ' ') {
510 *d++ = '+';
511 found = 1;
512 }
513 else {
514 *d++ = c;
515 }
516 ++s;
517 size++;
518 slen--;
519 }
520 *d = '\0';
521 }
522 else {
523 while ((c = *s) && slen) {
524 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
525 size += 2;
526 found = 1;
527 }
528 else if (c == ' ') {
529 found = 1;
530 }
531 ++s;
532 size++;
533 slen--;
534 }
535 }
536 }
537
538 if (len) {
539 *len = size;
540 }
541 if (!found) {
542 return APR_NOTFOUND;
543 }
544
545 return APR_SUCCESS;
546 }
547
apr_pescape_urlencoded(apr_pool_t * p,const char * str)548 APR_DECLARE(const char *) apr_pescape_urlencoded(apr_pool_t *p, const char *str)
549 {
550 apr_size_t len;
551
552 switch (apr_escape_urlencoded(NULL, str, APR_ESCAPE_STRING, &len)) {
553 case APR_SUCCESS: {
554 char *encoded = apr_palloc(p, len);
555 apr_escape_urlencoded(encoded, str, APR_ESCAPE_STRING, NULL);
556 return encoded;
557 }
558 case APR_NOTFOUND: {
559 break;
560 }
561 }
562
563 return str;
564 }
565
apr_escape_entity(char * escaped,const char * str,apr_ssize_t slen,int toasc,apr_size_t * len)566 APR_DECLARE(apr_status_t) apr_escape_entity(char *escaped, const char *str,
567 apr_ssize_t slen, int toasc, apr_size_t *len)
568 {
569 apr_size_t size = 1;
570 int found = 0;
571 const unsigned char *s = (const unsigned char *) str;
572 unsigned char *d = (unsigned char *) escaped;
573 unsigned c;
574
575 if (s) {
576 if (d) {
577 while ((c = *s) && slen) {
578 if (TEST_CHAR(c, T_ESCAPE_XML)) {
579 switch (c) {
580 case '>': {
581 memcpy(d, ">", 4);
582 size += 4;
583 d += 4;
584 break;
585 }
586 case '<': {
587 memcpy(d, "<", 4);
588 size += 4;
589 d += 4;
590 break;
591 }
592 case '&': {
593 memcpy(d, "&", 5);
594 size += 5;
595 d += 5;
596 break;
597 }
598 case '\"': {
599 memcpy(d, """, 6);
600 size += 6;
601 d += 6;
602 break;
603 }
604 case '\'': {
605 memcpy(d, "'", 6);
606 size += 6;
607 d += 6;
608 break;
609 }
610 }
611 found = 1;
612 }
613 else if (toasc && !apr_isascii(c)) {
614 int offset = apr_snprintf((char *) d, 6, "&#%3.3d;", c);
615 size += offset;
616 d += offset;
617 found = 1;
618 }
619 else {
620 *d++ = c;
621 size++;
622 }
623 ++s;
624 slen--;
625 }
626 *d = '\0';
627 }
628 else {
629 while ((c = *s) && slen) {
630 if (TEST_CHAR(c, T_ESCAPE_XML)) {
631 switch (c) {
632 case '>': {
633 size += 4;
634 break;
635 }
636 case '<': {
637 size += 4;
638 break;
639 }
640 case '&': {
641 size += 5;
642 break;
643 }
644 case '\"': {
645 size += 6;
646 break;
647 }
648 case '\'': {
649 size += 6;
650 break;
651 }
652 }
653 found = 1;
654 }
655 else if (toasc && !apr_isascii(c)) {
656 char buf[8];
657 size += apr_snprintf(buf, 6, "&#%3.3d;", c);
658 found = 1;
659 }
660 else {
661 size++;
662 }
663 ++s;
664 slen--;
665 }
666 }
667 }
668
669 if (len) {
670 *len = size;
671 }
672 if (!found) {
673 return APR_NOTFOUND;
674 }
675
676 return APR_SUCCESS;
677 }
678
apr_pescape_entity(apr_pool_t * p,const char * str,int toasc)679 APR_DECLARE(const char *) apr_pescape_entity(apr_pool_t *p, const char *str,
680 int toasc)
681 {
682 apr_size_t len;
683
684 switch (apr_escape_entity(NULL, str, APR_ESCAPE_STRING, toasc, &len)) {
685 case APR_SUCCESS: {
686 char *cmd = apr_palloc(p, len);
687 apr_escape_entity(cmd, str, APR_ESCAPE_STRING, toasc, NULL);
688 return cmd;
689 }
690 case APR_NOTFOUND: {
691 break;
692 }
693 }
694
695 return str;
696 }
697
698 /* maximum length of any ISO-LATIN-1 HTML entity name. */
699 #define MAXENTLEN (6)
700
apr_unescape_entity(char * unescaped,const char * str,apr_ssize_t slen,apr_size_t * len)701 APR_DECLARE(apr_status_t) apr_unescape_entity(char *unescaped, const char *str,
702 apr_ssize_t slen, apr_size_t *len)
703 {
704 int found = 0;
705 apr_size_t size = 1;
706 int val, i, j;
707 char *d = unescaped;
708 const char *s = str;
709 const char *ents;
710 static const char * const entlist[MAXENTLEN + 1] =
711 {
712 NULL, /* 0 */
713 NULL, /* 1 */
714 "lt\074gt\076", /* 2 */
715 "amp\046ETH\320eth\360", /* 3 */
716 "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
717 "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
718 "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
719 "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
720 "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
721 "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
722 "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
723 "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
724 "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
725 "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
726 "oslash\370ugrave\371uacute\372yacute\375" /* 6 */
727 };
728
729 if (s) {
730 if (d) {
731 for (; *s != '\0' && slen; s++, d++, size++, slen--) {
732 if (*s != '&') {
733 *d = *s;
734 continue;
735 }
736 /* find end of entity */
737 for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0;
738 i++) {
739 continue;
740 }
741
742 if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */
743 *d = *s;
744 continue;
745 }
746
747 /* is it numeric ? */
748 if (s[1] == '#') {
749 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
750 val = val * 10 + s[j] - '0';
751 }
752 s += i;
753 if (j < i || val <= 8 || (val >= 11 && val <= 31)
754 || (val >= 127 && val <= 160) || val >= 256) {
755 d--; /* no data to output */
756 size--;
757 }
758 else {
759 *d = RAW_ASCII_CHAR(val);
760 found = 1;
761 }
762 }
763 else {
764 j = i - 1;
765 if (j > MAXENTLEN || entlist[j] == NULL) {
766 /* wrong length */
767 *d = '&';
768 continue; /* skip it */
769 }
770 for (ents = entlist[j]; *ents != '\0'; ents += i) {
771 if (strncmp(s + 1, ents, j) == 0) {
772 break;
773 }
774 }
775
776 if (*ents == '\0') {
777 *d = '&'; /* unknown */
778 }
779 else {
780 *d = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
781 s += i;
782 slen -= i;
783 found = 1;
784 }
785 }
786 }
787 *d = '\0';
788 }
789 else {
790 for (; *s != '\0' && slen; s++, size++, slen--) {
791 if (*s != '&') {
792 continue;
793 }
794 /* find end of entity */
795 for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0;
796 i++) {
797 continue;
798 }
799
800 if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */
801 continue;
802 }
803
804 /* is it numeric ? */
805 if (s[1] == '#') {
806 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) {
807 val = val * 10 + s[j] - '0';
808 }
809 s += i;
810 if (j < i || val <= 8 || (val >= 11 && val <= 31)
811 || (val >= 127 && val <= 160) || val >= 256) {
812 /* no data to output */
813 size--;
814 }
815 else {
816 found = 1;
817 }
818 }
819 else {
820 j = i - 1;
821 if (j > MAXENTLEN || entlist[j] == NULL) {
822 /* wrong length */
823 continue; /* skip it */
824 }
825 for (ents = entlist[j]; *ents != '\0'; ents += i) {
826 if (strncmp(s + 1, ents, j) == 0) {
827 break;
828 }
829 }
830
831 if (*ents == '\0') {
832 /* unknown */
833 }
834 else {
835 s += i;
836 slen -= i;
837 found = 1;
838 }
839 }
840 }
841 }
842 }
843
844 if (len) {
845 *len = size;
846 }
847 if (!found) {
848 return APR_NOTFOUND;
849 }
850
851 return APR_SUCCESS;
852 }
853
apr_punescape_entity(apr_pool_t * p,const char * str)854 APR_DECLARE(const char *) apr_punescape_entity(apr_pool_t *p, const char *str)
855 {
856 apr_size_t len;
857
858 switch (apr_unescape_entity(NULL, str, APR_ESCAPE_STRING, &len)) {
859 case APR_SUCCESS: {
860 char *cmd = apr_palloc(p, len);
861 apr_unescape_entity(cmd, str, APR_ESCAPE_STRING, NULL);
862 return cmd;
863 }
864 case APR_NOTFOUND: {
865 break;
866 }
867 }
868
869 return str;
870 }
871
apr_escape_echo(char * escaped,const char * str,apr_ssize_t slen,int quote,apr_size_t * len)872 APR_DECLARE(apr_status_t) apr_escape_echo(char *escaped, const char *str,
873 apr_ssize_t slen, int quote, apr_size_t *len)
874 {
875 apr_size_t size = 1;
876 int found = 0;
877 const unsigned char *s = (const unsigned char *) str;
878 unsigned char *d = (unsigned char *) escaped;
879 unsigned c;
880
881 if (s) {
882 if (d) {
883 while ((c = *s) && slen) {
884 if (TEST_CHAR(c, T_ESCAPE_ECHO)) {
885 *d++ = '\\';
886 size++;
887 switch (c) {
888 case '\a':
889 *d++ = 'a';
890 size++;
891 found = 1;
892 break;
893 case '\b':
894 *d++ = 'b';
895 size++;
896 found = 1;
897 break;
898 case '\f':
899 *d++ = 'f';
900 size++;
901 found = 1;
902 break;
903 case '\n':
904 *d++ = 'n';
905 size++;
906 found = 1;
907 break;
908 case '\r':
909 *d++ = 'r';
910 size++;
911 found = 1;
912 break;
913 case '\t':
914 *d++ = 't';
915 size++;
916 found = 1;
917 break;
918 case '\v':
919 *d++ = 'v';
920 size++;
921 found = 1;
922 break;
923 case '\\':
924 *d++ = '\\';
925 size++;
926 found = 1;
927 break;
928 case '"':
929 if (quote) {
930 *d++ = c;
931 size++;
932 found = 1;
933 }
934 else {
935 d[-1] = c;
936 }
937 break;
938 default:
939 c2x(c, 'x', d);
940 d += 3;
941 size += 3;
942 found = 1;
943 break;
944 }
945 }
946 else {
947 *d++ = c;
948 size++;
949 }
950 ++s;
951 slen--;
952 }
953 *d = '\0';
954 }
955 else {
956 while ((c = *s) && slen) {
957 if (TEST_CHAR(c, T_ESCAPE_ECHO)) {
958 size++;
959 switch (c) {
960 case '\a':
961 case '\b':
962 case '\f':
963 case '\n':
964 case '\r':
965 case '\t':
966 case '\v':
967 case '\\':
968 size++;
969 found = 1;
970 break;
971 case '"':
972 if (quote) {
973 size++;
974 found = 1;
975 }
976 break;
977 default:
978 size += 3;
979 found = 1;
980 break;
981 }
982 }
983 else {
984 size++;
985 }
986 ++s;
987 slen--;
988 }
989 }
990 }
991
992 if (len) {
993 *len = size;
994 }
995 if (!found) {
996 return APR_NOTFOUND;
997 }
998
999 return APR_SUCCESS;
1000 }
1001
apr_pescape_echo(apr_pool_t * p,const char * str,int quote)1002 APR_DECLARE(const char *) apr_pescape_echo(apr_pool_t *p, const char *str,
1003 int quote)
1004 {
1005 apr_size_t len;
1006
1007 switch (apr_escape_echo(NULL, str, APR_ESCAPE_STRING, quote, &len)) {
1008 case APR_SUCCESS: {
1009 char *cmd = apr_palloc(p, len);
1010 apr_escape_echo(cmd, str, APR_ESCAPE_STRING, quote, NULL);
1011 return cmd;
1012 }
1013 case APR_NOTFOUND: {
1014 break;
1015 }
1016 }
1017
1018 return str;
1019 }
1020
apr_escape_hex(char * dest,const void * src,apr_size_t srclen,int colon,apr_size_t * len)1021 APR_DECLARE(apr_status_t) apr_escape_hex(char *dest, const void *src,
1022 apr_size_t srclen, int colon, apr_size_t *len)
1023 {
1024 const unsigned char *in = src;
1025 apr_size_t size;
1026
1027 if (!src) {
1028 return APR_NOTFOUND;
1029 }
1030
1031 if (dest) {
1032 for (size = 0; size < srclen; size++) {
1033 if (colon && size) {
1034 *dest++ = ':';
1035 }
1036 *dest++ = c2x_table[in[size] >> 4];
1037 *dest++ = c2x_table[in[size] & 0xf];
1038 }
1039 *dest = '\0';
1040 }
1041
1042 if (len) {
1043 if (colon && srclen) {
1044 *len = srclen * 3;
1045 }
1046 else {
1047 *len = srclen * 2 + 1;
1048 }
1049 }
1050
1051 return APR_SUCCESS;
1052 }
1053
apr_pescape_hex(apr_pool_t * p,const void * src,apr_size_t srclen,int colon)1054 APR_DECLARE(const char *) apr_pescape_hex(apr_pool_t *p, const void *src,
1055 apr_size_t srclen, int colon)
1056 {
1057 apr_size_t len;
1058
1059 switch (apr_escape_hex(NULL, src, srclen, colon, &len)) {
1060 case APR_SUCCESS: {
1061 char *cmd = apr_palloc(p, len);
1062 apr_escape_hex(cmd, src, srclen, colon, NULL);
1063 return cmd;
1064 }
1065 case APR_NOTFOUND: {
1066 break;
1067 }
1068 }
1069
1070 return src;
1071 }
1072
apr_unescape_hex(void * dest,const char * str,apr_ssize_t slen,int colon,apr_size_t * len)1073 APR_DECLARE(apr_status_t) apr_unescape_hex(void *dest, const char *str,
1074 apr_ssize_t slen, int colon, apr_size_t *len)
1075 {
1076 apr_size_t size = 0;
1077 int flip = 0;
1078 const unsigned char *s = (const unsigned char *) str;
1079 unsigned char *d = (unsigned char *) dest;
1080 unsigned c;
1081 unsigned char u = 0;
1082
1083 if (s) {
1084 if (d) {
1085 while ((c = *s) && slen) {
1086
1087 if (!flip) {
1088 u = 0;
1089 }
1090
1091 if (colon && c == ':' && !flip) {
1092 ++s;
1093 slen--;
1094 continue;
1095 }
1096 else if (apr_isdigit(c)) {
1097 u |= c - '0';
1098 }
1099 else if (apr_isupper(c) && c <= 'F') {
1100 u |= c - ('A' - 10);
1101 }
1102 else if (apr_islower(c) && c <= 'f') {
1103 u |= c - ('a' - 10);
1104 }
1105 else {
1106 return APR_BADCH;
1107 }
1108
1109 if (flip) {
1110 *d++ = u;
1111 size++;
1112 }
1113 else {
1114 u <<= 4;
1115 *d = u;
1116 }
1117 flip = !flip;
1118
1119 ++s;
1120 slen--;
1121 }
1122 }
1123 else {
1124 while ((c = *s) && slen) {
1125
1126 if (colon && c == ':' && !flip) {
1127 ++s;
1128 slen--;
1129 continue;
1130 }
1131 else if (apr_isdigit(c)) {
1132 /* valid */
1133 }
1134 else if (apr_isupper(c) && c <= 'F') {
1135 /* valid */
1136 }
1137 else if (apr_islower(c) && c <= 'f') {
1138 /* valid */
1139 }
1140 else {
1141 return APR_BADCH;
1142 }
1143
1144 if (flip) {
1145 size++;
1146 }
1147 flip = !flip;
1148
1149 ++s;
1150 slen--;
1151 }
1152 }
1153 }
1154
1155 if (len) {
1156 *len = size;
1157 }
1158 if (!s) {
1159 return APR_NOTFOUND;
1160 }
1161
1162 return APR_SUCCESS;
1163 }
1164
apr_punescape_hex(apr_pool_t * p,const char * str,int colon,apr_size_t * len)1165 APR_DECLARE(const void *) apr_punescape_hex(apr_pool_t *p, const char *str,
1166 int colon, apr_size_t *len)
1167 {
1168 apr_size_t size;
1169
1170 switch (apr_unescape_hex(NULL, str, APR_ESCAPE_STRING, colon, &size)) {
1171 case APR_SUCCESS: {
1172 void *cmd = apr_palloc(p, size);
1173 apr_unescape_hex(cmd, str, APR_ESCAPE_STRING, colon, len);
1174 return cmd;
1175 }
1176 case APR_BADCH:
1177 case APR_NOTFOUND: {
1178 break;
1179 }
1180 }
1181
1182 return NULL;
1183 }
1184