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