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