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