]> git.ipfire.org Git - thirdparty/squid.git/blame - src/format/Format.cc
Boilerplate: update copyright blurbs on Squid helpers
[thirdparty/squid.git] / src / format / Format.cc
CommitLineData
f7f3304a 1#include "squid.h"
38e16f92 2#include "AccessLogEntry.h"
582c2af2 3#include "client_side.h"
38e16f92
AJ
4#include "comm/Connection.h"
5#include "err_detail_type.h"
6#include "errorpage.h"
f4698e0b 7#include "fde.h"
38e16f92
AJ
8#include "format/Format.h"
9#include "format/Quoting.h"
31971e6a 10#include "format/Token.h"
95e6d864 11#include "fqdncache.h"
38e16f92
AJ
12#include "HttpRequest.h"
13#include "MemBuf.h"
14#include "rfc1738.h"
15#include "SquidTime.h"
16#include "Store.h"
b1bd952a 17#include "URL.h"
cb4f4424 18#if USE_OPENSSL
2f3e52b5 19#include "ssl/ErrorDetail.h"
cedca6e7 20#include "ssl/ServerBump.h"
2f3e52b5
CT
21#endif
22
bd85ea1f
AJ
23/// Convert a string to NULL pointer if it is ""
24#define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
38e16f92
AJ
25
26Format::Format::Format(const char *n) :
27 format(NULL),
28 next(NULL)
29{
30 name = xstrdup(n);
31}
32
33Format::Format::~Format()
34{
35 // erase the list without consuming stack space
36 while (next) {
37 // unlink the next entry for deletion
38 Format *temp = next;
39 next = temp->next;
40 temp->next = NULL;
41 delete temp;
42 }
43
44 // remove locals
45 xfree(name);
46 delete format;
47}
48
49bool
f4698e0b 50Format::Format::parse(const char *def)
38e16f92 51{
f4698e0b 52 const char *cur, *eos;
38e16f92
AJ
53 Token *new_lt, *last_lt;
54 enum Quoting quote = LOG_QUOTE_NONE;
55
56 debugs(46, 2, HERE << "got definition '" << def << "'");
57
58 if (format) {
59 debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
60 return false;
61 }
62
63 /* very inefficent parser, but who cares, this needs to be simple */
64 /* First off, let's tokenize, we'll optimize in a second pass.
65 * A token can either be a %-prefixed sequence (usually a dynamic
66 * token but it can be an escaped sequence), or a string. */
67 cur = def;
68 eos = def + strlen(def);
69 format = new_lt = last_lt = new Token;
70 cur += new_lt->parse(cur, &quote);
71
72 while (cur < eos) {
73 new_lt = new Token;
74 last_lt->next = new_lt;
75 last_lt = new_lt;
76 cur += new_lt->parse(cur, &quote);
77 }
78
79 return true;
80}
81
82void
5f621cd0 83Format::Format::dump(StoreEntry * entry, const char *directiveName)
38e16f92
AJ
84{
85 debugs(46, 4, HERE);
86
87 // loop rather than recursing to conserve stack space.
5f621cd0
AJ
88 for (Format *fmt = this; fmt; fmt = fmt->next) {
89 debugs(46, 3, HERE << "Dumping format definition for " << fmt->name);
90 storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
38e16f92 91
5f621cd0 92 for (Token *t = fmt->format; t; t = t->next) {
38e16f92
AJ
93 if (t->type == LFT_STRING)
94 storeAppendPrintf(entry, "%s", t->data.string);
95 else {
96 char argbuf[256];
97 char *arg = NULL;
98 ByteCode_t type = t->type;
99
100 switch (type) {
101 /* special cases */
102
103 case LFT_STRING:
104 break;
105#if USE_ADAPTATION
106 case LFT_ADAPTATION_LAST_HEADER_ELEM:
107#endif
108#if ICAP_CLIENT
109 case LFT_ICAP_REQ_HEADER_ELEM:
110 case LFT_ICAP_REP_HEADER_ELEM:
111#endif
112 case LFT_REQUEST_HEADER_ELEM:
113 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
114 case LFT_REPLY_HEADER_ELEM:
115
116 if (t->data.header.separator != ',')
117 snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
118 else
119 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
120
121 arg = argbuf;
122
123 switch (type) {
124 case LFT_REQUEST_HEADER_ELEM:
125 type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
126 break;
127 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
128 type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
129 break;
130 case LFT_REPLY_HEADER_ELEM:
131 type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM?
132 break;
133#if USE_ADAPTATION
134 case LFT_ADAPTATION_LAST_HEADER_ELEM:
135 type = LFT_ADAPTATION_LAST_HEADER;
136 break;
137#endif
138#if ICAP_CLIENT
139 case LFT_ICAP_REQ_HEADER_ELEM:
140 type = LFT_ICAP_REQ_HEADER;
141 break;
142 case LFT_ICAP_REP_HEADER_ELEM:
143 type = LFT_ICAP_REP_HEADER;
144 break;
145#endif
146 default:
147 break;
148 }
149
150 break;
151
152 case LFT_REQUEST_ALL_HEADERS:
153 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
154 case LFT_REPLY_ALL_HEADERS:
155
156#if USE_ADAPTATION
157 case LFT_ADAPTATION_LAST_ALL_HEADERS:
158#endif
159#if ICAP_CLIENT
160 case LFT_ICAP_REQ_ALL_HEADERS:
161 case LFT_ICAP_REP_ALL_HEADERS:
162#endif
163
164 switch (type) {
165 case LFT_REQUEST_ALL_HEADERS:
166 type = LFT_REQUEST_HEADER;
167 break;
168 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
169 type = LFT_ADAPTED_REQUEST_HEADER;
170 break;
171 case LFT_REPLY_ALL_HEADERS:
172 type = LFT_REPLY_HEADER;
173 break;
174#if USE_ADAPTATION
175 case LFT_ADAPTATION_LAST_ALL_HEADERS:
176 type = LFT_ADAPTATION_LAST_HEADER;
177 break;
178#endif
179#if ICAP_CLIENT
180 case LFT_ICAP_REQ_ALL_HEADERS:
181 type = LFT_ICAP_REQ_HEADER;
182 break;
183 case LFT_ICAP_REP_ALL_HEADERS:
184 type = LFT_ICAP_REP_HEADER;
185 break;
186#endif
187 default:
188 break;
189 }
190
191 break;
192
193 default:
194 if (t->data.string)
195 arg = t->data.string;
196
197 break;
198 }
199
200 entry->append("%", 1);
201
202 switch (t->quote) {
203
204 case LOG_QUOTE_QUOTES:
205 entry->append("\"", 1);
206 break;
207
208 case LOG_QUOTE_MIMEBLOB:
209 entry->append("[", 1);
210 break;
211
212 case LOG_QUOTE_URL:
213 entry->append("#", 1);
214 break;
215
216 case LOG_QUOTE_RAW:
217 entry->append("'", 1);
218 break;
219
220 case LOG_QUOTE_NONE:
221 break;
222 }
223
224 if (t->left)
225 entry->append("-", 1);
226
227 if (t->zero)
228 entry->append("0", 1);
229
8846a2b4
CT
230 if (t->widthMin >= 0)
231 storeAppendPrintf(entry, "%d", t->widthMin);
38e16f92 232
8846a2b4
CT
233 if (t->widthMax >= 0)
234 storeAppendPrintf(entry, ".%d", t->widthMax);
38e16f92
AJ
235
236 if (arg)
237 storeAppendPrintf(entry, "{%s}", arg);
238
aa99e35e 239 storeAppendPrintf(entry, "%s", t->label);
38e16f92
AJ
240
241 if (t->space)
242 entry->append(" ", 1);
243 }
244 }
245
246 entry->append("\n", 1);
247 }
248
249}
250
251static void
252log_quoted_string(const char *str, char *out)
253{
254 char *p = out;
255
256 while (*str) {
257 int l = strcspn(str, "\"\\\r\n\t");
258 memcpy(p, str, l);
259 str += l;
260 p += l;
261
262 switch (*str) {
263
264 case '\0':
265 break;
266
267 case '\r':
a38ec4b1
FC
268 *p = '\\';
269 ++p;
270 *p = 'r';
271 ++p;
cb4185f1 272 ++str;
38e16f92
AJ
273 break;
274
275 case '\n':
a38ec4b1
FC
276 *p = '\\';
277 ++p;
278 *p = 'n';
279 ++p;
cb4185f1 280 ++str;
38e16f92
AJ
281 break;
282
283 case '\t':
a38ec4b1
FC
284 *p = '\\';
285 ++p;
286 *p = 't';
287 ++p;
cb4185f1 288 ++str;
38e16f92
AJ
289 break;
290
291 default:
a38ec4b1
FC
292 *p = '\\';
293 ++p;
294 *p = *str;
295 ++p;
cb4185f1 296 ++str;
38e16f92
AJ
297 break;
298 }
299 }
300
cb4185f1 301 *p = '\0';
38e16f92
AJ
302}
303
304void
f4698e0b 305Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
38e16f92
AJ
306{
307 char tmp[1024];
308 String sb;
309
310 for (Token *fmt = format; fmt != NULL; fmt = fmt->next) { /* for each token */
311 const char *out = NULL;
312 int quote = 0;
313 long int outint = 0;
314 int doint = 0;
315 int dofree = 0;
316 int64_t outoff = 0;
317 int dooff = 0;
318
319 switch (fmt->type) {
320
321 case LFT_NONE:
322 out = "";
323 break;
324
325 case LFT_STRING:
326 out = fmt->data.string;
327 break;
328
329 case LFT_CLIENT_IP_ADDRESS:
d4204018
AJ
330 al->getLogClientIp(tmp, sizeof(tmp));
331 out = tmp;
38e16f92
AJ
332 break;
333
334 case LFT_CLIENT_FQDN:
4dd643d5 335 if (al->cache.caddr.isAnyAddr()) // e.g., ICAP OPTIONS lack client
38e16f92
AJ
336 out = "-";
337 else
338 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
339 if (!out) {
4dd643d5 340 out = al->cache.caddr.toStr(tmp,1024);
38e16f92
AJ
341 }
342
343 break;
344
345 case LFT_CLIENT_PORT:
346 if (al->request) {
4dd643d5 347 outint = al->request->client_addr.port();
38e16f92
AJ
348 doint = 1;
349 }
350 break;
351
38e16f92 352 case LFT_CLIENT_EUI:
8652f8e7 353#if USE_SQUID_EUI
38e16f92
AJ
354 // TODO make the ACL checklist have a direct link to any TCP details.
355 if (al->request && al->request->clientConnectionManager.valid() && al->request->clientConnectionManager->clientConnection != NULL) {
4dd643d5 356 if (al->request->clientConnectionManager->clientConnection->remote.isIPv4())
38e16f92
AJ
357 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
358 else
359 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
360 out = tmp;
361 }
8652f8e7
AJ
362#else
363 out = "-";
38e16f92 364#endif
8652f8e7 365 break;
38e16f92 366
8652f8e7
AJ
367 case LFT_SERVER_IP_ADDRESS:
368 if (al->hier.tcpServer != NULL) {
4dd643d5 369 out = al->hier.tcpServer->remote.toStr(tmp,sizeof(tmp));
8652f8e7
AJ
370 }
371 break;
38e16f92 372
8652f8e7 373 case LFT_SERVER_FQDN_OR_PEER_NAME:
38e16f92 374 out = al->hier.host;
38e16f92
AJ
375 break;
376
8652f8e7
AJ
377 case LFT_SERVER_PORT:
378 if (al->hier.tcpServer != NULL) {
4dd643d5 379 outint = al->hier.tcpServer->remote.port();
8652f8e7
AJ
380 doint = 1;
381 }
382 break;
38e16f92 383
28417506
CT
384 case LFT_LOCAL_LISTENING_IP: {
385 // avoid logging a dash if we have reliable info
47176c0b 386 const bool interceptedAtKnownPort = al->request ?
0d901ef4 387 (al->request->flags.interceptTproxy ||
fa720bfb 388 al->request->flags.intercepted) && al->cache.port != NULL :
47176c0b 389 false;
28417506 390 if (interceptedAtKnownPort) {
4dd643d5 391 const bool portAddressConfigured = !al->cache.port->s.isAnyAddr();
28417506 392 if (portAddressConfigured)
4dd643d5 393 out = al->cache.port->s.toStr(tmp, sizeof(tmp));
28417506 394 } else if (al->tcpClient != NULL)
4dd643d5 395 out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
28417506
CT
396 }
397 break;
398
8652f8e7 399 case LFT_CLIENT_LOCAL_IP:
a14f38d0 400 if (al->tcpClient != NULL) {
4dd643d5 401 out = al->tcpClient->local.toStr(tmp,sizeof(tmp));
38e16f92 402 }
38e16f92
AJ
403 break;
404
f123f5e9
CT
405 case LFT_CLIENT_LOCAL_TOS:
406 if (al->tcpClient != NULL) {
407 snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->tcpClient->tos);
408 out = tmp;
409 }
410 break;
411
412 case LFT_CLIENT_LOCAL_NFMARK:
413 if (al->tcpClient != NULL) {
414 snprintf(tmp, sizeof(tmp), "0x%x", al->tcpClient->nfmark);
415 out = tmp;
416 }
417 break;
418
28417506 419 case LFT_LOCAL_LISTENING_PORT:
fa720bfb 420 if (al->cache.port != NULL) {
4dd643d5 421 outint = al->cache.port->s.port();
28417506
CT
422 doint = 1;
423 }
424 break;
425
8652f8e7 426 case LFT_CLIENT_LOCAL_PORT:
a14f38d0 427 if (al->tcpClient != NULL) {
4dd643d5 428 outint = al->tcpClient->local.port();
38e16f92
AJ
429 doint = 1;
430 }
38e16f92
AJ
431 break;
432
8652f8e7
AJ
433 case LFT_SERVER_LOCAL_IP_OLD_27:
434 case LFT_SERVER_LOCAL_IP:
a14f38d0 435 if (al->hier.tcpServer != NULL) {
4dd643d5 436 out = al->hier.tcpServer->local.toStr(tmp,sizeof(tmp));
38e16f92
AJ
437 }
438 break;
439
8652f8e7 440 case LFT_SERVER_LOCAL_PORT:
a14f38d0 441 if (al->hier.tcpServer != NULL) {
4dd643d5 442 outint = al->hier.tcpServer->local.port();
38e16f92
AJ
443 doint = 1;
444 }
445
446 break;
447
f123f5e9
CT
448 case LFT_SERVER_LOCAL_TOS:
449 if (al->hier.tcpServer != NULL) {
450 snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->hier.tcpServer->tos);
451 out = tmp;
452 }
453 break;
454
455 case LFT_SERVER_LOCAL_NFMARK:
456 if (al->hier.tcpServer != NULL) {
457 snprintf(tmp, sizeof(tmp), "0x%x", al->hier.tcpServer->nfmark);
458 out = tmp;
459 }
460 break;
461
38e16f92
AJ
462 case LFT_TIME_SECONDS_SINCE_EPOCH:
463 // some platforms store time in 32-bit, some 64-bit...
464 outoff = static_cast<int64_t>(current_time.tv_sec);
465 dooff = 1;
466 break;
467
468 case LFT_TIME_SUBSECOND:
469 outint = current_time.tv_usec / fmt->divisor;
470 doint = 1;
471 break;
472
38e16f92
AJ
473 case LFT_TIME_LOCALTIME:
474
475 case LFT_TIME_GMT: {
476 const char *spec;
477
478 struct tm *t;
b22c1ad3 479 spec = fmt->data.string;
38e16f92
AJ
480
481 if (fmt->type == LFT_TIME_LOCALTIME) {
482 if (!spec)
483 spec = "%d/%b/%Y:%H:%M:%S %z";
484 t = localtime(&squid_curtime);
485 } else {
486 if (!spec)
487 spec = "%d/%b/%Y:%H:%M:%S";
488
489 t = gmtime(&squid_curtime);
490 }
491
492 strftime(tmp, sizeof(tmp), spec, t);
493
494 out = tmp;
495 }
496
497 break;
498
af0ded40 499 case LFT_TIME_START: {
86c63190 500 int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
9989b942 501 snprintf(tmp, sizeof(tmp), "%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(al->cache.start_time.tv_sec), precision, (int)(al->cache.start_time.tv_usec / fmt->divisor));
af0ded40
CT
502 out = tmp;
503 }
9d4e9cfb 504 break;
af0ded40 505
38e16f92
AJ
506 case LFT_TIME_TO_HANDLE_REQUEST:
507 outint = al->cache.msec;
508 doint = 1;
509 break;
510
511 case LFT_PEER_RESPONSE_TIME:
512 if (al->hier.peer_response_time < 0) {
513 out = "-";
514 } else {
515 outoff = al->hier.peer_response_time;
516 dooff = 1;
517 }
518 break;
519
16b70e2a
CT
520 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
521 const int64_t total_response_time = al->hier.totalResponseTime();
522 if (total_response_time < 0) {
38e16f92
AJ
523 out = "-";
524 } else {
16b70e2a 525 outoff = total_response_time;
38e16f92
AJ
526 dooff = 1;
527 }
16b70e2a
CT
528 }
529 break;
38e16f92
AJ
530
531 case LFT_DNS_WAIT_TIME:
532 if (al->request && al->request->dnsWait >= 0) {
533 outint = al->request->dnsWait;
534 doint = 1;
535 }
536 break;
537
538 case LFT_REQUEST_HEADER:
539
540 if (al->request)
541 sb = al->request->header.getByName(fmt->data.header.header);
542
543 out = sb.termedBuf();
544
545 quote = 1;
546
547 break;
548
549 case LFT_ADAPTED_REQUEST_HEADER:
550
38d92afa 551 if (al->adapted_request)
38e16f92
AJ
552 sb = al->adapted_request->header.getByName(fmt->data.header.header);
553
554 out = sb.termedBuf();
555
556 quote = 1;
557
558 break;
559
560 case LFT_REPLY_HEADER:
561 if (al->reply)
562 sb = al->reply->header.getByName(fmt->data.header.header);
563
564 out = sb.termedBuf();
565
566 quote = 1;
567
568 break;
569
570#if USE_ADAPTATION
31971e6a 571 case LFT_ADAPTATION_SUM_XACT_TIMES:
38e16f92
AJ
572 if (al->request) {
573 Adaptation::History::Pointer ah = al->request->adaptHistory();
574 if (ah != NULL)
575 ah->sumLogString(fmt->data.string, sb);
576 out = sb.termedBuf();
577 }
578 break;
579
31971e6a 580 case LFT_ADAPTATION_ALL_XACT_TIMES:
38e16f92
AJ
581 if (al->request) {
582 Adaptation::History::Pointer ah = al->request->adaptHistory();
583 if (ah != NULL)
584 ah->allLogString(fmt->data.string, sb);
585 out = sb.termedBuf();
586 }
587 break;
588
589 case LFT_ADAPTATION_LAST_HEADER:
590 if (al->request) {
591 const Adaptation::History::Pointer ah = al->request->adaptHistory();
592 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
593 sb = ah->allMeta.getByName(fmt->data.header.header);
594 }
595
596 // XXX: here and elsewhere: move such code inside the if guard
597 out = sb.termedBuf();
598
599 quote = 1;
600
601 break;
602
603 case LFT_ADAPTATION_LAST_HEADER_ELEM:
604 if (al->request) {
605 const Adaptation::History::Pointer ah = al->request->adaptHistory();
606 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
607 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
608 }
609
610 out = sb.termedBuf();
611
612 quote = 1;
613
614 break;
615
616 case LFT_ADAPTATION_LAST_ALL_HEADERS:
617 out = al->adapt.last_meta;
618
619 quote = 1;
620
621 break;
622#endif
623
624#if ICAP_CLIENT
625 case LFT_ICAP_ADDR:
626 if (!out)
4dd643d5 627 out = al->icap.hostAddr.toStr(tmp,1024);
38e16f92
AJ
628 break;
629
630 case LFT_ICAP_SERV_NAME:
631 out = al->icap.serviceName.termedBuf();
632 break;
633
634 case LFT_ICAP_REQUEST_URI:
635 out = al->icap.reqUri.termedBuf();
636 break;
637
638 case LFT_ICAP_REQUEST_METHOD:
639 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
640 break;
641
642 case LFT_ICAP_BYTES_SENT:
643 outoff = al->icap.bytesSent;
644 dooff = 1;
645 break;
646
647 case LFT_ICAP_BYTES_READ:
648 outoff = al->icap.bytesRead;
649 dooff = 1;
650 break;
651
652 case LFT_ICAP_BODY_BYTES_READ:
653 if (al->icap.bodyBytesRead >= 0) {
654 outoff = al->icap.bodyBytesRead;
655 dooff = 1;
656 }
657 // else if icap.bodyBytesRead < 0, we do not have any http data,
658 // so just print a "-" (204 responses etc)
659 break;
660
661 case LFT_ICAP_REQ_HEADER:
662 if (NULL != al->icap.request) {
663 sb = al->icap.request->header.getByName(fmt->data.header.header);
664 out = sb.termedBuf();
665 quote = 1;
666 }
667 break;
668
669 case LFT_ICAP_REQ_HEADER_ELEM:
7f0ceafc 670 if (al->icap.request)
38e16f92
AJ
671 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
672
673 out = sb.termedBuf();
674
675 quote = 1;
676
677 break;
678
679 case LFT_ICAP_REQ_ALL_HEADERS:
680 if (al->icap.request) {
681 HttpHeaderPos pos = HttpHeaderInitPos;
682 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
683 sb.append(e->name);
684 sb.append(": ");
685 sb.append(e->value);
686 sb.append("\r\n");
687 }
688 out = sb.termedBuf();
689 quote = 1;
690 }
691 break;
692
693 case LFT_ICAP_REP_HEADER:
694 if (NULL != al->icap.reply) {
695 sb = al->icap.reply->header.getByName(fmt->data.header.header);
696 out = sb.termedBuf();
697 quote = 1;
698 }
699 break;
700
701 case LFT_ICAP_REP_HEADER_ELEM:
702 if (NULL != al->icap.reply)
703 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
704
705 out = sb.termedBuf();
706
707 quote = 1;
708
709 break;
710
711 case LFT_ICAP_REP_ALL_HEADERS:
712 if (al->icap.reply) {
713 HttpHeaderPos pos = HttpHeaderInitPos;
714 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
715 sb.append(e->name);
716 sb.append(": ");
717 sb.append(e->value);
718 sb.append("\r\n");
719 }
720 out = sb.termedBuf();
721 quote = 1;
722 }
723 break;
724
725 case LFT_ICAP_TR_RESPONSE_TIME:
726 outint = al->icap.trTime;
727 doint = 1;
728 break;
729
730 case LFT_ICAP_IO_TIME:
731 outint = al->icap.ioTime;
732 doint = 1;
733 break;
734
735 case LFT_ICAP_STATUS_CODE:
736 outint = al->icap.resStatus;
737 doint = 1;
738 break;
739
740 case LFT_ICAP_OUTCOME:
741 out = al->icap.outcome;
742 break;
743
744 case LFT_ICAP_TOTAL_TIME:
745 outint = al->icap.processingTime;
746 doint = 1;
747 break;
748#endif
749 case LFT_REQUEST_HEADER_ELEM:
750 if (al->request)
751 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
752
753 out = sb.termedBuf();
754
755 quote = 1;
756
757 break;
758
759 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
760 if (al->adapted_request)
761 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
762
763 out = sb.termedBuf();
764
765 quote = 1;
766
767 break;
768
769 case LFT_REPLY_HEADER_ELEM:
770 if (al->reply)
771 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
772
773 out = sb.termedBuf();
774
775 quote = 1;
776
777 break;
778
779 case LFT_REQUEST_ALL_HEADERS:
780 out = al->headers.request;
781
782 quote = 1;
783
784 break;
785
786 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
787 out = al->headers.adapted_request;
788
789 quote = 1;
790
791 break;
792
793 case LFT_REPLY_ALL_HEADERS:
794 out = al->headers.reply;
795
796 quote = 1;
797
798 break;
799
800 case LFT_USER_NAME:
c0e8c76f
AJ
801#if USE_AUTH
802 if (al->request && al->request->auth_user_request != NULL)
803 out = strOrNull(al->request->auth_user_request->username());
804#endif
38e16f92 805 if (!out)
bd85ea1f 806 out = strOrNull(al->cache.extuser);
cb4f4424 807#if USE_OPENSSL
38e16f92 808 if (!out)
bd85ea1f 809 out = strOrNull(al->cache.ssluser);
38e16f92 810#endif
38e16f92 811 if (!out)
bd85ea1f 812 out = strOrNull(al->cache.rfc931);
38e16f92
AJ
813 break;
814
815 case LFT_USER_LOGIN:
c0e8c76f
AJ
816#if USE_AUTH
817 if (al->request && al->request->auth_user_request != NULL)
818 out = strOrNull(al->request->auth_user_request->username());
819#endif
38e16f92
AJ
820 break;
821
822 case LFT_USER_IDENT:
bd85ea1f 823 out = strOrNull(al->cache.rfc931);
38e16f92
AJ
824 break;
825
826 case LFT_USER_EXTERNAL:
bd85ea1f 827 out = strOrNull(al->cache.extuser);
38e16f92
AJ
828 break;
829
830 /* case LFT_USER_REALM: */
831 /* case LFT_USER_SCHEME: */
832
833 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
834 // but compiler complains if ommited
835 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
836 case LFT_HTTP_SENT_STATUS_CODE:
837 outint = al->http.code;
838
839 doint = 1;
840
841 break;
842
843 case LFT_HTTP_RECEIVED_STATUS_CODE:
955394ce 844 if (al->hier.peer_reply_status == Http::scNone) {
38e16f92
AJ
845 out = "-";
846 } else {
847 outint = al->hier.peer_reply_status;
848 doint = 1;
849 }
850 break;
851 /* case LFT_HTTP_STATUS:
852 * out = statusline->text;
853 * quote = 1;
854 * break;
855 */
856 case LFT_HTTP_BODY_BYTES_READ:
857 if (al->hier.bodyBytesRead >= 0) {
858 outoff = al->hier.bodyBytesRead;
859 dooff = 1;
860 }
861 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
862 // a peer server so just print a "-" (eg requests served from cache,
863 // or internal error messages).
864 break;
865
866 case LFT_SQUID_STATUS:
867 if (al->http.timedout || al->http.aborted) {
02c8dde5 868 snprintf(tmp, sizeof(tmp), "%s%s", LogTags_str[al->cache.code],
38e16f92
AJ
869 al->http.statusSfx());
870 out = tmp;
871 } else {
02c8dde5 872 out = LogTags_str[al->cache.code];
38e16f92
AJ
873 }
874
875 break;
876
877 case LFT_SQUID_ERROR:
878 if (al->request && al->request->errType != ERR_NONE)
879 out = errorPageName(al->request->errType);
880 break;
881
882 case LFT_SQUID_ERROR_DETAIL:
cb4f4424 883#if USE_OPENSSL
2f3e52b5
CT
884 if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
885 if (! (out = Ssl::GetErrorName(al->request->errDetail))) {
886 snprintf(tmp, sizeof(tmp), "SSL_ERR=%d", al->request->errDetail);
887 out = tmp;
888 }
e83cdc25 889 } else
2f3e52b5 890#endif
e83cdc25
A
891 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
892 if (al->request->errDetail > ERR_DETAIL_START &&
893 al->request->errDetail < ERR_DETAIL_MAX)
894 out = errorDetailName(al->request->errDetail);
895 else {
896 if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
897 snprintf(tmp, sizeof(tmp), "%s=0x%X",
898 errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
899 else
900 snprintf(tmp, sizeof(tmp), "%s=%d",
901 errorDetailName(al->request->errDetail), al->request->errDetail);
902 out = tmp;
903 }
38e16f92 904 }
38e16f92
AJ
905 break;
906
907 case LFT_SQUID_HIERARCHY:
908 if (al->hier.ping.timedout)
909 mb.append("TIMEOUT_", 8);
910
911 out = hier_code_str[al->hier.code];
912
913 break;
914
915 case LFT_MIME_TYPE:
916 out = al->http.content_type;
917
918 break;
919
920 case LFT_CLIENT_REQ_METHOD:
921 if (al->request) {
7f06a3d8
AJ
922 const SBuf &s = al->request->method.image();
923 sb.append(s.rawContent(), s.length());
924 out = sb.termedBuf();
38e16f92
AJ
925 quote = 1;
926 }
927 break;
928
929 case LFT_CLIENT_REQ_URI:
930 // original client URI
931 if (al->request) {
932 out = urlCanonical(al->request);
933 quote = 1;
934 }
935 break;
936
5aca9cf2
AJ
937 case LFT_CLIENT_REQ_URLSCHEME:
938 if (al->request) {
939 out = al->request->url.getScheme().c_str();
940 quote = 1;
941 }
942 break;
943
fa450988
AJ
944 case LFT_CLIENT_REQ_URLDOMAIN:
945 if (al->request) {
946 out = al->request->GetHost();
947 quote = 1;
948 }
949 break;
950
5aca9cf2
AJ
951 case LFT_CLIENT_REQ_URLPORT:
952 if (al->request) {
953 outint = al->request->port;
954 doint = 1;
955 }
956 break;
957
38e16f92
AJ
958 case LFT_REQUEST_URLPATH_OLD_31:
959 case LFT_CLIENT_REQ_URLPATH:
960 if (al->request) {
961 out = al->request->urlpath.termedBuf();
962 quote = 1;
963 }
964 break;
965
966 case LFT_CLIENT_REQ_VERSION:
967 if (al->request) {
968 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
969 out = tmp;
970 }
971 break;
972
973 case LFT_REQUEST_METHOD:
7f06a3d8
AJ
974 if (al->_private.method_str) // ICP, HTCP method code
975 out = al->_private.method_str;
976 else {
977 const SBuf &s = al->http.method.image();
978 sb.append(s.rawContent(), s.length());
979 out = sb.termedBuf();
980 quote = 1;
981 }
38e16f92
AJ
982 break;
983
984 case LFT_REQUEST_URI:
985 out = al->url;
986 break;
987
988 case LFT_REQUEST_VERSION_OLD_2X:
989 case LFT_REQUEST_VERSION:
990 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
991 out = tmp;
992 break;
993
994 case LFT_SERVER_REQ_METHOD:
995 if (al->adapted_request) {
7f06a3d8
AJ
996 const SBuf &s = al->adapted_request->method.image();
997 sb.append(s.rawContent(), s.length());
998 out = sb.termedBuf();
38e16f92
AJ
999 quote = 1;
1000 }
1001 break;
1002
1003 case LFT_SERVER_REQ_URI:
1004 // adapted request URI sent to server/peer
1005 if (al->adapted_request) {
1006 out = urlCanonical(al->adapted_request);
1007 quote = 1;
1008 }
1009 break;
1010
5aca9cf2
AJ
1011 case LFT_SERVER_REQ_URLSCHEME:
1012 if (al->adapted_request) {
1013 out = al->adapted_request->url.getScheme().c_str();
1014 quote = 1;
1015 }
1016 break;
1017
1018 case LFT_SERVER_REQ_URLDOMAIN:
1019 if (al->adapted_request) {
1020 out = al->adapted_request->GetHost();
1021 quote = 1;
1022 }
1023 break;
1024
1025 case LFT_SERVER_REQ_URLPORT:
1026 if (al->adapted_request) {
1027 outint = al->adapted_request->port;
1028 doint = 1;
1029 }
1030 break;
1031
38e16f92
AJ
1032 case LFT_SERVER_REQ_URLPATH:
1033 if (al->adapted_request) {
1034 out = al->adapted_request->urlpath.termedBuf();
1035 quote = 1;
1036 }
1037 break;
1038
1039 case LFT_SERVER_REQ_VERSION:
1040 if (al->adapted_request) {
1041 snprintf(tmp, sizeof(tmp), "%d.%d",
1042 (int) al->adapted_request->http_ver.major,
1043 (int) al->adapted_request->http_ver.minor);
1044 out = tmp;
1045 }
1046 break;
1047
d6df21d2 1048 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
cc0ca3b9 1049 outoff = al->http.clientRequestSz.messageTotal();
38e16f92
AJ
1050 dooff = 1;
1051 break;
1052
d6df21d2 1053 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
cc0ca3b9 1054 outoff = al->http.clientRequestSz.header;
38e16f92
AJ
1055 dooff =1;
1056 break;
d6df21d2 1057
38e16f92
AJ
1058 /*case LFT_REQUEST_SIZE_BODY: */
1059 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1060
d6df21d2 1061 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
cc0ca3b9 1062 outoff = al->http.clientReplySz.messageTotal();
38e16f92
AJ
1063 dooff = 1;
1064 break;
1065
1066 case LFT_REPLY_HIGHOFFSET:
1067 outoff = al->cache.highOffset;
1068
1069 dooff = 1;
1070
1071 break;
1072
1073 case LFT_REPLY_OBJECTSIZE:
1074 outoff = al->cache.objectSize;
1075
1076 dooff = 1;
1077
1078 break;
1079
d6df21d2 1080 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
cc0ca3b9 1081 outint = al->http.clientReplySz.header;
38e16f92
AJ
1082 doint = 1;
1083 break;
d6df21d2 1084
38e16f92
AJ
1085 /*case LFT_REPLY_SIZE_BODY: */
1086 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1087
d6df21d2 1088 case LFT_CLIENT_IO_SIZE_TOTAL:
cc0ca3b9 1089 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
d6df21d2
AJ
1090 doint = 1;
1091 break;
c13630e2 1092 /*case LFT_SERVER_IO_SIZE_TOTAL: */
d6df21d2 1093
38e16f92
AJ
1094 case LFT_TAG:
1095 if (al->request)
1096 out = al->request->tag.termedBuf();
1097
1098 quote = 1;
1099
1100 break;
1101
38e16f92
AJ
1102 case LFT_EXT_LOG:
1103 if (al->request)
1104 out = al->request->extacl_log.termedBuf();
1105
1106 quote = 1;
1107
1108 break;
1109
1110 case LFT_SEQUENCE_NUMBER:
1111 outoff = logSequenceNumber;
1112 dooff = 1;
1113 break;
1114
cb4f4424 1115#if USE_OPENSSL
08097970
AR
1116 case LFT_SSL_BUMP_MODE: {
1117 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1118 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1119 out = Ssl::bumpMode(mode);
1120 break;
1121 }
71cae389 1122
f4698e0b
CT
1123 case LFT_SSL_USER_CERT_SUBJECT:
1124 if (X509 *cert = al->cache.sslClientCert.get()) {
1125 if (X509_NAME *subject = X509_get_subject_name(cert)) {
1126 X509_NAME_oneline(subject, tmp, sizeof(tmp));
1127 out = tmp;
1128 }
1129 }
1130 break;
1131
1132 case LFT_SSL_USER_CERT_ISSUER:
1133 if (X509 *cert = al->cache.sslClientCert.get()) {
1134 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
1135 X509_NAME_oneline(issuer, tmp, sizeof(tmp));
1136 out = tmp;
1137 }
1138 }
1139 break;
cedca6e7
CT
1140 case LFT_SSL_CLIENT_SNI:
1141 if (al->request && al->request->clientConnectionManager.valid()) {
1142 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1143 if (!srvBump->clientSni.isEmpty())
1144 out = srvBump->clientSni.c_str();
1145 }
1146 }
1147 break;
789dda8d
CT
1148
1149 case LFT_SSL_SERVER_CERT_ISSUER:
1150 case LFT_SSL_SERVER_CERT_SUBJECT:
1151 // Not implemented
1152 break;
08097970 1153#endif
f123f5e9 1154
d074f918
TT
1155 case LFT_REQUEST_URLGROUP_OLD_2X:
1156 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
1157
d7f4a0b7 1158 case LFT_NOTE:
c7bcf010
CT
1159 tmp[0] = fmt->data.header.separator;
1160 tmp[1] = '\0';
1161 if (fmt->data.header.header && *fmt->data.header.header) {
1162 const char *separator = tmp;
cf9f0261 1163#if USE_ADAPTATION
7f0ceafc 1164 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
cf9f0261 1165 if (ah != NULL && ah->metaHeaders != NULL) {
c7bcf010 1166 if (const char *meta = ah->metaHeaders->find(fmt->data.header.header, separator))
cf9f0261
CT
1167 sb.append(meta);
1168 }
1169#endif
f4f55a21 1170 if (al->notes != NULL) {
c7bcf010 1171 if (const char *note = al->notes->find(fmt->data.header.header, separator)) {
cf9f0261 1172 if (sb.size())
c7bcf010 1173 sb.append(separator);
cf9f0261
CT
1174 sb.append(note);
1175 }
1176 }
d7f4a0b7
CT
1177 out = sb.termedBuf();
1178 quote = 1;
1179 } else {
c7bcf010
CT
1180 // if no argument given use default "\r\n" as notes separator
1181 const char *separator = fmt->data.string ? tmp : "\r\n";
cf9f0261 1182#if USE_ADAPTATION
7f0ceafc 1183 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
cf9f0261 1184 if (ah != NULL && ah->metaHeaders != NULL && !ah->metaHeaders->empty())
c7bcf010 1185 sb.append(ah->metaHeaders->toString(separator));
cf9f0261 1186#endif
f4f55a21 1187 if (al->notes != NULL && !al->notes->empty())
c7bcf010 1188 sb.append(al->notes->toString(separator));
f4f55a21 1189
d7f4a0b7
CT
1190 out = sb.termedBuf();
1191 quote = 1;
1192 }
1193 break;
08097970 1194
d4806c91
CT
1195 case LFT_CREDENTIALS:
1196#if USE_AUTH
1197 if (al->request && al->request->auth_user_request != NULL)
1198 out = strOrNull(al->request->auth_user_request->credentialsStr());
1199#endif
1200
1201 break;
1202
38e16f92
AJ
1203 case LFT_PERCENT:
1204 out = "%";
5aca9cf2 1205 break;
38e16f92 1206
e2849af8
A
1207 // XXX: external_acl_type format tokens which are not output by logformat.
1208 // They are listed here because the switch requires
1209 // every ByteCode_t to be explicitly enumerated.
1210 // But do not output due to lack of access to the values.
5aca9cf2
AJ
1211 case LFT_EXT_ACL_USER_CERT_RAW:
1212 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1213 case LFT_EXT_ACL_USER_CERT:
1214 case LFT_EXT_ACL_USER_CA_CERT:
1215 case LFT_EXT_ACL_CLIENT_EUI48:
1216 case LFT_EXT_ACL_CLIENT_EUI64:
1217 case LFT_EXT_ACL_NAME:
1218 case LFT_EXT_ACL_DATA:
38e16f92
AJ
1219 break;
1220 }
1221
1222 if (dooff) {
8846a2b4 1223 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
38e16f92
AJ
1224 out = tmp;
1225
1226 } else if (doint) {
8846a2b4 1227 snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
38e16f92
AJ
1228 out = tmp;
1229 }
1230
1231 if (out && *out) {
1232 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1233 char *newout = NULL;
1234 int newfree = 0;
1235
1236 switch (fmt->quote) {
1237
1238 case LOG_QUOTE_NONE:
1239 newout = rfc1738_escape_unescaped(out);
1240 break;
1241
1242 case LOG_QUOTE_QUOTES: {
1243 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1244 if (out_len >= sizeof(tmp)) {
1245 newout = (char *)xmalloc(out_len);
1246 newfree = 1;
1247 } else
1248 newout = tmp;
1249 log_quoted_string(out, newout);
1250 }
1251 break;
1252
1253 case LOG_QUOTE_MIMEBLOB:
1254 newout = QuoteMimeBlob(out);
1255 newfree = 1;
1256 break;
1257
1258 case LOG_QUOTE_URL:
1259 newout = rfc1738_escape(out);
1260 break;
1261
1262 case LOG_QUOTE_RAW:
1263 break;
1264 }
1265
1266 if (newout) {
1267 if (dofree)
1268 safe_free(out);
1269
1270 out = newout;
1271
1272 dofree = newfree;
1273 }
1274 }
1275
c32c6db7 1276 // enforce width limits if configured
af0ded40 1277 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !fmt->divisor;
8846a2b4
CT
1278 if (haveMaxWidth || fmt->widthMin) {
1279 const int minWidth = fmt->widthMin >= 0 ?
b8ad91f5 1280 fmt->widthMin :0;
c32c6db7 1281 const int maxWidth = haveMaxWidth ?
8846a2b4 1282 fmt->widthMax : strlen(out);
c32c6db7 1283
38e16f92 1284 if (fmt->left)
c32c6db7 1285 mb.Printf("%-*.*s", minWidth, maxWidth, out);
38e16f92 1286 else
c32c6db7 1287 mb.Printf("%*.*s", minWidth, maxWidth, out);
38e16f92
AJ
1288 } else
1289 mb.append(out, strlen(out));
1290 } else {
1291 mb.append("-", 1);
1292 }
1293
1294 if (fmt->space)
1295 mb.append(" ", 1);
1296
1297 sb.clean();
1298
1299 if (dofree)
1300 safe_free(out);
1301 }
1302}