]> git.ipfire.org Git - thirdparty/squid.git/blob - src/format/Format.cc
Fix and improve annotation reporting (#1516)
[thirdparty/squid.git] / src / format / Format.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #include "squid.h"
10 #include "AccessLogEntry.h"
11 #include "base64.h"
12 #include "client_side.h"
13 #include "comm/Connection.h"
14 #include "error/Detail.h"
15 #include "errorpage.h"
16 #include "fde.h"
17 #include "format/Format.h"
18 #include "format/Quoting.h"
19 #include "format/Token.h"
20 #include "http/Stream.h"
21 #include "HttpRequest.h"
22 #include "MemBuf.h"
23 #include "proxyp/Header.h"
24 #include "rfc1738.h"
25 #include "sbuf/Stream.h"
26 #include "sbuf/StringConvert.h"
27 #include "security/CertError.h"
28 #include "security/Certificate.h"
29 #include "security/NegotiationHistory.h"
30 #include "Store.h"
31 #include "tools.h"
32 #if USE_OPENSSL
33 #include "ssl/ErrorDetail.h"
34 #include "ssl/ServerBump.h"
35 #endif
36
37 /// Convert a string to NULL pointer if it is ""
38 #define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
39
40 const SBuf Format::Dash("-");
41
42 Format::Format::Format(const char *n) :
43 format(nullptr),
44 next(nullptr)
45 {
46 name = xstrdup(n);
47 }
48
49 Format::Format::~Format()
50 {
51 // erase the list without consuming stack space
52 while (next) {
53 // unlink the next entry for deletion
54 Format *temp = next;
55 next = temp->next;
56 temp->next = nullptr;
57 delete temp;
58 }
59
60 // remove locals
61 xfree(name);
62 delete format;
63 }
64
65 bool
66 Format::Format::parse(const char *def)
67 {
68 const char *cur, *eos;
69 Token *new_lt, *last_lt;
70 enum Quoting quote = LOG_QUOTE_NONE;
71
72 debugs(46, 2, "got definition '" << def << "'");
73
74 if (format) {
75 debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
76 return false;
77 }
78
79 /* very inefficient parser, but who cares, this needs to be simple */
80 /* First off, let's tokenize, we'll optimize in a second pass.
81 * A token can either be a %-prefixed sequence (usually a dynamic
82 * token but it can be an escaped sequence), or a string. */
83 cur = def;
84 eos = def + strlen(def);
85 format = new_lt = last_lt = new Token;
86 cur += new_lt->parse(cur, &quote);
87
88 while (cur < eos) {
89 new_lt = new Token;
90 last_lt->next = new_lt;
91 last_lt = new_lt;
92 cur += new_lt->parse(cur, &quote);
93 }
94
95 return true;
96 }
97
98 size_t
99 Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
100 {
101 Token tkn;
102 enum Quoting quote = LOG_QUOTE_NONE;
103 const auto tokenSize = tkn.parse(token, &quote);
104 assert(tokenSize > 0);
105 if (ale != nullptr) {
106 Format fmt("SimpleToken");
107 fmt.format = &tkn;
108 fmt.assemble(mb, ale, 0);
109 fmt.format = nullptr;
110 } else {
111 mb.append("-", 1);
112 }
113 return static_cast<size_t>(tokenSize);
114 }
115
116 void
117 Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
118 {
119 debugs(46, 4, MYNAME);
120
121 // loop rather than recursing to conserve stack space.
122 for (const Format *fmt = this; fmt; fmt = fmt->next) {
123 debugs(46, 3, "Dumping format definition for " << fmt->name);
124 if (directiveName)
125 storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
126
127 for (Token *t = fmt->format; t; t = t->next) {
128 if (t->type == LFT_STRING)
129 storeAppendPrintf(entry, "%s", t->data.string);
130 else {
131 char argbuf[256];
132 char *arg = nullptr;
133 ByteCode_t type = t->type;
134
135 switch (type) {
136 /* special cases */
137
138 case LFT_STRING:
139 break;
140 #if USE_ADAPTATION
141 case LFT_ADAPTATION_LAST_HEADER_ELEM:
142 #endif
143 #if ICAP_CLIENT
144 case LFT_ICAP_REQ_HEADER_ELEM:
145 case LFT_ICAP_REP_HEADER_ELEM:
146 #endif
147 case LFT_REQUEST_HEADER_ELEM:
148 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
149 case LFT_REPLY_HEADER_ELEM:
150
151 if (t->data.header.separator != ',')
152 snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
153 else
154 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
155
156 arg = argbuf;
157
158 switch (type) {
159 case LFT_REQUEST_HEADER_ELEM:
160 type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
161 break;
162 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
163 type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
164 break;
165 case LFT_REPLY_HEADER_ELEM:
166 type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM?
167 break;
168 #if USE_ADAPTATION
169 case LFT_ADAPTATION_LAST_HEADER_ELEM:
170 type = LFT_ADAPTATION_LAST_HEADER;
171 break;
172 #endif
173 #if ICAP_CLIENT
174 case LFT_ICAP_REQ_HEADER_ELEM:
175 type = LFT_ICAP_REQ_HEADER;
176 break;
177 case LFT_ICAP_REP_HEADER_ELEM:
178 type = LFT_ICAP_REP_HEADER;
179 break;
180 #endif
181 default:
182 break;
183 }
184
185 break;
186
187 case LFT_REQUEST_ALL_HEADERS:
188 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
189 case LFT_REPLY_ALL_HEADERS:
190
191 #if USE_ADAPTATION
192 case LFT_ADAPTATION_LAST_ALL_HEADERS:
193 #endif
194 #if ICAP_CLIENT
195 case LFT_ICAP_REQ_ALL_HEADERS:
196 case LFT_ICAP_REP_ALL_HEADERS:
197 #endif
198
199 switch (type) {
200 case LFT_REQUEST_ALL_HEADERS:
201 type = LFT_REQUEST_HEADER;
202 break;
203 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
204 type = LFT_ADAPTED_REQUEST_HEADER;
205 break;
206 case LFT_REPLY_ALL_HEADERS:
207 type = LFT_REPLY_HEADER;
208 break;
209 #if USE_ADAPTATION
210 case LFT_ADAPTATION_LAST_ALL_HEADERS:
211 type = LFT_ADAPTATION_LAST_HEADER;
212 break;
213 #endif
214 #if ICAP_CLIENT
215 case LFT_ICAP_REQ_ALL_HEADERS:
216 type = LFT_ICAP_REQ_HEADER;
217 break;
218 case LFT_ICAP_REP_ALL_HEADERS:
219 type = LFT_ICAP_REP_HEADER;
220 break;
221 #endif
222 default:
223 break;
224 }
225
226 break;
227
228 default:
229 if (t->data.string)
230 arg = t->data.string;
231
232 break;
233 }
234
235 entry->append("%", 1);
236
237 switch (t->quote) {
238
239 case LOG_QUOTE_QUOTES:
240 entry->append("\"", 1);
241 break;
242
243 case LOG_QUOTE_MIMEBLOB:
244 entry->append("[", 1);
245 break;
246
247 case LOG_QUOTE_URL:
248 entry->append("#", 1);
249 break;
250
251 case LOG_QUOTE_RAW:
252 entry->append("'", 1);
253 break;
254
255 case LOG_QUOTE_SHELL:
256 entry->append("/", 1);
257 break;
258
259 case LOG_QUOTE_NONE:
260 break;
261 }
262
263 if (t->left)
264 entry->append("-", 1);
265
266 if (t->zero)
267 entry->append("0", 1);
268
269 if (t->widthMin >= 0)
270 storeAppendPrintf(entry, "%d", t->widthMin);
271
272 if (t->widthMax >= 0)
273 storeAppendPrintf(entry, ".%d", t->widthMax);
274
275 if (arg)
276 storeAppendPrintf(entry, "{%s}", arg);
277
278 storeAppendPrintf(entry, "%s", t->label);
279
280 if (t->space)
281 entry->append(" ", 1);
282 }
283 }
284
285 if (eol)
286 entry->append("\n", 1);
287 }
288
289 }
290
291 static void
292 log_quoted_string(const char *str, char *out)
293 {
294 char *p = out;
295
296 while (*str) {
297 int l = strcspn(str, "\"\\\r\n\t");
298 memcpy(p, str, l);
299 str += l;
300 p += l;
301
302 switch (*str) {
303
304 case '\0':
305 break;
306
307 case '\r':
308 *p = '\\';
309 ++p;
310 *p = 'r';
311 ++p;
312 ++str;
313 break;
314
315 case '\n':
316 *p = '\\';
317 ++p;
318 *p = 'n';
319 ++p;
320 ++str;
321 break;
322
323 case '\t':
324 *p = '\\';
325 ++p;
326 *p = 't';
327 ++p;
328 ++str;
329 break;
330
331 default:
332 *p = '\\';
333 ++p;
334 *p = *str;
335 ++p;
336 ++str;
337 break;
338 }
339 }
340
341 *p = '\0';
342 }
343
344 /// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
345 /// headers from sent headers rather than failing to distinguish requests from responses.
346 /// \retval HttpReply sent to the HTTP client (access.log and default context).
347 /// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
348 /// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
349 static const Http::Message *
350 actualReplyHeader(const AccessLogEntry::Pointer &al)
351 {
352 const Http::Message *msg = al->reply.getRaw();
353 #if ICAP_CLIENT
354 // al->icap.reqMethod is methodNone in access.log context
355 if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
356 msg = al->adapted_request;
357 #endif
358 return msg;
359 }
360
361 /// XXX: Misnamed. See actualReplyHeader().
362 /// \return HttpRequest or HttpReply for %http::>h.
363 static const Http::Message *
364 actualRequestHeader(const AccessLogEntry::Pointer &al)
365 {
366 #if ICAP_CLIENT
367 // al->icap.reqMethod is methodNone in access.log context
368 if (al->icap.reqMethod == Adaptation::methodRespmod) {
369 // XXX: for now AccessLogEntry lacks virgin response headers
370 return nullptr;
371 }
372 #endif
373 return al->request;
374 }
375
376 void
377 Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
378 {
379 static char tmp[1024];
380 SBuf sb;
381
382 for (Token *fmt = format; fmt; fmt = fmt->next) { /* for each token */
383 const char *out = nullptr;
384 int quote = 0;
385 long int outint = 0;
386 int doint = 0;
387 int dofree = 0;
388 int64_t outoff = 0;
389 int dooff = 0;
390 struct timeval outtv = {};
391 int doMsec = 0;
392 int doSec = 0;
393 bool doUint64 = false;
394 uint64_t outUint64 = 0;
395
396 switch (fmt->type) {
397
398 case LFT_NONE:
399 out = "";
400 break;
401
402 case LFT_BYTE:
403 tmp[0] = static_cast<char>(fmt->data.byteValue);
404 tmp[1] = '\0';
405 out = tmp;
406 break;
407
408 case LFT_STRING:
409 out = fmt->data.string;
410 break;
411
412 case LFT_CLIENT_IP_ADDRESS:
413 al->getLogClientIp(tmp, sizeof(tmp));
414 out = tmp;
415 break;
416
417 case LFT_CLIENT_FQDN:
418 out = al->getLogClientFqdn(tmp, sizeof(tmp));
419 break;
420
421 case LFT_CLIENT_PORT:
422 if (al->request) {
423 outint = al->request->client_addr.port();
424 doint = 1;
425 } else if (al->tcpClient) {
426 outint = al->tcpClient->remote.port();
427 doint = 1;
428 }
429 break;
430
431 case LFT_CLIENT_EUI:
432 #if USE_SQUID_EUI
433 // TODO make the ACL checklist have a direct link to any TCP details.
434 if (al->request && al->request->clientConnectionManager.valid() &&
435 al->request->clientConnectionManager->clientConnection) {
436 const auto &conn = al->request->clientConnectionManager->clientConnection;
437 if (conn->remote.isIPv4())
438 conn->remoteEui48.encode(tmp, sizeof(tmp));
439 else
440 conn->remoteEui64.encode(tmp, sizeof(tmp));
441 out = tmp;
442 }
443 #endif
444 break;
445
446 case LFT_EXT_ACL_CLIENT_EUI48:
447 #if USE_SQUID_EUI
448 if (al->request && al->request->clientConnectionManager.valid() &&
449 al->request->clientConnectionManager->clientConnection &&
450 al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
451 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
452 out = tmp;
453 }
454 #endif
455 break;
456
457 case LFT_EXT_ACL_CLIENT_EUI64:
458 #if USE_SQUID_EUI
459 if (al->request && al->request->clientConnectionManager.valid() &&
460 al->request->clientConnectionManager->clientConnection &&
461 !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
462 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
463 out = tmp;
464 }
465 #endif
466 break;
467
468 case LFT_SERVER_IP_ADDRESS:
469 if (al->hier.tcpServer)
470 out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
471 break;
472
473 case LFT_SERVER_FQDN_OR_PEER_NAME:
474 out = al->hier.host;
475 break;
476
477 case LFT_SERVER_PORT:
478 if (al->hier.tcpServer) {
479 outint = al->hier.tcpServer->remote.port();
480 doint = 1;
481 }
482 break;
483
484 case LFT_LOCAL_LISTENING_IP:
485 if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
486 out = addr->toStr(tmp, sizeof(tmp));
487 break;
488
489 case LFT_CLIENT_LOCAL_IP:
490 if (al->tcpClient)
491 out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
492 break;
493
494 case LFT_CLIENT_LOCAL_TOS:
495 if (al->tcpClient) {
496 sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
497 out = sb.c_str();
498 }
499 break;
500
501 case LFT_TRANSPORT_CLIENT_CONNECTION_ID:
502 if (al->tcpClient) {
503 outUint64 = al->tcpClient->id.value;
504 doUint64 = true;
505 }
506 break;
507
508 case LFT_CLIENT_LOCAL_NFMARK:
509 if (al->tcpClient) {
510 sb.appendf("0x%x", al->tcpClient->nfmark);
511 out = sb.c_str();
512 }
513 break;
514
515 case LFT_LOCAL_LISTENING_PORT:
516 if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
517 outint = *port;
518 doint = 1;
519 }
520 break;
521
522 case LFT_CLIENT_LOCAL_PORT:
523 if (al->tcpClient) {
524 outint = al->tcpClient->local.port();
525 doint = 1;
526 }
527 break;
528
529 case LFT_SERVER_LOCAL_IP_OLD_27:
530 case LFT_SERVER_LOCAL_IP:
531 if (al->hier.tcpServer)
532 out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
533 break;
534
535 case LFT_SERVER_LOCAL_PORT:
536 if (al->hier.tcpServer) {
537 outint = al->hier.tcpServer->local.port();
538 doint = 1;
539 }
540 break;
541
542 case LFT_SERVER_LOCAL_TOS:
543 if (al->hier.tcpServer) {
544 sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
545 out = sb.c_str();
546 }
547 break;
548
549 case LFT_SERVER_LOCAL_NFMARK:
550 if (al->hier.tcpServer) {
551 sb.appendf("0x%x", al->hier.tcpServer->nfmark);
552 out = sb.c_str();
553 }
554 break;
555
556 case LFT_CLIENT_HANDSHAKE:
557 if (al->request && al->request->clientConnectionManager.valid()) {
558 const auto &handshake = al->request->clientConnectionManager->preservedClientData;
559 if (const auto rawLength = handshake.length()) {
560 // add 1 byte to optimize the c_str() conversion below
561 char *buf = sb.rawAppendStart(base64_encode_len(rawLength) + 1);
562
563 struct base64_encode_ctx ctx;
564 base64_encode_init(&ctx);
565 auto encLength = base64_encode_update(&ctx, buf, rawLength, reinterpret_cast<const uint8_t*>(handshake.rawContent()));
566 encLength += base64_encode_final(&ctx, buf + encLength);
567
568 sb.rawAppendFinish(buf, encLength);
569 out = sb.c_str();
570 }
571 }
572 break;
573
574 case LFT_TIME_SECONDS_SINCE_EPOCH:
575 // some platforms store time in 32-bit, some 64-bit...
576 outoff = static_cast<int64_t>(current_time.tv_sec);
577 dooff = 1;
578 break;
579
580 case LFT_TIME_SUBSECOND:
581 outint = current_time.tv_usec / fmt->divisor;
582 doint = 1;
583 break;
584
585 case LFT_TIME_LOCALTIME:
586 case LFT_TIME_GMT: {
587 const char *spec;
588 struct tm *t;
589 spec = fmt->data.string;
590
591 if (fmt->type == LFT_TIME_LOCALTIME) {
592 if (!spec)
593 spec = "%d/%b/%Y:%H:%M:%S %z";
594 t = localtime(&squid_curtime);
595 } else {
596 if (!spec)
597 spec = "%d/%b/%Y:%H:%M:%S";
598
599 t = gmtime(&squid_curtime);
600 }
601
602 strftime(tmp, sizeof(tmp), spec, t);
603 out = tmp;
604 }
605 break;
606
607 case LFT_TIME_START:
608 outtv = al->cache.start_time;
609 doSec = 1;
610 break;
611
612 case LFT_BUSY_TIME: {
613 const auto &stopwatch = al->busyTime;
614 if (stopwatch.ran()) {
615 // make sure total() returns nanoseconds compatible with outoff
616 using nanos = std::chrono::duration<decltype(outoff), std::nano>;
617 const nanos n = stopwatch.total();
618 outoff = n.count();
619 dooff = true;
620 }
621 }
622 break;
623
624 case LFT_TIME_TO_HANDLE_REQUEST:
625 outtv = al->cache.trTime;
626 doMsec = 1;
627 break;
628
629 case LFT_PEER_RESPONSE_TIME:
630 struct timeval peerResponseTime;
631 if (al->hier.peerResponseTime(peerResponseTime)) {
632 outtv = peerResponseTime;
633 doMsec = 1;
634 }
635 break;
636
637 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
638 struct timeval totalResponseTime;
639 if (al->hier.totalResponseTime(totalResponseTime)) {
640 outtv = totalResponseTime;
641 doMsec = 1;
642 }
643 }
644 break;
645
646 case LFT_DNS_WAIT_TIME:
647 if (al->request && al->request->dnsWait >= 0) {
648 // TODO: microsecond precision for dns wait time.
649 // Convert milliseconds to timeval struct:
650 outtv.tv_sec = al->request->dnsWait / 1000;
651 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
652 doMsec = 1;
653 }
654 break;
655
656 case LFT_REQUEST_HEADER:
657 if (const Http::Message *msg = actualRequestHeader(al)) {
658 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
659 out = sb.c_str();
660 quote = 1;
661 }
662 break;
663
664 case LFT_ADAPTED_REQUEST_HEADER:
665 if (al->adapted_request) {
666 sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
667 out = sb.c_str();
668 quote = 1;
669 }
670 break;
671
672 case LFT_REPLY_HEADER:
673 if (const Http::Message *msg = actualReplyHeader(al)) {
674 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
675 out = sb.c_str();
676 quote = 1;
677 }
678 break;
679
680 #if USE_ADAPTATION
681 case LFT_ADAPTATION_SUM_XACT_TIMES:
682 if (al->request) {
683 Adaptation::History::Pointer ah = al->request->adaptHistory();
684 if (ah) {
685 ah->sumLogString(fmt->data.string, sb);
686 out = sb.c_str();
687 }
688 }
689 break;
690
691 case LFT_ADAPTATION_ALL_XACT_TIMES:
692 if (al->request) {
693 Adaptation::History::Pointer ah = al->request->adaptHistory();
694 if (ah) {
695 ah->allLogString(fmt->data.string, sb);
696 out = sb.c_str();
697 }
698 }
699 break;
700
701 case LFT_ADAPTATION_LAST_HEADER:
702 if (al->request) {
703 const Adaptation::History::Pointer ah = al->request->adaptHistory();
704 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
705 sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
706 out = sb.c_str();
707 quote = 1;
708 }
709 }
710 break;
711
712 case LFT_ADAPTATION_LAST_HEADER_ELEM:
713 if (al->request) {
714 const Adaptation::History::Pointer ah = al->request->adaptHistory();
715 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
716 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
717 out = sb.c_str();
718 quote = 1;
719 }
720 }
721 break;
722
723 case LFT_ADAPTATION_LAST_ALL_HEADERS:
724 out = al->adapt.last_meta;
725 quote = 1;
726 break;
727 #endif
728
729 #if ICAP_CLIENT
730 case LFT_ICAP_ADDR:
731 out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
732 break;
733
734 case LFT_ICAP_SERV_NAME:
735 out = al->icap.serviceName.termedBuf();
736 break;
737
738 case LFT_ICAP_REQUEST_URI:
739 out = al->icap.reqUri.termedBuf();
740 break;
741
742 case LFT_ICAP_REQUEST_METHOD:
743 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
744 break;
745
746 case LFT_ICAP_BYTES_SENT:
747 outoff = al->icap.bytesSent;
748 dooff = 1;
749 break;
750
751 case LFT_ICAP_BYTES_READ:
752 outoff = al->icap.bytesRead;
753 dooff = 1;
754 break;
755
756 case LFT_ICAP_BODY_BYTES_READ:
757 if (al->icap.bodyBytesRead >= 0) {
758 outoff = al->icap.bodyBytesRead;
759 dooff = 1;
760 }
761 // else if icap.bodyBytesRead < 0, we do not have any http data,
762 // so just print a "-" (204 responses etc)
763 break;
764
765 case LFT_ICAP_REQ_HEADER:
766 if (al->icap.request) {
767 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
768 out = sb.c_str();
769 quote = 1;
770 }
771 break;
772
773 case LFT_ICAP_REQ_HEADER_ELEM:
774 if (al->icap.request) {
775 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
776 out = sb.c_str();
777 quote = 1;
778 }
779 break;
780
781 case LFT_ICAP_REQ_ALL_HEADERS:
782 if (al->icap.request) {
783 HttpHeaderPos pos = HttpHeaderInitPos;
784 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
785 sb.append(e->name);
786 sb.append(": ");
787 sb.append(StringToSBuf(e->value));
788 sb.append("\r\n");
789 }
790 out = sb.c_str();
791 quote = 1;
792 }
793 break;
794
795 case LFT_ICAP_REP_HEADER:
796 if (al->icap.reply) {
797 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
798 out = sb.c_str();
799 quote = 1;
800 }
801 break;
802
803 case LFT_ICAP_REP_HEADER_ELEM:
804 if (al->icap.reply) {
805 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
806 out = sb.c_str();
807 quote = 1;
808 }
809 break;
810
811 case LFT_ICAP_REP_ALL_HEADERS:
812 if (al->icap.reply) {
813 HttpHeaderPos pos = HttpHeaderInitPos;
814 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
815 sb.append(e->name);
816 sb.append(": ");
817 sb.append(StringToSBuf(e->value));
818 sb.append("\r\n");
819 }
820 out = sb.c_str();
821 quote = 1;
822 }
823 break;
824
825 case LFT_ICAP_TR_RESPONSE_TIME:
826 outtv = al->icap.trTime;
827 doMsec = 1;
828 break;
829
830 case LFT_ICAP_IO_TIME:
831 outtv = al->icap.ioTime;
832 doMsec = 1;
833 break;
834
835 case LFT_ICAP_STATUS_CODE:
836 outint = al->icap.resStatus;
837 doint = 1;
838 break;
839
840 case LFT_ICAP_OUTCOME:
841 out = al->icap.outcome;
842 break;
843
844 case LFT_ICAP_TOTAL_TIME:
845 outtv = al->icap.processingTime;
846 doMsec = 1;
847 break;
848 #endif
849 case LFT_REQUEST_HEADER_ELEM:
850 if (const Http::Message *msg = actualRequestHeader(al)) {
851 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
852 out = sb.c_str();
853 quote = 1;
854 }
855 break;
856
857 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
858 if (al->proxyProtocolHeader) {
859 sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
860 out = sb.c_str();
861 quote = 1;
862 }
863 break;
864
865 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
866 if (al->proxyProtocolHeader) {
867 sb = al->proxyProtocolHeader->toMime();
868 out = sb.c_str();
869 quote = 1;
870 }
871 break;
872
873 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
874 if (al->proxyProtocolHeader) {
875 sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
876 out = sb.c_str();
877 quote = 1;
878 }
879 break;
880
881 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
882 if (al->adapted_request) {
883 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
884 out = sb.c_str();
885 quote = 1;
886 }
887 break;
888
889 case LFT_REPLY_HEADER_ELEM:
890 if (const Http::Message *msg = actualReplyHeader(al)) {
891 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
892 out = sb.c_str();
893 quote = 1;
894 }
895 break;
896
897 case LFT_REQUEST_ALL_HEADERS:
898 #if ICAP_CLIENT
899 if (al->icap.reqMethod == Adaptation::methodRespmod) {
900 // XXX: since AccessLogEntry::Headers lacks virgin response
901 // headers, do nothing for now
902 out = nullptr;
903 } else
904 #endif
905 {
906 // just headers without start-line and CRLF
907 // XXX: reconcile with '<h'
908 out = al->headers.request;
909 quote = 1;
910 }
911 break;
912
913 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
914 // just headers without start-line and CRLF
915 // XXX: reconcile with '<h'
916 out = al->headers.adapted_request;
917 quote = 1;
918 break;
919
920 case LFT_REPLY_ALL_HEADERS: {
921 MemBuf allHeaders;
922 allHeaders.init();
923 // status-line + headers + CRLF
924 // XXX: reconcile with '>h' and '>ha'
925 al->packReplyHeaders(allHeaders);
926 sb.assign(allHeaders.content(), allHeaders.contentSize());
927 out = sb.c_str();
928 #if ICAP_CLIENT
929 if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
930 out = al->headers.adapted_request;
931 #endif
932 quote = 1;
933 }
934 break;
935
936 case LFT_USER_NAME:
937 #if USE_AUTH
938 if (al->request && al->request->auth_user_request)
939 out = strOrNull(al->request->auth_user_request->username());
940 #endif
941 if (!out && al->request && al->request->extacl_user.size()) {
942 if (const char *t = al->request->extacl_user.termedBuf())
943 out = t;
944 }
945 if (!out)
946 out = strOrNull(al->getExtUser());
947 #if USE_OPENSSL
948 if (!out)
949 out = strOrNull(al->cache.ssluser);
950 #endif
951 if (!out)
952 out = strOrNull(al->getClientIdent());
953 break;
954
955 case LFT_USER_LOGIN:
956 #if USE_AUTH
957 if (al->request && al->request->auth_user_request)
958 out = strOrNull(al->request->auth_user_request->username());
959 #endif
960 break;
961
962 case LFT_USER_IDENT:
963 out = strOrNull(al->getClientIdent());
964 break;
965
966 case LFT_USER_EXTERNAL:
967 out = strOrNull(al->getExtUser());
968 break;
969
970 /* case LFT_USER_REALM: */
971 /* case LFT_USER_SCHEME: */
972
973 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
974 // but compiler complains if omitted
975 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
976 case LFT_HTTP_SENT_STATUS_CODE:
977 outint = al->http.code;
978 doint = 1;
979 break;
980
981 case LFT_HTTP_RECEIVED_STATUS_CODE:
982 if (al->hier.peer_reply_status != Http::scNone) {
983 outint = al->hier.peer_reply_status;
984 doint = 1;
985 }
986 break;
987 /* case LFT_HTTP_STATUS:
988 * out = statusline->text;
989 * quote = 1;
990 * break;
991 */
992 case LFT_HTTP_BODY_BYTES_READ:
993 if (al->hier.bodyBytesRead >= 0) {
994 outoff = al->hier.bodyBytesRead;
995 dooff = 1;
996 }
997 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
998 // a peer server so just print a "-" (eg requests served from cache,
999 // or internal error messages).
1000 break;
1001
1002 case LFT_SQUID_STATUS:
1003 out = al->cache.code.c_str();
1004 break;
1005
1006 case LFT_SQUID_ERROR:
1007 if (const auto error = al->error())
1008 out = errorPageName(error->category);
1009 break;
1010
1011 case LFT_SQUID_ERROR_DETAIL:
1012 if (const auto error = al->error()) {
1013 if (!error->details.empty()) {
1014 sb = ToSBuf(error->details);
1015 out = sb.c_str();
1016 }
1017 }
1018 break;
1019
1020 case LFT_SQUID_HIERARCHY:
1021 if (al->hier.ping.timedout)
1022 mb.append("TIMEOUT_", 8);
1023 out = hier_code_str[al->hier.code];
1024 break;
1025
1026 case LFT_SQUID_REQUEST_ATTEMPTS:
1027 outint = al->requestAttempts;
1028 doint = 1;
1029 break;
1030
1031 case LFT_MIME_TYPE:
1032 out = al->http.content_type;
1033 break;
1034
1035 case LFT_CLIENT_REQ_METHOD:
1036 if (al->request) {
1037 sb = al->request->method.image();
1038 out = sb.c_str();
1039 quote = 1;
1040 }
1041 break;
1042
1043 case LFT_CLIENT_REQ_URI:
1044 if (const auto uri = al->effectiveVirginUrl()) {
1045 sb = *uri;
1046 out = sb.c_str();
1047 quote = 1;
1048 }
1049 break;
1050
1051 case LFT_CLIENT_REQ_URLSCHEME:
1052 if (al->request) {
1053 sb = al->request->url.getScheme().image();
1054 out = sb.c_str();
1055 quote = 1;
1056 }
1057 break;
1058
1059 case LFT_CLIENT_REQ_URLDOMAIN:
1060 if (al->request) {
1061 out = al->request->url.host();
1062 quote = 1;
1063 }
1064 break;
1065
1066 case LFT_CLIENT_REQ_URLPORT:
1067 if (al->request && al->request->url.port()) {
1068 outint = *al->request->url.port();
1069 doint = 1;
1070 }
1071 break;
1072
1073 case LFT_REQUEST_URLPATH_OLD_31:
1074 case LFT_CLIENT_REQ_URLPATH:
1075 if (al->request) {
1076 sb = al->request->url.path();
1077 out = sb.c_str();
1078 quote = 1;
1079 }
1080 break;
1081
1082 case LFT_CLIENT_REQ_VERSION:
1083 if (al->request) {
1084 sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
1085 out = sb.c_str();
1086 }
1087 break;
1088
1089 case LFT_REQUEST_METHOD:
1090 if (al->hasLogMethod()) {
1091 sb = al->getLogMethod();
1092 out = sb.c_str();
1093 quote = 1;
1094 }
1095 break;
1096
1097 case LFT_REQUEST_URI:
1098 if (!al->url.isEmpty()) {
1099 sb = al->url;
1100 out = sb.c_str();
1101 }
1102 break;
1103
1104 case LFT_REQUEST_VERSION_OLD_2X:
1105 case LFT_REQUEST_VERSION:
1106 sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
1107 out = sb.c_str();
1108 break;
1109
1110 case LFT_SERVER_REQ_METHOD:
1111 if (al->adapted_request) {
1112 sb = al->adapted_request->method.image();
1113 out = sb.c_str();
1114 quote = 1;
1115 }
1116 break;
1117
1118 case LFT_SERVER_REQ_URI:
1119 // adapted request URI sent to server/peer
1120 if (al->adapted_request) {
1121 sb = al->adapted_request->effectiveRequestUri();
1122 out = sb.c_str();
1123 quote = 1;
1124 }
1125 break;
1126
1127 case LFT_SERVER_REQ_URLSCHEME:
1128 if (al->adapted_request) {
1129 sb = al->adapted_request->url.getScheme().image();
1130 out = sb.c_str();
1131 quote = 1;
1132 }
1133 break;
1134
1135 case LFT_SERVER_REQ_URLDOMAIN:
1136 if (al->adapted_request) {
1137 out = al->adapted_request->url.host();
1138 quote = 1;
1139 }
1140 break;
1141
1142 case LFT_SERVER_REQ_URLPORT:
1143 if (al->adapted_request && al->adapted_request->url.port()) {
1144 outint = *al->adapted_request->url.port();
1145 doint = 1;
1146 }
1147 break;
1148
1149 case LFT_SERVER_REQ_URLPATH:
1150 if (al->adapted_request) {
1151 sb = al->adapted_request->url.path();
1152 out = sb.c_str();
1153 quote = 1;
1154 }
1155 break;
1156
1157 case LFT_SERVER_REQ_VERSION:
1158 if (al->adapted_request) {
1159 sb.appendf("%u.%u",
1160 al->adapted_request->http_ver.major,
1161 al->adapted_request->http_ver.minor);
1162 out = tmp;
1163 }
1164 break;
1165
1166 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
1167 outoff = al->http.clientRequestSz.messageTotal();
1168 dooff = 1;
1169 break;
1170
1171 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
1172 outoff = al->http.clientRequestSz.header;
1173 dooff =1;
1174 break;
1175
1176 /*case LFT_REQUEST_SIZE_BODY: */
1177 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1178
1179 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
1180 outoff = al->http.clientReplySz.messageTotal();
1181 dooff = 1;
1182 break;
1183
1184 case LFT_REPLY_HIGHOFFSET:
1185 outoff = al->cache.highOffset;
1186 dooff = 1;
1187 break;
1188
1189 case LFT_REPLY_OBJECTSIZE:
1190 outoff = al->cache.objectSize;
1191 dooff = 1;
1192 break;
1193
1194 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
1195 outint = al->http.clientReplySz.header;
1196 doint = 1;
1197 break;
1198
1199 /*case LFT_REPLY_SIZE_BODY: */
1200 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1201
1202 case LFT_CLIENT_IO_SIZE_TOTAL:
1203 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
1204 doint = 1;
1205 break;
1206 /*case LFT_SERVER_IO_SIZE_TOTAL: */
1207
1208 case LFT_TAG:
1209 if (al->request) {
1210 out = al->request->tag.termedBuf();
1211 quote = 1;
1212 }
1213 break;
1214
1215 case LFT_EXT_LOG:
1216 if (al->request) {
1217 out = al->request->extacl_log.termedBuf();
1218 quote = 1;
1219 }
1220 break;
1221
1222 case LFT_SEQUENCE_NUMBER:
1223 outoff = logSequenceNumber;
1224 dooff = 1;
1225 break;
1226
1227 #if USE_OPENSSL
1228 case LFT_SSL_BUMP_MODE: {
1229 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1230 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1231 out = Ssl::bumpMode(mode);
1232 }
1233 break;
1234
1235 case LFT_EXT_ACL_USER_CERT_RAW:
1236 if (al->request) {
1237 ConnStateData *conn = al->request->clientConnectionManager.get();
1238 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1239 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1240 sb = sslGetUserCertificatePEM(ssl);
1241 out = sb.c_str();
1242 }
1243 }
1244 }
1245 break;
1246
1247 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1248 if (al->request) {
1249 ConnStateData *conn = al->request->clientConnectionManager.get();
1250 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1251 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1252 sb = sslGetUserCertificatePEM(ssl);
1253 out = sb.c_str();
1254 }
1255 }
1256 }
1257 break;
1258
1259 case LFT_EXT_ACL_USER_CERT:
1260 if (al->request) {
1261 ConnStateData *conn = al->request->clientConnectionManager.get();
1262 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1263 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1264 out = sslGetUserAttribute(ssl, fmt->data.header.header);
1265 }
1266 }
1267 break;
1268
1269 case LFT_EXT_ACL_USER_CA_CERT:
1270 if (al->request) {
1271 ConnStateData *conn = al->request->clientConnectionManager.get();
1272 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1273 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1274 out = sslGetCAAttribute(ssl, fmt->data.header.header);
1275 }
1276 }
1277 break;
1278
1279 case LFT_SSL_USER_CERT_SUBJECT:
1280 if (const auto &cert = al->cache.sslClientCert) {
1281 sb = Security::SubjectName(*cert);
1282 out = sb.c_str();
1283 }
1284 break;
1285
1286 case LFT_SSL_USER_CERT_ISSUER:
1287 if (const auto &cert = al->cache.sslClientCert) {
1288 sb = Security::IssuerName(*cert);
1289 out = sb.c_str();
1290 }
1291 break;
1292
1293 case LFT_SSL_CLIENT_SNI:
1294 if (al->request && al->request->clientConnectionManager.valid()) {
1295 if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
1296 if (!conn->tlsClientSni().isEmpty()) {
1297 sb = conn->tlsClientSni();
1298 out = sb.c_str();
1299 }
1300 }
1301 }
1302 break;
1303
1304 case LFT_SSL_SERVER_CERT_ERRORS:
1305 if (al->request && al->request->clientConnectionManager.valid()) {
1306 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1307 const char *separator = fmt->data.string ? fmt->data.string : ":";
1308 for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
1309 if (!sb.isEmpty())
1310 sb.append(separator);
1311 sb.append(Ssl::GetErrorName(sslError->element.code, true));
1312 if (sslError->element.depth >= 0)
1313 sb.appendf("@depth=%d", sslError->element.depth);
1314 }
1315 if (!sb.isEmpty())
1316 out = sb.c_str();
1317 }
1318 }
1319 break;
1320
1321 case LFT_SSL_SERVER_CERT_ISSUER:
1322 case LFT_SSL_SERVER_CERT_SUBJECT:
1323 case LFT_SSL_SERVER_CERT_WHOLE:
1324 if (al->request && al->request->clientConnectionManager.valid()) {
1325 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1326 if (X509 *serverCert = srvBump->serverCert.get()) {
1327 if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
1328 out = Ssl::GetX509UserAttribute(serverCert, "DN");
1329 else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
1330 out = Ssl::GetX509CAAttribute(serverCert, "DN");
1331 else {
1332 assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
1333 sb = Ssl::GetX509PEM(serverCert);
1334 out = sb.c_str();
1335 quote = 1;
1336 }
1337 }
1338 }
1339 }
1340 break;
1341
1342 case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
1343 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1344 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
1345 break;
1346
1347 case LFT_TLS_SERVER_NEGOTIATED_VERSION:
1348 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1349 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
1350 break;
1351
1352 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
1353 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1354 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
1355 break;
1356
1357 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
1358 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1359 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
1360 break;
1361
1362 case LFT_TLS_CLIENT_SUPPORTED_VERSION:
1363 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1364 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
1365 break;
1366
1367 case LFT_TLS_SERVER_SUPPORTED_VERSION:
1368 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1369 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
1370 break;
1371
1372 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
1373 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1374 out = al->tcpClient->hasTlsNegotiations()->cipherName();
1375 break;
1376
1377 case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
1378 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1379 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
1380 break;
1381 #endif
1382
1383 case LFT_REQUEST_URLGROUP_OLD_2X:
1384 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
1385 break;
1386
1387 case LFT_NOTE:
1388 tmp[0] = fmt->data.header.separator;
1389 tmp[1] = '\0';
1390 if (fmt->data.header.header && *fmt->data.header.header) {
1391 const char *separator = tmp;
1392 static SBuf note;
1393 #if USE_ADAPTATION
1394 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1395 if (ah && ah->metaHeaders) {
1396 if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
1397 sb.append(note);
1398 }
1399 #endif
1400 if (al->notes) {
1401 if (al->notes->find(note, fmt->data.header.header, separator)) {
1402 if (!sb.isEmpty())
1403 sb.append(separator);
1404 sb.append(note);
1405 }
1406 }
1407 out = sb.c_str();
1408 quote = 1;
1409 } else {
1410 // No specific annotation requested. Report all annotations.
1411
1412 // if no argument given use default "\r\n" as notes separator
1413 const char *separator = fmt->data.string ? tmp : "\r\n";
1414 SBufStream os;
1415 #if USE_ADAPTATION
1416 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1417 if (ah && ah->metaHeaders)
1418 ah->metaHeaders->print(os, ": ", separator);
1419 #endif
1420 if (al->notes)
1421 al->notes->print(os, ": ", separator);
1422
1423 sb = os.buf();
1424 out = sb.c_str();
1425 quote = 1;
1426 }
1427 break;
1428
1429 case LFT_CREDENTIALS:
1430 #if USE_AUTH
1431 if (al->request && al->request->auth_user_request)
1432 out = strOrNull(al->request->auth_user_request->credentialsStr());
1433 #endif
1434 break;
1435
1436 case LFT_PERCENT:
1437 out = "%";
1438 break;
1439
1440 case LFT_EXT_ACL_NAME:
1441 out = al->lastAclName;
1442 break;
1443
1444 case LFT_EXT_ACL_DATA:
1445 if (!al->lastAclData.isEmpty())
1446 out = al->lastAclData.c_str();
1447 break;
1448
1449 case LFT_MASTER_XACTION:
1450 if (al->request) {
1451 doUint64 = true;
1452 outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
1453 break;
1454 }
1455 }
1456
1457 if (dooff) {
1458 sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
1459 out = sb.c_str();
1460
1461 } else if (doint) {
1462 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
1463 out = sb.c_str();
1464 } else if (doUint64) {
1465 sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
1466 out = sb.c_str();
1467 } else if (doMsec) {
1468 if (fmt->widthMax < 0) {
1469 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
1470 } else {
1471 int precision = fmt->widthMax;
1472 sb.appendf("%0*" PRId64 ".%0*" PRId64 "", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec * 1000 + outtv.tv_usec / 1000), precision, static_cast<int64_t>((outtv.tv_usec % 1000 )* (1000 / fmt->divisor)));
1473 }
1474 out = sb.c_str();
1475 } else if (doSec) {
1476 int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
1477 sb.appendf("%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec), precision, (int)(outtv.tv_usec / fmt->divisor));
1478 out = sb.c_str();
1479 }
1480
1481 if (out && *out) {
1482 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1483 // Do not write to the tmp buffer because it may contain the to-be-quoted value.
1484 static char quotedOut[2 * sizeof(tmp)];
1485 static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
1486 quotedOut[0] = '\0';
1487
1488 char *newout = nullptr;
1489 int newfree = 0;
1490
1491 switch (fmt->quote) {
1492
1493 case LOG_QUOTE_NONE:
1494 newout = rfc1738_escape_unescaped(out);
1495 break;
1496
1497 case LOG_QUOTE_QUOTES: {
1498 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1499 if (out_len >= sizeof(tmp)) {
1500 newout = (char *)xmalloc(out_len);
1501 newfree = 1;
1502 } else
1503 newout = quotedOut;
1504 log_quoted_string(out, newout);
1505 }
1506 break;
1507
1508 case LOG_QUOTE_MIMEBLOB:
1509 newout = QuoteMimeBlob(out);
1510 newfree = 1;
1511 break;
1512
1513 case LOG_QUOTE_URL:
1514 newout = rfc1738_escape(out);
1515 break;
1516
1517 case LOG_QUOTE_SHELL: {
1518 MemBuf mbq;
1519 mbq.init();
1520 strwordquote(&mbq, out);
1521 newout = mbq.content();
1522 mbq.stolen = 1;
1523 newfree = 1;
1524 }
1525 break;
1526
1527 case LOG_QUOTE_RAW:
1528 break;
1529 }
1530
1531 if (newout) {
1532 if (dofree)
1533 safe_free(out);
1534
1535 out = newout;
1536
1537 dofree = newfree;
1538 }
1539 }
1540
1541 // enforce width limits if configured
1542 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
1543 if (haveMaxWidth || fmt->widthMin) {
1544 const int minWidth = fmt->widthMin >= 0 ?
1545 fmt->widthMin :0;
1546 const int maxWidth = haveMaxWidth ?
1547 fmt->widthMax : strlen(out);
1548
1549 if (fmt->left)
1550 mb.appendf("%-*.*s", minWidth, maxWidth, out);
1551 else
1552 mb.appendf("%*.*s", minWidth, maxWidth, out);
1553 } else
1554 mb.append(out, strlen(out));
1555 } else {
1556 mb.append("-", 1);
1557 }
1558
1559 if (fmt->space)
1560 mb.append(" ", 1);
1561
1562 sb.clear();
1563
1564 if (dofree)
1565 safe_free(out);
1566 }
1567 }
1568