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