]> git.ipfire.org Git - thirdparty/squid.git/blame - src/esi/VarState.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / esi / VarState.cc
CommitLineData
924f73bc 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
26ac0430 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
924f73bc 7 */
8
bbc27441
AJ
9/* DEBUG: section 86 ESI processing */
10
582c2af2 11#include "squid.h"
f99c2cfe 12#include "esi/VarState.h"
ed6e9fb9 13#include "fatal.h"
924f73bc 14#include "HttpReply.h"
15
26ac0430
AJ
16char const *ESIVariableUserAgent::esiUserOs[]= {
17 "WIN",
18 "MAC",
19 "UNIX",
20 "OTHER"
21};
924f73bc 22
26ac0430
AJ
23char const * esiBrowsers[]= {"MSIE",
24 "MOZILLA",
25 "OTHER"
26 };
924f73bc 27
8c41d298
AJ
28CBDATA_CLASS_INIT(ESIVarState);
29
924f73bc 30void
31ESIVarState::Variable::eval (ESIVarState &state, char const *subref, char const *found_default) const
32{
33 /* No-op. We swallow it */
34
35 if (found_default)
36 ESISegment::ListAppend (state.getOutput(), found_default, strlen (found_default));
37}
38
39void
40ESIVarState::hostUsed()
41{
42 flags.host = 1;
43}
44
45void
46ESIVarState::cookieUsed()
47{
48 flags.cookie = 1;
49}
50
51void
52ESIVarState::languageUsed()
53{
54 flags.language = 1;
55}
56
57void
58ESIVarState::refererUsed()
59{
60 flags.referer = 1;
61}
62
63void
64ESIVarState::useragentUsed()
65{
66 flags.useragent = 1;
67}
68
69HttpHeader &
70ESIVarState::header()
71{
72 return hdr;
73}
74
75ESISegment::Pointer &
76ESIVarState::getOutput()
77{
78 return output;
79}
80
81char const *
82ESIVariableQuery::queryString() const
83{
84 return query_string;
85}
86
87struct _query_elem const *
e1381638
AJ
88ESIVariableQuery::queryVector() const {
89 return query;
90}
924f73bc 91
92size_t const &
93ESIVariableQuery::queryElements() const
94{
95 return query_elements;
96}
97
98void
99ESIVarState::feedData (const char *buf, size_t len)
100{
101 /* TODO: if needed - tune to skip segment iteration */
137a13ea 102 debugs (86,6, "esiVarState::feedData: accepting " << len << " bytes");
924f73bc 103 ESISegment::ListAppend (input, buf, len);
104}
105
106ESISegment::Pointer
107ESIVarState::extractList()
108{
109 doIt();
110 ESISegment::Pointer rv = output;
111 output = NULL;
bf8fe701 112 debugs(86, 6, "ESIVarStateExtractList: Extracted list");
924f73bc 113 return rv;
114}
115
116char *
117ESIVarState::extractChar ()
118{
119 if (!input.getRaw())
120 fatal ("Attempt to extract variable state with no data fed in \n");
121
122 doIt();
123
124 char *rv = output->listToChar();
125
126 ESISegmentFreeList (output);
127
bf8fe701 128 debugs(86, 6, "ESIVarStateExtractList: Extracted char");
924f73bc 129
130 return rv;
131}
132
924f73bc 133ESIVarState::~ESIVarState()
134{
8c41d298
AJ
135 // freeResources
136 input = NULL;
137 ESISegmentFreeList(output);
138 hdr.clean();
924f73bc 139
385acf91 140 while (!variablesForCleanup.empty()) {
38c22baf
FC
141 delete variablesForCleanup.back();
142 variablesForCleanup.pop_back();
143 }
924f73bc 144
145 delete defaultVariable;
146}
147
924f73bc 148char *
149ESIVariableUserAgent::getProductVersion (char const *s)
150{
151 char const *t;
152 int len;
86c63190 153 t = index(s,'/');
924f73bc 154
155 if (!t || !*(++t))
86c63190 156 return xstrdup("");
924f73bc 157
86c63190 158 len = strcspn(t, " \r\n()<>@,;:\\\"/[]?={}");
924f73bc 159
86c63190 160 return xstrndup(t, len + 1);
924f73bc 161}
162
163ESIVariableQuery::ESIVariableQuery(char const *uri) : query (NULL), query_sz (0), query_elements (0), query_string (NULL)
164{
165 /* Count off the query elements */
166 char const *query_start = strchr (uri, '?');
167
168 if (query_start && query_start[1] != '\0' ) {
169 unsigned int n;
86c63190 170 query_string = xstrdup(query_start + 1);
924f73bc 171 query_elements = 1;
172 char const *query_pos = query_start + 1;
173
86c63190 174 while ((query_pos = strchr(query_pos, '&'))) {
924f73bc 175 ++query_elements;
176 ++query_pos;
177 }
178
179 query = (_query_elem *)memReallocBuf(query, query_elements * sizeof (struct _query_elem),
180 &query_sz);
181 query_pos = query_start + 1;
182 n = 0;
183
184 while (query_pos) {
86c63190
AJ
185 char const *next = strchr(query_pos, '&');
186 char const *div = strchr(query_pos, '=');
924f73bc 187
188 if (next)
189 ++next;
190
191 assert (n < query_elements);
192
193 if (!div)
194 div = next;
195
196 if (!(div - query_pos + 1))
197 /* zero length between & and = or & and & */
198 continue;
199
86c63190 200 query[n].var = xstrndup(query_pos, div - query_pos + 1) ;
924f73bc 201
202 if (div == next) {
86c63190 203 query[n].val = xstrdup("");
924f73bc 204 } else {
86c63190 205 query[n].val = xstrndup(div + 1, next - div - 1);
924f73bc 206 }
207
208 query_pos = next;
209 ++n;
210 }
211 } else {
86c63190 212 query_string = xstrdup("");
924f73bc 213 }
214
215 if (query) {
216 unsigned int n = 0;
bf8fe701 217 debugs(86, 6, "esiVarStateNew: Parsed Query string: '" << uri << "'");
924f73bc 218
219 while (n < query_elements) {
bf8fe701 220 debugs(86, 6, "esiVarStateNew: Parsed Query element " << n + 1 << " '" << query[n].var << "'='" << query[n].val << "'");
924f73bc 221 ++n;
222 }
223 }
224}
225
226ESIVariableQuery::~ESIVariableQuery()
227{
228 if (query) {
229 unsigned int i;
230
231 for (i = 0; i < query_elements; ++i) {
232 safe_free(query[i].var);
233 safe_free(query[i].val);
234 }
235
236 memFreeBuf (query_sz, query);
237 }
238
239 safe_free (query_string);
240}
241
a5488e5d 242ESIVarState::ESIVarState(HttpHeader const *aHeader, char const *uri) :
f53969cc
SM
243 output(NULL),
244 hdr(hoReply)
924f73bc 245{
a5488e5d
AJ
246 memset(&flags, 0, sizeof(flags));
247
924f73bc 248 /* TODO: only grab the needed headers */
249 /* Note that as we pass these through to included requests, we
250 * cannot trim them */
4dd8a5fd 251 hdr.append(aHeader);
924f73bc 252
253 /* populate our variables trie with the available variables.
254 * Additional ones can be added during the parsing.
255 * If there is a lazy evaluation approach to this, consider it!
256 */
257 defaultVariable = new Variable;
258 addVariable ("HTTP_ACCEPT_LANGUAGE", 20, new ESIVariableLanguage);
259 addVariable ("HTTP_COOKIE", 11, new ESIVariableCookie);
260 addVariable ("HTTP_HOST", 9, new ESIVariableHost);
261 addVariable ("HTTP_REFERER", 12, new ESIVariableReferer);
262 addVariable ("HTTP_USER_AGENT", 15, new ESIVariableUserAgent(*this));
263 addVariable ("QUERY_STRING", 12, new ESIVariableQuery(uri));
264}
265
266void
30abd221 267ESIVarState::removeVariable (String const &name)
924f73bc 268{
9c175897 269 Variable *candidate = static_cast <Variable *>(variables.find (name.rawBuf(), name.size()));
924f73bc 270
271 if (candidate) {
272 /* XXX: remove me */
273 /* Note - this involves:
274 * extend libTrie to have a remove() call.
275 * delete from the vector.
276 * delete the object.
277 */
278 }
279}
280
281void
282ESIVarState::addVariable(char const *name, size_t len, Variable *aVariable)
283{
30abd221 284 String temp;
2fe0439c 285 temp.assign(name, len);
924f73bc 286 removeVariable (temp);
287 variables.add(name, len, aVariable);
288 variablesForCleanup.push_back(aVariable);
289}
290
291ESIVariableUserAgent::~ESIVariableUserAgent()
292{
293 safe_free (browserversion);
294}
295
296ESIVariableUserAgent::ESIVariableUserAgent(ESIVarState &state)
297{
298 /* An example:
299 * User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705) */
300 /* Grr this Node is painful - RFC 2616 specifies that 'by convention' the tokens are in order of importance
301 * in identifying the product. According to the RFC the above should be interpreted as:
302 * Product - Mozilla version 4.0
26ac0430 303 * in comments - compatible; .... 3705
924f73bc 304 *
305 * Useing the RFC a more appropriate header would be
306 * User-Agent: MSIE/6.0 Mozilla/4.0 Windows-NT/5.1 .NET-CLR/1.0.3705
307 * or something similar.
308 *
26ac0430 309 * Because we can't parse under those rules and get real-world useful answers, we follow the following
924f73bc 310 * algorithm:
311 * if the string Windows appears in the header, the OS is WIN.
312 * If the string Mac appears in the header, the OS is MAC.
313 * If the string nix, or BSD appears in the header, the OS is UNIX.
26ac0430 314 * If the string MSIE appears in the header, the BROWSER is MSIE, and the version is the string from
924f73bc 315 * MSIE<sp> to the first ;, or end of string.
26ac0430 316 * If the String MSIE does not appear in the header, and MOZILLA does, we use the version from the
924f73bc 317 * /version field.
318 * if MOZILLA doesn't appear, the browser is set to OTHER.
319 * In future, this may be better implemented as a regexp.
320 */
321
789217a2
FC
322 if (state.header().has(Http::HdrType::USER_AGENT)) {
323 char const *s = state.header().getStr(Http::HdrType::USER_AGENT);
924f73bc 324 UserOs = identifyOs(s);
325 char const *t, *t1;
326
327 /* Now the browser and version */
328
329 if ((t = strstr (s, "MSIE"))) {
330 browser = ESI_BROWSER_MSIE;
331 t = index (t, ' ');
332
333 if (!t)
86c63190 334 browserversion = xstrdup("");
924f73bc 335 else {
86c63190 336 t1 = index(t, ';');
924f73bc 337
338 if (!t1)
86c63190 339 browserversion = xstrdup(t + 1);
924f73bc 340 else
86c63190 341 browserversion = xstrndup(t + 1, t1-t);
924f73bc 342 }
343 } else if (strstr (s, "Mozilla")) {
344 browser = ESI_BROWSER_MOZILLA;
345 browserversion = getProductVersion(s);
346 } else {
347 browser = ESI_BROWSER_OTHER;
348 browserversion = getProductVersion(s);
349 }
350 } else {
351 UserOs = ESI_OS_OTHER;
352 browser = ESI_BROWSER_OTHER;
86c63190 353 browserversion = xstrdup("");
924f73bc 354 }
355}
356
357ESIVariableUserAgent::esiUserOs_t
358ESIVariableUserAgent::identifyOs(char const *s) const
359{
360 if (!s)
361 return ESI_OS_OTHER;
362
363 if (strstr (s, "Windows"))
364 return ESI_OS_WIN;
365 else if (strstr (s, "Mac"))
366 return ESI_OS_MAC;
367 else if (strstr (s, "nix") || strstr (s, "BSD"))
368 return ESI_OS_UNIX;
369 else
370 return ESI_OS_OTHER;
371}
372
373void
374ESIVariableCookie::eval (ESIVarState &state, char const *subref, char const *found_default) const
375{
376 const char *s = NULL;
377 state.cookieUsed();
378
789217a2 379 if (state.header().has(Http::HdrType::COOKIE)) {
924f73bc 380 if (!subref)
789217a2 381 s = state.header().getStr (Http::HdrType::COOKIE);
924f73bc 382 else {
36c774f7 383 const auto subCookie = state.header().getListMember(Http::HdrType::COOKIE, subref, ';');
924f73bc 384
36c774f7
EB
385 if (subCookie.length())
386 ESISegment::ListAppend(state.getOutput(), subCookie.rawContent(), subCookie.length());
924f73bc 387 else if (found_default)
388 ESISegment::ListAppend (state.getOutput(), found_default, strlen (found_default));
389 }
390 } else
391 s = found_default;
392
393 if (s)
394 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
395}
396
397void
398ESIVariableHost::eval (ESIVarState &state, char const *subref, char const *found_default) const
399{
400 const char *s = NULL;
401 state.hostUsed();
402
789217a2
FC
403 if (!subref && state.header().has(Http::HdrType::HOST)) {
404 s = state.header().getStr (Http::HdrType::HOST);
924f73bc 405 } else
406 s = found_default;
407
408 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
409}
410
411void
412ESIVariableLanguage::eval (ESIVarState &state, char const *subref, char const *found_default) const
413{
414 char const *s = NULL;
415 state.languageUsed();
416
789217a2 417 if (state.header().has(Http::HdrType::ACCEPT_LANGUAGE)) {
924f73bc 418 if (!subref) {
789217a2 419 String S (state.header().getList (Http::HdrType::ACCEPT_LANGUAGE));
9c175897 420 ESISegment::ListAppend (state.getOutput(), S.rawBuf(), S.size());
924f73bc 421 } else {
789217a2 422 if (state.header().hasListMember (Http::HdrType::ACCEPT_LANGUAGE, subref, ',')) {
924f73bc 423 s = "true";
424 } else {
425 s = "false";
426 }
427
428 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
429 }
430 } else {
431 s = found_default;
432 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
433 }
434}
435
436void
437ESIVariableQuery::eval (ESIVarState &state, char const *subref, char const *found_default) const
438{
439 char const *s = NULL;
440
441 if (!subref)
442 s = queryString();
443 else {
444 unsigned int i = 0;
445
446 while (i < queryElements() && !s) {
447 if (!strcmp (subref, queryVector()[i].var))
448 s = queryVector()[i].val;
449
450 ++i;
451 }
452
453 if (!s)
454 s = found_default;
455 }
456
457 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
458}
459
460void
461ESIVariableReferer::eval (ESIVarState &state, char const *subref, char const *found_default) const
462{
463 const char *s = NULL;
464 state.refererUsed();
465
789217a2
FC
466 if (!subref && state.header().has(Http::HdrType::REFERER))
467 s = state.header().getStr (Http::HdrType::REFERER);
924f73bc 468 else
469 s = found_default;
470
471 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
472}
473
474void
475ESIVariableUserAgent::eval (ESIVarState &state, char const *subref, char const *found_default) const
476{
477 char const *s = NULL;
478 state.useragentUsed();
479
789217a2 480 if (state.header().has(Http::HdrType::USER_AGENT)) {
924f73bc 481 if (!subref)
789217a2 482 s = state.header().getStr (Http::HdrType::USER_AGENT);
924f73bc 483 else {
484 if (!strcmp (subref, "os")) {
485 s = esiUserOs[UserOs];
486 } else if (!strcmp (subref, "browser")) {
487 s = esiBrowsers[browser];
488 } else if (!strcmp (subref, "version")) {
489 s = browserVersion();
490 } else
491 s = "";
492 }
493 } else
494 s = found_default;
495
496 ESISegment::ListAppend (state.getOutput(), s, strlen (s));
497}
498
499/* thoughts on long term:
500 * get $
501 * get () handler
502 * hand off to handler.
503 * one handler for variables.
504 * one handler for each function.
505 */
506
507class ESIVariableProcessor;
508
509class ESIFunction
510{
511
512public:
513 static ESIFunction *GetFunction (char const *symbol, ESIVariableProcessor &);
514 ESIFunction(ESIVariableProcessor &);
515 void doIt();
516
517private:
518 ESIVariableProcessor &processor;
519
520};
521
522ESIFunction::ESIFunction(ESIVariableProcessor &aProcessor) : processor(aProcessor)
523{}
524
525ESIFunction *
526ESIFunction::GetFunction(char const *symbol, ESIVariableProcessor &aProcessor)
527{
528 if (*symbol == '(')
529 return new ESIFunction(aProcessor);
530
531 return NULL;
532}
533
534class ESIVariableProcessor
535{
536
537public:
538 ESIVariableProcessor(char *, ESISegment::Pointer &, Trie &, ESIVarState *);
539 ~ESIVariableProcessor();
540 void doIt();
541
542private:
543 bool validChar (char c);
b0365bd9 544 void eval (ESIVarState::Variable *var, char const *subref, char const *foundDefault );
924f73bc 545 void doFunction();
546 void identifyFunction();
547 char *string;
548 ESISegment::Pointer &output;
549 Trie &variables;
550 ESIVarState *varState;
551 int state;
552 size_t len;
553 size_t pos;
554 size_t var_pos;
555 size_t done_pos;
556 char * found_subref;
557 char *found_default;
558 ESIVarState::Variable *vartype;
559 ESIFunction *currentFunction;
560};
561
562void
b0365bd9 563ESIVariableProcessor::eval (ESIVarState::Variable *var, char const *subref, char const *foundDefault )
924f73bc 564{
565 assert (var);
566
b0365bd9
FC
567 if (!foundDefault)
568 foundDefault = "";
924f73bc 569
b0365bd9 570 var->eval (*varState, subref, foundDefault);
924f73bc 571}
572
573bool
574ESIVariableProcessor::validChar (char c)
575{
576 if (('A' <= c && c <= 'Z') ||
577 ('a' <= c && c <= 'z') ||
578 '_' == c || '-' == c)
579 return true;
580
581 return false;
582}
583
584ESIVarState::Variable *
585ESIVarState::GetVar(char const *symbol, int len)
586{
587 assert (symbol);
588
589 void *result = variables.find (symbol, len);
590
591 if (result)
592 return static_cast<Variable *>(result);
593
594 return defaultVariable;
595}
596
597void
598ESIVarState::doIt ()
599{
600 char *string = input->listToChar();
601 ESISegmentFreeList (input);
602 ESIVariableProcessor theProcessor(string, output, variables, this);
603 theProcessor.doIt();
604 safe_free(string);
605}
606
607#define LOOKFORSTART 0
608ESIVariableProcessor::ESIVariableProcessor(char *aString, ESISegment::Pointer &aSegment, Trie &aTrie, ESIVarState *aState) :
f53969cc
SM
609 string(aString), output (aSegment), variables(aTrie), varState (aState),
610 state(LOOKFORSTART), pos(0), var_pos(0), done_pos(0), found_subref (NULL),
611 found_default (NULL), currentFunction(NULL)
924f73bc 612{
613 len = strlen (string);
614 vartype = varState->GetVar("",0);
615}
616
617void
618ESIFunction::doIt()
619{}
620
621/* because we are only used to process:
622 * - include URL's
623 * - non-esi elements
624 * - choose clauses
625 * buffering is ok - we won't delay the start of async activity, or
626 * of output data preparation
627 */
628/* Should make these an enum or something...
629 */
630void
631ESIVariableProcessor::doFunction()
632{
633 if (!currentFunction)
634 return;
635
636 /* stay in here whilst operating */
637 while (pos < len && state)
638 switch (state) {
639
640 case 2: /* looking for variable name */
641
642 if (!validChar(string[pos])) {
643 /* not a variable name char */
644
645 if (pos - var_pos) {
646 vartype = varState->GetVar (string + var_pos, pos - var_pos);
647 }
648
649 state = 3;
650 } else {
651 ++pos;
652 }
653
654 break;
655
656 case 3: /* looking for variable subref, end bracket or default indicator */
657
658 if (string[pos] == ')') {
659 /* end of string */
660 eval(vartype, found_subref, found_default);
661 done_pos = ++pos;
662 safe_free(found_subref);
663 safe_free(found_default);
664 state = LOOKFORSTART;
665 } else if (!found_subref && !found_default && string[pos] == '{') {
bf8fe701 666 debugs(86, 6, "ESIVarStateDoIt: Subref of some sort");
924f73bc 667 /* subreference of some sort */
668 /* look for the entry name */
669 var_pos = ++pos;
670 state = 4;
671 } else if (!found_default && string[pos] == '|') {
bf8fe701 672 debugs(86, 6, "esiVarStateDoIt: Default present");
924f73bc 673 /* extract default value */
674 state = 5;
675 var_pos = ++pos;
676 } else {
677 /* unexpected char, not a variable after all */
bf8fe701 678 debugs(86, 6, "esiVarStateDoIt: unexpected char after varname");
924f73bc 679 state = LOOKFORSTART;
680 pos = done_pos + 2;
681 }
682
683 break;
684
685 case 4: /* looking for variable subref */
686
687 if (string[pos] == '}') {
688 /* end of subref */
689 found_subref = xstrndup (&string[var_pos], pos - var_pos + 1);
bf8fe701 690 debugs(86, 6, "esiVarStateDoIt: found end of variable subref '" << found_subref << "'");
924f73bc 691 state = 3;
692 ++pos;
693 } else if (!validChar (string[pos])) {
bf8fe701 694 debugs(86, 6, "esiVarStateDoIt: found invalid char in variable subref");
924f73bc 695 /* not a valid subref */
696 safe_free(found_subref);
697 state = LOOKFORSTART;
698 pos = done_pos + 2;
699 } else {
700 ++pos;
701 }
702
703 break;
704
705 case 5: /* looking for a default value */
706
707 if (string[pos] == '\'') {
708 /* begins with a quote */
bf8fe701 709 debugs(86, 6, "esiVarStateDoIt: found quoted default");
924f73bc 710 state = 6;
711 var_pos = ++pos;
712 } else {
713 /* doesn't */
bf8fe701 714 debugs(86, 6, "esiVarStateDoIt: found unquoted default");
924f73bc 715 state = 7;
716 ++pos;
717 }
718
719 break;
720
721 case 6: /* looking for a quote terminate default value */
722
723 if (string[pos] == '\'') {
724 /* end of default */
725 found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
bf8fe701 726 debugs(86, 6, "esiVarStateDoIt: found end of quoted default '" << found_default << "'");
924f73bc 727 state = 3;
728 }
729
730 ++pos;
731 break;
732
733 case 7: /* looking for } terminate default value */
734
735 if (string[pos] == ')') {
736 /* end of default - end of variable*/
737 found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
bf8fe701 738 debugs(86, 6, "esiVarStateDoIt: found end of variable (w/ unquoted default) '" << found_default << "'");
924f73bc 739 eval(vartype,found_subref, found_default);
740 done_pos = ++pos;
741 safe_free(found_default);
742 safe_free(found_subref);
743 state = LOOKFORSTART;
744 }
745
746 ++pos;
747 break;
748
749 default:
750 fatal("esiVarStateDoIt: unexpected state\n");
751 }
752}
753
754void
755ESIVariableProcessor::identifyFunction()
756{
757 delete currentFunction;
758 currentFunction = ESIFunction::GetFunction (&string[pos], *this);
759
760 if (!currentFunction) {
761 state = LOOKFORSTART;
762 } else {
763 state = 2; /* process a function */
764 /* advance past function name */
765 var_pos = ++pos;
766 }
767}
768
769void
770ESIVariableProcessor::doIt()
771{
772 assert (output == NULL);
773
774 while (pos < len) {
775 /* skipping pre-variables */
776
777 if (string[pos] != '$') {
778 ++pos;
779 } else {
780 if (pos - done_pos)
781 /* extract known plain text */
782 ESISegment::ListAppend (output, string + done_pos, pos - done_pos);
783
784 done_pos = pos;
785
786 ++pos;
787
788 identifyFunction();
789
790 doFunction();
791 }
792 }
793
794 /* pos-done_pos chars are ready to copy */
795 if (pos-done_pos)
796 ESISegment::ListAppend (output, string+done_pos, pos - done_pos);
797
798 safe_free (found_default);
799
800 safe_free (found_subref);
801}
802
803ESIVariableProcessor::~ESIVariableProcessor()
804{
805 delete currentFunction;
806}
807
924f73bc 808/* XXX FIXME: this should be comma delimited, no? */
809void
810ESIVarState::buildVary (HttpReply *rep)
811{
812 char tempstr[1024];
813 tempstr[0]='\0';
814
815 if (flags.language)
816 strcat (tempstr, "Accept-Language ");
817
818 if (flags.cookie)
819 strcat (tempstr, "Cookie ");
820
821 if (flags.host)
822 strcat (tempstr, "Host ");
823
824 if (flags.referer)
825 strcat (tempstr, "Referer ");
826
827 if (flags.useragent)
828 strcat (tempstr, "User-Agent ");
829
830 if (!tempstr[0])
831 return;
832
789217a2 833 String strVary (rep->header.getList (Http::HdrType::VARY));
924f73bc 834
9c175897 835 if (!strVary.size() || strVary[0] != '*') {
789217a2 836 rep->header.putStr (Http::HdrType::VARY, tempstr);
924f73bc 837 }
838}
30abd221 839