]>
Commit | Line | Data |
---|---|---|
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 |
16 | char const *ESIVariableUserAgent::esiUserOs[]= { |
17 | "WIN", | |
18 | "MAC", | |
19 | "UNIX", | |
20 | "OTHER" | |
21 | }; | |
924f73bc | 22 | |
26ac0430 AJ |
23 | char const * esiBrowsers[]= {"MSIE", |
24 | "MOZILLA", | |
25 | "OTHER" | |
26 | }; | |
924f73bc | 27 | |
8c41d298 AJ |
28 | CBDATA_CLASS_INIT(ESIVarState); |
29 | ||
924f73bc | 30 | void |
31 | ESIVarState::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 | ||
39 | void | |
40 | ESIVarState::hostUsed() | |
41 | { | |
42 | flags.host = 1; | |
43 | } | |
44 | ||
45 | void | |
46 | ESIVarState::cookieUsed() | |
47 | { | |
48 | flags.cookie = 1; | |
49 | } | |
50 | ||
51 | void | |
52 | ESIVarState::languageUsed() | |
53 | { | |
54 | flags.language = 1; | |
55 | } | |
56 | ||
57 | void | |
58 | ESIVarState::refererUsed() | |
59 | { | |
60 | flags.referer = 1; | |
61 | } | |
62 | ||
63 | void | |
64 | ESIVarState::useragentUsed() | |
65 | { | |
66 | flags.useragent = 1; | |
67 | } | |
68 | ||
69 | HttpHeader & | |
70 | ESIVarState::header() | |
71 | { | |
72 | return hdr; | |
73 | } | |
74 | ||
75 | ESISegment::Pointer & | |
76 | ESIVarState::getOutput() | |
77 | { | |
78 | return output; | |
79 | } | |
80 | ||
81 | char const * | |
82 | ESIVariableQuery::queryString() const | |
83 | { | |
84 | return query_string; | |
85 | } | |
86 | ||
87 | struct _query_elem const * | |
e1381638 AJ |
88 | ESIVariableQuery::queryVector() const { |
89 | return query; | |
90 | } | |
924f73bc | 91 | |
92 | size_t const & | |
93 | ESIVariableQuery::queryElements() const | |
94 | { | |
95 | return query_elements; | |
96 | } | |
97 | ||
98 | void | |
99 | ESIVarState::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 | ||
106 | ESISegment::Pointer | |
107 | ESIVarState::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 | ||
116 | char * | |
117 | ESIVarState::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 | 133 | ESIVarState::~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 | 148 | char * |
149 | ESIVariableUserAgent::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 | ||
163 | ESIVariableQuery::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 | ||
226 | ESIVariableQuery::~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 | 242 | ESIVarState::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 | ||
266 | void | |
30abd221 | 267 | ESIVarState::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 | ||
281 | void | |
282 | ESIVarState::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 | ||
291 | ESIVariableUserAgent::~ESIVariableUserAgent() | |
292 | { | |
293 | safe_free (browserversion); | |
294 | } | |
295 | ||
296 | ESIVariableUserAgent::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 | ||
357 | ESIVariableUserAgent::esiUserOs_t | |
358 | ESIVariableUserAgent::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 | ||
373 | void | |
374 | ESIVariableCookie::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 | ||
397 | void | |
398 | ESIVariableHost::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 | ||
411 | void | |
412 | ESIVariableLanguage::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 | ||
436 | void | |
437 | ESIVariableQuery::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 | ||
460 | void | |
461 | ESIVariableReferer::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 | ||
474 | void | |
475 | ESIVariableUserAgent::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 | ||
507 | class ESIVariableProcessor; | |
508 | ||
509 | class ESIFunction | |
510 | { | |
511 | ||
512 | public: | |
513 | static ESIFunction *GetFunction (char const *symbol, ESIVariableProcessor &); | |
514 | ESIFunction(ESIVariableProcessor &); | |
515 | void doIt(); | |
516 | ||
517 | private: | |
518 | ESIVariableProcessor &processor; | |
519 | ||
520 | }; | |
521 | ||
522 | ESIFunction::ESIFunction(ESIVariableProcessor &aProcessor) : processor(aProcessor) | |
523 | {} | |
524 | ||
525 | ESIFunction * | |
526 | ESIFunction::GetFunction(char const *symbol, ESIVariableProcessor &aProcessor) | |
527 | { | |
528 | if (*symbol == '(') | |
529 | return new ESIFunction(aProcessor); | |
530 | ||
531 | return NULL; | |
532 | } | |
533 | ||
534 | class ESIVariableProcessor | |
535 | { | |
536 | ||
537 | public: | |
538 | ESIVariableProcessor(char *, ESISegment::Pointer &, Trie &, ESIVarState *); | |
539 | ~ESIVariableProcessor(); | |
540 | void doIt(); | |
541 | ||
542 | private: | |
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 | ||
562 | void | |
b0365bd9 | 563 | ESIVariableProcessor::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 | ||
573 | bool | |
574 | ESIVariableProcessor::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 | ||
584 | ESIVarState::Variable * | |
585 | ESIVarState::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 | ||
597 | void | |
598 | ESIVarState::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 | |
608 | ESIVariableProcessor::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 | ||
617 | void | |
618 | ESIFunction::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 | */ | |
630 | void | |
631 | ESIVariableProcessor::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 | ||
754 | void | |
755 | ESIVariableProcessor::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 | ||
769 | void | |
770 | ESIVariableProcessor::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 | ||
803 | ESIVariableProcessor::~ESIVariableProcessor() | |
804 | { | |
805 | delete currentFunction; | |
806 | } | |
807 | ||
924f73bc | 808 | /* XXX FIXME: this should be comma delimited, no? */ |
809 | void | |
810 | ESIVarState::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 |