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