* Two-pass parser for bracket content. * * Pass 1: determine type — if '=' appears at bracket-depth 0, it's a dict; * otherwise it's a list. Ignoring '=' inside nested brackets avoids * misdetection on input like "transcode=[PCMA PCMU]". * * Pass 2: tokenize on spaces (respecting bracket nesting) and build the * bencode structure. For dicts, each token is "key=valu
| 78 | * d — bracket nesting depth for bracket-matching loops |
| 79 | */ |
| 80 | bencode_item_t *parse_bracket_value(const char *s, int len, |
| 81 | bencode_buffer_t *buf, int depth) |
| 82 | { |
| 83 | const char *end, *p, *ks, *vs; |
| 84 | int is_dict = 0, d, klen, vlen; |
| 85 | bencode_item_t *container, *item; |
| 86 | |
| 87 | if (depth > BRACKET_MAX_DEPTH || len > BRACKET_MAX_LEN || !buf) |
| 88 | return NULL; |
| 89 | |
| 90 | end = s + len; |
| 91 | |
| 92 | /* Pass 1: detect dict vs list — scan for '=' at bracket-depth 0 */ |
| 93 | for (d = 0, p = s; p < end; p++) { |
| 94 | if (*p == '[') d++; |
| 95 | else if (*p == ']') { if (--d < 0) return NULL; /* stray ']' */ } |
| 96 | else if (*p == '=' && d == 0) { is_dict = 1; break; } |
| 97 | } |
| 98 | |
| 99 | container = is_dict ? bencode_dictionary(buf) : bencode_list(buf); |
| 100 | if (!container) |
| 101 | return NULL; |
| 102 | |
| 103 | /* Pass 2: tokenize and build */ |
| 104 | p = s; |
| 105 | while (p < end) { |
| 106 | while (p < end && *p == ' ') p++; /* skip inter-token spaces */ |
| 107 | if (p >= end) break; |
| 108 | |
| 109 | if (is_dict) { |
| 110 | /* --- dict mode: expect "key=value" pairs --- */ |
| 111 | |
| 112 | /* extract key: scan until '=' or space */ |
| 113 | ks = p; |
| 114 | while (p < end && *p != '=' && *p != ' ') p++; |
| 115 | klen = p - ks; |
| 116 | if (klen == 0 || p >= end || *p != '=') { |
| 117 | /* token without '=' in dict context — skip it */ |
| 118 | if (klen > 0) |
| 119 | LM_WARN("bare token '%.*s' in bracket dictionary " |
| 120 | "context (missing '='?), skipping\n", |
| 121 | klen, ks); |
| 122 | while (p < end && *p != ' ') p++; |
| 123 | continue; |
| 124 | } |
| 125 | p++; /* advance past '=' to start of value */ |
| 126 | |
| 127 | /* extract value */ |
| 128 | if (p < end && *p == '[') { |
| 129 | /* nested bracket value: find matching ']' */ |
| 130 | vs = p + 1; /* content starts after the '[' */ |
| 131 | for (d = 0; p < end; p++) { |
| 132 | if (*p == '[') d++; |
| 133 | else if (*p == ']' && --d == 0) { p++; break; } |
| 134 | } |
| 135 | if (d != 0) return NULL; /* unmatched '[' */ |
| 136 | /* p now points past ']'; inner content is vs..(p-1) |
| 137 | * i.e. between the '[' and ']' exclusive */ |