~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/ESIVarState.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2010-05-04 11:15:49 UTC
  • mfrom: (1.3.1 upstream)
  • mto: (20.3.1 squeeze) (21.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20100504111549-1apjh2g5sndki4te
Tags: upstream-3.1.3
ImportĀ upstreamĀ versionĀ 3.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
/*
3
 
 * $Id: ESIVarState.cc,v 1.10 2007/05/29 13:31:37 amosjeffries 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
 
    debugs (86,6, "esiVarState::feedData: accepting " << len << " bytes");
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
 
    debugs(86, 6, "ESIVarStateExtractList: Extracted list");
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
 
    debugs(86, 6, "ESIVarStateExtractList: Extracted char");
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
 
    hdr.clean();
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
 
char *
206
 
ESIVariableUserAgent::getProductVersion (char const *s)
207
 
{
208
 
    char const *t;
209
 
    int len;
210
 
    t = index (s,'/');
211
 
 
212
 
    if (!t || !*(++t))
213
 
        return xstrdup ("");
214
 
 
215
 
    len = strcspn (t, " \r\n()<>@,;:\\\"/[]?={}");
216
 
 
217
 
    return xstrndup (t, len + 1);
218
 
}
219
 
 
220
 
ESIVariableQuery::ESIVariableQuery(char const *uri) : query (NULL), query_sz (0), query_elements (0), query_string (NULL)
221
 
{
222
 
    /* Count off the query elements */
223
 
    char const *query_start = strchr (uri, '?');
224
 
 
225
 
    if (query_start && query_start[1] != '\0' ) {
226
 
        unsigned int n;
227
 
        query_string = xstrdup (query_start + 1);
228
 
        query_elements = 1;
229
 
        char const *query_pos = query_start + 1;
230
 
 
231
 
        while ((query_pos = strchr (query_pos, '&'))) {
232
 
            ++query_elements;
233
 
            ++query_pos;
234
 
        }
235
 
 
236
 
        query = (_query_elem *)memReallocBuf(query, query_elements * sizeof (struct _query_elem),
237
 
                                             &query_sz);
238
 
        query_pos = query_start + 1;
239
 
        n = 0;
240
 
 
241
 
        while (query_pos) {
242
 
            char const *next = strchr (query_pos, '&');
243
 
            char const *div = strchr (query_pos, '=');
244
 
 
245
 
            if (next)
246
 
                ++next;
247
 
 
248
 
            assert (n < query_elements);
249
 
 
250
 
            if (!div)
251
 
                div = next;
252
 
 
253
 
            if (!(div - query_pos + 1))
254
 
                /* zero length between & and = or & and & */
255
 
                continue;
256
 
 
257
 
            query[n].var = xstrndup (query_pos, div - query_pos + 1) ;
258
 
 
259
 
            if (div == next) {
260
 
                query[n].val = xstrdup ("");
261
 
            } else {
262
 
                query[n].val = xstrndup (div + 1, next - div - 1);
263
 
            }
264
 
 
265
 
            query_pos = next;
266
 
            ++n;
267
 
        }
268
 
    } else {
269
 
        query_string = xstrdup ("");
270
 
    }
271
 
 
272
 
    if (query) {
273
 
        unsigned int n = 0;
274
 
        debugs(86, 6, "esiVarStateNew: Parsed Query string: '" << uri << "'");
275
 
 
276
 
        while (n < query_elements) {
277
 
            debugs(86, 6, "esiVarStateNew: Parsed Query element " << n + 1 << " '" << query[n].var << "'='" << query[n].val << "'");
278
 
            ++n;
279
 
        }
280
 
    }
281
 
}
282
 
 
283
 
ESIVariableQuery::~ESIVariableQuery()
284
 
{
285
 
    if (query) {
286
 
        unsigned int i;
287
 
 
288
 
        for (i = 0; i < query_elements; ++i) {
289
 
            safe_free(query[i].var);
290
 
            safe_free(query[i].val);
291
 
        }
292
 
 
293
 
        memFreeBuf (query_sz, query);
294
 
    }
295
 
 
296
 
    safe_free (query_string);
297
 
}
298
 
 
299
 
ESIVarState::ESIVarState (HttpHeader const *aHeader, char const *uri)
300
 
        : output (NULL), hdr(hoReply)
301
 
