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