/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "AccessLogEntry.h"
+#include "base64.h"
#include "client_side.h"
#include "comm/Connection.h"
-#include "err_detail_type.h"
+#include "error/Detail.h"
#include "errorpage.h"
#include "fde.h"
#include "format/Format.h"
#include "format/Quoting.h"
#include "format/Token.h"
-#include "fqdncache.h"
+#include "http/Stream.h"
#include "HttpRequest.h"
#include "MemBuf.h"
+#include "proxyp/Header.h"
#include "rfc1738.h"
+#include "sbuf/Stream.h"
+#include "sbuf/StringConvert.h"
+#include "security/CertError.h"
+#include "security/Certificate.h"
#include "security/NegotiationHistory.h"
-#include "SquidTime.h"
#include "Store.h"
#include "tools.h"
-#include "URL.h"
#if USE_OPENSSL
#include "ssl/ErrorDetail.h"
#include "ssl/ServerBump.h"
/// Convert a string to NULL pointer if it is ""
#define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
+const SBuf Format::Dash("-");
+
Format::Format::Format(const char *n) :
- format(NULL),
- next(NULL)
+ format(nullptr),
+ next(nullptr)
{
name = xstrdup(n);
}
// unlink the next entry for deletion
Format *temp = next;
next = temp->next;
- temp->next = NULL;
+ temp->next = nullptr;
delete temp;
}
Token *new_lt, *last_lt;
enum Quoting quote = LOG_QUOTE_NONE;
- debugs(46, 2, HERE << "got definition '" << def << "'");
+ debugs(46, 2, "got definition '" << def << "'");
if (format) {
debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
return false;
}
- /* very inefficent parser, but who cares, this needs to be simple */
+ /* very inefficient parser, but who cares, this needs to be simple */
/* First off, let's tokenize, we'll optimize in a second pass.
* A token can either be a %-prefixed sequence (usually a dynamic
* token but it can be an escaped sequence), or a string. */
return true;
}
+size_t
+Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
+{
+ Token tkn;
+ enum Quoting quote = LOG_QUOTE_NONE;
+ const auto tokenSize = tkn.parse(token, "e);
+ assert(tokenSize > 0);
+ if (ale != nullptr) {
+ Format fmt("SimpleToken");
+ fmt.format = &tkn;
+ fmt.assemble(mb, ale, 0);
+ fmt.format = nullptr;
+ } else {
+ mb.append("-", 1);
+ }
+ return static_cast<size_t>(tokenSize);
+}
+
void
Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
{
- debugs(46, 4, HERE);
+ debugs(46, 4, MYNAME);
// loop rather than recursing to conserve stack space.
for (const Format *fmt = this; fmt; fmt = fmt->next) {
- debugs(46, 3, HERE << "Dumping format definition for " << fmt->name);
+ debugs(46, 3, "Dumping format definition for " << fmt->name);
if (directiveName)
storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
storeAppendPrintf(entry, "%s", t->data.string);
else {
char argbuf[256];
- char *arg = NULL;
+ char *arg = nullptr;
ByteCode_t type = t->type;
switch (type) {
*p = '\0';
}
-#if USE_OPENSSL
-static char *
-sslErrorName(Ssl::ssl_error_t err, char *buf, size_t size)
+/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
+/// headers from sent headers rather than failing to distinguish requests from responses.
+/// \retval HttpReply sent to the HTTP client (access.log and default context).
+/// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
+/// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
+static const Http::Message *
+actualReplyHeader(const AccessLogEntry::Pointer &al)
{
- snprintf(buf, size, "SSL_ERR=%d", err);
- return buf;
+ const Http::Message *msg = al->reply.getRaw();
+#if ICAP_CLIENT
+ // al->icap.reqMethod is methodNone in access.log context
+ if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
+ msg = al->adapted_request;
+#endif
+ return msg;
}
+
+/// XXX: Misnamed. See actualReplyHeader().
+/// \return HttpRequest or HttpReply for %http::>h.
+static const Http::Message *
+actualRequestHeader(const AccessLogEntry::Pointer &al)
+{
+#if ICAP_CLIENT
+ // al->icap.reqMethod is methodNone in access.log context
+ if (al->icap.reqMethod == Adaptation::methodRespmod) {
+ // XXX: for now AccessLogEntry lacks virgin response headers
+ return nullptr;
+ }
#endif
+ return al->request;
+}
void
Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
{
- char tmp[1024];
- String sb;
+ static char tmp[1024];
+ SBuf sb;
- for (Token *fmt = format; fmt != NULL; fmt = fmt->next) { /* for each token */
- const char *out = NULL;
+ for (Token *fmt = format; fmt; fmt = fmt->next) { /* for each token */
+ const char *out = nullptr;
int quote = 0;
long int outint = 0;
int doint = 0;
int dofree = 0;
int64_t outoff = 0;
int dooff = 0;
- struct timeval outtv = {0, 0};
+ struct timeval outtv = {};
int doMsec = 0;
int doSec = 0;
+ bool doUint64 = false;
+ uint64_t outUint64 = 0;
switch (fmt->type) {
break;
case LFT_CLIENT_FQDN:
- if (al->cache.caddr.isAnyAddr()) // e.g., ICAP OPTIONS lack client
- out = "-";
- else
- out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
- if (!out) {
- out = al->cache.caddr.toStr(tmp,1024);
- }
-
+ out = al->getLogClientFqdn(tmp, sizeof(tmp));
break;
case LFT_CLIENT_PORT:
if (al->request) {
outint = al->request->client_addr.port();
doint = 1;
+ } else if (al->tcpClient) {
+ outint = al->tcpClient->remote.port();
+ doint = 1;
}
break;
case LFT_CLIENT_EUI:
#if USE_SQUID_EUI
// TODO make the ACL checklist have a direct link to any TCP details.
- if (al->request && al->request->clientConnectionManager.valid() && al->request->clientConnectionManager->clientConnection != NULL) {
- if (al->request->clientConnectionManager->clientConnection->remote.isIPv4())
- al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
+ if (al->request && al->request->clientConnectionManager.valid() &&
+ al->request->clientConnectionManager->clientConnection) {
+ const auto &conn = al->request->clientConnectionManager->clientConnection;
+ if (conn->remote.isIPv4())
+ conn->remoteEui48.encode(tmp, sizeof(tmp));
else
- al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
+ conn->remoteEui64.encode(tmp, sizeof(tmp));
out = tmp;
}
#endif
case LFT_EXT_ACL_CLIENT_EUI48:
#if USE_SQUID_EUI
if (al->request && al->request->clientConnectionManager.valid() &&
- al->request->clientConnectionManager->clientConnection != NULL &&
+ al->request->clientConnectionManager->clientConnection &&
al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
- al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
+ al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
out = tmp;
}
#endif
case LFT_EXT_ACL_CLIENT_EUI64:
#if USE_SQUID_EUI
if (al->request && al->request->clientConnectionManager.valid() &&
- al->request->clientConnectionManager->clientConnection != NULL &&
+ al->request->clientConnectionManager->clientConnection &&
!al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
- al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
+ al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
out = tmp;
}
#endif
break;
case LFT_SERVER_IP_ADDRESS:
- if (al->hier.tcpServer != NULL) {
- out = al->hier.tcpServer->remote.toStr(tmp,sizeof(tmp));
- }
+ if (al->hier.tcpServer)
+ out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
break;
case LFT_SERVER_FQDN_OR_PEER_NAME:
break;
case LFT_SERVER_PORT:
- if (al->hier.tcpServer != NULL) {
+ if (al->hier.tcpServer) {
outint = al->hier.tcpServer->remote.port();
doint = 1;
}
break;
- case LFT_LOCAL_LISTENING_IP: {
- // avoid logging a dash if we have reliable info
- const bool interceptedAtKnownPort = al->request ?
- (al->request->flags.interceptTproxy ||
- al->request->flags.intercepted) && al->cache.port != NULL :
- false;
- if (interceptedAtKnownPort) {
- const bool portAddressConfigured = !al->cache.port->s.isAnyAddr();
- if (portAddressConfigured)
- out = al->cache.port->s.toStr(tmp, sizeof(tmp));
- } else if (al->tcpClient != NULL)
- out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
- }
- break;
+ case LFT_LOCAL_LISTENING_IP:
+ if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
+ out = addr->toStr(tmp, sizeof(tmp));
+ break;
case LFT_CLIENT_LOCAL_IP:
- if (al->tcpClient != NULL) {
- out = al->tcpClient->local.toStr(tmp,sizeof(tmp));
- }
+ if (al->tcpClient)
+ out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
break;
case LFT_CLIENT_LOCAL_TOS:
- if (al->tcpClient != NULL) {
- snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->tcpClient->tos);
- out = tmp;
+ if (al->tcpClient) {
+ sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
+ out = sb.c_str();
+ }
+ break;
+
+ case LFT_TRANSPORT_CLIENT_CONNECTION_ID:
+ if (al->tcpClient) {
+ outUint64 = al->tcpClient->id.value;
+ doUint64 = true;
}
break;
case LFT_CLIENT_LOCAL_NFMARK:
- if (al->tcpClient != NULL) {
- snprintf(tmp, sizeof(tmp), "0x%x", al->tcpClient->nfmark);
- out = tmp;
+ if (al->tcpClient) {
+ sb.appendf("0x%x", al->tcpClient->nfmark);
+ out = sb.c_str();
}
break;
case LFT_LOCAL_LISTENING_PORT:
- if (al->cache.port != NULL) {
- outint = al->cache.port->s.port();
- doint = 1;
- } else if (al->request) {
- outint = al->request->my_addr.port();
+ if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
+ outint = port;
doint = 1;
}
break;
case LFT_CLIENT_LOCAL_PORT:
- if (al->tcpClient != NULL) {
+ if (al->tcpClient) {
outint = al->tcpClient->local.port();
doint = 1;
}
case LFT_SERVER_LOCAL_IP_OLD_27:
case LFT_SERVER_LOCAL_IP:
- if (al->hier.tcpServer != NULL) {
- out = al->hier.tcpServer->local.toStr(tmp,sizeof(tmp));
- }
+ if (al->hier.tcpServer)
+ out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
break;
case LFT_SERVER_LOCAL_PORT:
- if (al->hier.tcpServer != NULL) {
+ if (al->hier.tcpServer) {
outint = al->hier.tcpServer->local.port();
doint = 1;
}
-
break;
case LFT_SERVER_LOCAL_TOS:
- if (al->hier.tcpServer != NULL) {
- snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->hier.tcpServer->tos);
- out = tmp;
+ if (al->hier.tcpServer) {
+ sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
+ out = sb.c_str();
}
break;
case LFT_SERVER_LOCAL_NFMARK:
- if (al->hier.tcpServer != NULL) {
- snprintf(tmp, sizeof(tmp), "0x%x", al->hier.tcpServer->nfmark);
- out = tmp;
+ if (al->hier.tcpServer) {
+ sb.appendf("0x%x", al->hier.tcpServer->nfmark);
+ out = sb.c_str();
+ }
+ break;
+
+ case LFT_CLIENT_HANDSHAKE:
+ if (al->request && al->request->clientConnectionManager.valid()) {
+ const auto &handshake = al->request->clientConnectionManager->preservedClientData;
+ if (const auto rawLength = handshake.length()) {
+ // add 1 byte to optimize the c_str() conversion below
+ char *buf = sb.rawAppendStart(base64_encode_len(rawLength) + 1);
+
+ struct base64_encode_ctx ctx;
+ base64_encode_init(&ctx);
+ auto encLength = base64_encode_update(&ctx, buf, rawLength, reinterpret_cast<const uint8_t*>(handshake.rawContent()));
+ encLength += base64_encode_final(&ctx, buf + encLength);
+
+ sb.rawAppendFinish(buf, encLength);
+ out = sb.c_str();
+ }
}
break;
break;
case LFT_TIME_LOCALTIME:
-
case LFT_TIME_GMT: {
const char *spec;
-
struct tm *t;
spec = fmt->data.string;
}
strftime(tmp, sizeof(tmp), spec, t);
-
out = tmp;
}
-
break;
case LFT_TIME_START:
doSec = 1;
break;
+ case LFT_BUSY_TIME: {
+ const auto &stopwatch = al->busyTime;
+ if (stopwatch.ran()) {
+ // make sure total() returns nanoseconds compatible with outoff
+ using nanos = std::chrono::duration<decltype(outoff), std::nano>;
+ const nanos n = stopwatch.total();
+ outoff = n.count();
+ dooff = true;
+ }
+ }
+ break;
+
case LFT_TIME_TO_HANDLE_REQUEST:
outtv = al->cache.trTime;
doMsec = 1;
break;
case LFT_PEER_RESPONSE_TIME:
- if (al->hier.peer_response_time.tv_sec == -1) {
- out = "-";
- } else {
- outtv = al->hier.peer_response_time;
+ struct timeval peerResponseTime;
+ if (al->hier.peerResponseTime(peerResponseTime)) {
+ outtv = peerResponseTime;
doMsec = 1;
}
break;
case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
- timeval total_response_time;
- al->hier.totalResponseTime(total_response_time);
- if (total_response_time.tv_sec == -1) {
- out = "-";
- } else {
- outtv = total_response_time;
+ struct timeval totalResponseTime;
+ if (al->hier.totalResponseTime(totalResponseTime)) {
+ outtv = totalResponseTime;
doMsec = 1;
}
}
case LFT_DNS_WAIT_TIME:
if (al->request && al->request->dnsWait >= 0) {
// TODO: microsecond precision for dns wait time.
- // Convert miliseconds to timeval struct:
+ // Convert milliseconds to timeval struct:
outtv.tv_sec = al->request->dnsWait / 1000;
outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
doMsec = 1;
break;
case LFT_REQUEST_HEADER:
-
- if (al->request)
- sb = al->request->header.getByName(fmt->data.header.header);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ if (const Http::Message *msg = actualRequestHeader(al)) {
+ sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_ADAPTED_REQUEST_HEADER:
-
- if (al->adapted_request)
- sb = al->adapted_request->header.getByName(fmt->data.header.header);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ if (al->adapted_request) {
+ sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_REPLY_HEADER:
- if (al->reply)
- sb = al->reply->header.getByName(fmt->data.header.header);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ if (const Http::Message *msg = actualReplyHeader(al)) {
+ sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
+ out = sb.c_str();
+ quote = 1;
+ }
break;
#if USE_ADAPTATION
case LFT_ADAPTATION_SUM_XACT_TIMES:
if (al->request) {
Adaptation::History::Pointer ah = al->request->adaptHistory();
- if (ah != NULL)
+ if (ah) {
ah->sumLogString(fmt->data.string, sb);
- out = sb.termedBuf();
+ out = sb.c_str();
+ }
}
break;
case LFT_ADAPTATION_ALL_XACT_TIMES:
if (al->request) {
Adaptation::History::Pointer ah = al->request->adaptHistory();
- if (ah != NULL)
+ if (ah) {
ah->allLogString(fmt->data.string, sb);
- out = sb.termedBuf();
+ out = sb.c_str();
+ }
}
break;
case LFT_ADAPTATION_LAST_HEADER:
if (al->request) {
const Adaptation::History::Pointer ah = al->request->adaptHistory();
- if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
- sb = ah->allMeta.getByName(fmt->data.header.header);
+ if (ah) { // XXX: add adapt::<all_h but use lastMeta here
+ sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
+ out = sb.c_str();
+ quote = 1;
+ }
}
-
- // XXX: here and elsewhere: move such code inside the if guard
- out = sb.termedBuf();
-
- quote = 1;
-
break;
case LFT_ADAPTATION_LAST_HEADER_ELEM:
if (al->request) {
const Adaptation::History::Pointer ah = al->request->adaptHistory();
- if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
+ if (ah) { // XXX: add adapt::<all_h but use lastMeta here
sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ out = sb.c_str();
+ quote = 1;
+ }
}
-
- out = sb.termedBuf();
-
- quote = 1;
-
break;
case LFT_ADAPTATION_LAST_ALL_HEADERS:
out = al->adapt.last_meta;
-
quote = 1;
-
break;
#endif
#if ICAP_CLIENT
case LFT_ICAP_ADDR:
- if (!out)
- out = al->icap.hostAddr.toStr(tmp,1024);
+ out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
break;
case LFT_ICAP_SERV_NAME:
break;
case LFT_ICAP_REQ_HEADER:
- if (NULL != al->icap.request) {
- sb = al->icap.request->header.getByName(fmt->data.header.header);
- out = sb.termedBuf();
+ if (al->icap.request) {
+ sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_ICAP_REQ_HEADER_ELEM:
- if (al->icap.request)
+ if (al->icap.request) {
sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_ICAP_REQ_ALL_HEADERS:
while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
sb.append(e->name);
sb.append(": ");
- sb.append(e->value);
+ sb.append(StringToSBuf(e->value));
sb.append("\r\n");
}
- out = sb.termedBuf();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_ICAP_REP_HEADER:
- if (NULL != al->icap.reply) {
- sb = al->icap.reply->header.getByName(fmt->data.header.header);
- out = sb.termedBuf();
+ if (al->icap.reply) {
+ sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_ICAP_REP_HEADER_ELEM:
- if (NULL != al->icap.reply)
+ if (al->icap.reply) {
sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_ICAP_REP_ALL_HEADERS:
while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
sb.append(e->name);
sb.append(": ");
- sb.append(e->value);
+ sb.append(StringToSBuf(e->value));
sb.append("\r\n");
}
- out = sb.termedBuf();
+ out = sb.c_str();
quote = 1;
}
break;
break;
#endif
case LFT_REQUEST_HEADER_ELEM:
- if (al->request)
- sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ if (const Http::Message *msg = actualRequestHeader(al)) {
+ sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ out = sb.c_str();
+ quote = 1;
+ }
+ break;
- out = sb.termedBuf();
+ case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
+ if (al->proxyProtocolHeader) {
+ sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
+ out = sb.c_str();
+ quote = 1;
+ }
+ break;
- quote = 1;
+ case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
+ if (al->proxyProtocolHeader) {
+ sb = al->proxyProtocolHeader->toMime();
+ out = sb.c_str();
+ quote = 1;
+ }
+ break;
+ case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
+ if (al->proxyProtocolHeader) {
+ sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_ADAPTED_REQUEST_HEADER_ELEM:
- if (al->adapted_request)
+ if (al->adapted_request) {
sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_REPLY_HEADER_ELEM:
- if (al->reply)
- sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
- out = sb.termedBuf();
-
- quote = 1;
-
+ if (const Http::Message *msg = actualReplyHeader(al)) {
+ sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ out = sb.c_str();
+ quote = 1;
+ }
break;
case LFT_REQUEST_ALL_HEADERS:
- out = al->headers.request;
-
- quote = 1;
-
+#if ICAP_CLIENT
+ if (al->icap.reqMethod == Adaptation::methodRespmod) {
+ // XXX: since AccessLogEntry::Headers lacks virgin response
+ // headers, do nothing for now
+ out = nullptr;
+ } else
+#endif
+ {
+ // just headers without start-line and CRLF
+ // XXX: reconcile with '<h'
+ out = al->headers.request;
+ quote = 1;
+ }
break;
case LFT_ADAPTED_REQUEST_ALL_HEADERS:
+ // just headers without start-line and CRLF
+ // XXX: reconcile with '<h'
out = al->headers.adapted_request;
-
quote = 1;
-
break;
- case LFT_REPLY_ALL_HEADERS:
- out = al->headers.reply;
-
+ case LFT_REPLY_ALL_HEADERS: {
+ MemBuf allHeaders;
+ allHeaders.init();
+ // status-line + headers + CRLF
+ // XXX: reconcile with '>h' and '>ha'
+ al->packReplyHeaders(allHeaders);
+ sb.assign(allHeaders.content(), allHeaders.contentSize());
+ out = sb.c_str();
+#if ICAP_CLIENT
+ if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
+ out = al->headers.adapted_request;
+#endif
quote = 1;
-
- break;
+ }
+ break;
case LFT_USER_NAME:
#if USE_AUTH
- if (al->request && al->request->auth_user_request != NULL)
+ if (al->request && al->request->auth_user_request)
out = strOrNull(al->request->auth_user_request->username());
#endif
+ if (!out && al->request && al->request->extacl_user.size()) {
+ if (const char *t = al->request->extacl_user.termedBuf())
+ out = t;
+ }
if (!out)
- out = strOrNull(al->cache.extuser);
+ out = strOrNull(al->getExtUser());
#if USE_OPENSSL
if (!out)
out = strOrNull(al->cache.ssluser);
#endif
if (!out)
- out = strOrNull(al->cache.rfc931);
+ out = strOrNull(al->getClientIdent());
break;
case LFT_USER_LOGIN:
#if USE_AUTH
- if (al->request && al->request->auth_user_request != NULL)
+ if (al->request && al->request->auth_user_request)
out = strOrNull(al->request->auth_user_request->username());
#endif
break;
case LFT_USER_IDENT:
- out = strOrNull(al->cache.rfc931);
+ out = strOrNull(al->getClientIdent());
break;
case LFT_USER_EXTERNAL:
- if (al->request && al->request->extacl_user.size())
- out = al->request->extacl_user.termedBuf();
-
- if (!out)
- out = strOrNull(al->cache.extuser);
+ out = strOrNull(al->getExtUser());
break;
/* case LFT_USER_REALM: */
/* case LFT_USER_SCHEME: */
// the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
- // but compiler complains if ommited
+ // but compiler complains if omitted
case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
case LFT_HTTP_SENT_STATUS_CODE:
outint = al->http.code;
-
doint = 1;
-
break;
case LFT_HTTP_RECEIVED_STATUS_CODE:
- if (al->hier.peer_reply_status == Http::scNone) {
- out = "-";
- } else {
+ if (al->hier.peer_reply_status != Http::scNone) {
outint = al->hier.peer_reply_status;
doint = 1;
}
break;
case LFT_SQUID_ERROR:
- if (al->request && al->request->errType != ERR_NONE)
- out = errorPageName(al->request->errType);
+ if (const auto error = al->error())
+ out = errorPageName(error->category);
break;
case LFT_SQUID_ERROR_DETAIL:
-#if USE_OPENSSL
- if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
- if (! (out = Ssl::GetErrorName(al->request->errDetail)))
- out = sslErrorName(al->request->errDetail, tmp, sizeof(tmp));
- } else
-#endif
- if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
- if (al->request->errDetail > ERR_DETAIL_START && al->request->errDetail < ERR_DETAIL_MAX)
- out = errorDetailName(al->request->errDetail);
- else {
- if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
- snprintf(tmp, sizeof(tmp), "%s=0x%X",
- errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
- else
- snprintf(tmp, sizeof(tmp), "%s=%d",
- errorDetailName(al->request->errDetail), al->request->errDetail);
- out = tmp;
- }
+ if (const auto error = al->error()) {
+ if (const auto detail = error->detail) {
+ sb = detail->brief();
+ out = sb.c_str();
}
+ }
break;
case LFT_SQUID_HIERARCHY:
if (al->hier.ping.timedout)
mb.append("TIMEOUT_", 8);
-
out = hier_code_str[al->hier.code];
+ break;
+ case LFT_SQUID_REQUEST_ATTEMPTS:
+ outint = al->requestAttempts;
+ doint = 1;
break;
case LFT_MIME_TYPE:
out = al->http.content_type;
-
break;
case LFT_CLIENT_REQ_METHOD:
if (al->request) {
- const SBuf &s = al->request->method.image();
- sb.append(s.rawContent(), s.length());
- out = sb.termedBuf();
+ sb = al->request->method.image();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_CLIENT_REQ_URI:
- // original client URI
- if (al->request) {
- const SBuf &s = al->request->effectiveRequestUri();
- sb.append(s.rawContent(), s.length());
- out = sb.termedBuf();
+ if (const auto uri = al->effectiveVirginUrl()) {
+ sb = *uri;
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_CLIENT_REQ_URLSCHEME:
if (al->request) {
- out = al->request->url.getScheme().c_str();
+ sb = al->request->url.getScheme().image();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_REQUEST_URLPATH_OLD_31:
case LFT_CLIENT_REQ_URLPATH:
if (al->request) {
- SBuf s = al->request->url.path();
- out = s.c_str();
+ sb = al->request->url.path();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_CLIENT_REQ_VERSION:
if (al->request) {
- snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
- out = tmp;
+ sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
+ out = sb.c_str();
}
break;
case LFT_REQUEST_METHOD:
- {
- const SBuf s(al->getLogMethod());
- sb.append(s.rawContent(), s.length());
- out = sb.termedBuf();
- quote = 1;
- }
- break;
+ if (al->hasLogMethod()) {
+ sb = al->getLogMethod();
+ out = sb.c_str();
+ quote = 1;
+ }
+ break;
case LFT_REQUEST_URI:
- out = al->url;
+ if (!al->url.isEmpty()) {
+ sb = al->url;
+ out = sb.c_str();
+ }
break;
case LFT_REQUEST_VERSION_OLD_2X:
case LFT_REQUEST_VERSION:
- snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
- out = tmp;
+ sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
+ out = sb.c_str();
break;
case LFT_SERVER_REQ_METHOD:
if (al->adapted_request) {
- const SBuf &s = al->adapted_request->method.image();
- sb.append(s.rawContent(), s.length());
- out = sb.termedBuf();
+ sb = al->adapted_request->method.image();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_SERVER_REQ_URI:
// adapted request URI sent to server/peer
if (al->adapted_request) {
- const SBuf &s = al->adapted_request->effectiveRequestUri();
- sb.append(s.rawContent(), s.length());
- out = sb.termedBuf();
+ sb = al->adapted_request->effectiveRequestUri();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_SERVER_REQ_URLSCHEME:
if (al->adapted_request) {
- out = al->adapted_request->url.getScheme().c_str();
+ sb = al->adapted_request->url.getScheme().image();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_SERVER_REQ_URLPATH:
if (al->adapted_request) {
- SBuf s = al->adapted_request->url.path();
- out = s.c_str();
+ sb = al->adapted_request->url.path();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_SERVER_REQ_VERSION:
if (al->adapted_request) {
- snprintf(tmp, sizeof(tmp), "%d.%d",
- (int) al->adapted_request->http_ver.major,
- (int) al->adapted_request->http_ver.minor);
+ sb.appendf("%u.%u",
+ al->adapted_request->http_ver.major,
+ al->adapted_request->http_ver.minor);
out = tmp;
}
break;
case LFT_REPLY_HIGHOFFSET:
outoff = al->cache.highOffset;
-
dooff = 1;
-
break;
case LFT_REPLY_OBJECTSIZE:
outoff = al->cache.objectSize;
-
dooff = 1;
-
break;
case LFT_ADAPTED_REPLY_SIZE_HEADERS:
/*case LFT_SERVER_IO_SIZE_TOTAL: */
case LFT_TAG:
- if (al->request)
+ if (al->request) {
out = al->request->tag.termedBuf();
-
- quote = 1;
-
+ quote = 1;
+ }
break;
case LFT_EXT_LOG:
- if (al->request)
+ if (al->request) {
out = al->request->extacl_log.termedBuf();
-
- quote = 1;
-
+ quote = 1;
+ }
break;
case LFT_SEQUENCE_NUMBER:
case LFT_EXT_ACL_USER_CERT_RAW:
if (al->request) {
ConnStateData *conn = al->request->clientConnectionManager.get();
- if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
- if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
- out = sslGetUserCertificatePEM(ssl);
+ if (conn && Comm::IsConnOpen(conn->clientConnection)) {
+ if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+ sb = sslGetUserCertificatePEM(ssl);
+ out = sb.c_str();
+ }
}
}
break;
case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
if (al->request) {
ConnStateData *conn = al->request->clientConnectionManager.get();
- if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
- if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
- out = sslGetUserCertificatePEM(ssl);
+ if (conn && Comm::IsConnOpen(conn->clientConnection)) {
+ if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+ sb = sslGetUserCertificatePEM(ssl);
+ out = sb.c_str();
+ }
}
}
break;
case LFT_EXT_ACL_USER_CERT:
if (al->request) {
ConnStateData *conn = al->request->clientConnectionManager.get();
- if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
- if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
- out = sslGetUserAttribute(ssl, format->data.header.header);
+ if (conn && Comm::IsConnOpen(conn->clientConnection)) {
+ if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
+ out = sslGetUserAttribute(ssl, fmt->data.header.header);
}
}
break;
case LFT_EXT_ACL_USER_CA_CERT:
if (al->request) {
ConnStateData *conn = al->request->clientConnectionManager.get();
- if (conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
- if (SSL *ssl = fd_table[conn->clientConnection->fd].ssl)
- out = sslGetCAAttribute(ssl, format->data.header.header);
+ if (conn && Comm::IsConnOpen(conn->clientConnection)) {
+ if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
+ out = sslGetCAAttribute(ssl, fmt->data.header.header);
}
}
break;
case LFT_SSL_USER_CERT_SUBJECT:
- if (X509 *cert = al->cache.sslClientCert.get()) {
- if (X509_NAME *subject = X509_get_subject_name(cert)) {
- X509_NAME_oneline(subject, tmp, sizeof(tmp));
- out = tmp;
- }
+ if (const auto &cert = al->cache.sslClientCert) {
+ sb = Security::SubjectName(*cert);
+ out = sb.c_str();
}
break;
case LFT_SSL_USER_CERT_ISSUER:
- if (X509 *cert = al->cache.sslClientCert.get()) {
- if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
- X509_NAME_oneline(issuer, tmp, sizeof(tmp));
- out = tmp;
- }
+ if (const auto &cert = al->cache.sslClientCert) {
+ sb = Security::IssuerName(*cert);
+ out = sb.c_str();
}
break;
case LFT_SSL_CLIENT_SNI:
if (al->request && al->request->clientConnectionManager.valid()) {
- if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
- if (!srvBump->clientSni.isEmpty())
- out = srvBump->clientSni.c_str();
+ if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
+ if (!conn->tlsClientSni().isEmpty()) {
+ sb = conn->tlsClientSni();
+ out = sb.c_str();
+ }
}
}
break;
if (al->request && al->request->clientConnectionManager.valid()) {
if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
const char *separator = fmt->data.string ? fmt->data.string : ":";
- for (Ssl::CertErrors *sslError = srvBump->sslErrors; sslError != NULL; sslError = sslError->next) {
- if (sb.size())
+ for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
+ if (!sb.isEmpty())
sb.append(separator);
- if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
- sb.append(errorName);
- else
- sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
- if (sslError->element.depth >= 0) {
- snprintf(tmp, sizeof(tmp), "@depth=%d", sslError->element.depth);
- sb.append(tmp);
- }
+ sb.append(Ssl::GetErrorName(sslError->element.code, true));
+ if (sslError->element.depth >= 0)
+ sb.appendf("@depth=%d", sslError->element.depth);
}
- if (sb.size())
- out = sb.termedBuf();
+ if (!sb.isEmpty())
+ out = sb.c_str();
}
}
break;
case LFT_SSL_SERVER_CERT_ISSUER:
case LFT_SSL_SERVER_CERT_SUBJECT:
- // Not implemented
+ case LFT_SSL_SERVER_CERT_WHOLE:
+ if (al->request && al->request->clientConnectionManager.valid()) {
+ if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
+ if (X509 *serverCert = srvBump->serverCert.get()) {
+ if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
+ out = Ssl::GetX509UserAttribute(serverCert, "DN");
+ else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
+ out = Ssl::GetX509CAAttribute(serverCert, "DN");
+ else {
+ assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
+ sb = Ssl::GetX509PEM(serverCert);
+ out = sb.c_str();
+ quote = 1;
+ }
+ }
+ }
+ }
break;
case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
- if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+ if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
break;
case LFT_TLS_SERVER_NEGOTIATED_VERSION:
- if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+ if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
break;
case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
- if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+ if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
out = al->tcpClient->hasTlsNegotiations()->helloVersion();
break;
case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
- if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+ if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
break;
case LFT_TLS_CLIENT_SUPPORTED_VERSION:
- if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+ if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
break;
case LFT_TLS_SERVER_SUPPORTED_VERSION:
- if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+ if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
break;
case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
- if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+ if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
out = al->tcpClient->hasTlsNegotiations()->cipherName();
break;
case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
- if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+ if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
break;
#endif
case LFT_REQUEST_URLGROUP_OLD_2X:
assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
+ break;
case LFT_NOTE:
tmp[0] = fmt->data.header.separator;
tmp[1] = '\0';
if (fmt->data.header.header && *fmt->data.header.header) {
const char *separator = tmp;
+ static SBuf note;
#if USE_ADAPTATION
Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
- if (ah != NULL && ah->metaHeaders != NULL) {
- if (const char *meta = ah->metaHeaders->find(fmt->data.header.header, separator))
- sb.append(meta);
+ if (ah && ah->metaHeaders) {
+ if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
+ sb.append(note);
}
#endif
- if (al->notes != NULL) {
- if (const char *note = al->notes->find(fmt->data.header.header, separator)) {
- if (sb.size())
+ if (al->notes) {
+ if (al->notes->find(note, fmt->data.header.header, separator)) {
+ if (!sb.isEmpty())
sb.append(separator);
sb.append(note);
}
}
- out = sb.termedBuf();
+ out = sb.c_str();
quote = 1;
} else {
// if no argument given use default "\r\n" as notes separator
const char *separator = fmt->data.string ? tmp : "\r\n";
#if USE_ADAPTATION
Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
- if (ah != NULL && ah->metaHeaders != NULL && !ah->metaHeaders->empty())
+ if (ah && ah->metaHeaders && !ah->metaHeaders->empty())
sb.append(ah->metaHeaders->toString(separator));
#endif
- if (al->notes != NULL && !al->notes->empty())
+ if (al->notes && !al->notes->empty())
sb.append(al->notes->toString(separator));
- out = sb.termedBuf();
+ out = sb.c_str();
quote = 1;
}
break;
case LFT_CREDENTIALS:
#if USE_AUTH
- if (al->request && al->request->auth_user_request != NULL)
+ if (al->request && al->request->auth_user_request)
out = strOrNull(al->request->auth_user_request->credentialsStr());
#endif
-
break;
case LFT_PERCENT:
break;
case LFT_EXT_ACL_DATA:
- out = al->lastAclData;
+ if (!al->lastAclData.isEmpty())
+ out = al->lastAclData.c_str();
break;
+
+ case LFT_MASTER_XACTION:
+ if (al->request) {
+ doUint64 = true;
+ outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
+ break;
+ }
}
if (dooff) {
- snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
- out = tmp;
+ sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
+ out = sb.c_str();
} else if (doint) {
- snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
- out = tmp;
+ sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
+ out = sb.c_str();
+ } else if (doUint64) {
+ sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
+ out = sb.c_str();
} else if (doMsec) {
if (fmt->widthMax < 0) {
- snprintf(tmp, sizeof(tmp), "%0*ld", fmt->widthMin , tvToMsec(outtv));
+ sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
} else {
int precision = fmt->widthMax;
- snprintf(tmp, sizeof(tmp), "%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)));
+ 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)));
}
- out = tmp;
+ out = sb.c_str();
} else if (doSec) {
int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
- snprintf(tmp, sizeof(tmp), "%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));
- out = tmp;
+ 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));
+ out = sb.c_str();
}
if (out && *out) {
if (quote || fmt->quote != LOG_QUOTE_NONE) {
- char *newout = NULL;
+ // Do not write to the tmp buffer because it may contain the to-be-quoted value.
+ static char quotedOut[2 * sizeof(tmp)];
+ static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
+ quotedOut[0] = '\0';
+
+ char *newout = nullptr;
int newfree = 0;
switch (fmt->quote) {
newout = (char *)xmalloc(out_len);
newfree = 1;
} else
- newout = tmp;
+ newout = quotedOut;
log_quoted_string(out, newout);
}
break;
}
// enforce width limits if configured
- const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec;
+ const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
if (haveMaxWidth || fmt->widthMin) {
const int minWidth = fmt->widthMin >= 0 ?
fmt->widthMin :0;
if (fmt->space)
mb.append(" ", 1);
- sb.clean();
+ sb.clear();
if (dofree)
safe_free(out);