]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/format/Format.cc
Simplify appending SBuf to String (#2108)
[thirdparty/squid.git] / src / format / Format.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2025 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
40const SBuf Format::Dash("-");
41
42Format::Format::Format(const char *n) :
43 format(nullptr),
44 next(nullptr)
45{
46 name = xstrdup(n);
47}
48
49Format::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
65bool
66Format::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
98size_t
99Format::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
116void
117Format::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
291static void
292log_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).
349static const Http::Message *
350actualReplyHeader(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.
363static const Http::Message *
364actualRequestHeader(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
376void
377Format::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 // XXX: al->hier.totalPeeringTime is not updated until prepareLogWithRequestDetails().
639 // TODO: Avoid the need for updates by keeping totalPeeringTime (or even ALE::hier) in one place.
640 const auto &timer = (!al->hier.totalPeeringTime.ran() && al->request) ?
641 al->request->hier.totalPeeringTime : al->hier.totalPeeringTime;
642 if (timer.ran()) {
643 using namespace std::chrono_literals;
644 const auto duration = timer.total();
645 outtv.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
646 const auto totalUsec = std::chrono::duration_cast<std::chrono::microseconds>(duration);
647 outtv.tv_usec = (totalUsec % std::chrono::microseconds(1s)).count();
648 doMsec = 1;
649 }
650 }
651 break;
652
653 case LFT_DNS_WAIT_TIME:
654 if (al->request && al->request->dnsWait >= 0) {
655 // TODO: microsecond precision for dns wait time.
656 // Convert milliseconds to timeval struct:
657 outtv.tv_sec = al->request->dnsWait / 1000;
658 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
659 doMsec = 1;
660 }
661 break;
662
663 case LFT_REQUEST_HEADER:
664 if (const Http::Message *msg = actualRequestHeader(al)) {
665 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
666 out = sb.c_str();
667 quote = 1;
668 }
669 break;
670
671 case LFT_ADAPTED_REQUEST_HEADER:
672 if (al->adapted_request) {
673 sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
674 out = sb.c_str();
675 quote = 1;
676 }
677 break;
678
679 case LFT_REPLY_HEADER:
680 if (const Http::Message *msg = actualReplyHeader(al)) {
681 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
682 out = sb.c_str();
683 quote = 1;
684 }
685 break;
686
687#if USE_ADAPTATION
688 case LFT_ADAPTATION_SUM_XACT_TIMES:
689 if (al->request) {
690 Adaptation::History::Pointer ah = al->request->adaptHistory();
691 if (ah) {
692 ah->sumLogString(fmt->data.string, sb);
693 out = sb.c_str();
694 }
695 }
696 break;
697
698 case LFT_ADAPTATION_ALL_XACT_TIMES:
699 if (al->request) {
700 Adaptation::History::Pointer ah = al->request->adaptHistory();
701 if (ah) {
702 ah->allLogString(fmt->data.string, sb);
703 out = sb.c_str();
704 }
705 }
706 break;
707
708 case LFT_ADAPTATION_LAST_HEADER:
709 if (al->request) {
710 const Adaptation::History::Pointer ah = al->request->adaptHistory();
711 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
712 sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
713 out = sb.c_str();
714 quote = 1;
715 }
716 }
717 break;
718
719 case LFT_ADAPTATION_LAST_HEADER_ELEM:
720 if (al->request) {
721 const Adaptation::History::Pointer ah = al->request->adaptHistory();
722 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
723 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
724 out = sb.c_str();
725 quote = 1;
726 }
727 }
728 break;
729
730 case LFT_ADAPTATION_LAST_ALL_HEADERS:
731 out = al->adapt.last_meta;
732 quote = 1;
733 break;
734#endif
735
736#if ICAP_CLIENT
737 case LFT_ICAP_ADDR:
738 out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
739 break;
740
741 case LFT_ICAP_SERV_NAME:
742 out = al->icap.serviceName.termedBuf();
743 break;
744
745 case LFT_ICAP_REQUEST_URI:
746 out = al->icap.reqUri.termedBuf();
747 break;
748
749 case LFT_ICAP_REQUEST_METHOD:
750 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
751 break;
752
753 case LFT_ICAP_BYTES_SENT:
754 outoff = al->icap.bytesSent;
755 dooff = 1;
756 break;
757
758 case LFT_ICAP_BYTES_READ:
759 outoff = al->icap.bytesRead;
760 dooff = 1;
761 break;
762
763 case LFT_ICAP_BODY_BYTES_READ:
764 if (al->icap.bodyBytesRead >= 0) {
765 outoff = al->icap.bodyBytesRead;
766 dooff = 1;
767 }
768 // else if icap.bodyBytesRead < 0, we do not have any http data,
769 // so just print a "-" (204 responses etc)
770 break;
771
772 case LFT_ICAP_REQ_HEADER:
773 if (al->icap.request) {
774 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
775 out = sb.c_str();
776 quote = 1;
777 }
778 break;
779
780 case LFT_ICAP_REQ_HEADER_ELEM:
781 if (al->icap.request) {
782 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
783 out = sb.c_str();
784 quote = 1;
785 }
786 break;
787
788 case LFT_ICAP_REQ_ALL_HEADERS:
789 if (al->icap.request) {
790 HttpHeaderPos pos = HttpHeaderInitPos;
791 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
792 sb.append(e->name);
793 sb.append(": ");
794 sb.append(StringToSBuf(e->value));
795 sb.append("\r\n");
796 }
797 out = sb.c_str();
798 quote = 1;
799 }
800 break;
801
802 case LFT_ICAP_REP_HEADER:
803 if (al->icap.reply) {
804 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
805 out = sb.c_str();
806 quote = 1;
807 }
808 break;
809
810 case LFT_ICAP_REP_HEADER_ELEM:
811 if (al->icap.reply) {
812 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
813 out = sb.c_str();
814 quote = 1;
815 }
816 break;
817
818 case LFT_ICAP_REP_ALL_HEADERS:
819 if (al->icap.reply) {
820 HttpHeaderPos pos = HttpHeaderInitPos;
821 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
822 sb.append(e->name);
823 sb.append(": ");
824 sb.append(StringToSBuf(e->value));
825 sb.append("\r\n");
826 }
827 out = sb.c_str();
828 quote = 1;
829 }
830 break;
831
832 case LFT_ICAP_TR_RESPONSE_TIME:
833 outtv = al->icap.trTime;
834 doMsec = 1;
835 break;
836
837 case LFT_ICAP_IO_TIME:
838 outtv = al->icap.ioTime;
839 doMsec = 1;
840 break;
841
842 case LFT_ICAP_STATUS_CODE:
843 outint = al->icap.resStatus;
844 doint = 1;
845 break;
846
847 case LFT_ICAP_OUTCOME:
848 out = al->icap.outcome;
849 break;
850
851 case LFT_ICAP_TOTAL_TIME:
852 outtv = al->icap.processingTime;
853 doMsec = 1;
854 break;
855#endif
856 case LFT_REQUEST_HEADER_ELEM:
857 if (const Http::Message *msg = actualRequestHeader(al)) {
858 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
859 out = sb.c_str();
860 quote = 1;
861 }
862 break;
863
864 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
865 if (al->proxyProtocolHeader) {
866 sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
867 out = sb.c_str();
868 quote = 1;
869 }
870 break;
871
872 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
873 if (al->proxyProtocolHeader) {
874 sb = al->proxyProtocolHeader->toMime();
875 out = sb.c_str();
876 quote = 1;
877 }
878 break;
879
880 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
881 if (al->proxyProtocolHeader) {
882 sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
883 out = sb.c_str();
884 quote = 1;
885 }
886 break;
887
888 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
889 if (al->adapted_request) {
890 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
891 out = sb.c_str();
892 quote = 1;
893 }
894 break;
895
896 case LFT_REPLY_HEADER_ELEM:
897 if (const Http::Message *msg = actualReplyHeader(al)) {
898 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
899 out = sb.c_str();
900 quote = 1;
901 }
902 break;
903
904 case LFT_REQUEST_ALL_HEADERS:
905#if ICAP_CLIENT
906 if (al->icap.reqMethod == Adaptation::methodRespmod) {
907 // XXX: since AccessLogEntry::Headers lacks virgin response
908 // headers, do nothing for now
909 out = nullptr;
910 } else
911#endif
912 {
913 // just headers without start-line and CRLF
914 // XXX: reconcile with '<h'
915 out = al->headers.request;
916 quote = 1;
917 }
918 break;
919
920 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
921 // just headers without start-line and CRLF
922 // XXX: reconcile with '<h'
923 out = al->headers.adapted_request;
924 quote = 1;
925 break;
926
927 case LFT_REPLY_ALL_HEADERS: {
928 MemBuf allHeaders;
929 allHeaders.init();
930 // status-line + headers + CRLF
931 // XXX: reconcile with '>h' and '>ha'
932 al->packReplyHeaders(allHeaders);
933 sb.assign(allHeaders.content(), allHeaders.contentSize());
934 out = sb.c_str();
935#if ICAP_CLIENT
936 if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
937 out = al->headers.adapted_request;
938#endif
939 quote = 1;
940 }
941 break;
942
943 case LFT_USER_NAME:
944#if USE_AUTH
945 if (al->request && al->request->auth_user_request)
946 out = strOrNull(al->request->auth_user_request->username());
947#endif
948 if (!out && al->request && al->request->extacl_user.size()) {
949 if (const char *t = al->request->extacl_user.termedBuf())
950 out = t;
951 }
952 if (!out)
953 out = strOrNull(al->getExtUser());
954#if USE_OPENSSL
955 if (!out)
956 out = strOrNull(al->cache.ssluser);
957#endif
958 break;
959
960 case LFT_USER_LOGIN:
961#if USE_AUTH
962 if (al->request && al->request->auth_user_request)
963 out = strOrNull(al->request->auth_user_request->username());
964#endif
965 break;
966
967 case LFT_USER_EXTERNAL:
968 out = strOrNull(al->getExtUser());
969 break;
970
971 /* case LFT_USER_REALM: */
972 /* case LFT_USER_SCHEME: */
973
974 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
975 // but compiler complains if omitted
976 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
977 case LFT_HTTP_SENT_STATUS_CODE:
978 outint = al->http.code;
979 doint = 1;
980 break;
981
982 case LFT_HTTP_RECEIVED_STATUS_CODE:
983 if (al->hier.peer_reply_status != Http::scNone) {
984 outint = al->hier.peer_reply_status;
985 doint = 1;
986 }
987 break;
988 /* case LFT_HTTP_STATUS:
989 * out = statusline->text;
990 * quote = 1;
991 * break;
992 */
993 case LFT_HTTP_BODY_BYTES_READ:
994 if (al->hier.bodyBytesRead >= 0) {
995 outoff = al->hier.bodyBytesRead;
996 dooff = 1;
997 }
998 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
999 // a peer server so just print a "-" (eg requests served from cache,
1000 // or internal error messages).
1001 break;
1002
1003 case LFT_SQUID_STATUS:
1004 out = al->cache.code.c_str();
1005 break;
1006
1007 case LFT_SQUID_ERROR:
1008 if (const auto error = al->error())
1009 out = errorPageName(error->category);
1010 break;
1011
1012 case LFT_SQUID_ERROR_DETAIL:
1013 if (const auto error = al->error()) {
1014 if (!error->details.empty()) {
1015 sb = ToSBuf(error->details);
1016 out = sb.c_str();
1017 }
1018 }
1019 break;
1020
1021 case LFT_SQUID_HIERARCHY:
1022 if (al->hier.ping.timedout)
1023 mb.append("TIMEOUT_", 8);
1024 out = hier_code_str[al->hier.code];
1025 break;
1026
1027 case LFT_SQUID_REQUEST_ATTEMPTS:
1028 outint = al->requestAttempts;
1029 doint = 1;
1030 break;
1031
1032 case LFT_MIME_TYPE:
1033 out = al->http.content_type;
1034 break;
1035
1036 case LFT_CLIENT_REQ_METHOD:
1037 if (al->request) {
1038 sb = al->request->method.image();
1039 out = sb.c_str();
1040 quote = 1;
1041 }
1042 break;
1043
1044 case LFT_CLIENT_REQ_URI:
1045 if (const auto uri = al->effectiveVirginUrl()) {
1046 sb = *uri;
1047 out = sb.c_str();
1048 quote = 1;
1049 }
1050 break;
1051
1052 case LFT_CLIENT_REQ_URLSCHEME:
1053 if (al->request) {
1054 sb = al->request->url.getScheme().image();
1055 out = sb.c_str();
1056 quote = 1;
1057 }
1058 break;
1059
1060 case LFT_CLIENT_REQ_URLDOMAIN:
1061 if (al->request) {
1062 out = al->request->url.host();
1063 quote = 1;
1064 }
1065 break;
1066
1067 case LFT_CLIENT_REQ_URLPORT:
1068 if (al->request && al->request->url.port()) {
1069 outint = *al->request->url.port();
1070 doint = 1;
1071 }
1072 break;
1073
1074 case LFT_REQUEST_URLPATH_OLD_31:
1075 case LFT_CLIENT_REQ_URLPATH:
1076 if (al->request) {
1077 sb = al->request->url.path();
1078 out = sb.c_str();
1079 quote = 1;
1080 }
1081 break;
1082
1083 case LFT_CLIENT_REQ_VERSION:
1084 if (al->request) {
1085 sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
1086 out = sb.c_str();
1087 }
1088 break;
1089
1090 case LFT_REQUEST_METHOD:
1091 if (al->hasLogMethod()) {
1092 sb = al->getLogMethod();
1093 out = sb.c_str();
1094 quote = 1;
1095 }
1096 break;
1097
1098 case LFT_REQUEST_URI:
1099 if (!al->url.isEmpty()) {
1100 sb = al->url;
1101 out = sb.c_str();
1102 }
1103 break;
1104
1105 case LFT_REQUEST_VERSION_OLD_2X:
1106 case LFT_REQUEST_VERSION:
1107 sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
1108 out = sb.c_str();
1109 break;
1110
1111 case LFT_SERVER_REQ_METHOD:
1112 if (al->adapted_request) {
1113 sb = al->adapted_request->method.image();
1114 out = sb.c_str();
1115 quote = 1;
1116 }
1117 break;
1118
1119 case LFT_SERVER_REQ_URI:
1120 // adapted request URI sent to server/peer
1121 if (al->adapted_request) {
1122 sb = al->adapted_request->effectiveRequestUri();
1123 out = sb.c_str();
1124 quote = 1;
1125 }
1126 break;
1127
1128 case LFT_SERVER_REQ_URLSCHEME:
1129 if (al->adapted_request) {
1130 sb = al->adapted_request->url.getScheme().image();
1131 out = sb.c_str();
1132 quote = 1;
1133 }
1134 break;
1135
1136 case LFT_SERVER_REQ_URLDOMAIN:
1137 if (al->adapted_request) {
1138 out = al->adapted_request->url.host();
1139 quote = 1;
1140 }
1141 break;
1142
1143 case LFT_SERVER_REQ_URLPORT:
1144 if (al->adapted_request && al->adapted_request->url.port()) {
1145 outint = *al->adapted_request->url.port();
1146 doint = 1;
1147 }
1148 break;
1149
1150 case LFT_SERVER_REQ_URLPATH:
1151 if (al->adapted_request) {
1152 sb = al->adapted_request->url.path();
1153 out = sb.c_str();
1154 quote = 1;
1155 }
1156 break;
1157
1158 case LFT_SERVER_REQ_VERSION:
1159 if (al->adapted_request) {
1160 sb.appendf("%u.%u",
1161 al->adapted_request->http_ver.major,
1162 al->adapted_request->http_ver.minor);
1163 out = tmp;
1164 }
1165 break;
1166
1167 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
1168 outoff = al->http.clientRequestSz.messageTotal();
1169 dooff = 1;
1170 break;
1171
1172 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
1173 outoff = al->http.clientRequestSz.header;
1174 dooff =1;
1175 break;
1176
1177 /*case LFT_REQUEST_SIZE_BODY: */
1178 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1179
1180 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
1181 outoff = al->http.clientReplySz.messageTotal();
1182 dooff = 1;
1183 break;
1184
1185 case LFT_REPLY_HIGHOFFSET:
1186 outoff = al->cache.highOffset;
1187 dooff = 1;
1188 break;
1189
1190 case LFT_REPLY_OBJECTSIZE:
1191 outoff = al->cache.objectSize;
1192 dooff = 1;
1193 break;
1194
1195 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
1196 outint = al->http.clientReplySz.header;
1197 doint = 1;
1198 break;
1199
1200 /*case LFT_REPLY_SIZE_BODY: */
1201 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1202
1203 case LFT_CLIENT_IO_SIZE_TOTAL:
1204 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
1205 doint = 1;
1206 break;
1207 /*case LFT_SERVER_IO_SIZE_TOTAL: */
1208
1209 case LFT_TAG:
1210 if (al->request) {
1211 out = al->request->tag.termedBuf();
1212 quote = 1;
1213 }
1214 break;
1215
1216 case LFT_EXT_LOG:
1217 if (al->request) {
1218 out = al->request->extacl_log.termedBuf();
1219 quote = 1;
1220 }
1221 break;
1222
1223 case LFT_SEQUENCE_NUMBER:
1224 outoff = logSequenceNumber;
1225 dooff = 1;
1226 break;
1227
1228#if USE_OPENSSL
1229 case LFT_SSL_BUMP_MODE: {
1230 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1231 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1232 out = Ssl::bumpMode(mode);
1233 }
1234 break;
1235
1236 case LFT_EXT_ACL_USER_CERT_RAW:
1237 if (al->request) {
1238 ConnStateData *conn = al->request->clientConnectionManager.get();
1239 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1240 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1241 sb = sslGetUserCertificatePEM(ssl);
1242 out = sb.c_str();
1243 }
1244 }
1245 }
1246 break;
1247
1248 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1249 if (al->request) {
1250 ConnStateData *conn = al->request->clientConnectionManager.get();
1251 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1252 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1253 sb = sslGetUserCertificatePEM(ssl);
1254 out = sb.c_str();
1255 }
1256 }
1257 }
1258 break;
1259
1260 case LFT_EXT_ACL_USER_CERT:
1261 if (al->request) {
1262 ConnStateData *conn = al->request->clientConnectionManager.get();
1263 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1264 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1265 out = sslGetUserAttribute(ssl, fmt->data.header.header);
1266 }
1267 }
1268 break;
1269
1270 case LFT_EXT_ACL_USER_CA_CERT:
1271 if (al->request) {
1272 ConnStateData *conn = al->request->clientConnectionManager.get();
1273 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1274 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1275 out = sslGetCAAttribute(ssl, fmt->data.header.header);
1276 }
1277 }
1278 break;
1279
1280 case LFT_SSL_USER_CERT_SUBJECT:
1281 if (const auto &cert = al->cache.sslClientCert) {
1282 sb = Security::SubjectName(*cert);
1283 out = sb.c_str();
1284 }
1285 break;
1286
1287 case LFT_SSL_USER_CERT_ISSUER:
1288 if (const auto &cert = al->cache.sslClientCert) {
1289 sb = Security::IssuerName(*cert);
1290 out = sb.c_str();
1291 }
1292 break;
1293
1294 case LFT_SSL_CLIENT_SNI:
1295 if (al->request && al->request->clientConnectionManager.valid()) {
1296 if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
1297 if (!conn->tlsClientSni().isEmpty()) {
1298 sb = conn->tlsClientSni();
1299 out = sb.c_str();
1300 }
1301 }
1302 }
1303 break;
1304
1305 case LFT_SSL_SERVER_CERT_ERRORS:
1306 if (al->request && al->request->clientConnectionManager.valid()) {
1307 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1308 const char *separator = fmt->data.string ? fmt->data.string : ":";
1309 for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
1310 if (!sb.isEmpty())
1311 sb.append(separator);
1312 sb.append(Ssl::GetErrorName(sslError->element.code, true));
1313 if (sslError->element.depth >= 0)
1314 sb.appendf("@depth=%d", sslError->element.depth);
1315 }
1316 if (!sb.isEmpty())
1317 out = sb.c_str();
1318 }
1319 }
1320 break;
1321
1322 case LFT_SSL_SERVER_CERT_ISSUER:
1323 case LFT_SSL_SERVER_CERT_SUBJECT:
1324 case LFT_SSL_SERVER_CERT_WHOLE:
1325 if (al->request && al->request->clientConnectionManager.valid()) {
1326 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1327 if (X509 *serverCert = srvBump->serverCert.get()) {
1328 if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
1329 out = Ssl::GetX509UserAttribute(serverCert, "DN");
1330 else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
1331 out = Ssl::GetX509CAAttribute(serverCert, "DN");
1332 else {
1333 assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
1334 sb = Ssl::GetX509PEM(serverCert);
1335 out = sb.c_str();
1336 quote = 1;
1337 }
1338 }
1339 }
1340 }
1341 break;
1342
1343 case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
1344 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1345 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
1346 break;
1347
1348 case LFT_TLS_SERVER_NEGOTIATED_VERSION:
1349 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1350 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
1351 break;
1352
1353 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
1354 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1355 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
1356 break;
1357
1358 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
1359 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1360 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
1361 break;
1362
1363 case LFT_TLS_CLIENT_SUPPORTED_VERSION:
1364 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1365 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
1366 break;
1367
1368 case LFT_TLS_SERVER_SUPPORTED_VERSION:
1369 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1370 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
1371 break;
1372
1373 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
1374 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1375 out = al->tcpClient->hasTlsNegotiations()->cipherName();
1376 break;
1377
1378 case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
1379 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1380 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
1381 break;
1382#endif
1383
1384 case LFT_REQUEST_URLGROUP_OLD_2X:
1385 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
1386 break;
1387
1388 case LFT_NOTE:
1389 tmp[0] = fmt->data.header.separator;
1390 tmp[1] = '\0';
1391 if (fmt->data.header.header && *fmt->data.header.header) {
1392 const char *separator = tmp;
1393 static SBuf note;
1394#if USE_ADAPTATION
1395 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1396 if (ah && ah->metaHeaders) {
1397 if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
1398 sb.append(note);
1399 }
1400#endif
1401 if (al->notes) {
1402 if (al->notes->find(note, fmt->data.header.header, separator)) {
1403 if (!sb.isEmpty())
1404 sb.append(separator);
1405 sb.append(note);
1406 }
1407 }
1408 out = sb.c_str();
1409 quote = 1;
1410 } else {
1411 // No specific annotation requested. Report all annotations.
1412
1413 // if no argument given use default "\r\n" as notes separator
1414 const char *separator = fmt->data.string ? tmp : "\r\n";
1415 SBufStream os;
1416#if USE_ADAPTATION
1417 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1418 if (ah && ah->metaHeaders)
1419 ah->metaHeaders->print(os, ": ", separator);
1420#endif
1421 if (al->notes)
1422 al->notes->print(os, ": ", separator);
1423
1424 sb = os.buf();
1425 out = sb.c_str();
1426 quote = 1;
1427 }
1428 break;
1429
1430 case LFT_CREDENTIALS:
1431#if USE_AUTH
1432 if (al->request && al->request->auth_user_request)
1433 out = strOrNull(al->request->auth_user_request->credentialsStr());
1434#endif
1435 break;
1436
1437 case LFT_PERCENT:
1438 out = "%";
1439 break;
1440
1441 case LFT_EXT_ACL_NAME:
1442 if (!al->lastAclName.isEmpty())
1443 out = al->lastAclName.c_str();
1444 break;
1445
1446 case LFT_EXT_ACL_DATA:
1447 if (!al->lastAclData.isEmpty())
1448 out = al->lastAclData.c_str();
1449 break;
1450
1451 case LFT_MASTER_XACTION:
1452 if (al->request) {
1453 doUint64 = true;
1454 outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
1455 break;
1456 }
1457 }
1458
1459 if (dooff) {
1460 sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
1461 out = sb.c_str();
1462
1463 } else if (doint) {
1464 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
1465 out = sb.c_str();
1466 } else if (doUint64) {
1467 sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
1468 out = sb.c_str();
1469 } else if (doMsec) {
1470 if (fmt->widthMax < 0) {
1471 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
1472 } else {
1473 int precision = fmt->widthMax;
1474 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)));
1475 }
1476 out = sb.c_str();
1477 } else if (doSec) {
1478 int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
1479 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));
1480 out = sb.c_str();
1481 }
1482
1483 if (out && *out) {
1484 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1485 // Do not write to the tmp buffer because it may contain the to-be-quoted value.
1486 static char quotedOut[2 * sizeof(tmp)];
1487 static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
1488 quotedOut[0] = '\0';
1489
1490 char *newout = nullptr;
1491 int newfree = 0;
1492
1493 switch (fmt->quote) {
1494
1495 case LOG_QUOTE_NONE:
1496 newout = rfc1738_escape_unescaped(out);
1497 break;
1498
1499 case LOG_QUOTE_QUOTES: {
1500 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1501 if (out_len >= sizeof(tmp)) {
1502 newout = (char *)xmalloc(out_len);
1503 newfree = 1;
1504 } else
1505 newout = quotedOut;
1506 log_quoted_string(out, newout);
1507 }
1508 break;
1509
1510 case LOG_QUOTE_MIMEBLOB:
1511 newout = QuoteMimeBlob(out);
1512 newfree = 1;
1513 break;
1514
1515 case LOG_QUOTE_URL:
1516 newout = rfc1738_escape(out);
1517 break;
1518
1519 case LOG_QUOTE_SHELL: {
1520 MemBuf mbq;
1521 mbq.init();
1522 strwordquote(&mbq, out);
1523 newout = mbq.content();
1524 mbq.stolen = 1;
1525 newfree = 1;
1526 }
1527 break;
1528
1529 case LOG_QUOTE_RAW:
1530 break;
1531 }
1532
1533 if (newout) {
1534 if (dofree)
1535 safe_free(out);
1536
1537 out = newout;
1538
1539 dofree = newfree;
1540 }
1541 }
1542
1543 // enforce width limits if configured
1544 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
1545 if (haveMaxWidth || fmt->widthMin) {
1546 const int minWidth = fmt->widthMin >= 0 ?
1547 fmt->widthMin :0;
1548 const int maxWidth = haveMaxWidth ?
1549 fmt->widthMax : strlen(out);
1550
1551 if (fmt->left)
1552 mb.appendf("%-*.*s", minWidth, maxWidth, out);
1553 else
1554 mb.appendf("%*.*s", minWidth, maxWidth, out);
1555 } else
1556 mb.append(out, strlen(out));
1557 } else {
1558 mb.append("-", 1);
1559 }
1560
1561 if (fmt->space)
1562 mb.append(" ", 1);
1563
1564 sb.clear();
1565
1566 if (dofree)
1567 safe_free(out);
1568 }
1569}
1570