]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHeaderTools.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / HttpHeaderTools.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 66 HTTP Header Tools
6 * AUTHOR: Alex Rousskov
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "HttpHeader.h"
38 #include "HttpHdrContRange.h"
39 #include "ACLChecklist.h"
40 #include "MemBuf.h"
41
42 static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
43
44
45 HttpHeaderFieldInfo *
46 httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
47 {
48 int i;
49 HttpHeaderFieldInfo *table = NULL;
50 assert(attrs && count);
51
52 /* allocate space */
53 table = new HttpHeaderFieldInfo[count];
54
55 for (i = 0; i < count; ++i) {
56 const http_hdr_type id = attrs[i].id;
57 HttpHeaderFieldInfo *info = table + id;
58 /* sanity checks */
59 assert(id >= 0 && id < count);
60 assert(attrs[i].name);
61 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
62 /* copy and init fields */
63 info->id = id;
64 info->type = attrs[i].type;
65 info->name = attrs[i].name;
66 assert(info->name.size());
67 }
68
69 return table;
70 }
71
72 void
73 httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
74 {
75 int i;
76
77 for (i = 0; i < count; ++i)
78 table[i].name.clean();
79
80 delete [] table;
81 }
82
83 void
84 httpHeaderMaskInit(HttpHeaderMask * mask, int value)
85 {
86 memset(mask, value, sizeof(*mask));
87 }
88
89 /* calculates a bit mask of a given array; does not reset mask! */
90 void
91 httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
92 {
93 size_t i;
94 const int * enums = (const int *) http_hdr_type_enums;
95 assert(mask && enums);
96 assert(count < sizeof(*mask) * 8); /* check for overflow */
97
98 for (i = 0; i < count; ++i) {
99 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
100 CBIT_SET(*mask, enums[i]);
101 }
102 }
103
104 /* same as httpHeaderPutStr, but formats the string using snprintf first */
105 void
106 #if STDC_HEADERS
107 httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
108 #else
109 httpHeaderPutStrf(va_alist)
110 va_dcl
111 #endif
112 {
113 #if STDC_HEADERS
114 va_list args;
115 va_start(args, fmt);
116 #else
117
118 va_list args;
119 HttpHeader *hdr = NULL;
120 http_hdr_type id = HDR_ENUM_END;
121 const char *fmt = NULL;
122 va_start(args);
123 hdr = va_arg(args, HttpHeader *);
124 id = va_arg(args, http_hdr_type);
125 fmt = va_arg(args, char *);
126 #endif
127
128 httpHeaderPutStrvf(hdr, id, fmt, args);
129 va_end(args);
130 }
131
132 /* used by httpHeaderPutStrf */
133 static void
134 httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
135 {
136 MemBuf mb;
137 mb.init();
138 mb.vPrintf(fmt, vargs);
139 hdr->putStr(id, mb.buf);
140 mb.clean();
141 }
142
143
144 /** wrapper arrounf PutContRange */
145 void
146 httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
147 {
148 HttpHdrContRange *cr = httpHdrContRangeCreate();
149 assert(hdr && ent_len >= 0);
150 httpHdrContRangeSet(cr, spec, ent_len);
151 hdr->putContRange(cr);
152 httpHdrContRangeDestroy(cr);
153 }
154
155
156 /**
157 * return true if a given directive is found in at least one of
158 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
159 * present we ignore HDR_CONNECTION.
160 */
161 int
162 httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
163 {
164 String list;
165 http_hdr_type ht;
166 int res;
167 /* what type of header do we have? */
168
169 if (hdr->has(HDR_PROXY_CONNECTION))
170 ht = HDR_PROXY_CONNECTION;
171 else if (hdr->has(HDR_CONNECTION))
172 ht = HDR_CONNECTION;
173 else
174 return 0;
175
176 list = hdr->getList(ht);
177
178 res = strListIsMember(&list, directive, ',');
179
180 list.clean();
181
182 return res;
183 }
184
185 /* returns true iff "m" is a member of the list */
186 int
187 strListIsMember(const String * list, const char *m, char del)
188 {
189 const char *pos = NULL;
190 const char *item;
191 int ilen = 0;
192 int mlen;
193 assert(list && m);
194 mlen = strlen(m);
195
196 while (strListGetItem(list, del, &item, &ilen, &pos)) {
197 if (mlen == ilen && !strncasecmp(item, m, ilen))
198 return 1;
199 }
200
201 return 0;
202 }
203
204 /* returns true iff "s" is a substring of a member of the list */
205 int
206 strListIsSubstr(const String * list, const char *s, char del)
207 {
208 assert(list && del);
209 return list->pos(s) != 0;
210
211 /*
212 * Note: the original code with a loop is broken because it uses strstr()
213 * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may
214 * return true when it should not. If 's' does not contain a 'del', the
215 * implementaion is equavalent to strstr()! Thus, we replace the loop with
216 * strstr() above until strnstr() is available.
217 */
218 }
219
220 /* appends an item to the list */
221 void
222 strListAdd(String * str, const char *item, char del)
223 {
224 assert(str && item);
225
226 if (str->size()) {
227 char buf[3];
228 buf[0] = del;
229 buf[1] = ' ';
230 buf[2] = '\0';
231 str->append(buf, 2);
232 }
233
234 str->append(item, strlen(item));
235 }
236
237 /*
238 * iterates through a 0-terminated string of items separated by 'del's.
239 * white space around 'del' is considered to be a part of 'del'
240 * like strtok, but preserves the source, and can iterate several strings at once
241 *
242 * returns true if next item is found.
243 * init pos with NULL to start iteration.
244 */
245 int
246 strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos)
247 {
248 size_t len;
249 static char delim[3][8] = {
250 "\"?,",
251 "\"\\",
252 " ?,\t\r\n"
253 };
254 int quoted = 0;
255 assert(str && item && pos);
256
257 delim[0][1] = del;
258 delim[2][1] = del;
259
260 if (!*pos) {
261 *pos = str->buf();
262
263 if (!*pos)
264 return 0;
265 }
266
267 /* skip leading ws and delimiters */
268 *pos += strspn(*pos, delim[2]);
269
270 *item = *pos; /* remember item's start */
271
272 /* find next delimiter */
273 do {
274 *pos += strcspn(*pos, delim[quoted]);
275
276 if (**pos == del)
277 break;
278
279 if (**pos == '"') {
280 quoted = !quoted;
281 *pos += 1;
282 }
283
284 if (quoted && **pos == '\\') {
285 *pos += 1;
286
287 if (**pos)
288 *pos += 1;
289 }
290 } while (**pos);
291
292 len = *pos - *item; /* *pos points to del or '\0' */
293
294 /* rtrim */
295 while (len > 0 && xisspace((*item)[len - 1]))
296 len--;
297
298 if (ilen)
299 *ilen = len;
300
301 return len > 0;
302 }
303
304 /* handy to printf prefixes of potentially very long buffers */
305 const char *
306 getStringPrefix(const char *str, const char *end)
307 {
308 #define SHORT_PREFIX_SIZE 512
309 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
310 const int sz = 1 + (end ? end - str : strlen(str));
311 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
312 return buf;
313 }
314
315 /*
316 * parses an int field, complains if soemthing went wrong, returns true on
317 * success
318 */
319 int
320 httpHeaderParseInt(const char *start, int *value)
321 {
322 assert(value);
323 *value = atoi(start);
324
325 if (!*value && !xisdigit(*start)) {
326 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
327 return 0;
328 }
329
330 return 1;
331 }
332
333 int
334 httpHeaderParseOffset(const char *start, int64_t * value)
335 {
336 errno = 0;
337 int64_t res = strtoll(start, NULL, 10);
338 if (!res && EINVAL == errno) /* maybe not portable? */
339 return 0;
340 *value = res;
341 return 1;
342 }
343
344
345 /* Parses a quoted-string field (RFC 2616 section 2.2), complains if
346 * something went wrong, returns non-zero on success.
347 * start should point at the first ".
348 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
349 */
350 int
351 httpHeaderParseQuotedString (const char *start, String *val)
352 {
353 const char *end, *pos;
354 val->clean();
355 assert (*start == '"');
356 pos = start + 1;
357
358 while (1) {
359 if (!(end = index (pos,'"'))) {
360 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
361 return 0;
362 }
363
364 /* check for quoted-chars */
365 if (*(end - 1) != '\\') {
366 /* done */
367 val->append(start + 1, end-start-1);
368 return 1;
369 }
370
371 /* try for the end again */
372 pos = end + 1;
373 }
374 }
375
376 /*
377 * httpHdrMangle checks the anonymizer (header_access) configuration.
378 * Returns 1 if the header is allowed.
379 */
380 static int
381 httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
382 {
383 int retval;
384
385 /* check with anonymizer tables */
386 header_mangler *hm;
387 ACLChecklist *checklist;
388 assert(e);
389
390 if (ROR_REQUEST == req_or_rep) {
391 hm = &Config.request_header_access[e->id];
392 } else if (ROR_REPLY == req_or_rep) {
393 hm = &Config.reply_header_access[e->id];
394 } else {
395 /* error. But let's call it "request". */
396 hm = &Config.request_header_access[e->id];
397 }
398
399 checklist = aclChecklistCreate(hm->access_list, request, NULL);
400
401 if (1 == checklist->fastCheck()) {
402 /* aclCheckFast returns 1 for allow. */
403 retval = 1;
404 } else if (NULL == hm->replacement) {
405 /* It was denied, and we don't have any replacement */
406 retval = 0;
407 } else {
408 /* It was denied, but we have a replacement. Replace the
409 * header on the fly, and return that the new header
410 * is allowed.
411 */
412 e->value = hm->replacement;
413 retval = 1;
414 }
415
416 delete checklist;
417 return retval;
418 }
419
420 /* Mangles headers for a list of headers. */
421 void
422 httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
423 {
424 HttpHeaderEntry *e;
425 HttpHeaderPos p = HttpHeaderInitPos;
426
427 int headers_deleted = 0;
428 while ((e = l->getEntry(&p)))
429 if (0 == httpHdrMangle(e, request, req_or_rep))
430 l->delAt(p, headers_deleted);
431
432 if (headers_deleted)
433 l->refreshMask();
434 }
435
436 /*
437 * return 1 if manglers are configured. Used to set a flag
438 * for optimization during request forwarding.
439 */
440 int
441 httpReqHdrManglersConfigured()
442 {
443 for (int i = 0; i < HDR_ENUM_END; i++) {
444 if (NULL != Config.request_header_access[i].access_list)
445 return 1;
446 }
447
448 return 0;
449 }