]> git.ipfire.org Git - thirdparty/squid.git/blob - src/format/Token.cc
Merged from trunk r12948.
[thirdparty/squid.git] / src / format / Token.cc
1 #include "squid.h"
2 #include "format/Config.h"
3 #include "format/Token.h"
4 #include "format/TokenTableEntry.h"
5 #include "globals.h"
6 #include "SquidConfig.h"
7 #include "Store.h"
8
9 // Due to token overlaps between 1 and 2 letter tokens (Bug 3310)
10 // We split the token table into sets determined by the token length
11 namespace Format
12 {
13
14 /// 1-char tokens.
15 static TokenTableEntry TokenTable1C[] = {
16
17 {">a", LFT_CLIENT_IP_ADDRESS},
18 {">p", LFT_CLIENT_PORT},
19 {">A", LFT_CLIENT_FQDN},
20
21 {"<a", LFT_SERVER_IP_ADDRESS},
22 {"<p", LFT_SERVER_PORT},
23 {"<A", LFT_SERVER_FQDN_OR_PEER_NAME},
24
25 {">h", LFT_REQUEST_HEADER},
26 {">h", LFT_REQUEST_ALL_HEADERS},
27 {"<h", LFT_REPLY_HEADER},
28 {"<h", LFT_REPLY_ALL_HEADERS},
29
30 {">v", LFT_REQUEST_VERSION_OLD_2X},
31
32 {"%", LFT_PERCENT},
33
34 {NULL, LFT_NONE} /* this must be last */
35 };
36
37 /// 2-char tokens
38 static TokenTableEntry TokenTable2C[] = {
39
40 {">la", LFT_CLIENT_LOCAL_IP},
41 {"la", LFT_LOCAL_LISTENING_IP},
42 {">lp", LFT_CLIENT_LOCAL_PORT},
43 {"lp", LFT_LOCAL_LISTENING_PORT},
44 /*{ "lA", LFT_LOCAL_NAME }, */
45
46 {"<la", LFT_SERVER_LOCAL_IP},
47 {"oa", LFT_SERVER_LOCAL_IP_OLD_27},
48 {"<lp", LFT_SERVER_LOCAL_PORT},
49
50 {"ts", LFT_TIME_SECONDS_SINCE_EPOCH},
51 {"tu", LFT_TIME_SUBSECOND},
52 {"tl", LFT_TIME_LOCALTIME},
53 {"tg", LFT_TIME_GMT},
54 {"tr", LFT_TIME_TO_HANDLE_REQUEST},
55
56 {"<pt", LFT_PEER_RESPONSE_TIME},
57 {"<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME},
58 {"dt", LFT_DNS_WAIT_TIME},
59
60 {">ha", LFT_ADAPTED_REQUEST_HEADER},
61 {">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS},
62
63 {"un", LFT_USER_NAME},
64 {"ul", LFT_USER_LOGIN},
65 /*{ "ur", LFT_USER_REALM }, */
66 /*{ "us", LFT_USER_SCHEME }, */
67 {"ui", LFT_USER_IDENT},
68 {"ue", LFT_USER_EXTERNAL},
69
70 {"Hs", LFT_HTTP_SENT_STATUS_CODE_OLD_30},
71 {">Hs", LFT_HTTP_SENT_STATUS_CODE},
72 {"<Hs", LFT_HTTP_RECEIVED_STATUS_CODE},
73 /*{ "Ht", LFT_HTTP_STATUS }, */
74 {"<bs", LFT_HTTP_BODY_BYTES_READ},
75
76 {"Ss", LFT_SQUID_STATUS},
77 {"Sh", LFT_SQUID_HIERARCHY},
78
79 {"mt", LFT_MIME_TYPE},
80
81 {">rm", LFT_CLIENT_REQ_METHOD},
82 {">ru", LFT_CLIENT_REQ_URI},
83 {">rp", LFT_CLIENT_REQ_URLPATH},
84 /*{">rq", LFT_CLIENT_REQ_QUERY},*/
85 {">rv", LFT_CLIENT_REQ_VERSION},
86
87 {"rm", LFT_REQUEST_METHOD},
88 {"ru", LFT_REQUEST_URI}, /* doesn't include the query-string */
89 {"rp", LFT_REQUEST_URLPATH_OLD_31},
90 /* { "rq", LFT_REQUEST_QUERY }, * / / * the query-string, INCLUDING the leading ? */
91 {"rv", LFT_REQUEST_VERSION},
92
93 {"<rm", LFT_SERVER_REQ_METHOD},
94 {"<ru", LFT_SERVER_REQ_URI},
95 {"<rp", LFT_SERVER_REQ_URLPATH},
96 /*{"<rq", LFT_SERVER_REQ_QUERY},*/
97 {"<rv", LFT_SERVER_REQ_VERSION},
98
99 {">st", LFT_REQUEST_SIZE_TOTAL },
100 /*{ ">sl", LFT_REQUEST_SIZE_LINE }, * / / * the request line "GET ... " */
101 {">sh", LFT_REQUEST_SIZE_HEADERS },
102 /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
103 /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
104
105 {"<st", LFT_REPLY_SIZE_TOTAL},
106 {"<sH", LFT_REPLY_HIGHOFFSET},
107 {"<sS", LFT_REPLY_OBJECTSIZE},
108 /*{ "<sl", LFT_REPLY_SIZE_LINE }, * / / * the reply line (protocol, code, text) */
109 {"<sh", LFT_REPLY_SIZE_HEADERS },
110 /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
111 /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
112
113 {"et", LFT_TAG},
114 {"st", LFT_IO_SIZE_TOTAL},
115 {"ea", LFT_EXT_LOG},
116 {"sn", LFT_SEQUENCE_NUMBER},
117
118 {NULL, LFT_NONE} /* this must be last */
119 };
120
121 /// Miscellaneous >2 byte tokens
122 static TokenTableEntry TokenTableMisc[] = {
123 {">eui", LFT_CLIENT_EUI},
124 {">qos", LFT_CLIENT_LOCAL_TOS},
125 {"<qos", LFT_SERVER_LOCAL_TOS},
126 {">nfmark", LFT_CLIENT_LOCAL_NFMARK},
127 {"<nfmark", LFT_SERVER_LOCAL_NFMARK},
128 {"err_code", LFT_SQUID_ERROR },
129 {"err_detail", LFT_SQUID_ERROR_DETAIL },
130 {"note", LFT_NOTE },
131 {NULL, LFT_NONE} /* this must be last */
132 };
133
134 #if USE_ADAPTATION
135 static TokenTableEntry TokenTableAdapt[] = {
136 {"all_trs", LFT_ADAPTATION_ALL_XACT_TIMES},
137 {"sum_trs", LFT_ADAPTATION_SUM_XACT_TIMES},
138 {"<last_h", LFT_ADAPTATION_LAST_HEADER},
139 {NULL, LFT_NONE} /* this must be last */
140 };
141 #endif
142
143 #if ICAP_CLIENT
144 /// ICAP (icap::) tokens
145 static TokenTableEntry TokenTableIcap[] = {
146 {"tt", LFT_ICAP_TOTAL_TIME},
147 {"<last_h", LFT_ADAPTATION_LAST_HEADER}, // deprecated
148
149 {"<A", LFT_ICAP_ADDR},
150 {"<service_name", LFT_ICAP_SERV_NAME},
151 {"ru", LFT_ICAP_REQUEST_URI},
152 {"rm", LFT_ICAP_REQUEST_METHOD},
153 {">st", LFT_ICAP_BYTES_SENT},
154 {"<st", LFT_ICAP_BYTES_READ},
155 {"<bs", LFT_ICAP_BODY_BYTES_READ},
156
157 {">h", LFT_ICAP_REQ_HEADER},
158 {"<h", LFT_ICAP_REP_HEADER},
159
160 {"tr", LFT_ICAP_TR_RESPONSE_TIME},
161 {"tio", LFT_ICAP_IO_TIME},
162 {"to", LFT_ICAP_OUTCOME},
163 {"Hs", LFT_ICAP_STATUS_CODE},
164
165 {NULL, LFT_NONE} /* this must be last */
166 };
167 #endif
168
169 #if USE_SSL
170 // SSL (ssl::) tokens
171 static TokenTableEntry TokenTableSsl[] = {
172 {"bump_mode", LFT_SSL_BUMP_MODE},
173 {">cert_subject", LFT_SSL_USER_CERT_SUBJECT},
174 {">cert_issuer", LFT_SSL_USER_CERT_ISSUER},
175 {NULL, LFT_NONE}
176 };
177 #endif
178 } // namespace Format
179
180 /// Register all components custom format tokens
181 void
182 Format::Token::Init()
183 {
184 // TODO standard log tokens
185 // TODO external ACL fmt tokens
186
187 #if USE_ADAPTATION
188 TheConfig.registerTokens(String("adapt"),::Format::TokenTableAdapt);
189 #endif
190 #if ICAP_CLIENT
191 TheConfig.registerTokens(String("icap"),::Format::TokenTableIcap);
192 #endif
193 #if USE_SSL
194 TheConfig.registerTokens(String("ssl"),::Format::TokenTableSsl);
195 #endif
196 }
197
198 /// Scans a token table to see if the next token exists there
199 /// returns a pointer to next unparsed byte and updates type member if found
200 const char *
201 Format::Token::scanForToken(TokenTableEntry const table[], const char *cur)
202 {
203 for (TokenTableEntry const *lte = table; lte->configTag != NULL; ++lte) {
204 debugs(46, 8, HERE << "compare tokens '" << lte->configTag << "' with '" << cur << "'");
205 if (strncmp(lte->configTag, cur, strlen(lte->configTag)) == 0) {
206 type = lte->tokenType;
207 label = lte->configTag;
208 debugs(46, 7, HERE << "Found token '" << label << "'");
209 return cur + strlen(lte->configTag);
210 }
211 }
212 return cur;
213 }
214
215 /* parses a single token. Returns the token length in characters,
216 * and fills in the lt item with the token information.
217 * def is for sure null-terminated
218 */
219 int
220 Format::Token::parse(const char *def, Quoting *quoting)
221 {
222 const char *cur = def;
223
224 int l;
225
226 l = strcspn(cur, "%");
227
228 if (l > 0) {
229 char *cp;
230 /* it's a string for sure, until \0 or the next % */
231 cp = (char *)xmalloc(l + 1);
232 xstrncpy(cp, cur, l + 1);
233 type = LFT_STRING;
234 data.string = cp;
235
236 while (l > 0) {
237 switch (*cur) {
238
239 case '"':
240
241 if (*quoting == LOG_QUOTE_NONE)
242 *quoting = LOG_QUOTE_QUOTES;
243 else if (*quoting == LOG_QUOTE_QUOTES)
244 *quoting = LOG_QUOTE_NONE;
245
246 break;
247
248 case '[':
249 if (*quoting == LOG_QUOTE_NONE)
250 *quoting = LOG_QUOTE_MIMEBLOB;
251
252 break;
253
254 case ']':
255 if (*quoting == LOG_QUOTE_MIMEBLOB)
256 *quoting = LOG_QUOTE_NONE;
257
258 break;
259 }
260
261 ++cur;
262 --l;
263 }
264
265 goto done;
266 }
267
268 if (!*cur)
269 goto done;
270
271 ++cur;
272
273 // select quoting style for his particular token
274 switch (*cur) {
275
276 case '"':
277 quote = LOG_QUOTE_QUOTES;
278 ++cur;
279 break;
280
281 case '\'':
282 quote = LOG_QUOTE_RAW;
283 ++cur;
284 break;
285
286 case '[':
287 quote = LOG_QUOTE_MIMEBLOB;
288 ++cur;
289 break;
290
291 case '#':
292 quote = LOG_QUOTE_URL;
293 ++cur;
294 break;
295
296 default:
297 quote = *quoting;
298 break;
299 }
300
301 if (*cur == '-') {
302 left = true;
303 ++cur;
304 }
305
306 if (*cur == '0') {
307 zero = true;
308 ++cur;
309 }
310
311 char *endp;
312 if (xisdigit(*cur)) {
313 widthMin = strtol(cur, &endp, 10);
314 cur = endp;
315 }
316
317 if (*cur == '.' && xisdigit(*(++cur))) {
318 widthMax = strtol(cur, &endp, 10);
319 cur = endp;
320 }
321
322 if (*cur == '{') {
323 char *cp;
324 ++cur;
325 l = strcspn(cur, "}");
326 cp = (char *)xmalloc(l + 1);
327 xstrncpy(cp, cur, l + 1);
328 data.string = cp;
329 cur += l;
330
331 if (*cur == '}')
332 ++cur;
333 }
334
335 type = LFT_NONE;
336
337 // Scan each registered token namespace
338 debugs(46, 9, HERE << "check for token in " << TheConfig.tokens.size() << " namespaces.");
339 for (std::list<TokenNamespace>::const_iterator itr = TheConfig.tokens.begin(); itr != TheConfig.tokens.end(); ++itr) {
340 debugs(46, 7, HERE << "check for possible " << itr->prefix << ":: token");
341 const size_t len = itr->prefix.size();
342 if (itr->prefix.cmp(cur, len) == 0 && cur[len] == ':' && cur[len+1] == ':') {
343 debugs(46, 5, HERE << "check for " << itr->prefix << ":: token in '" << cur << "'");
344 const char *old = cur;
345 cur = scanForToken(itr->tokenSet, cur+len+2);
346 if (old != cur) // found
347 break;
348 else // reset to start of namespace
349 cur = cur - len - 2;
350 }
351 }
352
353 if (type == LFT_NONE) {
354 // For upward compatibility, assume "http::" prefix as default prefix
355 // for all log access formatting codes, except those starting with a
356 // "%" or a known namespace. (ie "icap::", "adapt::")
357 if (strncmp(cur,"http::", 6) == 0 && *(cur+6) != '%' )
358 cur += 6;
359
360 // NP: scan the sets of tokens in decreasing size to guarantee no
361 // mistakes made with overlapping names. (Bug 3310)
362
363 // Scan for various long tokens
364 debugs(46, 5, HERE << "scan for possible Misc token");
365 cur = scanForToken(TokenTableMisc, cur);
366 // scan for 2-char tokens
367 if (type == LFT_NONE) {
368 debugs(46, 5, HERE << "scan for possible 2C token");
369 cur = scanForToken(TokenTable2C, cur);
370 }
371 // finally scan for 1-char tokens.
372 if (type == LFT_NONE) {
373 debugs(46, 5, HERE << "scan for possible 1C token");
374 cur = scanForToken(TokenTable1C, cur);
375 }
376 }
377
378 if (type == LFT_NONE) {
379 fatalf("Can't parse configuration token: '%s'\n", def);
380 }
381
382 if (*cur == ' ') {
383 space = true;
384 ++cur;
385 }
386
387 done:
388
389 switch (type) {
390
391 #if USE_ADAPTATION
392 case LFT_ADAPTATION_LAST_HEADER:
393 #endif
394
395 #if ICAP_CLIENT
396 case LFT_ICAP_REQ_HEADER:
397
398 case LFT_ICAP_REP_HEADER:
399 #endif
400
401 case LFT_ADAPTED_REQUEST_HEADER:
402
403 case LFT_REQUEST_HEADER:
404
405 case LFT_REPLY_HEADER:
406
407 if (data.string) {
408 char *header = data.string;
409 char *cp = strchr(header, ':');
410
411 if (cp) {
412 *cp = '\0';
413 ++cp;
414
415 if (*cp == ',' || *cp == ';' || *cp == ':') {
416 data.header.separator = *cp;
417 ++cp;
418 } else {
419 data.header.separator = ',';
420 }
421
422 data.header.element = cp;
423
424 switch (type) {
425 case LFT_REQUEST_HEADER:
426 type = LFT_REQUEST_HEADER_ELEM;
427 break;
428
429 case LFT_ADAPTED_REQUEST_HEADER:
430 type = LFT_ADAPTED_REQUEST_HEADER_ELEM;
431 break;
432
433 case LFT_REPLY_HEADER:
434 type = LFT_REPLY_HEADER_ELEM;
435 break;
436 #if USE_ADAPTATION
437 case LFT_ADAPTATION_LAST_HEADER:
438 type = LFT_ADAPTATION_LAST_HEADER_ELEM;
439 break;
440 #endif
441 #if ICAP_CLIENT
442 case LFT_ICAP_REQ_HEADER:
443 type = LFT_ICAP_REQ_HEADER_ELEM;
444 break;
445 case LFT_ICAP_REP_HEADER:
446 type = LFT_ICAP_REP_HEADER_ELEM;
447 break;
448 #endif
449 default:
450 break;
451 }
452 }
453
454 data.header.header = header;
455 } else {
456 switch (type) {
457 case LFT_REQUEST_HEADER:
458 type = LFT_REQUEST_ALL_HEADERS;
459 break;
460
461 case LFT_ADAPTED_REQUEST_HEADER:
462 type = LFT_ADAPTED_REQUEST_ALL_HEADERS;
463 break;
464
465 case LFT_REPLY_HEADER:
466 type = LFT_REPLY_ALL_HEADERS;
467 break;
468 #if USE_ADAPTATION
469 case LFT_ADAPTATION_LAST_HEADER:
470 type = LFT_ADAPTATION_LAST_ALL_HEADERS;
471 break;
472 #endif
473 #if ICAP_CLIENT
474 case LFT_ICAP_REQ_HEADER:
475 type = LFT_ICAP_REQ_ALL_HEADERS;
476 break;
477 case LFT_ICAP_REP_HEADER:
478 type = LFT_ICAP_REP_ALL_HEADERS;
479 break;
480 #endif
481 default:
482 break;
483 }
484 Config.onoff.log_mime_hdrs = 1;
485 }
486
487 break;
488
489 case LFT_CLIENT_FQDN:
490 Config.onoff.log_fqdn = 1;
491 break;
492
493 case LFT_TIME_SUBSECOND:
494 divisor = 1000;
495
496 if (widthMax > 0) {
497 int i;
498 divisor = 1000000;
499
500 for (i = widthMax; i > 1; --i)
501 divisor /= 10;
502
503 if (!divisor)
504 divisor = 0;
505 }
506 break;
507
508 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
509 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"Hs\" formatting code is deprecated. Use the \">Hs\" instead.");
510 type = LFT_HTTP_SENT_STATUS_CODE;
511 break;
512
513 case LFT_SERVER_LOCAL_IP_OLD_27:
514 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"oa\" formatting code is deprecated. Use the \"<la\" instead.");
515 type = LFT_SERVER_LOCAL_IP;
516 break;
517
518 case LFT_REQUEST_URLPATH_OLD_31:
519 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rp\" formatting code is deprecated. Use the \">rp\" instead.");
520 type = LFT_CLIENT_REQ_URLPATH;
521 break;
522
523 case LFT_REQUEST_VERSION_OLD_2X:
524 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \">v\" formatting code is deprecated. Use the \">rv\" instead.");
525 type = LFT_REQUEST_VERSION;
526 break;
527
528 #if !USE_SQUID_EUI
529 case LFT_CLIENT_EUI:
530 debugs(46, DBG_CRITICAL, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid.");
531 break;
532 #endif
533
534 default:
535 break;
536 }
537
538 return (cur - def);
539 }
540
541 Format::Token::~Token()
542 {
543 label = NULL; // drop reference to global static.
544 safe_free(data.string);
545 while (next) {
546 Token *tokens = next;
547 next = next->next;
548 tokens->next = NULL;
549 delete tokens;
550 }
551 }
552