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