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