{
302
 
    /* TODO: only grab the needed headers */
303
 
    /* Note that as we pass these through to included requests, we
304
 
     * cannot trim them */
305
 
    hdr.append(aHeader);
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
321
 
ESIVarState::removeVariable (String const &name)
322
 
{
323
 
    Variable *candidate = static_cast <Variable *>(variables.find (name.buf(), name.size()));
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
 
{
338
 
    String temp;
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
357
 
     * in comments - compatible; .... 3705 
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
 
     *
363
 
     * Because we can't parse under those rules and get real-world useful answers, we follow the following 
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.
368
 
     * If the string MSIE appears in the header, the BROWSER is MSIE, and the version is the string from 
369
 
     * MSIE<sp> to the first ;, or end of string.
370
 
     * If the String MSIE does not appear in the header, and MOZILLA does, we use the version from the 
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
 
 
376
 
    if (state.header().has(HDR_USER_AGENT)) {
377
 
        char const *s = state.header().getStr(HDR_USER_AGENT);
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)
388
 
                browserversion = xstrdup ("");
389
 
            else {
390
 
                t1 = index (t, ';');
391
 
 
392
 
                if (!t1)
393
 
                    browserversion = xstrdup (t + 1);
394
 
                else
395
 
                    browserversion = xstrndup (t + 1, t1-t);
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;
407
 
        browserversion = xstrdup ("");
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
 
 
433
 
    if (state.header().has(HDR_COOKIE)) {
434
 
        if (!subref)
435
 
            s = state.header().getStr (HDR_COOKIE);
436
 
        else {
437
 
            String S = state.header().getListMember (HDR_COOKIE, subref, ';');
438
 
 
439
 
            if (S.size())
440
 
                ESISegment::ListAppend (state.getOutput(), S.buf(), S.size());
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
 
 
457
 
    if (!subref && state.header().has(HDR_HOST)) {
458
 
        s = state.header().getStr (HDR_HOST);
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
 
 
471
 
    if (state.header().has(HDR_ACCEPT_LANGUAGE)) {
472
 
        if (!subref) {
473
 
            String S (state.header().getList (HDR_ACCEPT_LANGUAGE));
474
 
            ESISegment::ListAppend (state.getOutput(), S.buf(), S.size());
475
 
        } else {
476
 
            if (state.header().hasListMember (HDR_ACCEPT_LANGUAGE, subref, ',')) {
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
 
 
520
 
    if (!subref && state.header().has(HDR_REFERER))
521
 
        s = state.header().getStr (HDR_REFERER);
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
 
 
534
 
    if (state.header().has(HDR_USER_AGENT)) {
535
 
        if (!subref)
536
 
            s = state.header().getStr (HDR_USER_AGENT);
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);
598
 
    void eval (ESIVarState::Variable *var, char const *subref, char const *found_default );
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
617
 
ESIVariableProcessor::eval (ESIVarState::Variable *var, char const *subref, char const *found_default )
618
 
{
619
 
    assert (var);
620
 
 
621
 
    if (!found_default)
622
 
        found_default = "";
623
 
 
624
 
    var->eval (*varState, subref, found_default);
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] == '{') {
720
 
                debugs(86, 6, "ESIVarStateDoIt: Subref of some sort");
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] == '|') {
726
 
                debugs(86, 6, "esiVarStateDoIt: Default present");
727
 
                /* extract default value */
728
 
                state = 5;
729
 
                var_pos = ++pos;
730
 
            } else {
731
 
                /* unexpected char, not a variable after all */
732
 
                debugs(86, 6, "esiVarStateDoIt: unexpected char after varname");
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);
744
 
                debugs(86, 6, "esiVarStateDoIt: found end of variable subref '" << found_subref << "'");
745
 
                state = 3;
746
 
                ++pos;
747
 
            } else if (!validChar (string[pos])) {
748
 
                debugs(86, 6, "esiVarStateDoIt: found invalid char in variable subref");
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 */
763
 
                debugs(86, 6, "esiVarStateDoIt: found quoted default");
764
 
                state = 6;
765
 
                var_pos = ++pos;
766
 
            } else {
767
 
                /* doesn't */
768
 
                debugs(86, 6, "esiVarStateDoIt: found unquoted default");
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);
780
 
                debugs(86, 6, "esiVarStateDoIt: found end of quoted default '" << found_default << "'");
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);
792
 
                debugs(86, 6, "esiVarStateDoIt: found end of variable (w/ unquoted default) '" << found_default << "'");
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
 
 
862
 
 
863
 
/* XXX FIXME: this should be comma delimited, no? */
864
 
void
865
 
ESIVarState::buildVary (HttpReply *rep)
866
 
{
867
 
    char tempstr[1024];
868
 
    tempstr[0]='\0';
869
 
 
870
 
    if (flags.language)
871
 
        strcat (tempstr, "Accept-Language ");
872
 
 
873
 
    if (flags.cookie)
874
 
        strcat (tempstr, "Cookie ");
875
 
 
876
 
    if (flags.host)
877
 
        strcat (tempstr, "Host ");
878
 
 
879
 
    if (flags.referer)
880
 
        strcat (tempstr, "Referer ");
881
 
 
882
 
    if (flags.useragent)
883
 
        strcat (tempstr, "User-Agent ");
884
 
 
885
 
    if (!tempstr[0])
886
 
        return;
887
 
 
888
 
    String strVary (rep->header.getList (HDR_VARY));
889
 
 
890
 
    if (!strVary.size() || strVary.buf()[0] != '*') {
891
 
        rep->header.putStr (HDR_VARY, tempstr);
892
 
    }
893
 
}
894