]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/format/Format.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / format / Format.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2020 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 "err_detail_type.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 "fqdncache.h"
21#include "http/Stream.h"
22#include "HttpRequest.h"
23#include "MemBuf.h"
24#include "proxyp/Header.h"
25#include "rfc1738.h"
26#include "sbuf/Stream.h"
27#include "sbuf/StringConvert.h"
28#include "security/CertError.h"
29#include "security/NegotiationHistory.h"
30#include "SquidTime.h"
31#include "Store.h"
32#include "tools.h"
33#if USE_OPENSSL
34#include "ssl/ErrorDetail.h"
35#include "ssl/ServerBump.h"
36#endif
37
38/// Convert a string to NULL pointer if it is ""
39#define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
40
41const SBuf Format::Dash("-");
42
43Format::Format::Format(const char *n) :
44 format(NULL),
45 next(NULL)
46{
47 name = xstrdup(n);
48}
49
50Format::Format::~Format()
51{
52 // erase the list without consuming stack space
53 while (next) {
54 // unlink the next entry for deletion
55 Format *temp = next;
56 next = temp->next;
57 temp->next = NULL;
58 delete temp;
59 }
60
61 // remove locals
62 xfree(name);
63 delete format;
64}
65
66bool
67Format::Format::parse(const char *def)
68{
69 const char *cur, *eos;
70 Token *new_lt, *last_lt;
71 enum Quoting quote = LOG_QUOTE_NONE;
72
73 debugs(46, 2, HERE << "got definition '" << def << "'");
74
75 if (format) {
76 debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
77 return false;
78 }
79
80 /* very inefficent parser, but who cares, this needs to be simple */
81 /* First off, let's tokenize, we'll optimize in a second pass.
82 * A token can either be a %-prefixed sequence (usually a dynamic
83 * token but it can be an escaped sequence), or a string. */
84 cur = def;
85 eos = def + strlen(def);
86 format = new_lt = last_lt = new Token;
87 cur += new_lt->parse(cur, &quote);
88
89 while (cur < eos) {
90 new_lt = new Token;
91 last_lt->next = new_lt;
92 last_lt = new_lt;
93 cur += new_lt->parse(cur, &quote);
94 }
95
96 return true;
97}
98
99size_t
100Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
101{
102 Token tkn;
103 enum Quoting quote = LOG_QUOTE_NONE;
104 const auto tokenSize = tkn.parse(token, &quote);
105 assert(tokenSize > 0);
106 if (ale != nullptr) {
107 Format fmt("SimpleToken");
108 fmt.format = &tkn;
109 fmt.assemble(mb, ale, 0);
110 fmt.format = nullptr;
111 } else
112 mb.append("-", 1);
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, HERE);
120
121 // loop rather than recursing to conserve stack space.
122 for (const Format *fmt = this; fmt; fmt = fmt->next) {
123 debugs(46, 3, HERE << "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 = NULL;
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#if USE_OPENSSL
345static char *
346sslErrorName(Security::ErrorCode err, char *buf, size_t size)
347{
348 snprintf(buf, size, "SSL_ERR=%d", err);
349 return buf;
350}
351#endif
352
353/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
354/// headers from sent headers rather than failing to distinguish requests from responses.
355/// \retval HttpReply sent to the HTTP client (access.log and default context).
356/// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
357/// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
358static const Http::Message *
359actualReplyHeader(const AccessLogEntry::Pointer &al)
360{
361 const Http::Message *msg = al->reply.getRaw();
362#if ICAP_CLIENT
363 // al->icap.reqMethod is methodNone in access.log context
364 if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
365 msg = al->adapted_request;
366#endif
367 return msg;
368}
369
370/// XXX: Misnamed. See actualReplyHeader().
371/// \return HttpRequest or HttpReply for %http::>h.
372static const Http::Message *
373actualRequestHeader(const AccessLogEntry::Pointer &al)
374{
375#if ICAP_CLIENT
376 // al->icap.reqMethod is methodNone in access.log context
377 if (al->icap.reqMethod == Adaptation::methodRespmod) {
378 // XXX: for now AccessLogEntry lacks virgin response headers
379 return nullptr;
380 }
381#endif
382 return al->request;
383}
384
385void
386Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
387{
388 static char tmp[1024];
389 SBuf sb;
390
391 for (Token *fmt = format; fmt; fmt = fmt->next) { /* for each token */
392 const char *out = nullptr;
393 int quote = 0;
394 long int outint = 0;
395 int doint = 0;
396 int dofree = 0;
397 int64_t outoff = 0;
398 int dooff = 0;
399 struct timeval outtv = {0, 0};
400 int doMsec = 0;
401 int doSec = 0;
402 bool doUint64 = false;
403 uint64_t outUint64 = 0;
404
405 switch (fmt->type) {
406
407 case LFT_NONE:
408 out = "";
409 break;
410
411 case LFT_STRING:
412 out = fmt->data.string;
413 break;
414
415 case LFT_CLIENT_IP_ADDRESS:
416 al->getLogClientIp(tmp, sizeof(tmp));
417 out = tmp;
418 break;
419
420 case LFT_CLIENT_FQDN:
421 if (al->cache.caddr.isAnyAddr()) // e.g., ICAP OPTIONS lack client
422 out = "-";
423 else
424 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
425
426 if (!out) {
427 out = al->cache.caddr.toStr(tmp, sizeof(tmp));
428 }
429 break;
430
431 case LFT_CLIENT_PORT:
432 if (al->request) {
433 outint = al->request->client_addr.port();
434 doint = 1;
435 } else if (al->tcpClient) {
436 outint = al->tcpClient->remote.port();
437 doint = 1;
438 }
439 break;
440
441 case LFT_CLIENT_EUI:
442#if USE_SQUID_EUI
443 // TODO make the ACL checklist have a direct link to any TCP details.
444 if (al->request && al->request->clientConnectionManager.valid() &&
445 al->request->clientConnectionManager->clientConnection) {
446 const auto &conn = al->request->clientConnectionManager->clientConnection;
447 if (conn->remote.isIPv4())
448 conn->remoteEui48.encode(tmp, sizeof(tmp));
449 else
450 conn->remoteEui64.encode(tmp, sizeof(tmp));
451 out = tmp;
452 }
453#endif
454 break;
455
456 case LFT_EXT_ACL_CLIENT_EUI48:
457#if USE_SQUID_EUI
458 if (al->request && al->request->clientConnectionManager.valid() &&
459 al->request->clientConnectionManager->clientConnection &&
460 al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
461 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
462 out = tmp;
463 }
464#endif
465 break;
466
467 case LFT_EXT_ACL_CLIENT_EUI64:
468#if USE_SQUID_EUI
469 if (al->request && al->request->clientConnectionManager.valid() &&
470 al->request->clientConnectionManager->clientConnection &&
471 !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
472 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
473 out = tmp;
474 }
475#endif
476 break;
477
478 case LFT_SERVER_IP_ADDRESS:
479 if (al->hier.tcpServer)
480 out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
481 break;
482
483 case LFT_SERVER_FQDN_OR_PEER_NAME:
484 out = al->hier.host;
485 break;
486
487 case LFT_SERVER_PORT:
488 if (al->hier.tcpServer) {
489 outint = al->hier.tcpServer->remote.port();
490 doint = 1;
491 }
492 break;
493
494 case LFT_LOCAL_LISTENING_IP:
495 if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
496 out = addr->toStr(tmp, sizeof(tmp));
497 break;
498
499 case LFT_CLIENT_LOCAL_IP:
500 if (al->tcpClient)
501 out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
502 break;
503
504 case LFT_CLIENT_LOCAL_TOS:
505 if (al->tcpClient) {
506 sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
507 out = sb.c_str();
508 }
509 break;
510
511 case LFT_CLIENT_LOCAL_NFMARK:
512 if (al->tcpClient) {
513 sb.appendf("0x%x", al->tcpClient->nfmark);
514 out = sb.c_str();
515 }
516 break;
517
518 case LFT_LOCAL_LISTENING_PORT:
519 if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw())) {
520 outint = addr->port();
521 doint = 1;
522 }
523 break;
524
525 case LFT_CLIENT_LOCAL_PORT:
526 if (al->tcpClient) {
527 outint = al->tcpClient->local.port();
528 doint = 1;
529 }
530 break;
531
532 case LFT_SERVER_LOCAL_IP_OLD_27:
533 case LFT_SERVER_LOCAL_IP:
534 if (al->hier.tcpServer)
535 out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
536 break;
537
538 case LFT_SERVER_LOCAL_PORT:
539 if (al->hier.tcpServer) {
540 outint = al->hier.tcpServer->local.port();
541 doint = 1;
542 }
543 break;
544
545 case LFT_SERVER_LOCAL_TOS:
546 if (al->hier.tcpServer) {
547 sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
548 out = sb.c_str();
549 }
550 break;
551
552 case LFT_SERVER_LOCAL_NFMARK:
553 if (al->hier.tcpServer) {
554 sb.appendf("0x%x", al->hier.tcpServer->nfmark);
555 out = sb.c_str();
556 }
557 break;
558
559 case LFT_CLIENT_HANDSHAKE:
560 if (al->request && al->request->clientConnectionManager.valid()) {
561 const auto &handshake = al->request->clientConnectionManager->preservedClientData;
562 if (const auto rawLength = handshake.length()) {
563 // add 1 byte to optimize the c_str() conversion below
564 char *buf = sb.rawAppendStart(base64_encode_len(rawLength) + 1);
565
566 struct base64_encode_ctx ctx;
567 base64_encode_init(&ctx);
568 auto encLength = base64_encode_update(&ctx, buf, rawLength, reinterpret_cast<const uint8_t*>(handshake.rawContent()));
569 encLength += base64_encode_final(&ctx, buf + encLength);
570
571 sb.rawAppendFinish(buf, encLength);
572 out = sb.c_str();
573 }
574 }
575 break;
576
577 case LFT_TIME_SECONDS_SINCE_EPOCH:
578 // some platforms store time in 32-bit, some 64-bit...
579 outoff = static_cast<int64_t>(current_time.tv_sec);
580 dooff = 1;
581 break;
582
583 case LFT_TIME_SUBSECOND:
584 outint = current_time.tv_usec / fmt->divisor;
585 doint = 1;
586 break;
587
588 case LFT_TIME_LOCALTIME:
589 case LFT_TIME_GMT: {
590 const char *spec;
591 struct tm *t;
592 spec = fmt->data.string;
593
594 if (fmt->type == LFT_TIME_LOCALTIME) {
595 if (!spec)
596 spec = "%d/%b/%Y:%H:%M:%S %z";
597 t = localtime(&squid_curtime);
598 } else {
599 if (!spec)
600 spec = "%d/%b/%Y:%H:%M:%S";
601
602 t = gmtime(&squid_curtime);
603 }
604
605 strftime(tmp, sizeof(tmp), spec, t);
606 out = tmp;
607 }
608 break;
609
610 case LFT_TIME_START:
611 outtv = al->cache.start_time;
612 doSec = 1;
613 break;
614
615 case LFT_TIME_TO_HANDLE_REQUEST:
616 outtv = al->cache.trTime;
617 doMsec = 1;
618 break;
619
620 case LFT_PEER_RESPONSE_TIME:
621 struct timeval peerResponseTime;
622 if (al->hier.peerResponseTime(peerResponseTime)) {
623 outtv = peerResponseTime;
624 doMsec = 1;
625 }
626 break;
627
628 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
629 struct timeval totalResponseTime;
630 if (al->hier.totalResponseTime(totalResponseTime)) {
631 outtv = totalResponseTime;
632 doMsec = 1;
633 }
634 }
635 break;
636
637 case LFT_DNS_WAIT_TIME:
638 if (al->request && al->request->dnsWait >= 0) {
639 // TODO: microsecond precision for dns wait time.
640 // Convert miliseconds to timeval struct:
641 outtv.tv_sec = al->request->dnsWait / 1000;
642 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
643 doMsec = 1;
644 }
645 break;
646
647 case LFT_REQUEST_HEADER:
648 if (const Http::Message *msg = actualRequestHeader(al)) {
649 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
650 out = sb.c_str();
651 quote = 1;
652 }
653 break;
654
655 case LFT_ADAPTED_REQUEST_HEADER:
656 if (al->adapted_request) {
657 sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
658 out = sb.c_str();
659 quote = 1;
660 }
661 break;
662
663 case LFT_REPLY_HEADER:
664 if (const Http::Message *msg = actualReplyHeader(al)) {
665 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
666 out = sb.c_str();
667 quote = 1;
668 }
669 break;
670
671#if USE_ADAPTATION
672 case LFT_ADAPTATION_SUM_XACT_TIMES:
673 if (al->request) {
674 Adaptation::History::Pointer ah = al->request->adaptHistory();
675 if (ah) {
676 ah->sumLogString(fmt->data.string, sb);
677 out = sb.c_str();
678 }
679 }
680 break;
681
682 case LFT_ADAPTATION_ALL_XACT_TIMES:
683 if (al->request) {
684 Adaptation::History::Pointer ah = al->request->adaptHistory();
685 if (ah) {
686 ah->allLogString(fmt->data.string, sb);
687 out = sb.c_str();
688 }
689 }
690 break;
691
692 case LFT_ADAPTATION_LAST_HEADER:
693 if (al->request) {
694 const Adaptation::History::Pointer ah = al->request->adaptHistory();
695 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
696 sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
697 out = sb.c_str();
698 quote = 1;
699 }
700 }
701 break;
702
703 case LFT_ADAPTATION_LAST_HEADER_ELEM:
704 if (al->request) {
705 const Adaptation::History::Pointer ah = al->request->adaptHistory();
706 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
707 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
708 out = sb.c_str();
709 quote = 1;
710 }
711 }
712 break;
713
714 case LFT_ADAPTATION_LAST_ALL_HEADERS:
715 out = al->adapt.last_meta;
716 quote = 1;
717 break;
718#endif
719
720#if ICAP_CLIENT
721 case LFT_ICAP_ADDR:
722 out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
723 break;
724
725 case LFT_ICAP_SERV_NAME:
726 out = al->icap.serviceName.termedBuf();
727 break;
728
729 case LFT_ICAP_REQUEST_URI:
730 out = al->icap.reqUri.termedBuf();
731 break;
732
733 case LFT_ICAP_REQUEST_METHOD:
734 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
735 break;
736
737 case LFT_ICAP_BYTES_SENT:
738 outoff = al->icap.bytesSent;
739 dooff = 1;
740 break;
741
742 case LFT_ICAP_BYTES_READ:
743 outoff = al->icap.bytesRead;
744 dooff = 1;
745 break;
746
747 case LFT_ICAP_BODY_BYTES_READ:
748 if (al->icap.bodyBytesRead >= 0) {
749 outoff = al->icap.bodyBytesRead;
750 dooff = 1;
751 }
752 // else if icap.bodyBytesRead < 0, we do not have any http data,
753 // so just print a "-" (204 responses etc)
754 break;
755
756 case LFT_ICAP_REQ_HEADER:
757 if (al->icap.request) {
758 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
759 out = sb.c_str();
760 quote = 1;
761 }
762 break;
763
764 case LFT_ICAP_REQ_HEADER_ELEM:
765 if (al->icap.request) {
766 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
767 out = sb.c_str();
768 quote = 1;
769 }
770 break;
771
772 case LFT_ICAP_REQ_ALL_HEADERS:
773 if (al->icap.request) {
774 HttpHeaderPos pos = HttpHeaderInitPos;
775 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
776 sb.append(e->name);
777 sb.append(": ");
778 sb.append(StringToSBuf(e->value));
779 sb.append("\r\n");
780 }
781 out = sb.c_str();
782 quote = 1;
783 }
784 break;
785
786 case LFT_ICAP_REP_HEADER:
787 if (al->icap.reply) {
788 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
789 out = sb.c_str();
790 quote = 1;
791 }
792 break;
793
794 case LFT_ICAP_REP_HEADER_ELEM:
795 if (al->icap.reply) {
796 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
797 out = sb.c_str();
798 quote = 1;
799 }
800 break;
801
802 case LFT_ICAP_REP_ALL_HEADERS:
803 if (al->icap.reply) {
804 HttpHeaderPos pos = HttpHeaderInitPos;
805 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
806 sb.append(e->name);
807 sb.append(": ");
808 sb.append(StringToSBuf(e->value));
809 sb.append("\r\n");
810 }
811 out = sb.c_str();
812 quote = 1;
813 }
814 break;
815
816 case LFT_ICAP_TR_RESPONSE_TIME:
817 outtv = al->icap.trTime;
818 doMsec = 1;
819 break;
820
821 case LFT_ICAP_IO_TIME:
822 outtv = al->icap.ioTime;
823 doMsec = 1;
824 break;
825
826 case LFT_ICAP_STATUS_CODE:
827 outint = al->icap.resStatus;
828 doint = 1;
829 break;
830
831 case LFT_ICAP_OUTCOME:
832 out = al->icap.outcome;
833 break;
834
835 case LFT_ICAP_TOTAL_TIME:
836 outtv = al->icap.processingTime;
837 doMsec = 1;
838 break;
839#endif
840 case LFT_REQUEST_HEADER_ELEM:
841 if (const Http::Message *msg = actualRequestHeader(al)) {
842 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
843 out = sb.c_str();
844 quote = 1;
845 }
846 break;
847
848 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
849 if (al->proxyProtocolHeader) {
850 sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
851 out = sb.c_str();
852 quote = 1;
853 }
854 break;
855
856 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
857 if (al->proxyProtocolHeader) {
858 sb = al->proxyProtocolHeader->toMime();
859 out = sb.c_str();
860 quote = 1;
861 }
862 break;
863
864 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
865 if (al->proxyProtocolHeader) {
866 sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
867 out = sb.c_str();
868 quote = 1;
869 }
870 break;
871
872 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
873 if (al->adapted_request) {
874 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
875 out = sb.c_str();
876 quote = 1;
877 }
878 break;
879
880 case LFT_REPLY_HEADER_ELEM:
881 if (const Http::Message *msg = actualReplyHeader(al)) {
882 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
883 out = sb.c_str();
884 quote = 1;
885 }
886 break;
887
888 case LFT_REQUEST_ALL_HEADERS:
889#if ICAP_CLIENT
890 if (al->icap.reqMethod == Adaptation::methodRespmod) {
891 // XXX: since AccessLogEntry::Headers lacks virgin response
892 // headers, do nothing for now
893 out = nullptr;
894 } else
895#endif
896 {
897 // just headers without start-line and CRLF
898 // XXX: reconcile with '<h'
899 out = al->headers.request;
900 quote = 1;
901 }
902 break;
903
904 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
905 // just headers without start-line and CRLF
906 // XXX: reconcile with '<h'
907 out = al->headers.adapted_request;
908 quote = 1;
909 break;
910
911 case LFT_REPLY_ALL_HEADERS: {
912 MemBuf allHeaders;
913 allHeaders.init();
914 // status-line + headers + CRLF
915 // XXX: reconcile with '>h' and '>ha'
916 al->packReplyHeaders(allHeaders);
917 sb.assign(allHeaders.content(), allHeaders.contentSize());
918 out = sb.c_str();
919#if ICAP_CLIENT
920 if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
921 out = al->headers.adapted_request;
922#endif
923 quote = 1;
924 }
925 break;
926
927 case LFT_USER_NAME:
928#if USE_AUTH
929 if (al->request && al->request->auth_user_request)
930 out = strOrNull(al->request->auth_user_request->username());
931#endif
932 if (!out && al->request && al->request->extacl_user.size()) {
933 if (const char *t = al->request->extacl_user.termedBuf())
934 out = t;
935 }
936 if (!out)
937 out = strOrNull(al->getExtUser());
938#if USE_OPENSSL
939 if (!out)
940 out = strOrNull(al->cache.ssluser);
941#endif
942 if (!out)
943 out = strOrNull(al->getClientIdent());
944 break;
945
946 case LFT_USER_LOGIN:
947#if USE_AUTH
948 if (al->request && al->request->auth_user_request)
949 out = strOrNull(al->request->auth_user_request->username());
950#endif
951 break;
952
953 case LFT_USER_IDENT:
954 out = strOrNull(al->getClientIdent());
955 break;
956
957 case LFT_USER_EXTERNAL:
958 out = strOrNull(al->getExtUser());
959 break;
960
961 /* case LFT_USER_REALM: */
962 /* case LFT_USER_SCHEME: */
963
964 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
965 // but compiler complains if ommited
966 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
967 case LFT_HTTP_SENT_STATUS_CODE:
968 outint = al->http.code;
969 doint = 1;
970 break;
971
972 case LFT_HTTP_RECEIVED_STATUS_CODE:
973 if (al->hier.peer_reply_status != Http::scNone) {
974 outint = al->hier.peer_reply_status;
975 doint = 1;
976 }
977 break;
978 /* case LFT_HTTP_STATUS:
979 * out = statusline->text;
980 * quote = 1;
981 * break;
982 */
983 case LFT_HTTP_BODY_BYTES_READ:
984 if (al->hier.bodyBytesRead >= 0) {
985 outoff = al->hier.bodyBytesRead;
986 dooff = 1;
987 }
988 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
989 // a peer server so just print a "-" (eg requests served from cache,
990 // or internal error messages).
991 break;
992
993 case LFT_SQUID_STATUS:
994 out = al->cache.code.c_str();
995 break;
996
997 case LFT_SQUID_ERROR:
998 if (al->request && al->request->errType != ERR_NONE)
999 out = errorPageName(al->request->errType);
1000 break;
1001
1002 case LFT_SQUID_ERROR_DETAIL:
1003#if USE_OPENSSL
1004 if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
1005 out = Ssl::GetErrorName(al->request->errDetail);
1006 if (!out)
1007 out = sslErrorName(al->request->errDetail, tmp, sizeof(tmp));
1008 } else
1009#endif
1010 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
1011 if (al->request->errDetail > ERR_DETAIL_START && al->request->errDetail < ERR_DETAIL_MAX)
1012 out = errorDetailName(al->request->errDetail);
1013 else {
1014 if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
1015 sb.appendf("%s=0x%X",
1016 errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
1017 else
1018 sb.appendf("%s=%d",
1019 errorDetailName(al->request->errDetail), al->request->errDetail);
1020 out = sb.c_str();
1021 }
1022 }
1023 break;
1024
1025 case LFT_SQUID_HIERARCHY:
1026 if (al->hier.ping.timedout)
1027 mb.append("TIMEOUT_", 8);
1028 out = hier_code_str[al->hier.code];
1029 break;
1030
1031 case LFT_MIME_TYPE:
1032 out = al->http.content_type;
1033 break;
1034
1035 case LFT_CLIENT_REQ_METHOD:
1036 if (al->request) {
1037 sb = al->request->method.image();
1038 out = sb.c_str();
1039 quote = 1;
1040 }
1041 break;
1042
1043 case LFT_CLIENT_REQ_URI:
1044 if (const auto uri = al->effectiveVirginUrl()) {
1045 sb = *uri;
1046 out = sb.c_str();
1047 quote = 1;
1048 }
1049 break;
1050
1051 case LFT_CLIENT_REQ_URLSCHEME:
1052 if (al->request) {
1053 sb = al->request->url.getScheme().image();
1054 out = sb.c_str();
1055 quote = 1;
1056 }
1057 break;
1058
1059 case LFT_CLIENT_REQ_URLDOMAIN:
1060 if (al->request) {
1061 out = al->request->url.host();
1062 quote = 1;
1063 }
1064 break;
1065
1066 case LFT_CLIENT_REQ_URLPORT:
1067 if (al->request) {
1068 outint = al->request->url.port();
1069 doint = 1;
1070 }
1071 break;
1072
1073 case LFT_REQUEST_URLPATH_OLD_31:
1074 case LFT_CLIENT_REQ_URLPATH:
1075 if (al->request) {
1076 sb = al->request->url.path();
1077 out = sb.c_str();
1078 quote = 1;
1079 }
1080 break;
1081
1082 case LFT_CLIENT_REQ_VERSION:
1083 if (al->request) {
1084 sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
1085 out = sb.c_str();
1086 }
1087 break;
1088
1089 case LFT_REQUEST_METHOD:
1090 if (al->hasLogMethod()) {
1091 sb = al->getLogMethod();
1092 out = sb.c_str();
1093 quote = 1;
1094 }
1095 break;
1096
1097 case LFT_REQUEST_URI:
1098 if (!al->url.isEmpty()) {
1099 sb = al->url;
1100 out = sb.c_str();
1101 }
1102 break;
1103
1104 case LFT_REQUEST_VERSION_OLD_2X:
1105 case LFT_REQUEST_VERSION:
1106 sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
1107 out = sb.c_str();
1108 break;
1109
1110 case LFT_SERVER_REQ_METHOD:
1111 if (al->adapted_request) {
1112 sb = al->adapted_request->method.image();
1113 out = sb.c_str();
1114 quote = 1;
1115 }
1116 break;
1117
1118 case LFT_SERVER_REQ_URI:
1119 // adapted request URI sent to server/peer
1120 if (al->adapted_request) {
1121 sb = al->adapted_request->effectiveRequestUri();
1122 out = sb.c_str();
1123 quote = 1;
1124 }
1125 break;
1126
1127 case LFT_SERVER_REQ_URLSCHEME:
1128 if (al->adapted_request) {
1129 sb = al->adapted_request->url.getScheme().image();
1130 out = sb.c_str();
1131 quote = 1;
1132 }
1133 break;
1134
1135 case LFT_SERVER_REQ_URLDOMAIN:
1136 if (al->adapted_request) {
1137 out = al->adapted_request->url.host();
1138 quote = 1;
1139 }
1140 break;
1141
1142 case LFT_SERVER_REQ_URLPORT:
1143 if (al->adapted_request) {
1144 outint = al->adapted_request->url.port();
1145 doint = 1;
1146 }
1147 break;
1148
1149 case LFT_SERVER_REQ_URLPATH:
1150 if (al->adapted_request) {
1151 sb = al->adapted_request->url.path();
1152 out = sb.c_str();
1153 quote = 1;
1154 }
1155 break;
1156
1157 case LFT_SERVER_REQ_VERSION:
1158 if (al->adapted_request) {
1159 sb.appendf("%u.%u",
1160 al->adapted_request->http_ver.major,
1161 al->adapted_request->http_ver.minor);
1162 out = tmp;
1163 }
1164 break;
1165
1166 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
1167 outoff = al->http.clientRequestSz.messageTotal();
1168 dooff = 1;
1169 break;
1170
1171 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
1172 outoff = al->http.clientRequestSz.header;
1173 dooff =1;
1174 break;
1175
1176 /*case LFT_REQUEST_SIZE_BODY: */
1177 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1178
1179 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
1180 outoff = al->http.clientReplySz.messageTotal();
1181 dooff = 1;
1182 break;
1183
1184 case LFT_REPLY_HIGHOFFSET:
1185 outoff = al->cache.highOffset;
1186 dooff = 1;
1187 break;
1188
1189 case LFT_REPLY_OBJECTSIZE:
1190 outoff = al->cache.objectSize;
1191 dooff = 1;
1192 break;
1193
1194 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
1195 outint = al->http.clientReplySz.header;
1196 doint = 1;
1197 break;
1198
1199 /*case LFT_REPLY_SIZE_BODY: */
1200 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1201
1202 case LFT_CLIENT_IO_SIZE_TOTAL:
1203 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
1204 doint = 1;
1205 break;
1206 /*case LFT_SERVER_IO_SIZE_TOTAL: */
1207
1208 case LFT_TAG:
1209 if (al->request) {
1210 out = al->request->tag.termedBuf();
1211 quote = 1;
1212 }
1213 break;
1214
1215 case LFT_EXT_LOG:
1216 if (al->request) {
1217 out = al->request->extacl_log.termedBuf();
1218 quote = 1;
1219 }
1220 break;
1221
1222 case LFT_SEQUENCE_NUMBER:
1223 outoff = logSequenceNumber;
1224 dooff = 1;
1225 break;
1226
1227#if USE_OPENSSL
1228 case LFT_SSL_BUMP_MODE: {
1229 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1230 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1231 out = Ssl::bumpMode(mode);
1232 }
1233 break;
1234
1235 case LFT_EXT_ACL_USER_CERT_RAW:
1236 if (al->request) {
1237 ConnStateData *conn = al->request->clientConnectionManager.get();
1238 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1239 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1240 sb = sslGetUserCertificatePEM(ssl);
1241 out = sb.c_str();
1242 }
1243 }
1244 }
1245 break;
1246
1247 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1248 if (al->request) {
1249 ConnStateData *conn = al->request->clientConnectionManager.get();
1250 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1251 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1252 sb = sslGetUserCertificatePEM(ssl);
1253 out = sb.c_str();
1254 }
1255 }
1256 }
1257 break;
1258
1259 case LFT_EXT_ACL_USER_CERT:
1260 if (al->request) {
1261 ConnStateData *conn = al->request->clientConnectionManager.get();
1262 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1263 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1264 out = sslGetUserAttribute(ssl, fmt->data.header.header);
1265 }
1266 }
1267 break;
1268
1269 case LFT_EXT_ACL_USER_CA_CERT:
1270 if (al->request) {
1271 ConnStateData *conn = al->request->clientConnectionManager.get();
1272 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1273 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
1274 out = sslGetCAAttribute(ssl, fmt->data.header.header);
1275 }
1276 }
1277 break;
1278
1279 case LFT_SSL_USER_CERT_SUBJECT:
1280 if (X509 *cert = al->cache.sslClientCert.get()) {
1281 if (X509_NAME *subject = X509_get_subject_name(cert)) {
1282 X509_NAME_oneline(subject, tmp, sizeof(tmp));
1283 out = tmp;
1284 }
1285 }
1286 break;
1287
1288 case LFT_SSL_USER_CERT_ISSUER:
1289 if (X509 *cert = al->cache.sslClientCert.get()) {
1290 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
1291 X509_NAME_oneline(issuer, tmp, sizeof(tmp));
1292 out = tmp;
1293 }
1294 }
1295 break;
1296
1297 case LFT_SSL_CLIENT_SNI:
1298 if (al->request && al->request->clientConnectionManager.valid()) {
1299 if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
1300 if (!conn->tlsClientSni().isEmpty()) {
1301 sb = conn->tlsClientSni();
1302 out = sb.c_str();
1303 }
1304 }
1305 }
1306 break;
1307
1308 case LFT_SSL_SERVER_CERT_ERRORS:
1309 if (al->request && al->request->clientConnectionManager.valid()) {
1310 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1311 const char *separator = fmt->data.string ? fmt->data.string : ":";
1312 for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
1313 if (!sb.isEmpty())
1314 sb.append(separator);
1315 if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
1316 sb.append(errorName);
1317 else
1318 sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
1319 if (sslError->element.depth >= 0)
1320 sb.appendf("@depth=%d", sslError->element.depth);
1321 }
1322 if (!sb.isEmpty())
1323 out = sb.c_str();
1324 }
1325 }
1326 break;
1327
1328 case LFT_SSL_SERVER_CERT_ISSUER:
1329 case LFT_SSL_SERVER_CERT_SUBJECT:
1330 case LFT_SSL_SERVER_CERT_WHOLE:
1331 if (al->request && al->request->clientConnectionManager.valid()) {
1332 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1333 if (X509 *serverCert = srvBump->serverCert.get()) {
1334 if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
1335 out = Ssl::GetX509UserAttribute(serverCert, "DN");
1336 else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
1337 out = Ssl::GetX509CAAttribute(serverCert, "DN");
1338 else {
1339 assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
1340 sb = Ssl::GetX509PEM(serverCert);
1341 out = sb.c_str();
1342 quote = 1;
1343 }
1344 }
1345 }
1346 }
1347 break;
1348
1349 case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
1350 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1351 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
1352 break;
1353
1354 case LFT_TLS_SERVER_NEGOTIATED_VERSION:
1355 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1356 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
1357 break;
1358
1359 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
1360 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1361 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
1362 break;
1363
1364 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
1365 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1366 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
1367 break;
1368
1369 case LFT_TLS_CLIENT_SUPPORTED_VERSION:
1370 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1371 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
1372 break;
1373
1374 case LFT_TLS_SERVER_SUPPORTED_VERSION:
1375 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1376 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
1377 break;
1378
1379 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
1380 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
1381 out = al->tcpClient->hasTlsNegotiations()->cipherName();
1382 break;
1383
1384 case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
1385 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
1386 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
1387 break;
1388#endif
1389
1390 case LFT_REQUEST_URLGROUP_OLD_2X:
1391 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
1392
1393 case LFT_NOTE:
1394 tmp[0] = fmt->data.header.separator;
1395 tmp[1] = '\0';
1396 if (fmt->data.header.header && *fmt->data.header.header) {
1397 const char *separator = tmp;
1398 static SBuf note;
1399#if USE_ADAPTATION
1400 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1401 if (ah && ah->metaHeaders) {
1402 if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
1403 sb.append(note);
1404 }
1405#endif
1406 if (al->notes) {
1407 if (al->notes->find(note, fmt->data.header.header, separator)) {
1408 if (!sb.isEmpty())
1409 sb.append(separator);
1410 sb.append(note);
1411 }
1412 }
1413 out = sb.c_str();
1414 quote = 1;
1415 } else {
1416 // if no argument given use default "\r\n" as notes separator
1417 const char *separator = fmt->data.string ? tmp : "\r\n";
1418#if USE_ADAPTATION
1419 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
1420 if (ah && ah->metaHeaders && !ah->metaHeaders->empty())
1421 sb.append(ah->metaHeaders->toString(separator));
1422#endif
1423 if (al->notes && !al->notes->empty())
1424 sb.append(al->notes->toString(separator));
1425
1426 out = sb.c_str();
1427 quote = 1;
1428 }
1429 break;
1430
1431 case LFT_CREDENTIALS:
1432#if USE_AUTH
1433 if (al->request && al->request->auth_user_request)
1434 out = strOrNull(al->request->auth_user_request->credentialsStr());
1435#endif
1436 break;
1437
1438 case LFT_PERCENT:
1439 out = "%";
1440 break;
1441
1442 case LFT_EXT_ACL_NAME:
1443 out = al->lastAclName;
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 = NULL;
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