| 590 | } |
| 591 | |
| 592 | bool cbm_http_query_param(const char *query, const char *name, char *buf, int bufsz) { |
| 593 | if (!query || !name || !buf || bufsz <= 0) |
| 594 | return false; |
| 595 | buf[0] = '\0'; |
| 596 | size_t name_len = strlen(name); |
| 597 | |
| 598 | const char *p = query; |
| 599 | while (*p) { |
| 600 | const char *pair_end = strchr(p, '&'); |
| 601 | if (!pair_end) |
| 602 | pair_end = p + strlen(p); |
| 603 | const char *eq = memchr(p, '=', (size_t)(pair_end - p)); |
| 604 | size_t klen = eq ? (size_t)(eq - p) : (size_t)(pair_end - p); |
| 605 | |
| 606 | if (klen == name_len && memcmp(p, name, name_len) == 0) { |
| 607 | if (!eq) |
| 608 | return false; /* present but no value */ |
| 609 | /* Percent-decode the value into buf. */ |
| 610 | const char *v = eq + 1; |
| 611 | int out = 0; |
| 612 | while (v < pair_end) { |
| 613 | char ch = *v; |
| 614 | if (ch == '+') { |
| 615 | ch = ' '; |
| 616 | v++; |
| 617 | } else if (ch == '%') { |
| 618 | if (pair_end - v < 3) /* needs two hex digits */ |
| 619 | return false; |
| 620 | int hi = hex_val(v[1]); |
| 621 | int lo = hex_val(v[2]); |
| 622 | if (hi < 0 || lo < 0) |
| 623 | return false; |
| 624 | ch = (char)((hi << 4) | lo); |
| 625 | if (ch == '\0') |
| 626 | return false; /* decoded NUL never allowed */ |
| 627 | v += 3; |
| 628 | } else { |
| 629 | v++; |
| 630 | } |
| 631 | if (out >= bufsz - 1) { |
| 632 | buf[0] = '\0'; |
| 633 | return false; /* oversize — reject, never truncate */ |
| 634 | } |
| 635 | buf[out++] = ch; |
| 636 | } |
| 637 | buf[out] = '\0'; |
| 638 | return out > 0; |
| 639 | } |
| 640 | p = (*pair_end == '&') ? pair_end + 1 : pair_end; |
| 641 | } |
| 642 | return false; |
| 643 | } |
no test coverage detected