]> git.ipfire.org Git - thirdparty/squid.git/blob - src/format/Format.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / format / Format.cc
1 /*
2 * Copyright (C) 1996-2022 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_STRING:
403 out = fmt->data.string;
404 break;
405
406 case LFT_CLIENT_IP_ADDRESS:
407 al->getLogClientIp(tmp, sizeof(tmp));
408 out = tmp;
409 break;
410
411 case LFT_CLIENT_FQDN:
412 out = al->getLogClientFqdn(tmp, sizeof(tmp));
413 break;
414
415 case LFT_CLIENT_PORT:
416 if (al->request) {
417 outint = al->request->client_addr.port();
418 doint = 1;
419 } else if (al->tcpClient) {
420 outint = al->tcpClient->remote.port();
421 doint = 1;
422 }
423 break;
424
425 case LFT_CLIENT_EUI:
426 #if USE_SQUID_EUI
427 // TODO make the ACL checklist have a direct link to any TCP details.
428 if (al->request && al->request->clientConnectionManager.valid() &&
429 al->request->clientConnectionManager->clientConnection) {
430 const auto &conn = al->request->clientConnectionManager->clientConnection;
431 if (conn->remote.isIPv4())
432 conn->remoteEui48.encode(tmp, sizeof(tmp));
433 else
434 conn->remoteEui64.encode(tmp, sizeof(tmp));
435 out = tmp;
436 }
437 #endif
438 break;
439
440 case LFT_EXT_ACL_CLIENT_EUI48:
441 #if USE_SQUID_EUI
442 if (al->request && al->request->clientConnectionManager.valid() &&
443 al->request->clientConnectionManager->clientConnection &&
444 al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
445 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
446 out = tmp;
447 }
448 #endif
449 break;
450
451 case LFT_EXT_ACL_CLIENT_EUI64:
452 #if USE_SQUID_EUI
453 if (al->request && al->request->clientConnectionManager.valid() &&
454 al->request->clientConnectionManager->clientConnection &&
455 !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
456 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
457 out = tmp;
458 }
459 #endif
460 break;
461
462 case LFT_SERVER_IP_ADDRESS:
463 if (al->hier.tcpServer)
464 out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
465 break;
466
467 case LFT_SERVER_FQDN_OR_PEER_NAME:
468 out = al->hier.host;
469 break;
470
471 case LFT_SERVER_PORT:
472 if (al->hier.tcpServer) {
473 outint = al->hier.tcpServer->remote.port();
474 doint = 1;
475 }
476 break;
477
478 case LFT_LOCAL_LISTENING_IP:
479 if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
480 out = addr->toStr(tmp, sizeof(tmp));
481 break;
482
483 case LFT_CLIENT_LOCAL_IP:
484 if (al->tcpClient)
485 out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
486 break;
487
488 case LFT_CLIENT_LOCAL_TOS:
489 if (al->tcpClient) {
490 sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
491 out = sb.c_str();
492 }
493 break;
494
495 case LFT_TRANSPORT_CLIENT_CONNECTION_ID:
496 if (al->tcpClient) {
497 outUint64 = al->tcpClient->id.value;
498 doUint64 = true;
499 }
500 break;
501
502 case LFT_CLIENT_LOCAL_NFMARK:
503 if (al->tcpClient) {
504 sb.appendf("0x%x", al->tcpClient->nfmark);
505 out = sb.c_str();
506 }
507 break;
508
509 case LFT_LOCAL_LISTENING_PORT:
510 if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
511 outint = port;
512 doint = 1;
513 }
514 break;
515
516 case LFT_CLIENT_LOCAL_PORT:
517 if (al->tcpClient) {
518 outint = al->tcpClient->local.port();
519 doint = 1;
520 }
521 break;
522
523 case LFT_SERVER_LOCAL_IP_OLD_27:
524 case LFT_SERVER_LOCAL_IP:
525 if (al->hier.tcpServer)
526 out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
527 break;
528
529 case LFT_SERVER_LOCAL_PORT:
530 if (al->hier.tcpServer) {
531 outint = al->hier.tcpServer->local.port();
532 doint = 1;
533 }
534 break;
535
536 case LFT_SERVER_LOCAL_TOS:
537 if (al->hier.tcpServer) {
538 sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
539 out = sb.c_str();
540 }
541 break;
542
543 case LFT_SERVER_LOCAL_NFMARK:
544 if (al->hier.tcpServer) {
545 sb.appendf("0x%x", al->hier.tcpServer->nfmark);
546 out = sb.c_str();
547 }
548 break;
549
550 case LFT_CLIENT_HANDSHAKE:
551 if (al->request && al->request->clientConnectionManager.valid()) {
552 const auto &handshake = al->request->clientConnectionManager->preservedClientData;
553 if (const auto rawLength = handshake.length()) {
554 // add 1 byte to optimize the c_str() conversion below
555 char *buf = sb.rawAppendStart(base64_encode_len(rawLength) + 1);
556
557 struct base64_encode_ctx ctx;
558 base64_encode_init(&ctx);
559 auto encLength = base64_encode_update(&ctx, buf, rawLength, reinterpret_cast<const uint8_t*>(handshake.rawContent()));
560 encLength += base64_encode_final(&ctx, buf + encLength);
561
562 sb.rawAppendFinish(buf, encLength);
563 out = sb.c_str();
564 }
565 }
566 break;
567
568 case LFT_TIME_SECONDS_SINCE_EPOCH:
569 // some platforms store time in 32-bit, some 64-bit...
570 outoff = static_cast<int64_t>(current_time.tv_sec);
571 dooff = 1;
572 break;
573
574 case LFT_TIME_SUBSECOND:
575 outint = current_time.tv_usec / fmt->divisor;
576 doint = 1;
577 break;
578
579 case LFT_TIME_LOCALTIME:
580 case LFT_TIME_GMT: {
581 const char *spec;
582 struct tm *t;
583 spec = fmt->data.string;
584
585 if (fmt->type == LFT_TIME_LOCALTIME) {
586 if (!spec)
587 spec = "%d/%b/%Y:%H:%M:%S %z";
588 t = localtime(&squid_curtime);
589 } else {
590 if (!spec)
591 spec = "%d/%b/%Y:%H:%M:%S";
592
593 t = gmtime(&squid_curtime);
594 }
595
596 strftime(tmp, sizeof(tmp), spec, t);
597 out = tmp;
598 }
599 break;
600
601 case LFT_TIME_START:
602 outtv = al->cache.start_time;
603 doSec = 1;
604 break;
605
606 case LFT_TIME_TO_HANDLE_REQUEST:
607 outtv = al->cache.trTime;
608 doMsec = 1;
609 break;
610
611 case LFT_PEER_RESPONSE_TIME:
612 struct timeval peerResponseTime;
613 if (al->hier.peerResponseTime(peerResponseTime)) {
614 outtv = peerResponseTime;
615 doMsec = 1;
616 }
617 break;
618
619 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
620 struct timeval totalResponseTime;
621 if (al->hier.totalResponseTime(totalResponseTime)) {
622 outtv = totalResponseTime;
623 doMsec = 1;
624 }
625 }
626 break;
627
628 case LFT_DNS_WAIT_TIME:
629 if (al->request && al->request->dnsWait >= 0) {
630 // TODO: microsecond precision for dns wait time.
631 // Convert milliseconds to timeval struct:
632 outtv.tv_sec = al->request->dnsWait / 1000;
633 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
634 doMsec = 1;
635 }
636 break;
637
638 case LFT_REQUEST_HEADER:
639 if (const Http::Message *msg = actualRequestHeader(al)) {
640 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
641 out = sb.c_str();
642 quote = 1;
643 }
644 break;
645
646 case LFT_ADAPTED_REQUEST_HEADER:
647 if (al->adapted_request) {
648 sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
649 out = sb.c_str();
650 quote = 1;
651 }
652 break;
653
654 case LFT_REPLY_HEADER:
655 if (const Http::Message *msg = actualReplyHeader(al)) {
656 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
657 out = sb.c_str();
658 quote = 1;
659 }
660 break;
661
662 #if USE_ADAPTATION
663 case LFT_ADAPTATION_SUM_XACT_TIMES:
664 if (al->request) {
665 Adaptation::History::Pointer ah = al->request->adaptHistory();
666 if (ah) {
667 ah->sumLogString(fmt->data.string, sb);
668 out = sb.c_str();
669 }
670 }
671 break;
672
673 case LFT_ADAPTATION_ALL_XACT_TIMES:
674 if (al->request) {
675 Adaptation::History::Pointer ah = al->request->adaptHistory();
676 if (ah) {
677 ah->allLogString(fmt->data.string, sb);
678 out = sb.c_str();
679 }
680 }
681 break;
682
683 case LFT_ADAPTATION_LAST_HEADER:
684 if (al->request) {
685 const Adaptation::History::Pointer ah = al->request->adaptHistory();
686 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
687 sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
688 out = sb.c_str();
689 quote = 1;
690 }
691 }
692 break;
693
694 case LFT_ADAPTATION_LAST_HEADER_ELEM:
695 if (al->request) {
696 const Adaptation::History::Pointer ah = al->request->adaptHistory();
697 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
698 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
699 out = sb.c_str();
700 quote = 1;
701 }
702 }
703 break;
704
705 case LFT_ADAPTATION_LAST_ALL_HEADERS:
706 out = al->adapt.last_meta;
707 quote = 1;
708 break;
709 #endif
710
711 #if ICAP_CLIENT
712 case LFT_ICAP_ADDR:
713 out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
714 break;
715
716 case LFT_ICAP_SERV_NAME:
717 out = al->icap.serviceName.termedBuf();
718 break;
719
720 case LFT_ICAP_REQUEST_URI:
721 out = al->icap.reqUri.termedBuf();
722 break;
723
724 case LFT_ICAP_REQUEST_METHOD:
725 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
726 break;
727
728 case LFT_ICAP_BYTES_SENT:
729 outoff = al->icap.bytesSent;
730 dooff = 1;
731 break;
732
733 case LFT_ICAP_BYTES_READ:
734 outoff = al->icap.bytesRead;
735 dooff = 1;
736 break;
737
738 case LFT_ICAP_BODY_BYTES_READ:
739 if (al->icap.bodyBytesRead >= 0) {
740 outoff = al->icap.bodyBytesRead;
741 dooff = 1;
742 }
743 // else if icap.bodyBytesRead < 0, we do not have any http data,
744 // so just print a "-" (204 responses etc)
745 break;
746
747 case LFT_ICAP_REQ_HEADER:
748 if (al->icap.request) {
749 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
750 out = sb.c_str();
751 quote = 1;
752 }
753 break;
754
755 case LFT_ICAP_REQ_HEADER_ELEM:
756 if (al->icap.request) {
757 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
758 out = sb.c_str();
759 quote = 1;
760 }
761 break;
762
763 case LFT_ICAP_REQ_ALL_HEADERS:
764 if (al->icap.request) {
765 HttpHeaderPos pos = HttpHeaderInitPos;
766 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
767 sb.append(e->name);
768 sb.append(": ");
769 sb.append(StringToSBuf(e->value));
770 sb.append("\r\n");
771 }
772 out = sb.c_str();
773 quote = 1;
774 }
775 break;
776
777 case LFT_ICAP_REP_HEADER:
778 if (al->icap.reply) {
779 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
780 out = sb.c_str();
781 quote = 1;
782 }
783 break;
784
785 case LFT_ICAP_REP_HEADER_ELEM:
786 if (al->icap.reply) {
787 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
788 out = sb.c_str();
789 quote = 1;
790 }
791 break;
792
793 case LFT_ICAP_REP_ALL_HEADERS:
794 if (al->icap.reply) {
795 HttpHeaderPos pos = HttpHeaderInitPos;
796 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
797 sb.append(e->name);
798 sb.append(": ");
799 sb.append(StringToSBuf(e->value));
800 sb.append("\r\n");
801 }
802 out = sb.c_str();
803 quote = 1;
804 }
805 break;
806
807 case LFT_ICAP_TR_RESPONSE_TIME:
808 outtv = al->icap.trTime;
809 doMsec = 1;
810 break;
811
812 case LFT_ICAP_IO_TIME:
813 outtv = al->icap.ioTime;
814 doMsec = 1;
815 break;
816
817 case LFT_ICAP_STATUS_CODE:
818 outint = al->icap.resStatus;
819 doint = 1;
820 break;
821
822 case LFT_ICAP_OUTCOME:
823 out = al->icap.outcome;
824 break;
825
826 case LFT_ICAP_TOTAL_TIME:
827 outtv = al->icap.processingTime;
828 doMsec = 1;
829 break;
830 #endif
831 case LFT_REQUEST_HEADER_ELEM:
832 if (const Http::Message *msg = actualRequestHeader(al)) {
833 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
834 out = sb.c_str();
835 quote = 1;
836 }
837 break;
838
839 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
840 if (al->proxyProtocolHeader) {
841 sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
842 out = sb.c_str();
843 quote = 1;
844 }
845 break;
846
847 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
848 if (al->proxyProtocolHeader) {
849 sb = al->proxyProtocolHeader->toMime();
850 out = sb.c_str();
851 quote = 1;
852 }
853 break;
854
855 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
856 if (al->proxyProtocolHeader) {
857 sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
858 out = sb.c_str();
859 quote = 1;
860 }
861 break;
862
863 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
864 if (al->adapted_request) {
865 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
866 out = sb.c_str();
867 quote = 1;
868 }
869 break;
870
871 case LFT_REPLY_HEADER_ELEM:
872 if (const Http::Message *msg = actualReplyHeader(al)) {
873 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
874 out = sb.c_str();
875 quote = 1;
876 }
877 break;
878
879 case LFT_REQUEST_ALL_HEADERS:
880 #if ICAP_CLIENT
881 if (al->icap.reqMethod == Adaptation::methodRespmod) {
882 // XXX: since AccessLogEntry::Headers lacks virgin response
883 // headers, do nothing for now
884 out = nullptr;
885 } else
886 #endif
887 {
888 // just headers without start-line and CRLF
889 // XXX: reconcile with '<h'
890 out = al->headers.request;
891 quote = 1;
892 }
893 break;
894
895 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
896 // just headers without start-line and CRLF
897 // XXX: reconcile with '<h'
898 out = al->headers.adapted_request;
899 quote = 1;
900 break;
901
902 case LFT_REPLY_ALL_HEADERS: {
903 MemBuf allHeaders;
904 allHeaders.init();
905 // status-line + headers + CRLF
906 // XXX: reconcile with '>h' and '>ha'
907 al->packReplyHeaders(allHeaders);
908 sb.assign(allHeaders.content(), allHeaders.contentSize());
909 out = sb.c_str();
910 #if ICAP_CLIENT
911 if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
912 out = al->headers.adapted_request;
913 #endif
914 quote = 1;
915 }
916 break;
917
918 case LFT_USER_NAME:
919 #if USE_AUTH
920 if (al->request && al->request->auth_user_request)
921 out = strOrNull(al->request->auth_user_request->username());
922 #endif
923 if (!out && al->request && al->request->extacl_user.size()) {
924 if (const char *t = al->request->extacl_user.termedBuf())
925 out = t;
926 }
927 if (!out)
928 out = strOrNull(al->getExtUser());
929 #if USE_OPENSSL
930 if (!out)
931 out = strOrNull(al->cache.ssluser);
932 #endif
933 if (!out)
934 out = strOrNull(al->getClientIdent());
935 break;
936
937 case LFT_USER_LOGIN:
938 #if USE_AUTH
939 if (al->request && al->request->auth_user_request)
940 out = strOrNull(al->request->auth_user_request->username());
941 #endif
942 break;
943
944 case LFT_USER_IDENT:
945 out = strOrNull(al->getClientIdent());
946 break;
947
948 case LFT_USER_EXTERNAL:
949 out = strOrNull(al->getExtUser());
950 break;
951
952 /* case LFT_USER_REALM: */
953 /* case LFT_USER_SCHEME: */
954
955 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
956 // but compiler complains if omitted
957 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
958 case LFT_HTTP_SENT_STATUS_CODE:
959 outint = al->http.code;
960 doint = 1;
961 break;
962
963 case LFT_HTTP_RECEIVED_STATUS_CODE:
964 if (al->hier.peer_reply_status != Http::scNone) {
965 outint = al->hier.peer_reply_status;
966 doint = 1;
967 }
968 break;
969 /* case LFT_HTTP_STATUS:
970 * out = statusline->text;
971 * quote = 1;
972 * break;
973 */
974 case LFT_HTTP_BODY_BYTES_READ:
975 if (al->hier.bodyBytesRead >= 0) {
976 outoff = al->hier.bodyBytesRead;
977 dooff = 1;
978 }
979 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
980 // a peer server so just print a "-" (eg requests served from cache,
981 // or internal error messages).
982 break;
983
984 case LFT_SQUID_STATUS:
985 out = al->cache.code.c_str();
986 break;
987
988 case LFT_SQUID_ERROR:
989 if (const auto error = al->error())
990 out = errorPageName(error->category);
991 break;
992
993 case LFT_SQUID_ERROR_DETAIL:
994 if (const auto error = al->error()) {
995 if (const auto detail = error->detail) {
996 sb = detail->brief();
997 out = sb.c_str();
998 }
999 }
1000 break;
1001
1002 case LFT_SQUID_HIERARCHY:
1003 if (al->hier.ping.timedout)
1004 mb.append("TIMEOUT_", 8);
1005 out = hier_code_str[al->hier.code];
1006 break;
1007
1008 case LFT_MIME_TYPE:
1009 out = al->http.content_type;
1010 break;
1011
1012 case LFT_CLIENT_REQ_METHOD:
1013 if (al->request) {
1014 sb = al->request->method.image();
1015 out = sb.c_str();
1016 quote = 1;
1017 }
1018 break;
1019
1020 case LFT_CLIENT_REQ_URI:
1021 if (const auto uri = al->effectiveVirginUrl()) {
1022 sb = *uri;
1023 out = sb.c_str();
1024 quote = 1;
1025 }
1026 break;
1027
1028 case LFT_CLIENT_REQ_URLSCHEME:
1029 if (al->request) {
1030 sb = al->request->url.getScheme().image();
1031 out = sb.c_str();
1032 quote = 1;
1033 }
1034 break;
1035
1036 case LFT_CLIENT_REQ_URLDOMAIN:
1037 if (al->request) {
1038 out = al->request->url.host();
1039 quote = 1;
1040 }
1041 break;
1042
1043 case LFT_CLIENT_REQ_URLPORT:
1044 if (al->request) {
1045 outint = al->request->url.port();
1046 doint = 1;
1047 }
1048 break;
1049
1050 case LFT_REQUEST_URLPATH_OLD_31:
1051 case LFT_CLIENT_REQ_URLPATH:
1052 if (al->request) {
1053 sb = al->request->url.path();
1054 out = sb.c_str();
1055 quote = 1;
1056 }
1057 break;
1058
1059 case LFT_CLIENT_REQ_VERSION:
1060 if (al->request) {
1061 sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
1062 out = sb.c_str();
1063 }
1064 break;
1065
1066 case LFT_REQUEST_METHOD:
1067 if (al->hasLogMethod()) {
1068 sb = al->getLogMethod();
1069 out = sb.c_str();
1070 quote = 1;
1071 }
1072 break;
1073
1074 case LFT_REQUEST_URI:
1075 if (!al->url.isEmpty()) {
1076 sb = al->url;
1077 out = sb.c_str();
1078 }
1079 break;
1080
1081 case LFT_REQUEST_VERSION_OLD_2X:
1082 case LFT_REQUEST_VERSION:
1083 sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
1084 out = sb.c_str();
1085 break;
1086
1087 case LFT_SERVER_REQ_METHOD:
1088 if (al->adapted_request) {
1089 sb = al->adapted_request->method.image();
1090 out = sb.c_str();
1091 quote = 1;
1092 }
1093 break;
1094
1095 case LFT_SERVER_REQ_URI:
1096 // adapted request URI sent to server/peer
1097 if (al->adapted_request) {
1098 sb = al->adapted_request->effectiveRequestUri();
1099 out = sb.c_str();
1100 quote = 1;
1101 }
1102 break;
1103
1104 case LFT_SERVER_REQ_URLSCHEME:
1105 if (al->adapted_request) {
1106 sb = al->adapted_request->url.getScheme().image();
1107 out = sb.c_str();
1108 quote = 1;
1109 }
1110 break;
1111
1112 case LFT_SERVER_REQ_URLDOMAIN:
1113 if (al->adapted_request) {
1114 out = al->adapted_request->url.host();
1115 quote = 1;
1116 }
1117 break;
1118
1119 case LFT_SERVER_REQ_URLPORT:
1120 if (al->adapted_request) {
1121 outint = al->adapted_request->url.port();
1122 doint = 1;
1123 }
1124 break;
1125
1126 case LFT_SERVER_REQ_URLPATH:
1127 if (al->adapted_request) {
1128 sb = al->adapted_request->url.path();
1129 out = sb.c_str();
1130 quote = 1;
1131 }
1132 break;
1133
1134 case LFT_SERVER_REQ_VERSION:
1135 if (al->adapted_request) {
1136 sb.appendf("%u.%u",
1137 al->adapted_request->http_ver.major,
1138 al->adapted_request->http_ver.minor);
1139 out = tmp;
1140 }
1141 break;
1142
1143 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
1144 outoff = al->http.clientRequestSz.messageTotal();
1145 dooff = 1;
1146 break;
1147
1148 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
1149 outoff = al->http.clientRequestSz.header;
1150 dooff =1;
1151 break;
1152
1153 /*case LFT_REQUEST_SIZE_BODY: */
1154 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1155
1156 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
1157 outoff = al->http.clientReplySz.messageTotal();
1158 dooff = 1;
1159 break;
1160
1161 case LFT_REPLY_HIGHOFFSET:
1162 outoff = al->cache.highOffset;
1163 dooff = 1;
1164 break;
1165
1166 case LFT_REPLY_OBJECTSIZE:
1167 outoff = al->cache.objectSize;
1168 dooff = 1;
1169 break;
1170
1171 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
1172 outint = al->http.clientReplySz.header;
1173 doint = 1;
1174 break;
1175
1176 /*case LFT_REPLY_SIZE_BODY: */
1177 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1178
1179 case LFT_CLIENT_IO_SIZE_TOTAL:
1180 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
1181 doint = 1;
1182 break;
1183 /*case LFT_SERVER_IO_SIZE_TOTAL: */
1184
1185 case LFT_TAG:
1186 if (al->request) {
1187 out = al->request->tag.termedBuf();
1188 quote = 1;
1189 }
1190 break;
1191
1192 case LFT_EXT_LOG:
1193 if (al->request) {
1194 out = al->request->extacl_log.termedBuf();
1195 quote = 1;
1196 }
1197 break;
1198
1199 case LFT_SEQUENCE_NUMBER:
1200 outoff = logSequenceNumber;
1201 dooff = 1;
1202 break;
1203
1204 #if USE_OPENSSL
1205 case LFT_SSL_BUMP_MODE: {
1206 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1207 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1208 out = Ssl::bumpMode(mode);
1209 }
1210 break;
1211
1212 case LFT_EXT_ACL_USER_CERT_RAW:
1213 if (al->request) {
1214 ConnStateData *conn = al->request->clientConnectionManager.get();
1215 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1216 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1217 sb = sslGetUserCertificatePEM(ssl);
1218 out = sb.c_str();
1219 }
1220 }
1221 }
1222 break;
1223
1224 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1225 if (al->request) {
1226 ConnStateData *conn = al->request->clientConnectionManager.get();
1227 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1228 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1229 sb = sslGetUserCertificatePEM(ssl);
1230 out = sb.c_str();
1231 }
1232 }
1233 }
1234 break;
1235
1236 case LFT_EXT_ACL_USER_CERT:
1237 if (al->request) {
1238 ConnStateData *conn = al->request->clientConnectionManager.get();
1239 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1240 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1241 out = sslGetUserAttribute(ssl, fmt->data.header.header);
1242 }
1243 }
1244 break;
1245
1246 case LFT_EXT_ACL_USER_CA_CERT:
1247 if (al->request) {
1248 ConnStateData *conn = al->request->clientConnectionManager.get();
1249 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1250 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1251 out = sslGetCAAttribute(ssl, fmt->data.header.header);
1252 }
1253 }
1254 break;
1255
1256 case LFT_SSL_USER_CERT_SUBJECT:
1257 if (const auto &cert = al->cache.sslClientCert) {
1258 sb = Security::SubjectName(*cert);
1259 out = sb.c_str();
1260 }
1261 break;
1262
1263 case LFT_SSL_USER_CERT_ISSUER:
1264 if (const auto &cert = al->cache.sslClientCert) {
1265 sb = Security::IssuerName(*cert);
1266 out = sb.c_str();
1267 }
1268 break;
1269
1270 case LFT_SSL_CLIENT_SNI:
1271 if (al->request && al->request->clientConnectionManager.valid()) {
1272 if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
1273 if (!conn->tlsClientSni().isEmpty()) {
1274 sb = conn->tlsClientSni();
1275 out = sb.c_str();
1276 }
1277 }
1278 }
1279 break;
1280
1281 case LFT_SSL_SERVER_CERT_ERRORS:
1282 if (al->request && al->request->clientConnectionManager.valid()) {
1283 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1284 const char *separator = fmt->data.string ? fmt->data.string : ":";
1285 for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
1286 if (!sb.isEmpty())
1287 sb.append(separator);
1288 sb.append(Ssl::GetErrorName(sslError->element.code, true));
1289 if (sslError->element.depth >= 0)
1290 sb.appendf("@depth=%d", sslError->element.depth);
1291 }
1292 if (!sb.isEmpty())
1293 out = sb.c_str();
1294 }
1295 }
1296 break;
1297
1298 case LFT_SSL_SERVER_CERT_ISSUER:
1299 case LFT_SSL_SERVER_CERT_SUBJECT:
1300 case LFT_SSL_SERVER_CERT_WHOLE:
1301 if (al->request && al->request->clientConnectionManager.valid()) {
1302 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1303 if (X509 *serverCert = srvBump->serverCert.get()) {
1304 if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
1305 out = Ssl::GetX509UserAttribute(serverCert, "DN");
1306 else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
1307 out = Ssl::GetX509CAAttribute(serverCert, "DN");
1308 else {
1309 assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
1310 sb = Ssl::GetX509PEM(serverCert);
1311 out = sb.c_str();
1312 quote = 1;
1313 }
1314 }
1315 }
1316 }
1317 break;
1318
1319 case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
1320 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1321 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
1322 break;
1323
1324 case LFT_TLS_SERVER_NEGOTIATED_VERSION:
1325 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1326 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
1327 break;
1328
1329 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
1330 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1331 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
1332 break;
1333
1334 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
1335 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1336 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
1337 break;
1338
1339 case LFT_TLS_CLIENT_SUPPORTED_VERSION:
1340 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1341 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
1342 break;
1343
1344 case LFT_TLS_SERVER_SUPPORTED_VERSION:
1345 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1346 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
1347 break;
1348
1349 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
1350 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1351 out = al->tcpClient->hasTlsNegotiations()->cipherName();
1352 break;
1353
1354 case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
1355 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1356 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
1357 break;
1358 #endif
1359
1360 case LFT_REQUEST_URLGROUP_OLD_2X:
1361 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
1362 break;
1363
1364 case LFT_NOTE:
1365 tmp[0] = fmt->data.header.separator;
1366 tmp[1] = '\0';
1367 if (fmt->data.header.header && *fmt->data.header.header) {
1368 const char *separator = tmp;
1369 static SBuf note;
1370 #if USE_ADAPTATION
1371 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1372 if (ah && ah->metaHeaders) {
1373 if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
1374 sb.append(note);
1375 }
1376 #endif
1377 if (al->notes) {
1378 if (al->notes->find(note, fmt->data.header.header, separator)) {
1379 if (!sb.isEmpty())
1380 sb.append(separator);
1381 sb.append(note);
1382 }
1383 }
1384 out = sb.c_str();
1385 quote = 1;
1386 } else {
1387 // if no argument given use default "\r\n" as notes separator
1388 const char *separator = fmt->data.string ? tmp : "\r\n";
1389 #if USE_ADAPTATION
1390 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1391 if (ah && ah->metaHeaders && !ah->metaHeaders->empty())
1392 sb.append(ah->metaHeaders->toString(separator));
1393 #endif
1394 if (al->notes && !al->notes->empty())
1395 sb.append(al->notes->toString(separator));
1396
1397 out = sb.c_str();
1398 quote = 1;
1399 }
1400 break;
1401
1402 case LFT_CREDENTIALS:
1403 #if USE_AUTH
1404 if (al->request && al->request->auth_user_request)
1405 out = strOrNull(al->request->auth_user_request->credentialsStr());
1406 #endif
1407 break;
1408
1409 case LFT_PERCENT:
1410 out = "%";
1411 break;
1412
1413 case LFT_EXT_ACL_NAME:
1414 out = al->lastAclName;
1415 break;
1416
1417 case LFT_EXT_ACL_DATA:
1418 if (!al->lastAclData.isEmpty())
1419 out = al->lastAclData.c_str();
1420 break;
1421
1422 case LFT_MASTER_XACTION:
1423 if (al->request) {
1424 doUint64 = true;
1425 outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
1426 break;
1427 }
1428 }
1429
1430 if (dooff) {
1431 sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
1432 out = sb.c_str();
1433
1434 } else if (doint) {
1435 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
1436 out = sb.c_str();
1437 } else if (doUint64) {
1438 sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
1439 out = sb.c_str();
1440 } else if (doMsec) {
1441 if (fmt->widthMax < 0) {
1442 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
1443 } else {
1444 int precision = fmt->widthMax;
1445 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)));
1446 }
1447 out = sb.c_str();
1448 } else if (doSec) {
1449 int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
1450 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));
1451 out = sb.c_str();
1452 }
1453
1454 if (out && *out) {
1455 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1456 // Do not write to the tmp buffer because it may contain the to-be-quoted value.
1457 static char quotedOut[2 * sizeof(tmp)];
1458 static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
1459 quotedOut[0] = '\0';
1460
1461 char *newout = nullptr;
1462 int newfree = 0;
1463
1464 switch (fmt->quote) {
1465
1466 case LOG_QUOTE_NONE:
1467 newout = rfc1738_escape_unescaped(out);
1468 break;
1469
1470 case LOG_QUOTE_QUOTES: {
1471 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1472 if (out_len >= sizeof(tmp)) {
1473 newout = (char *)xmalloc(out_len);
1474 newfree = 1;
1475 } else
1476 newout = quotedOut;
1477 log_quoted_string(out, newout);
1478 }
1479 break;
1480
1481 case LOG_QUOTE_MIMEBLOB:
1482 newout = QuoteMimeBlob(out);
1483 newfree = 1;
1484 break;
1485
1486 case LOG_QUOTE_URL:
1487 newout = rfc1738_escape(out);
1488 break;
1489
1490 case LOG_QUOTE_SHELL: {
1491 MemBuf mbq;
1492 mbq.init();
1493 strwordquote(&mbq, out);
1494 newout = mbq.content();
1495 mbq.stolen = 1;
1496 newfree = 1;
1497 }
1498 break;
1499
1500 case LOG_QUOTE_RAW:
1501 break;
1502 }
1503
1504 if (newout) {
1505 if (dofree)
1506 safe_free(out);
1507
1508 out = newout;
1509
1510 dofree = newfree;
1511 }
1512 }
1513
1514 // enforce width limits if configured
1515 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
1516 if (haveMaxWidth || fmt->widthMin) {
1517 const int minWidth = fmt->widthMin >= 0 ?
1518 fmt->widthMin :0;
1519 const int maxWidth = haveMaxWidth ?
1520 fmt->widthMax : strlen(out);
1521
1522 if (fmt->left)
1523 mb.appendf("%-*.*s", minWidth, maxWidth, out);
1524 else
1525 mb.appendf("%*.*s", minWidth, maxWidth, out);
1526 } else
1527 mb.append(out, strlen(out));
1528 } else {
1529 mb.append("-", 1);
1530 }
1531
1532 if (fmt->space)
1533 mb.append(" ", 1);
1534
1535 sb.clear();
1536
1537 if (dofree)
1538 safe_free(out);
1539 }
1540 }
1541