]>
Commit | Line | Data |
---|---|---|
7faf2bdb | 1 | /* |
262a0e14 | 2 | * $Id$ |
7faf2bdb | 3 | * |
4 | * DEBUG: section 66 HTTP Header Tools | |
5 | * AUTHOR: Alex Rousskov | |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 8 | * ---------------------------------------------------------- |
7faf2bdb | 9 | * |
2b6662ba | 10 | * Squid is the result of efforts by numerous individuals from |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
7faf2bdb | 18 | * |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
26ac0430 | 23 | * |
7faf2bdb | 24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
26ac0430 | 28 | * |
7faf2bdb | 29 | * You should have received a copy of the GNU General Public License |
30 | * along with this program; if not, write to the Free Software | |
cbdec147 | 31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 32 | * |
7faf2bdb | 33 | */ |
34 | ||
f7f3304a | 35 | #include "squid-old.h" |
c0941a6a | 36 | #include "acl/FilledChecklist.h" |
3b07476b | 37 | #include "acl/Gadgets.h" |
27bc2077 AJ |
38 | #include "compat/strtoll.h" |
39 | #include "HttpHdrContRange.h" | |
40 | #include "HttpHeader.h" | |
3b07476b CT |
41 | #include "HttpHeaderTools.h" |
42 | #include "HttpRequest.h" | |
0eb49b6d | 43 | #include "MemBuf.h" |
3b07476b | 44 | #include "Store.h" |
7faf2bdb | 45 | |
2246b732 | 46 | static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs); |
de336bbe | 47 | |
48 | ||
49 | HttpHeaderFieldInfo * | |
b644367b | 50 | httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count) |
7faf2bdb | 51 | { |
52 | int i; | |
de336bbe | 53 | HttpHeaderFieldInfo *table = NULL; |
54 | assert(attrs && count); | |
55 | ||
56 | /* allocate space */ | |
0353e724 | 57 | table = new HttpHeaderFieldInfo[count]; |
7faf2bdb | 58 | |
7faf2bdb | 59 | for (i = 0; i < count; ++i) { |
62e76326 | 60 | const http_hdr_type id = attrs[i].id; |
61 | HttpHeaderFieldInfo *info = table + id; | |
62 | /* sanity checks */ | |
63 | assert(id >= 0 && id < count); | |
64 | assert(attrs[i].name); | |
0353e724 | 65 | assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */ |
62e76326 | 66 | /* copy and init fields */ |
67 | info->id = id; | |
68 | info->type = attrs[i].type; | |
69 | info->name = attrs[i].name; | |
70 | assert(info->name.size()); | |
7faf2bdb | 71 | } |
62e76326 | 72 | |
de336bbe | 73 | return table; |
74 | } | |
75 | ||
76 | void | |
b644367b | 77 | httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count) |
de336bbe | 78 | { |
79 | int i; | |
62e76326 | 80 | |
de336bbe | 81 | for (i = 0; i < count; ++i) |
30abd221 | 82 | table[i].name.clean(); |
62e76326 | 83 | |
7f4c8dec | 84 | delete [] table; |
7faf2bdb | 85 | } |
86 | ||
d8b249ef | 87 | void |
97474590 | 88 | httpHeaderMaskInit(HttpHeaderMask * mask, int value) |
d8b249ef | 89 | { |
97474590 | 90 | memset(mask, value, sizeof(*mask)); |
d8b249ef | 91 | } |
92 | ||
b50e327b | 93 | /** calculates a bit mask of a given array; does not reset mask! */ |
d8b249ef | 94 | void |
8abf232c | 95 | httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count) |
7faf2bdb | 96 | { |
e6ccf245 | 97 | size_t i; |
8abf232c | 98 | const int * enums = (const int *) http_hdr_type_enums; |
d8b249ef | 99 | assert(mask && enums); |
99edd1c3 | 100 | assert(count < sizeof(*mask) * 8); /* check for overflow */ |
7faf2bdb | 101 | |
102 | for (i = 0; i < count; ++i) { | |
62e76326 | 103 | assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */ |
104 | CBIT_SET(*mask, enums[i]); | |
7faf2bdb | 105 | } |
7faf2bdb | 106 | } |
107 | ||
2246b732 | 108 | /* same as httpHeaderPutStr, but formats the string using snprintf first */ |
2246b732 | 109 | void |
eeb423fb | 110 | httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...) |
2246b732 | 111 | { |
9bc73deb | 112 | va_list args; |
113 | va_start(args, fmt); | |
62e76326 | 114 | |
2246b732 | 115 | httpHeaderPutStrvf(hdr, id, fmt, args); |
116 | va_end(args); | |
117 | } | |
118 | ||
119 | /* used by httpHeaderPutStrf */ | |
120 | static void | |
121 | httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs) | |
122 | { | |
2246b732 | 123 | MemBuf mb; |
2fe7eff9 | 124 | mb.init(); |
125 | mb.vPrintf(fmt, vargs); | |
a9925b40 | 126 | hdr->putStr(id, mb.buf); |
2fe7eff9 | 127 | mb.clean(); |
2246b732 | 128 | } |
129 | ||
130 | ||
9035d1d5 | 131 | /** wrapper arrounf PutContRange */ |
d192d11f | 132 | void |
47f6e231 | 133 | httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len) |
d192d11f | 134 | { |
135 | HttpHdrContRange *cr = httpHdrContRangeCreate(); | |
136 | assert(hdr && ent_len >= 0); | |
137 | httpHdrContRangeSet(cr, spec, ent_len); | |
a9925b40 | 138 | hdr->putContRange(cr); |
d192d11f | 139 | httpHdrContRangeDestroy(cr); |
140 | } | |
141 | ||
a9771e51 | 142 | |
9035d1d5 | 143 | /** |
9bc73deb | 144 | * return true if a given directive is found in at least one of |
145 | * the "connection" header-fields note: if HDR_PROXY_CONNECTION is | |
146 | * present we ignore HDR_CONNECTION. | |
99edd1c3 | 147 | */ |
148 | int | |
5999b776 | 149 | httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive) |
99edd1c3 | 150 | { |
30abd221 | 151 | String list; |
9bc73deb | 152 | int res; |
153 | /* what type of header do we have? */ | |
62e76326 | 154 | |
95e78500 | 155 | #if USE_HTTP_VIOLATIONS |
a9925b40 | 156 | if (hdr->has(HDR_PROXY_CONNECTION)) |
95e78500 AJ |
157 | list = hdr->getList(HDR_PROXY_CONNECTION); |
158 | else | |
159 | #endif | |
2e881a6f A |
160 | if (hdr->has(HDR_CONNECTION)) |
161 | list = hdr->getList(HDR_CONNECTION); | |
162 | else | |
163 | return 0; | |
9bc73deb | 164 | |
9bc73deb | 165 | res = strListIsMember(&list, directive, ','); |
62e76326 | 166 | |
30abd221 | 167 | list.clean(); |
168 | ||
9bc73deb | 169 | return res; |
99edd1c3 | 170 | } |
171 | ||
b50e327b | 172 | /** returns true iff "m" is a member of the list */ |
99edd1c3 | 173 | int |
30abd221 | 174 | strListIsMember(const String * list, const char *m, char del) |
99edd1c3 | 175 | { |
176 | const char *pos = NULL; | |
177 | const char *item; | |
9bc73deb | 178 | int ilen = 0; |
179 | int mlen; | |
99edd1c3 | 180 | assert(list && m); |
9bc73deb | 181 | mlen = strlen(m); |
62e76326 | 182 | |
9bc73deb | 183 | while (strListGetItem(list, del, &item, &ilen, &pos)) { |
62e76326 | 184 | if (mlen == ilen && !strncasecmp(item, m, ilen)) |
185 | return 1; | |
99edd1c3 | 186 | } |
62e76326 | 187 | |
99edd1c3 | 188 | return 0; |
189 | } | |
190 | ||
b50e327b | 191 | /** returns true iff "s" is a substring of a member of the list */ |
edf3ffdc | 192 | int |
30abd221 | 193 | strListIsSubstr(const String * list, const char *s, char del) |
edf3ffdc | 194 | { |
9bc73deb | 195 | assert(list && del); |
b34dcde2 | 196 | return (list->find(s) != String::npos); |
9bc73deb | 197 | |
b50e327b | 198 | /** \note |
9bc73deb | 199 | * Note: the original code with a loop is broken because it uses strstr() |
200 | * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may | |
201 | * return true when it should not. If 's' does not contain a 'del', the | |
202 | * implementaion is equavalent to strstr()! Thus, we replace the loop with | |
203 | * strstr() above until strnstr() is available. | |
204 | */ | |
edf3ffdc | 205 | } |
206 | ||
b50e327b | 207 | /** appends an item to the list */ |
99edd1c3 | 208 | void |
30abd221 | 209 | strListAdd(String * str, const char *item, char del) |
99edd1c3 | 210 | { |
211 | assert(str && item); | |
62e76326 | 212 | |
528b2c61 | 213 | if (str->size()) { |
62e76326 | 214 | char buf[3]; |
215 | buf[0] = del; | |
216 | buf[1] = ' '; | |
217 | buf[2] = '\0'; | |
218 | str->append(buf, 2); | |
2246b732 | 219 | } |
62e76326 | 220 | |
528b2c61 | 221 | str->append(item, strlen(item)); |
99edd1c3 | 222 | } |
223 | ||
b50e327b | 224 | /** |
7faf2bdb | 225 | * iterates through a 0-terminated string of items separated by 'del's. |
226 | * white space around 'del' is considered to be a part of 'del' | |
227 | * like strtok, but preserves the source, and can iterate several strings at once | |
228 | * | |
229 | * returns true if next item is found. | |
230 | * init pos with NULL to start iteration. | |
231 | */ | |
232 | int | |
30abd221 | 233 | strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos) |
7faf2bdb | 234 | { |
235 | size_t len; | |
f26664fd HN |
236 | /* ',' is always enabled as field delimiter as this is required for |
237 | * processing merged header values properly, even if Cookie normally | |
238 | * uses ';' as delimiter. | |
239 | */ | |
e1381638 | 240 | static char delim[3][8] = { |
26ac0430 AJ |
241 | "\"?,", |
242 | "\"\\", | |
243 | " ?,\t\r\n" | |
b5c8ff59 | 244 | }; |
f7573ac0 | 245 | int quoted = 0; |
7faf2bdb | 246 | assert(str && item && pos); |
62e76326 | 247 | |
f7573ac0 | 248 | delim[0][1] = del; |
b5c8ff59 | 249 | delim[2][1] = del; |
f7573ac0 | 250 | |
7bd6a314 | 251 | if (!*pos) { |
9d7a89a5 | 252 | *pos = str->termedBuf(); |
62e76326 | 253 | |
254 | if (!*pos) | |
255 | return 0; | |
a3f9588e | 256 | } |
7faf2bdb | 257 | |
f26664fd | 258 | /* skip leading whitespace and delimiters */ |
b5c8ff59 | 259 | *pos += strspn(*pos, delim[2]); |
5aff5fce | 260 | |
7faf2bdb | 261 | *item = *pos; /* remember item's start */ |
62e76326 | 262 | |
7faf2bdb | 263 | /* find next delimiter */ |
f7573ac0 | 264 | do { |
265 | *pos += strcspn(*pos, delim[quoted]); | |
f7573ac0 | 266 | if (**pos == '"') { |
267 | quoted = !quoted; | |
268 | *pos += 1; | |
e1381638 | 269 | } else if (quoted && **pos == '\\') { |
f7573ac0 | 270 | *pos += 1; |
f7573ac0 | 271 | if (**pos) |
272 | *pos += 1; | |
e1381638 AJ |
273 | } else { |
274 | break; /* Delimiter found, marking the end of this value */ | |
f7573ac0 | 275 | } |
276 | } while (**pos); | |
62e76326 | 277 | |
7faf2bdb | 278 | len = *pos - *item; /* *pos points to del or '\0' */ |
62e76326 | 279 | |
7faf2bdb | 280 | /* rtrim */ |
b6a2f15e | 281 | while (len > 0 && xisspace((*item)[len - 1])) |
62e76326 | 282 | len--; |
283 | ||
7faf2bdb | 284 | if (ilen) |
62e76326 | 285 | *ilen = len; |
286 | ||
7faf2bdb | 287 | return len > 0; |
288 | } | |
289 | ||
b50e327b | 290 | /** handy to printf prefixes of potentially very long buffers */ |
7faf2bdb | 291 | const char * |
d8b249ef | 292 | getStringPrefix(const char *str, const char *end) |
7faf2bdb | 293 | { |
d8b249ef | 294 | #define SHORT_PREFIX_SIZE 512 |
7faf2bdb | 295 | LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE); |
b644367b | 296 | const int sz = 1 + (end ? end - str : strlen(str)); |
d8b249ef | 297 | xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz); |
7faf2bdb | 298 | return buf; |
299 | } | |
b5107edb | 300 | |
b50e327b | 301 | /** |
b5107edb | 302 | * parses an int field, complains if soemthing went wrong, returns true on |
303 | * success | |
304 | */ | |
305 | int | |
306 | httpHeaderParseInt(const char *start, int *value) | |
307 | { | |
308 | assert(value); | |
309 | *value = atoi(start); | |
62e76326 | 310 | |
b6a2f15e | 311 | if (!*value && !xisdigit(*start)) { |
bf8fe701 | 312 | debugs(66, 2, "failed to parse an int header field near '" << start << "'"); |
62e76326 | 313 | return 0; |
b5107edb | 314 | } |
62e76326 | 315 | |
b5107edb | 316 | return 1; |
317 | } | |
318 | ||
47f6e231 | 319 | int |
320 | httpHeaderParseOffset(const char *start, int64_t * value) | |
321 | { | |
66e255bc | 322 | errno = 0; |
47f6e231 | 323 | int64_t res = strtoll(start, NULL, 10); |
324 | if (!res && EINVAL == errno) /* maybe not portable? */ | |
26ac0430 | 325 | return 0; |
47f6e231 | 326 | *value = res; |
327 | return 1; | |
328 | } | |
329 | ||
de336bbe | 330 | |
b50e327b AJ |
331 | /** |
332 | * Parses a quoted-string field (RFC 2616 section 2.2), complains if | |
43ae1d95 | 333 | * something went wrong, returns non-zero on success. |
34460e19 | 334 | * Un-escapes quoted-pair characters found within the string. |
229af339 | 335 | * start should point at the first double-quote. |
43ae1d95 | 336 | */ |
337 | int | |
34460e19 | 338 | httpHeaderParseQuotedString(const char *start, const int len, String *val) |
43ae1d95 | 339 | { |
340 | const char *end, *pos; | |
30abd221 | 341 | val->clean(); |
9abd1514 | 342 | if (*start != '"') { |
5f5ddef6 | 343 | debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'"); |
6d97f5f1 | 344 | return 0; |
9abd1514 | 345 | } |
43ae1d95 | 346 | pos = start + 1; |
347 | ||
34460e19 | 348 | while (*pos != '"' && len > (pos-start)) { |
5f5ddef6 CT |
349 | |
350 | if (*pos =='\r') { | |
351 | pos++; | |
352 | if ((pos-start) > len || *pos != '\n') { | |
353 | debugs(66, 2, HERE << "failed to parse a quoted-string header field with '\\r' octet " << (start-pos) | |
354 | << " bytes into '" << start << "'"); | |
355 | val->clean(); | |
356 | return 0; | |
357 | } | |
358 | } | |
359 | ||
360 | if (*pos == '\n') { | |
361 | pos++; | |
362 | if ( (pos-start) > len || (*pos != ' ' && *pos != '\t')) { | |
363 | debugs(66, 2, HERE << "failed to parse multiline quoted-string header field '" << start << "'"); | |
364 | val->clean(); | |
365 | return 0; | |
366 | } | |
367 | // TODO: replace the entire LWS with a space | |
368 | val->append(" "); | |
369 | pos++; | |
370 | debugs(66, 2, HERE << "len < pos-start => " << len << " < " << (pos-start)); | |
371 | continue; | |
372 | } | |
373 | ||
a0133f10 | 374 | bool quoted = (*pos == '\\'); |
5f5ddef6 | 375 | if (quoted) { |
a0133f10 | 376 | pos++; |
5f5ddef6 CT |
377 | if (!*pos || (pos-start) > len) { |
378 | debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'"); | |
379 | val->clean(); | |
380 | return 0; | |
381 | } | |
6d97f5f1 | 382 | } |
34460e19 | 383 | end = pos; |
c4b86597 | 384 | while (end < (start+len) && *end != '\\' && *end != '\"' && (unsigned char)*end > 0x1F && *end != 0x7F) |
34460e19 | 385 | end++; |
c4b86597 | 386 | if (((unsigned char)*end <= 0x1F && *end != '\r' && *end != '\n') || *end == 0x7F) { |
5f5ddef6 | 387 | debugs(66, 2, HERE << "failed to parse a quoted-string header field with CTL octet " << (start-pos) |
34460e19 AJ |
388 | << " bytes into '" << start << "'"); |
389 | val->clean(); | |
390 | return 0; | |
391 | } | |
6d97f5f1 A |
392 | val->append(pos, end-pos); |
393 | pos = end; | |
43ae1d95 | 394 | } |
5f5ddef6 CT |
395 | |
396 | if (*pos != '\"') { | |
397 | debugs(66, 2, HERE << "failed to parse a quoted-string header field which did not end with \" "); | |
398 | val->clean(); | |
399 | return 0; | |
400 | } | |
9abd1514 HN |
401 | /* Make sure it's defined even if empty "" */ |
402 | if (!val->defined()) | |
6d97f5f1 | 403 | val->limitInit("", 0); |
9abd1514 | 404 | return 1; |
43ae1d95 | 405 | } |
406 | ||
b50e327b AJ |
407 | /** |
408 | * Checks the anonymizer (header_access) configuration. | |
4f4611bf | 409 | * |
b50e327b AJ |
410 | * \retval 0 Header is explicitly blocked for removal |
411 | * \retval 1 Header is explicitly allowed | |
412 | * \retval 1 Header has been replaced, the current version can be used. | |
413 | * \retval 1 Header has no access controls to test | |
6bccf575 | 414 | */ |
2d72d4fd | 415 | static int |
8c01ada0 | 416 | httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep) |
6bccf575 | 417 | { |
e9b1e111 | 418 | int retval; |
419 | ||
6bccf575 | 420 | /* check with anonymizer tables */ |
3b07476b | 421 | HeaderManglers *hms = NULL; |
6bccf575 | 422 | assert(e); |
8c01ada0 | 423 | |
424 | if (ROR_REQUEST == req_or_rep) { | |
3b07476b | 425 | hms = Config.request_header_access; |
8c01ada0 | 426 | } else if (ROR_REPLY == req_or_rep) { |
3b07476b | 427 | hms = Config.reply_header_access; |
8c01ada0 | 428 | } else { |
429 | /* error. But let's call it "request". */ | |
3b07476b | 430 | hms = Config.request_header_access; |
8c01ada0 | 431 | } |
432 | ||
3b07476b CT |
433 | /* manglers are not configured for this message kind */ |
434 | if (!hms) | |
435 | return 1; | |
436 | ||
437 | const header_mangler *hm = hms->find(*e); | |
438 | ||
b50e327b | 439 | /* mangler or checklist went away. default allow */ |
af6a12ee | 440 | if (!hm || !hm->access_list) { |
b50e327b AJ |
441 | return 1; |
442 | } | |
443 | ||
c0941a6a | 444 | ACLFilledChecklist checklist(hm->access_list, request, NULL); |
62e76326 | 445 | |
2efeb0b7 | 446 | if (checklist.fastCheck() == ACCESS_ALLOWED) { |
b50e327b | 447 | /* aclCheckFast returns true for allow. */ |
62e76326 | 448 | retval = 1; |
a453662f | 449 | } else if (NULL == hm->replacement) { |
62e76326 | 450 | /* It was denied, and we don't have any replacement */ |
451 | retval = 0; | |
a453662f | 452 | } else { |
62e76326 | 453 | /* It was denied, but we have a replacement. Replace the |
454 | * header on the fly, and return that the new header | |
455 | * is allowed. | |
456 | */ | |
457 | e->value = hm->replacement; | |
458 | retval = 1; | |
a453662f | 459 | } |
e9b1e111 | 460 | |
e9b1e111 | 461 | return retval; |
6bccf575 | 462 | } |
463 | ||
b50e327b | 464 | /** Mangles headers for a list of headers. */ |
6bccf575 | 465 | void |
8c01ada0 | 466 | httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep) |
6bccf575 | 467 | { |
468 | HttpHeaderEntry *e; | |
469 | HttpHeaderPos p = HttpHeaderInitPos; | |
62e76326 | 470 | |
ba9fb01d | 471 | int headers_deleted = 0; |
a9925b40 | 472 | while ((e = l->getEntry(&p))) |
8c01ada0 | 473 | if (0 == httpHdrMangle(e, request, req_or_rep)) |
ba9fb01d | 474 | l->delAt(p, headers_deleted); |
475 | ||
476 | if (headers_deleted) | |
477 | l->refreshMask(); | |
6bccf575 | 478 | } |
5967c0bf | 479 | |
3b07476b CT |
480 | static |
481 | void header_mangler_clean(header_mangler &m) | |
482 | { | |
483 | aclDestroyAccessList(&m.access_list); | |
484 | safe_free(m.replacement); | |
485 | } | |
486 | ||
487 | static | |
488 | void header_mangler_dump_access(StoreEntry * entry, const char *option, | |
489 | const header_mangler &m, const char *name) | |
490 | { | |
491 | if (m.access_list != NULL) { | |
492 | storeAppendPrintf(entry, "%s ", option); | |
493 | dump_acl_access(entry, name, m.access_list); | |
494 | } | |
495 | } | |
496 | ||
497 | static | |
498 | void header_mangler_dump_replacement(StoreEntry * entry, const char *option, | |
499 | const header_mangler &m, const char *name) | |
500 | { | |
501 | if (m.replacement) | |
502 | storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement); | |
503 | } | |
504 | ||
505 | HeaderManglers::HeaderManglers() | |
506 | { | |
507 | memset(known, 0, sizeof(known)); | |
508 | memset(&all, 0, sizeof(all)); | |
509 | } | |
510 | ||
511 | HeaderManglers::~HeaderManglers() | |
512 | { | |
513 | for (int i = 0; i < HDR_ENUM_END; i++) | |
514 | header_mangler_clean(known[i]); | |
515 | ||
516 | typedef ManglersByName::iterator MBNI; | |
517 | for (MBNI i = custom.begin(); i != custom.end(); ++i) | |
518 | header_mangler_clean(i->second); | |
519 | ||
520 | header_mangler_clean(all); | |
521 | } | |
522 | ||
523 | void | |
524 | HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const | |
5967c0bf | 525 | { |
526 | for (int i = 0; i < HDR_ENUM_END; i++) { | |
3b07476b CT |
527 | header_mangler_dump_access(entry, name, known[i], |
528 | httpHeaderNameById(i)); | |
5967c0bf | 529 | } |
530 | ||
3b07476b CT |
531 | typedef ManglersByName::const_iterator MBNCI; |
532 | for (MBNCI i = custom.begin(); i != custom.end(); ++i) | |
533 | header_mangler_dump_access(entry, name, i->second, i->first.c_str()); | |
534 | ||
535 | header_mangler_dump_access(entry, name, all, "All"); | |
5967c0bf | 536 | } |
3b07476b CT |
537 | |
538 | void | |
539 | HeaderManglers::dumpReplacement(StoreEntry * entry, const char *name) const | |
540 | { | |
541 | for (int i = 0; i < HDR_ENUM_END; i++) { | |
542 | header_mangler_dump_replacement(entry, name, known[i], | |
543 | httpHeaderNameById(i)); | |
544 | } | |
545 | ||
546 | typedef ManglersByName::const_iterator MBNCI; | |
547 | for (MBNCI i = custom.begin(); i != custom.end(); ++i) { | |
548 | header_mangler_dump_replacement(entry, name, i->second, | |
549 | i->first.c_str()); | |
550 | } | |
551 | ||
552 | header_mangler_dump_replacement(entry, name, all, "All"); | |
553 | } | |
554 | ||
555 | header_mangler * | |
556 | HeaderManglers::track(const char *name) | |
557 | { | |
558 | int id = httpHeaderIdByNameDef(name, strlen(name)); | |
559 | ||
560 | if (id == HDR_BAD_HDR) { // special keyword or a custom header | |
561 | if (strcmp(name, "All") == 0) | |
562 | id = HDR_ENUM_END; | |
563 | else if (strcmp(name, "Other") == 0) | |
564 | id = HDR_OTHER; | |
565 | } | |
566 | ||
567 | header_mangler *m = NULL; | |
568 | if (id == HDR_ENUM_END) { | |
569 | m = &all; | |
570 | } else | |
571 | if (id == HDR_BAD_HDR) { | |
572 | m = &custom[name]; | |
573 | } else { | |
574 | m = &known[id]; // including HDR_OTHER | |
575 | } | |
576 | ||
577 | assert(m); | |
578 | return m; | |
579 | } | |
580 | ||
581 | void | |
582 | HeaderManglers::setReplacement(const char *name, const char *value) | |
583 | { | |
584 | // for backword compatibility, we allow replacements to be configured | |
585 | // for headers w/o access rules, but such replacements are ignored | |
586 | header_mangler *m = track(name); | |
587 | ||
588 | safe_free(m->replacement); // overwrite old value if any | |
589 | m->replacement = xstrdup(value); | |
590 | } | |
591 | ||
592 | const header_mangler * | |
593 | HeaderManglers::find(const HttpHeaderEntry &e) const | |
594 | { | |
595 | // a known header with a configured ACL list | |
596 | if (e.id != HDR_OTHER && 0 <= e.id && e.id < HDR_ENUM_END && | |
597 | known[e.id].access_list) | |
598 | return &known[e.id]; | |
599 | ||
600 | // a custom header | |
601 | if (e.id == HDR_OTHER) { | |
602 | // does it have an ACL list configured? | |
603 | // Optimize: use a name type that we do not need to convert to here | |
604 | const ManglersByName::const_iterator i = custom.find(e.name.termedBuf()); | |
605 | if (i != custom.end()) | |
606 | return &i->second; | |
607 | } | |
608 | ||
609 | // Next-to-last resort: "Other" rules match any custom header | |
610 | if (e.id == HDR_OTHER && known[HDR_OTHER].access_list) | |
611 | return &known[HDR_OTHER]; | |
612 | ||
613 | // Last resort: "All" rules match any header | |
614 | if (all.access_list) | |
615 | return &all; | |
616 | ||
617 | return NULL; | |
618 | } | |
619 |