| preludedrew | 4e71eeb | 2010-12-30 20:09:04 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2001-2004 Brandon Long |
| 3 | * All Rights Reserved. |
| 4 | * |
| 5 | * ClearSilver Templating System |
| 6 | * |
| 7 | * This code is made available under the terms of the ClearSilver License. |
| 8 | * http://www.clearsilver.net/license.hdf |
| 9 | * |
| 10 | */ |
| 11 | |
| 12 | #include "cs_config.h" |
| 13 | |
| 14 | #include <unistd.h> |
| 15 | #include <ctype.h> |
| 16 | #include <stdlib.h> |
| 17 | #include <stdio.h> |
| 18 | #include <string.h> |
| 19 | #include <stdarg.h> |
| 20 | #include <regex.h> |
| 21 | #include "neo_misc.h" |
| 22 | #include "neo_err.h" |
| 23 | #include "neo_str.h" |
| 24 | #include "ulist.h" |
| 25 | |
| 26 | #ifndef va_copy |
| 27 | #ifdef __va_copy |
| 28 | # define va_copy(dest,src) __va_copy(dest,src) |
| 29 | #else |
| 30 | # define va_copy(dest,src) ((dest) = (src)) |
| 31 | #endif |
| 32 | #endif |
| 33 | |
| 34 | char *neos_strip (char *s) |
| 35 | { |
| 36 | int x; |
| 37 | |
| 38 | x = strlen(s) - 1; |
| 39 | while (x>=0 && isspace(s[x])) s[x--] = '\0'; |
| 40 | |
| 41 | while (*s && isspace(*s)) s++; |
| 42 | |
| 43 | return s; |
| 44 | } |
| 45 | |
| 46 | char *neos_rstrip (char *s) |
| 47 | { |
| 48 | int n = strlen (s)-1; |
| 49 | |
| 50 | while (n >= 0 && isspace(s[n])) |
| 51 | { |
| 52 | s[n] = '\0'; |
| 53 | n--; |
| 54 | } |
| 55 | |
| 56 | return s; |
| 57 | } |
| 58 | |
| 59 | void neos_lower(char *s) |
| 60 | { |
| 61 | while(*s != 0) { |
| 62 | *s = tolower(*s); |
| 63 | s++; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | |
| 68 | void string_init (STRING *str) |
| 69 | { |
| 70 | str->buf = NULL; |
| 71 | str->len = 0; |
| 72 | str->max = 0; |
| 73 | } |
| 74 | |
| 75 | void string_clear (STRING *str) |
| 76 | { |
| 77 | if (str->buf != NULL) |
| 78 | free(str->buf); |
| 79 | string_init(str); |
| 80 | } |
| 81 | |
| 82 | static NEOERR* string_check_length (STRING *str, int l) |
| 83 | { |
| 84 | if (str->buf == NULL) |
| 85 | { |
| 86 | if (l * 10 > 256) |
| 87 | str->max = l * 10; |
| 88 | else |
| 89 | str->max = 256; |
| 90 | str->buf = (char *) malloc (sizeof(char) * str->max); |
| 91 | if (str->buf == NULL) |
| 92 | return nerr_raise (NERR_NOMEM, "Unable to allocate render buf of size %d", |
| 93 | str->max); |
| 94 | /* ne_warn("Creating string %x at %d (%5.2fK)", str, str->max, (str->max / 1024.0)); */ |
| 95 | } |
| 96 | else if (str->len + l >= str->max) |
| 97 | { |
| 98 | do |
| 99 | { |
| 100 | str->max *= 2; |
| 101 | } while (str->len + l >= str->max); |
| 102 | str->buf = (char *) realloc (str->buf, sizeof(char) * str->max); |
| 103 | if (str->buf == NULL) |
| 104 | return nerr_raise (NERR_NOMEM, "Unable to allocate STRING buf of size %d", |
| 105 | str->max); |
| 106 | /* ne_warn("Growing string %x to %d (%5.2fK)", str, str->max, (str->max / 1024.0)); */ |
| 107 | } |
| 108 | return STATUS_OK; |
| 109 | } |
| 110 | |
| 111 | NEOERR *string_set (STRING *str, const char *buf) |
| 112 | { |
| 113 | str->len = 0; |
| 114 | return nerr_pass (string_append (str, buf)); |
| 115 | } |
| 116 | |
| 117 | NEOERR *string_append (STRING *str, const char *buf) |
| 118 | { |
| 119 | NEOERR *err; |
| 120 | int l; |
| 121 | |
| 122 | l = strlen(buf); |
| 123 | err = string_check_length (str, l); |
| 124 | if (err != STATUS_OK) return nerr_pass (err); |
| 125 | strcpy(str->buf + str->len, buf); |
| 126 | str->len += l; |
| 127 | |
| 128 | return STATUS_OK; |
| 129 | } |
| 130 | |
| 131 | NEOERR *string_appendn (STRING *str, const char *buf, int l) |
| 132 | { |
| 133 | NEOERR *err; |
| 134 | |
| 135 | err = string_check_length (str, l+1); |
| 136 | if (err != STATUS_OK) return nerr_pass (err); |
| 137 | memcpy(str->buf + str->len, buf, l); |
| 138 | str->len += l; |
| 139 | str->buf[str->len] = '\0'; |
| 140 | |
| 141 | return STATUS_OK; |
| 142 | } |
| 143 | |
| 144 | /* this is much more efficient with C99 snprintfs... */ |
| 145 | NEOERR *string_appendvf (STRING *str, const char *fmt, va_list ap) |
| 146 | { |
| 147 | NEOERR *err; |
| 148 | char buf[4096]; |
| 149 | int bl, size; |
| 150 | va_list tmp; |
| 151 | |
| 152 | va_copy(tmp, ap); |
| 153 | /* determine length */ |
| 154 | size = sizeof (buf); |
| 155 | bl = vsnprintf (buf, size, fmt, tmp); |
| 156 | if (bl > -1 && bl < size) |
| 157 | return string_appendn (str, buf, bl); |
| 158 | |
| 159 | /* Handle non-C99 snprintfs (requires extra malloc/free and copy) */ |
| 160 | if (bl == -1) |
| 161 | { |
| 162 | char *a_buf; |
| 163 | |
| 164 | va_copy(tmp, ap); |
| 165 | a_buf = vnsprintf_alloc(size*2, fmt, tmp); |
| 166 | if (a_buf == NULL) |
| 167 | return nerr_raise(NERR_NOMEM, |
| 168 | "Unable to allocate memory for formatted string"); |
| 169 | err = string_append(str, a_buf); |
| 170 | free(a_buf); |
| 171 | return nerr_pass(err); |
| 172 | } |
| 173 | |
| 174 | err = string_check_length (str, bl+1); |
| 175 | if (err != STATUS_OK) return nerr_pass (err); |
| 176 | va_copy(tmp, ap); |
| 177 | vsprintf (str->buf + str->len, fmt, tmp); |
| 178 | str->len += bl; |
| 179 | str->buf[str->len] = '\0'; |
| 180 | |
| 181 | return STATUS_OK; |
| 182 | } |
| 183 | |
| 184 | NEOERR *string_appendf (STRING *str, const char *fmt, ...) |
| 185 | { |
| 186 | NEOERR *err; |
| 187 | va_list ap; |
| 188 | |
| 189 | va_start (ap, fmt); |
| 190 | err = string_appendvf (str, fmt, ap); |
| 191 | va_end (ap); |
| 192 | return nerr_pass(err); |
| 193 | } |
| 194 | |
| 195 | NEOERR *string_append_char (STRING *str, char c) |
| 196 | { |
| 197 | NEOERR *err; |
| 198 | err = string_check_length (str, 1); |
| 199 | if (err != STATUS_OK) return nerr_pass (err); |
| 200 | str->buf[str->len] = c; |
| 201 | str->buf[str->len + 1] = '\0'; |
| 202 | str->len += 1; |
| 203 | |
| 204 | return STATUS_OK; |
| 205 | } |
| 206 | |
| 207 | void string_array_init (STRING_ARRAY *arr) |
| 208 | { |
| 209 | arr->entries = NULL; |
| 210 | arr->count = 0; |
| 211 | arr->max = 0; |
| 212 | } |
| 213 | |
| 214 | NEOERR *string_array_split (ULIST **list, char *s, const char *sep, |
| 215 | int max) |
| 216 | { |
| 217 | NEOERR *err; |
| 218 | char *p, *n, *f; |
| 219 | int sl; |
| 220 | int x = 0; |
| 221 | |
| 222 | if (sep[0] == '\0') |
| 223 | return nerr_raise (NERR_ASSERT, "separator must be at least one character"); |
| 224 | |
| 225 | err = uListInit (list, 10, 0); |
| 226 | if (err) return nerr_pass(err); |
| 227 | |
| 228 | sl = strlen(sep); |
| 229 | p = (sl == 1) ? strchr (s, sep[0]) : strstr (s, sep); |
| 230 | f = s; |
| 231 | while (p != NULL) |
| 232 | { |
| 233 | if (x >= max) break; |
| 234 | *p = '\0'; |
| 235 | n = strdup(f); |
| 236 | *p = sep[0]; |
| 237 | if (n) err = uListAppend (*list, n); |
| 238 | else err = nerr_raise(NERR_NOMEM, |
| 239 | "Unable to allocate memory to split %s", s); |
| 240 | if (err) goto split_err; |
| 241 | f = p+sl; |
| 242 | p = (sl == 1) ? strchr (f, sep[0]) : strstr (f, sep); |
| 243 | x++; |
| 244 | } |
| 245 | /* Handle remainder */ |
| 246 | n = strdup(f); |
| 247 | if (n) err = uListAppend (*list, n); |
| 248 | else err = nerr_raise(NERR_NOMEM, |
| 249 | "Unable to allocate memory to split %s", s); |
| 250 | if (err) goto split_err; |
| 251 | return STATUS_OK; |
| 252 | |
| 253 | split_err: |
| 254 | uListDestroy(list, ULIST_FREE); |
| 255 | return err; |
| 256 | } |
| 257 | |
| 258 | void string_array_clear (STRING_ARRAY *arr) |
| 259 | { |
| 260 | int x; |
| 261 | |
| 262 | for (x = 0; x < arr->count; x++) |
| 263 | { |
| 264 | if (arr->entries[x] != NULL) free (arr->entries[x]); |
| 265 | arr->entries[x] = NULL; |
| 266 | } |
| 267 | free (arr->entries); |
| 268 | arr->entries = NULL; |
| 269 | arr->count = 0; |
| 270 | } |
| 271 | |
| 272 | /* Mostly used by vprintf_alloc for non-C99 compliant snprintfs, |
| 273 | * this is like vsprintf_alloc except it takes a "suggested" size */ |
| 274 | int vnisprintf_alloc (char **buf, int start_size, const char *fmt, va_list ap) |
| 275 | { |
| 276 | int bl, size; |
| 277 | va_list tmp; |
| 278 | |
| 279 | *buf = NULL; |
| 280 | size = start_size; |
| 281 | |
| 282 | *buf = (char *) malloc (size * sizeof(char)); |
| 283 | if (*buf == NULL) return 0; |
| 284 | while (1) |
| 285 | { |
| 286 | va_copy(tmp, ap); |
| 287 | bl = vsnprintf (*buf, size, fmt, tmp); |
| 288 | if (bl > -1 && bl < size) |
| 289 | return bl; |
| 290 | if (bl > -1) |
| 291 | size = bl + 1; |
| 292 | else |
| 293 | size *= 2; |
| 294 | *buf = (char *) realloc (*buf, size * sizeof(char)); |
| 295 | if (*buf == NULL) return 0; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | char *vnsprintf_alloc (int start_size, const char *fmt, va_list ap) |
| 300 | { |
| 301 | char *r; |
| 302 | vnisprintf_alloc(&r, start_size, fmt, ap); |
| 303 | return r; |
| 304 | } |
| 305 | |
| 306 | /* This works better with a C99 compliant vsnprintf, but should work ok |
| 307 | * with versions that return a -1 if it overflows the buffer */ |
| 308 | int visprintf_alloc (char **buf, const char *fmt, va_list ap) |
| 309 | { |
| 310 | char ibuf[4096]; |
| 311 | int bl, size; |
| 312 | va_list tmp; |
| 313 | |
| 314 | /* PPC doesn't like you re-using a va_list... and it might not be |
| 315 | * supposed to work at all */ |
| 316 | va_copy(tmp, ap); |
| 317 | |
| 318 | size = sizeof (ibuf); |
| 319 | bl = vsnprintf (ibuf, sizeof (ibuf), fmt, tmp); |
| 320 | if (bl > -1 && bl < size) |
| 321 | { |
| 322 | *buf = (char *) calloc(bl+1, sizeof(char)); |
| 323 | if (*buf == NULL) return 0; |
| 324 | strncpy(*buf, ibuf, bl); |
| 325 | return bl; |
| 326 | } |
| 327 | |
| 328 | if (bl > -1) |
| 329 | size = bl + 1; |
| 330 | else |
| 331 | size *= 2; |
| 332 | |
| 333 | return vnisprintf_alloc(buf, size, fmt, ap); |
| 334 | } |
| 335 | |
| 336 | char *vsprintf_alloc (const char *fmt, va_list ap) |
| 337 | { |
| 338 | char *r; |
| 339 | visprintf_alloc(&r, fmt, ap); |
| 340 | return r; |
| 341 | } |
| 342 | |
| 343 | /* technically, sprintf's can have null values, so we need to be able to |
| 344 | * return a length also like real sprintf */ |
| 345 | int isprintf_alloc (char **buf, const char *fmt, ...) |
| 346 | { |
| 347 | va_list ap; |
| 348 | int r; |
| 349 | |
| 350 | va_start (ap, fmt); |
| 351 | r = visprintf_alloc (buf, fmt, ap); |
| 352 | va_end (ap); |
| 353 | return r; |
| 354 | } |
| 355 | |
| 356 | char *sprintf_alloc (const char *fmt, ...) |
| 357 | { |
| 358 | va_list ap; |
| 359 | char *r; |
| 360 | |
| 361 | va_start (ap, fmt); |
| 362 | r = vsprintf_alloc (fmt, ap); |
| 363 | va_end (ap); |
| 364 | return r; |
| 365 | } |
| 366 | |
| 367 | /* This is mostly just here for completeness, I doubt anyone would use |
| 368 | * this (its more efficient (time-wise) if start_size is bigger than the |
| 369 | * resulting string. Its less efficient than sprintf_alloc if we have a |
| 370 | * C99 snprintf and it doesn't fit in start_size. |
| 371 | * BTW: If you are really worried about the efficiency of these |
| 372 | * functions, maybe you shouldn't be using them in the first place... */ |
| 373 | char *nsprintf_alloc (int start_size, const char *fmt, ...) |
| 374 | { |
| 375 | va_list ap; |
| 376 | char *r; |
| 377 | |
| 378 | va_start (ap, fmt); |
| 379 | r = vnsprintf_alloc (start_size, fmt, ap); |
| 380 | va_end (ap); |
| 381 | return r; |
| 382 | } |
| 383 | |
| 384 | BOOL reg_search (const char *re, const char *str) |
| 385 | { |
| 386 | regex_t search_re; |
| 387 | int errcode; |
| 388 | char buf[256]; |
| 389 | |
| 390 | if ((errcode = regcomp(&search_re, re, REG_ICASE | REG_EXTENDED | REG_NOSUB))) |
| 391 | { |
| 392 | regerror (errcode, &search_re, buf, sizeof(buf)); |
| 393 | ne_warn ("Unable to compile regex %s: %s", re, buf); |
| 394 | return FALSE; |
| 395 | } |
| 396 | errcode = regexec (&search_re, str, 0, NULL, 0); |
| 397 | regfree (&search_re); |
| 398 | if (errcode == 0) |
| 399 | return TRUE; |
| 400 | return FALSE; |
| 401 | } |
| 402 | |
| 403 | NEOERR *string_readline (STRING *str, FILE *fp) |
| 404 | { |
| 405 | NEOERR *err; |
| 406 | |
| 407 | /* minimum size for a readline is 256 above current position */ |
| 408 | err = string_check_length (str, str->len + 256); |
| 409 | if (err != STATUS_OK) return nerr_pass (err); |
| 410 | |
| 411 | while (fgets(str->buf + str->len, str->max - str->len, fp) != NULL) |
| 412 | { |
| 413 | str->len = strlen(str->buf); |
| 414 | if (str->buf[str->len-1] == '\n') break; |
| 415 | err = string_check_length (str, str->len + 256); |
| 416 | if (err != STATUS_OK) return nerr_pass (err); |
| 417 | } |
| 418 | return STATUS_OK; |
| 419 | } |
| 420 | |
| 421 | NEOERR* neos_escape(UINT8 *buf, int buflen, char esc_char, const char *escape, |
| 422 | char **esc) |
| 423 | { |
| 424 | int nl = 0; |
| 425 | int l = 0; |
| 426 | int x = 0; |
| 427 | char *s; |
| 428 | int match = 0; |
| 429 | |
| 430 | while (l < buflen) |
| 431 | { |
| 432 | if (buf[l] == esc_char) |
| 433 | { |
| 434 | nl += 2; |
| 435 | } |
| 436 | else |
| 437 | { |
| 438 | x = 0; |
| 439 | while (escape[x]) |
| 440 | { |
| 441 | if (escape[x] == buf[l]) |
| 442 | { |
| 443 | nl +=2; |
| 444 | break; |
| 445 | } |
| 446 | x++; |
| 447 | } |
| 448 | } |
| 449 | nl++; |
| 450 | l++; |
| 451 | } |
| 452 | |
| 453 | s = (char *) malloc (sizeof(char) * (nl + 1)); |
| 454 | if (s == NULL) |
| 455 | return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", |
| 456 | buf); |
| 457 | |
| 458 | nl = 0; l = 0; |
| 459 | while (l < buflen) |
| 460 | { |
| 461 | match = 0; |
| 462 | if (buf[l] == esc_char) |
| 463 | { |
| 464 | match = 1; |
| 465 | } |
| 466 | else |
| 467 | { |
| 468 | x = 0; |
| 469 | while (escape[x]) |
| 470 | { |
| 471 | if (escape[x] == buf[l]) |
| 472 | { |
| 473 | match = 1; |
| 474 | break; |
| 475 | } |
| 476 | x++; |
| 477 | } |
| 478 | } |
| 479 | if (match) |
| 480 | { |
| 481 | s[nl++] = esc_char; |
| 482 | s[nl++] = "0123456789ABCDEF"[buf[l] / 16]; |
| 483 | s[nl++] = "0123456789ABCDEF"[buf[l] % 16]; |
| 484 | l++; |
| 485 | } |
| 486 | else |
| 487 | { |
| 488 | s[nl++] = buf[l++]; |
| 489 | } |
| 490 | } |
| 491 | s[nl] = '\0'; |
| 492 | |
| 493 | *esc = s; |
| 494 | return STATUS_OK; |
| 495 | } |
| 496 | |
| 497 | UINT8 *neos_unescape (UINT8 *s, int buflen, char esc_char) |
| 498 | { |
| 499 | int i = 0, o = 0; |
| 500 | |
| 501 | if (s == NULL) return s; |
| 502 | while (i < buflen) |
| 503 | { |
| 504 | if (s[i] == esc_char && (i+2 < buflen) && |
| 505 | isxdigit(s[i+1]) && isxdigit(s[i+2])) |
| 506 | { |
| 507 | UINT8 num; |
| 508 | num = (s[i+1] >= 'A') ? ((s[i+1] & 0xdf) - 'A') + 10 : (s[i+1] - '0'); |
| 509 | num *= 16; |
| 510 | num += (s[i+2] >= 'A') ? ((s[i+2] & 0xdf) - 'A') + 10 : (s[i+2] - '0'); |
| 511 | s[o++] = num; |
| 512 | i+=3; |
| 513 | } |
| 514 | else { |
| 515 | s[o++] = s[i++]; |
| 516 | } |
| 517 | } |
| 518 | if (i && o) s[o] = '\0'; |
| 519 | return s; |
| 520 | } |
| 521 | |
| 522 | char *repr_string_alloc (const char *s) |
| 523 | { |
| 524 | int l,x,i; |
| 525 | int nl = 0; |
| 526 | char *rs; |
| 527 | |
| 528 | if (s == NULL) |
| 529 | { |
| 530 | return strdup("NULL"); |
| 531 | } |
| 532 | |
| 533 | l = strlen(s); |
| 534 | for (x = 0; x < l; x++) |
| 535 | { |
| 536 | if (isprint(s[x]) && s[x] != '"' && s[x] != '\\') |
| 537 | { |
| 538 | nl++; |
| 539 | } |
| 540 | else |
| 541 | { |
| 542 | if (s[x] == '\n' || s[x] == '\t' || s[x] == '\r' || s[x] == '"' || |
| 543 | s[x] == '\\') |
| 544 | { |
| 545 | nl += 2; |
| 546 | } |
| 547 | else nl += 4; |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | rs = (char *) malloc ((nl+3) * sizeof(char)); |
| 552 | if (rs == NULL) |
| 553 | return NULL; |
| 554 | |
| 555 | i = 0; |
| 556 | rs[i++] = '"'; |
| 557 | for (x = 0; x < l; x++) |
| 558 | { |
| 559 | if (isprint(s[x]) && s[x] != '"' && s[x] != '\\') |
| 560 | { |
| 561 | rs[i++] = s[x]; |
| 562 | } |
| 563 | else |
| 564 | { |
| 565 | rs[i++] = '\\'; |
| 566 | switch (s[x]) |
| 567 | { |
| 568 | case '\n': |
| 569 | rs[i++] = 'n'; |
| 570 | break; |
| 571 | case '\t': |
| 572 | rs[i++] = 't'; |
| 573 | break; |
| 574 | case '\r': |
| 575 | rs[i++] = 'r'; |
| 576 | break; |
| 577 | case '"': |
| 578 | rs[i++] = '"'; |
| 579 | break; |
| 580 | case '\\': |
| 581 | rs[i++] = '\\'; |
| 582 | break; |
| 583 | default: |
| 584 | sprintf(&(rs[i]), "%03o", (s[x] & 0377)); |
| 585 | i += 3; |
| 586 | break; |
| 587 | } |
| 588 | } |
| 589 | } |
| 590 | rs[i++] = '"'; |
| 591 | rs[i] = '\0'; |
| 592 | return rs; |
| 593 | } |
| 594 | |
| 595 | // List of all characters that must be escaped |
| 596 | // List based on http://www.blooberry.com/indexdot/html/topics/urlencoding.htm |
| 597 | static char EscapedChars[] = "$&+,/:;=?@ \"<>#%{}|\\^~[]`'"; |
| 598 | |
| 599 | // Check if a single character needs to be escaped |
| 600 | static BOOL is_reserved_char(char c) |
| 601 | { |
| 602 | int i = 0; |
| 603 | |
| 604 | if (c < 32 || c > 122) { |
| 605 | return TRUE; |
| 606 | } else { |
| 607 | while (EscapedChars[i]) { |
| 608 | if (c == EscapedChars[i]) { |
| 609 | return TRUE; |
| 610 | } |
| 611 | ++i; |
| 612 | } |
| 613 | } |
| 614 | return FALSE; |
| 615 | } |
| 616 | |
| 617 | NEOERR *neos_js_escape (const char *in, char **esc) |
| 618 | { |
| 619 | int nl = 0; |
| 620 | int l = 0; |
| 621 | unsigned char *buf = (unsigned char *)in; |
| 622 | unsigned char *s; |
| 623 | |
| 624 | while (buf[l]) |
| 625 | { |
| 626 | if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || |
| 627 | buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || |
| 628 | buf[l] == '&' || buf[l] == ';' || buf[l] < 32) |
| 629 | { |
| 630 | nl += 3; |
| 631 | } |
| 632 | nl++; |
| 633 | l++; |
| 634 | } |
| 635 | |
| 636 | s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); |
| 637 | if (s == NULL) |
| 638 | return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", |
| 639 | buf); |
| 640 | |
| 641 | nl = 0; l = 0; |
| 642 | while (buf[l]) |
| 643 | { |
| 644 | if (buf[l] == '/' || buf[l] == '"' || buf[l] == '\'' || |
| 645 | buf[l] == '\\' || buf[l] == '>' || buf[l] == '<' || |
| 646 | buf[l] == '&' || buf[l] == ';' || buf[l] < 32) |
| 647 | { |
| 648 | s[nl++] = '\\'; |
| 649 | s[nl++] = 'x'; |
| 650 | s[nl++] = "0123456789ABCDEF"[(buf[l] >> 4) & 0xF]; |
| 651 | s[nl++] = "0123456789ABCDEF"[buf[l] & 0xF]; |
| 652 | l++; |
| 653 | } |
| 654 | else |
| 655 | { |
| 656 | s[nl++] = buf[l++]; |
| 657 | } |
| 658 | } |
| 659 | s[nl] = '\0'; |
| 660 | |
| 661 | *esc = (char *)s; |
| 662 | return STATUS_OK; |
| 663 | } |
| 664 | |
| 665 | |
| 666 | NEOERR *neos_url_escape (const char *in, char **esc, |
| 667 | const char *other) |
| 668 | { |
| 669 | int nl = 0; |
| 670 | int l = 0; |
| 671 | int x = 0; |
| 672 | unsigned char *buf = (unsigned char *)in; |
| 673 | unsigned char *uother = (unsigned char *)other; |
| 674 | unsigned char *s; |
| 675 | int match = 0; |
| 676 | |
| 677 | while (buf[l]) |
| 678 | { |
| 679 | if (is_reserved_char(buf[l])) |
| 680 | { |
| 681 | nl += 2; |
| 682 | } |
| 683 | else if (uother) |
| 684 | { |
| 685 | x = 0; |
| 686 | while (uother[x]) |
| 687 | { |
| 688 | if (uother[x] == buf[l]) |
| 689 | { |
| 690 | nl +=2; |
| 691 | break; |
| 692 | } |
| 693 | x++; |
| 694 | } |
| 695 | } |
| 696 | nl++; |
| 697 | l++; |
| 698 | } |
| 699 | |
| 700 | s = (unsigned char *) malloc (sizeof(unsigned char) * (nl + 1)); |
| 701 | if (s == NULL) |
| 702 | return nerr_raise (NERR_NOMEM, "Unable to allocate memory to escape %s", |
| 703 | buf); |
| 704 | |
| 705 | nl = 0; l = 0; |
| 706 | while (buf[l]) |
| 707 | { |
| 708 | match = 0; |
| 709 | if (buf[l] == ' ') |
| 710 | { |
| 711 | s[nl++] = '+'; |
| 712 | l++; |
| 713 | } |
| 714 | else |
| 715 | { |
| 716 | if (is_reserved_char(buf[l])) |
| 717 | { |
| 718 | match = 1; |
| 719 | } |
| 720 | else if (uother) |
| 721 | { |
| 722 | x = 0; |
| 723 | while (uother[x]) |
| 724 | { |
| 725 | if (uother[x] == buf[l]) |
| 726 | { |
| 727 | match = 1; |
| 728 | break; |
| 729 | } |
| 730 | x++; |
| 731 | } |
| 732 | } |
| 733 | if (match) |
| 734 | { |
| 735 | s[nl++] = '%'; |
| 736 | s[nl++] = "0123456789ABCDEF"[buf[l] / 16]; |
| 737 | s[nl++] = "0123456789ABCDEF"[buf[l] % 16]; |
| 738 | l++; |
| 739 | } |
| 740 | else |
| 741 | { |
| 742 | s[nl++] = buf[l++]; |
| 743 | } |
| 744 | } |
| 745 | } |
| 746 | s[nl] = '\0'; |
| 747 | |
| 748 | *esc = (char *)s; |
| 749 | return STATUS_OK; |
| 750 | } |
| 751 | |
| 752 | NEOERR *neos_html_escape (const char *src, int slen, |
| 753 | char **out) |
| 754 | { |
| 755 | NEOERR *err = STATUS_OK; |
| 756 | STRING out_s; |
| 757 | int x; |
| 758 | char *ptr; |
| 759 | |
| 760 | string_init(&out_s); |
| 761 | err = string_append (&out_s, ""); |
| 762 | if (err) return nerr_pass (err); |
| 763 | *out = NULL; |
| 764 | |
| 765 | x = 0; |
| 766 | while (x < slen) |
| 767 | { |
| 768 | ptr = strpbrk(src + x, "&<>\"'\r"); |
| 769 | if (ptr == NULL || (ptr-src >= slen)) |
| 770 | { |
| 771 | err = string_appendn (&out_s, src + x, slen-x); |
| 772 | x = slen; |
| 773 | } |
| 774 | else |
| 775 | { |
| 776 | err = string_appendn (&out_s, src + x, (ptr - src) - x); |
| 777 | if (err != STATUS_OK) break; |
| 778 | x = ptr - src; |
| 779 | if (src[x] == '&') |
| 780 | err = string_append (&out_s, "&"); |
| 781 | else if (src[x] == '<') |
| 782 | err = string_append (&out_s, "<"); |
| 783 | else if (src[x] == '>') |
| 784 | err = string_append (&out_s, ">"); |
| 785 | else if (src[x] == '"') |
| 786 | err = string_append (&out_s, """); |
| 787 | else if (src[x] == '\'') |
| 788 | err = string_append (&out_s, "'"); |
| 789 | else if (src[x] != '\r') |
| 790 | err = nerr_raise (NERR_ASSERT, "src[x] == '%c'", src[x]); |
| 791 | x++; |
| 792 | } |
| 793 | if (err != STATUS_OK) break; |
| 794 | } |
| 795 | if (err) |
| 796 | { |
| 797 | string_clear (&out_s); |
| 798 | return nerr_pass (err); |
| 799 | } |
| 800 | *out = out_s.buf; |
| 801 | return STATUS_OK; |
| 802 | } |
| 803 | |
| 804 | char *URL_PROTOCOLS[] = {"http://", "https://", "ftp://", "mailto:"}; |
| 805 | |
| 806 | NEOERR *neos_url_validate (const char *in, char **esc) |
| 807 | { |
| 808 | NEOERR *err = STATUS_OK; |
| 809 | STRING out_s; |
| 810 | int valid = 0; |
| 811 | size_t i; |
| 812 | size_t inlen; |
| 813 | int num_protocols = sizeof(URL_PROTOCOLS) / sizeof(char*); |
| 814 | void* slashpos; |
| 815 | void* colonpos; |
| 816 | |
| 817 | inlen = strlen(in); |
| 818 | |
| 819 | /* |
| 820 | * <a href="//b:80"> or <a href="a/b:80"> are allowed by browsers |
| 821 | * and ":" is treated as part of the path, while |
| 822 | * <a href="www.google.com:80"> is an invalid url |
| 823 | * and ":" is treated as a scheme separator. |
| 824 | * |
| 825 | * Hence allow for ":" in the path part of a url (after /) |
| 826 | */ |
| 827 | slashpos = memchr(in, '/', inlen); |
| 828 | if (slashpos == NULL) { |
| 829 | i = inlen; |
| 830 | } |
| 831 | else { |
| 832 | i = (size_t)((char*)slashpos - in); |
| 833 | } |
| 834 | |
| 835 | colonpos = memchr(in, ':', i); |
| 836 | |
| 837 | if (colonpos == NULL) { |
| 838 | // no scheme in 'in': so this is a relative url |
| 839 | valid = 1; |
| 840 | } |
| 841 | else { |
| 842 | for (i = 0; i < num_protocols; i++) |
| 843 | { |
| 844 | if ((inlen >= strlen(URL_PROTOCOLS[i])) && |
| 845 | strncmp(in, URL_PROTOCOLS[i], strlen(URL_PROTOCOLS[i])) == 0) { |
| 846 | // 'in' starts with one of the allowed protocols |
| 847 | valid = 1; |
| 848 | break; |
| 849 | } |
| 850 | |
| 851 | } |
| 852 | } |
| 853 | |
| 854 | if (valid) |
| 855 | return neos_html_escape(in, inlen, esc); |
| 856 | |
| 857 | // 'in' contains an unsupported scheme, replace with '#' |
| 858 | string_init(&out_s); |
| 859 | err = string_append (&out_s, "#"); |
| 860 | if (err) return nerr_pass (err); |
| 861 | |
| 862 | *esc = out_s.buf; |
| 863 | return STATUS_OK; |
| 864 | |
| 865 | } |
| 866 | |
| 867 | NEOERR *neos_var_escape (NEOS_ESCAPE context, |
| 868 | const char *in, |
| 869 | char **esc) |
| 870 | { |
| 871 | |
| 872 | /* Just dup and return if we do nothing. */ |
| 873 | if (context == NEOS_ESCAPE_NONE || |
| 874 | context == NEOS_ESCAPE_FUNCTION) |
| 875 | { |
| 876 | *esc = strdup(in); |
| 877 | return STATUS_OK; |
| 878 | } |
| 879 | |
| 880 | /* Now we escape based on context. This is the order of precedence: |
| 881 | * url > script > style > html |
| 882 | */ |
| 883 | if (context & NEOS_ESCAPE_URL) |
| 884 | return nerr_pass(neos_url_escape(in, esc, NULL)); |
| 885 | else if (context & NEOS_ESCAPE_SCRIPT) |
| 886 | return nerr_pass(neos_js_escape(in, esc)); |
| 887 | else if (context & NEOS_ESCAPE_HTML) |
| 888 | return nerr_pass(neos_html_escape(in, strlen(in), esc)); |
| 889 | |
| 890 | return nerr_raise(NERR_ASSERT, "unknown escape context supplied: %d", |
| 891 | context); |
| 892 | } |