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 34static 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 52static 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 81APR_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 140APR_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 158static 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 179APR_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 292APR_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 */ 327static const char c2x_table[] = "0123456789abcdef"; 328 329static 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 341APR_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 390APR_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 409APR_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 } 440 else { 441 *d++ = c; 442 } 443 ++s; 444 size++; 445 slen--; 446 } 447 *d = '\0'; 448 } 449 else { 450 while ((c = *s) && slen) { 451 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { 452 size += 2; 453 found = 1; 454 } 455 ++s; 456 size++; 457 slen--; 458 } 459 } 460 461 if (len) { 462 *len = size; 463 } 464 if (!found) { 465 return APR_NOTFOUND; 466 } 467 468 return APR_SUCCESS; 469} 470 471APR_DECLARE(const char *) apr_pescape_path(apr_pool_t *p, const char *str, 472 int partial) 473{ 474 apr_size_t len; 475 476 switch (apr_escape_path(NULL, str, APR_ESCAPE_STRING, partial, &len)) { 477 case APR_SUCCESS: { 478 char *path = apr_palloc(p, len); 479 apr_escape_path(path, str, APR_ESCAPE_STRING, partial, NULL); 480 return path; 481 } 482 case APR_NOTFOUND: { 483 break; 484 } 485 } 486 487 return str; 488} 489 490APR_DECLARE(apr_status_t) apr_escape_urlencoded(char *escaped, const char *str, 491 apr_ssize_t slen, apr_size_t *len) 492{ 493 apr_size_t size = 1; 494 int found = 0; 495 const unsigned char *s = (const unsigned char *) str; 496 unsigned char *d = (unsigned char *) escaped; 497 unsigned c; 498 499 if (s) { 500 if (d) { 501 while ((c = *s) && slen) { 502 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) { 503 d = c2x(c, '%', d); 504 size += 2; 505 found = 1; 506 } 507 else if (c == ' ') { 508 *d++ = '+'; 509 found = 1; 510 } 511 else { 512 *d++ = c; 513 } 514 ++s; 515 size++; 516 slen--; 517 } 518 *d = '\0'; 519 } 520 else { 521 while ((c = *s) && slen) { 522 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) { 523 size += 2; 524 found = 1; 525 } 526 else if (c == ' ') { 527 found = 1; 528 } 529 ++s; 530 size++; 531 slen--; 532 } 533 } 534 } 535 536 if (len) { 537 *len = size; 538 } 539 if (!found) { 540 return APR_NOTFOUND; 541 } 542 543 return APR_SUCCESS; 544} 545 546APR_DECLARE(const char *) apr_pescape_urlencoded(apr_pool_t *p, const char *str) 547{ 548 apr_size_t len; 549 550 switch (apr_escape_urlencoded(NULL, str, APR_ESCAPE_STRING, &len)) { 551 case APR_SUCCESS: { 552 char *encoded = apr_palloc(p, len); 553 apr_escape_urlencoded(encoded, str, APR_ESCAPE_STRING, NULL); 554 return encoded; 555 } 556 case APR_NOTFOUND: { 557 break; 558 } 559 } 560 561 return str; 562} 563 564APR_DECLARE(apr_status_t) apr_escape_entity(char *escaped, const char *str, 565 apr_ssize_t slen, int toasc, apr_size_t *len) 566{ 567 apr_size_t size = 1; 568 int found = 0; 569 const unsigned char *s = (const unsigned char *) str; 570 unsigned char *d = (unsigned char *) escaped; 571 unsigned c; 572 573 if (s) { 574 if (d) { 575 while ((c = *s) && slen) { 576 if (TEST_CHAR(c, T_ESCAPE_XML)) { 577 switch (c) { 578 case '>': { 579 memcpy(d, ">", 4); 580 size += 4; 581 d += 4; 582 break; 583 } 584 case '<': { 585 memcpy(d, "<", 4); 586 size += 4; 587 d += 4; 588 break; 589 } 590 case '&': { 591 memcpy(d, "&", 5); 592 size += 5; 593 d += 5; 594 break; 595 } 596 case '\"': { 597 memcpy(d, """, 6); 598 size += 6; 599 d += 6; 600 break; 601 } 602 case '\'': { 603 memcpy(d, "'", 6); 604 size += 6; 605 d += 6; 606 break; 607 } 608 } 609 found = 1; 610 } 611 else if (toasc && !apr_isascii(c)) { 612 int offset = apr_snprintf((char *) d, 6, "&#%3.3d;", c); 613 size += offset; 614 d += offset; 615 found = 1; 616 } 617 else { 618 *d++ = c; 619 size++; 620 } 621 ++s; 622 slen--; 623 } 624 *d = '\0'; 625 } 626 else { 627 while ((c = *s) && slen) { 628 if (TEST_CHAR(c, T_ESCAPE_XML)) { 629 switch (c) { 630 case '>': { 631 size += 4; 632 break; 633 } 634 case '<': { 635 size += 4; 636 break; 637 } 638 case '&': { 639 size += 5; 640 break; 641 } 642 case '\"': { 643 size += 6; 644 break; 645 } 646 case '\'': { 647 size += 6; 648 break; 649 } 650 } 651 found = 1; 652 } 653 else if (toasc && !apr_isascii(c)) { 654 char buf[8]; 655 size += apr_snprintf(buf, 6, "&#%3.3d;", c); 656 found = 1; 657 } 658 else { 659 size++; 660 } 661 ++s; 662 slen--; 663 } 664 } 665 } 666 667 if (len) { 668 *len = size; 669 } 670 if (!found) { 671 return APR_NOTFOUND; 672 } 673 674 return APR_SUCCESS; 675} 676 677APR_DECLARE(const char *) apr_pescape_entity(apr_pool_t *p, const char *str, 678 int toasc) 679{ 680 apr_size_t len; 681 682 switch (apr_escape_entity(NULL, str, APR_ESCAPE_STRING, toasc, &len)) { 683 case APR_SUCCESS: { 684 char *cmd = apr_palloc(p, len); 685 apr_escape_entity(cmd, str, APR_ESCAPE_STRING, toasc, NULL); 686 return cmd; 687 } 688 case APR_NOTFOUND: { 689 break; 690 } 691 } 692 693 return str; 694} 695 696/* maximum length of any ISO-LATIN-1 HTML entity name. */ 697#define MAXENTLEN (6) 698 699APR_DECLARE(apr_status_t) apr_unescape_entity(char *unescaped, const char *str, 700 apr_ssize_t slen, apr_size_t *len) 701{ 702 int found = 0; 703 apr_size_t size = 1; 704 int val, i, j; 705 char *d = unescaped; 706 const char *s = str; 707 const char *ents; 708 static const char * const entlist[MAXENTLEN + 1] = 709 { 710 NULL, /* 0 */ 711 NULL, /* 1 */ 712 "lt\074gt\076", /* 2 */ 713 "amp\046ETH\320eth\360", /* 3 */ 714 "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml" 715 "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */ 716 "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc" 717 "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352" 718 "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */ 719 "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311" 720 "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde" 721 "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340" 722 "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave" 723 "\354iacute\355ntilde\361ograve\362oacute\363otilde\365" 724 "oslash\370ugrave\371uacute\372yacute\375" /* 6 */ 725 }; 726 727 if (s) { 728 if (d) { 729 for (; *s != '\0' && slen; s++, d++, size++, slen--) { 730 if (*s != '&') { 731 *d = *s; 732 continue; 733 } 734 /* find end of entity */ 735 for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0; 736 i++) { 737 continue; 738 } 739 740 if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */ 741 *d = *s; 742 continue; 743 } 744 745 /* is it numeric ? */ 746 if (s[1] == '#') { 747 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) { 748 val = val * 10 + s[j] - '0'; 749 } 750 s += i; 751 if (j < i || val <= 8 || (val >= 11 && val <= 31) 752 || (val >= 127 && val <= 160) || val >= 256) { 753 d--; /* no data to output */ 754 size--; 755 } 756 else { 757 *d = RAW_ASCII_CHAR(val); 758 found = 1; 759 } 760 } 761 else { 762 j = i - 1; 763 if (j > MAXENTLEN || entlist[j] == NULL) { 764 /* wrong length */ 765 *d = '&'; 766 continue; /* skip it */ 767 } 768 for (ents = entlist[j]; *ents != '\0'; ents += i) { 769 if (strncmp(s + 1, ents, j) == 0) { 770 break; 771 } 772 } 773 774 if (*ents == '\0') { 775 *d = '&'; /* unknown */ 776 } 777 else { 778 *d = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]); 779 s += i; 780 slen -= i; 781 found = 1; 782 } 783 } 784 } 785 *d = '\0'; 786 } 787 else { 788 for (; *s != '\0' && slen; s++, size++, slen--) { 789 if (*s != '&') { 790 continue; 791 } 792 /* find end of entity */ 793 for (i = 1; s[i] != ';' && s[i] != '\0' && (slen - i) != 0; 794 i++) { 795 continue; 796 } 797 798 if (s[i] == '\0' || (slen - i) == 0) { /* treat as normal data */ 799 continue; 800 } 801 802 /* is it numeric ? */ 803 if (s[1] == '#') { 804 for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) { 805 val = val * 10 + s[j] - '0'; 806 } 807 s += i; 808 if (j < i || val <= 8 || (val >= 11 && val <= 31) 809 || (val >= 127 && val <= 160) || val >= 256) { 810 /* no data to output */ 811 size--; 812 } 813 else { 814 found = 1; 815 } 816 } 817 else { 818 j = i - 1; 819 if (j > MAXENTLEN || entlist[j] == NULL) { 820 /* wrong length */ 821 continue; /* skip it */ 822 } 823 for (ents = entlist[j]; *ents != '\0'; ents += i) { 824 if (strncmp(s + 1, ents, j) == 0) { 825 break; 826 } 827 } 828 829 if (*ents == '\0') { 830 /* unknown */ 831 } 832 else { 833 s += i; 834 slen -= i; 835 found = 1; 836 } 837 } 838 } 839 } 840 } 841 842 if (len) { 843 *len = size; 844 } 845 if (!found) { 846 return APR_NOTFOUND; 847 } 848 849 return APR_SUCCESS; 850} 851 852APR_DECLARE(const char *) apr_punescape_entity(apr_pool_t *p, const char *str) 853{ 854 apr_size_t len; 855 856 switch (apr_unescape_entity(NULL, str, APR_ESCAPE_STRING, &len)) { 857 case APR_SUCCESS: { 858 char *cmd = apr_palloc(p, len); 859 apr_unescape_entity(cmd, str, APR_ESCAPE_STRING, NULL); 860 return cmd; 861 } 862 case APR_NOTFOUND: { 863 break; 864 } 865 } 866 867 return str; 868} 869 870APR_DECLARE(apr_status_t) apr_escape_echo(char *escaped, const char *str, 871 apr_ssize_t slen, int quote, apr_size_t *len) 872{ 873 apr_size_t size = 1; 874 int found = 0; 875 const unsigned char *s = (const unsigned char *) str; 876 unsigned char *d = (unsigned char *) escaped; 877 unsigned c; 878 879 if (s) { 880 if (d) { 881 while ((c = *s) && slen) { 882 if (TEST_CHAR(c, T_ESCAPE_ECHO)) { 883 *d++ = '\\'; 884 size++; 885 switch (c) { 886 case '\a': 887 *d++ = 'a'; 888 size++; 889 found = 1; 890 break; 891 case '\b': 892 *d++ = 'b'; 893 size++; 894 found = 1; 895 break; 896 case '\f': 897 *d++ = 'f'; 898 size++; 899 found = 1; 900 break; 901 case '\n': 902 *d++ = 'n'; 903 size++; 904 found = 1; 905 break; 906 case '\r': 907 *d++ = 'r'; 908 size++; 909 found = 1; 910 break; 911 case '\t': 912 *d++ = 't'; 913 size++; 914 found = 1; 915 break; 916 case '\v': 917 *d++ = 'v'; 918 size++; 919 found = 1; 920 break; 921 case '\\': 922 *d++ = '\\'; 923 size++; 924 found = 1; 925 break; 926 case '"': 927 if (quote) { 928 *d++ = c; 929 size++; 930 found = 1; 931 } 932 else { 933 d[-1] = c; 934 } 935 break; 936 default: 937 c2x(c, 'x', d); 938 d += 3; 939 size += 3; 940 found = 1; 941 break; 942 } 943 } 944 else { 945 *d++ = c; 946 size++; 947 } 948 ++s; 949 slen--; 950 } 951 *d = '\0'; 952 } 953 else { 954 while ((c = *s) && slen) { 955 if (TEST_CHAR(c, T_ESCAPE_ECHO)) { 956 size++; 957 switch (c) { 958 case '\a': 959 case '\b': 960 case '\f': 961 case '\n': 962 case '\r': 963 case '\t': 964 case '\v': 965 case '\\': 966 size++; 967 found = 1; 968 break; 969 case '"': 970 if (quote) { 971 size++; 972 found = 1; 973 } 974 break; 975 default: 976 size += 3; 977 found = 1; 978 break; 979 } 980 } 981 else { 982 size++; 983 } 984 ++s; 985 slen--; 986 } 987 } 988 } 989 990 if (len) { 991 *len = size; 992 } 993 if (!found) { 994 return APR_NOTFOUND; 995 } 996 997 return APR_SUCCESS; 998} 999 1000APR_DECLARE(const char *) apr_pescape_echo(apr_pool_t *p, const char *str, 1001 int quote) 1002{ 1003 apr_size_t len; 1004 1005 switch (apr_escape_echo(NULL, str, APR_ESCAPE_STRING, quote, &len)) { 1006 case APR_SUCCESS: { 1007 char *cmd = apr_palloc(p, len); 1008 apr_escape_echo(cmd, str, APR_ESCAPE_STRING, quote, NULL); 1009 return cmd; 1010 } 1011 case APR_NOTFOUND: { 1012 break; 1013 } 1014 } 1015 1016 return str; 1017} 1018 1019APR_DECLARE(apr_status_t) apr_escape_hex(char *dest, const void *src, 1020 apr_size_t srclen, int colon, apr_size_t *len) 1021{ 1022 const unsigned char *in = src; 1023 apr_size_t size; 1024 1025 if (!src) { 1026 return APR_NOTFOUND; 1027 } 1028 1029 if (dest) { 1030 for (size = 0; size < srclen; size++) { 1031 if (colon && size) { 1032 *dest++ = ':'; 1033 } 1034 *dest++ = c2x_table[in[size] >> 4]; 1035 *dest++ = c2x_table[in[size] & 0xf]; 1036 } 1037 *dest = '\0'; 1038 } 1039 1040 if (len) { 1041 if (colon && srclen) { 1042 *len = srclen * 3; 1043 } 1044 else { 1045 *len = srclen * 2 + 1; 1046 } 1047 } 1048 1049 return APR_SUCCESS; 1050} 1051 1052APR_DECLARE(const char *) apr_pescape_hex(apr_pool_t *p, const void *src, 1053 apr_size_t srclen, int colon) 1054{ 1055 apr_size_t len; 1056 1057 switch (apr_escape_hex(NULL, src, srclen, colon, &len)) { 1058 case APR_SUCCESS: { 1059 char *cmd = apr_palloc(p, len); 1060 apr_escape_hex(cmd, src, srclen, colon, NULL); 1061 return cmd; 1062 } 1063 case APR_NOTFOUND: { 1064 break; 1065 } 1066 } 1067 1068 return src; 1069} 1070 1071APR_DECLARE(apr_status_t) apr_unescape_hex(void *dest, const char *str, 1072 apr_ssize_t slen, int colon, apr_size_t *len) 1073{ 1074 apr_size_t size = 0; 1075 int flip = 0; 1076 const unsigned char *s = (const unsigned char *) str; 1077 unsigned char *d = (unsigned char *) dest; 1078 unsigned c; 1079 unsigned char u = 0; 1080 1081 if (s) { 1082 if (d) { 1083 while ((c = *s) && slen) { 1084 1085 if (!flip) { 1086 u = 0; 1087 } 1088 1089 if (colon && c == ':' && !flip) { 1090 ++s; 1091 slen--; 1092 continue; 1093 } 1094 else if (apr_isdigit(c)) { 1095 u |= c - '0'; 1096 } 1097 else if (apr_isupper(c) && c <= 'F') { 1098 u |= c - ('A' - 10); 1099 } 1100 else if (apr_islower(c) && c <= 'f') { 1101 u |= c - ('a' - 10); 1102 } 1103 else { 1104 return APR_BADCH; 1105 } 1106 1107 if (flip) { 1108 *d++ = u; 1109 size++; 1110 } 1111 else { 1112 u <<= 4; 1113 *d = u; 1114 } 1115 flip = !flip; 1116 1117 ++s; 1118 slen--; 1119 } 1120 } 1121 else { 1122 while ((c = *s) && slen) { 1123 1124 if (colon && c == ':' && !flip) { 1125 ++s; 1126 slen--; 1127 continue; 1128 } 1129 else if (apr_isdigit(c)) { 1130 /* valid */ 1131 } 1132 else if (apr_isupper(c) && c <= 'F') { 1133 /* valid */ 1134 } 1135 else if (apr_islower(c) && c <= 'f') { 1136 /* valid */ 1137 } 1138 else { 1139 return APR_BADCH; 1140 } 1141 1142 if (flip) { 1143 size++; 1144 } 1145 flip = !flip; 1146 1147 ++s; 1148 slen--; 1149 } 1150 } 1151 } 1152 1153 if (len) { 1154 *len = size; 1155 } 1156 if (!s) { 1157 return APR_NOTFOUND; 1158 } 1159 1160 return APR_SUCCESS; 1161} 1162 1163APR_DECLARE(const void *) apr_punescape_hex(apr_pool_t *p, const char *str, 1164 int colon, apr_size_t *len) 1165{ 1166 apr_size_t size; 1167 1168 switch (apr_unescape_hex(NULL, str, APR_ESCAPE_STRING, colon, &size)) { 1169 case APR_SUCCESS: { 1170 void *cmd = apr_palloc(p, size); 1171 apr_unescape_hex(cmd, str, APR_ESCAPE_STRING, colon, len); 1172 return cmd; 1173 } 1174 case APR_BADCH: 1175 case APR_NOTFOUND: { 1176 break; 1177 } 1178 } 1179 1180 return NULL; 1181} 1182