]> git.ipfire.org Git - thirdparty/squid.git/blame - src/format/Format.cc
SourceLayout: rename Array.h to base/Vector.h
[thirdparty/squid.git] / src / format / Format.cc
CommitLineData
f7f3304a 1#include "squid.h"
38e16f92 2#include "AccessLogEntry.h"
582c2af2 3#include "client_side.h"
38e16f92
AJ
4#include "comm/Connection.h"
5#include "err_detail_type.h"
6#include "errorpage.h"
f4698e0b 7#include "fde.h"
38e16f92
AJ
8#include "format/Format.h"
9#include "format/Quoting.h"
31971e6a 10#include "format/Token.h"
95e6d864 11#include "fqdncache.h"
38e16f92
AJ
12#include "HttpRequest.h"
13#include "MemBuf.h"
14#include "rfc1738.h"
15#include "SquidTime.h"
16#include "Store.h"
b1bd952a 17#include "URL.h"
2f3e52b5
CT
18#if USE_SSL
19#include "ssl/ErrorDetail.h"
20#endif
21
bd85ea1f
AJ
22/// Convert a string to NULL pointer if it is ""
23#define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
38e16f92
AJ
24
25Format::Format::Format(const char *n) :
26 format(NULL),
27 next(NULL)
28{
29 name = xstrdup(n);
30}
31
32Format::Format::~Format()
33{
34 // erase the list without consuming stack space
35 while (next) {
36 // unlink the next entry for deletion
37 Format *temp = next;
38 next = temp->next;
39 temp->next = NULL;
40 delete temp;
41 }
42
43 // remove locals
44 xfree(name);
45 delete format;
46}
47
48bool
f4698e0b 49Format::Format::parse(const char *def)
38e16f92 50{
f4698e0b 51 const char *cur, *eos;
38e16f92
AJ
52 Token *new_lt, *last_lt;
53 enum Quoting quote = LOG_QUOTE_NONE;
54
55 debugs(46, 2, HERE << "got definition '" << def << "'");
56
57 if (format) {
58 debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
59 return false;
60 }
61
62 /* very inefficent parser, but who cares, this needs to be simple */
63 /* First off, let's tokenize, we'll optimize in a second pass.
64 * A token can either be a %-prefixed sequence (usually a dynamic
65 * token but it can be an escaped sequence), or a string. */
66 cur = def;
67 eos = def + strlen(def);
68 format = new_lt = last_lt = new Token;
69 cur += new_lt->parse(cur, &quote);
70
71 while (cur < eos) {
72 new_lt = new Token;
73 last_lt->next = new_lt;
74 last_lt = new_lt;
75 cur += new_lt->parse(cur, &quote);
76 }
77
78 return true;
79}
80
81void
5f621cd0 82Format::Format::dump(StoreEntry * entry, const char *directiveName)
38e16f92
AJ
83{
84 debugs(46, 4, HERE);
85
86 // loop rather than recursing to conserve stack space.
5f621cd0
AJ
87 for (Format *fmt = this; fmt; fmt = fmt->next) {
88 debugs(46, 3, HERE << "Dumping format definition for " << fmt->name);
89 storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
38e16f92 90
5f621cd0 91 for (Token *t = fmt->format; t; t = t->next) {
38e16f92
AJ
92 if (t->type == LFT_STRING)
93 storeAppendPrintf(entry, "%s", t->data.string);
94 else {
95 char argbuf[256];
96 char *arg = NULL;
97 ByteCode_t type = t->type;
98
99 switch (type) {
100 /* special cases */
101
102 case LFT_STRING:
103 break;
104#if USE_ADAPTATION
105 case LFT_ADAPTATION_LAST_HEADER_ELEM:
106#endif
107#if ICAP_CLIENT
108 case LFT_ICAP_REQ_HEADER_ELEM:
109 case LFT_ICAP_REP_HEADER_ELEM:
110#endif
111 case LFT_REQUEST_HEADER_ELEM:
112 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
113 case LFT_REPLY_HEADER_ELEM:
114
115 if (t->data.header.separator != ',')
116 snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
117 else
118 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
119
120 arg = argbuf;
121
122 switch (type) {
123 case LFT_REQUEST_HEADER_ELEM:
124 type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
125 break;
126 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
127 type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
128 break;
129 case LFT_REPLY_HEADER_ELEM:
130 type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM?
131 break;
132#if USE_ADAPTATION
133 case LFT_ADAPTATION_LAST_HEADER_ELEM:
134 type = LFT_ADAPTATION_LAST_HEADER;
135 break;
136#endif
137#if ICAP_CLIENT
138 case LFT_ICAP_REQ_HEADER_ELEM:
139 type = LFT_ICAP_REQ_HEADER;
140 break;
141 case LFT_ICAP_REP_HEADER_ELEM:
142 type = LFT_ICAP_REP_HEADER;
143 break;
144#endif
145 default:
146 break;
147 }
148
149 break;
150
151 case LFT_REQUEST_ALL_HEADERS:
152 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
153 case LFT_REPLY_ALL_HEADERS:
154
155#if USE_ADAPTATION
156 case LFT_ADAPTATION_LAST_ALL_HEADERS:
157#endif
158#if ICAP_CLIENT
159 case LFT_ICAP_REQ_ALL_HEADERS:
160 case LFT_ICAP_REP_ALL_HEADERS:
161#endif
162
163 switch (type) {
164 case LFT_REQUEST_ALL_HEADERS:
165 type = LFT_REQUEST_HEADER;
166 break;
167 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
168 type = LFT_ADAPTED_REQUEST_HEADER;
169 break;
170 case LFT_REPLY_ALL_HEADERS:
171 type = LFT_REPLY_HEADER;
172 break;
173#if USE_ADAPTATION
174 case LFT_ADAPTATION_LAST_ALL_HEADERS:
175 type = LFT_ADAPTATION_LAST_HEADER;
176 break;
177#endif
178#if ICAP_CLIENT
179 case LFT_ICAP_REQ_ALL_HEADERS:
180 type = LFT_ICAP_REQ_HEADER;
181 break;
182 case LFT_ICAP_REP_ALL_HEADERS:
183 type = LFT_ICAP_REP_HEADER;
184 break;
185#endif
186 default:
187 break;
188 }
189
190 break;
191
192 default:
193 if (t->data.string)
194 arg = t->data.string;
195
196 break;
197 }
198
199 entry->append("%", 1);
200
201 switch (t->quote) {
202
203 case LOG_QUOTE_QUOTES:
204 entry->append("\"", 1);
205 break;
206
207 case LOG_QUOTE_MIMEBLOB:
208 entry->append("[", 1);
209 break;
210
211 case LOG_QUOTE_URL:
212 entry->append("#", 1);
213 break;
214
215 case LOG_QUOTE_RAW:
216 entry->append("'", 1);
217 break;
218
219 case LOG_QUOTE_NONE:
220 break;
221 }
222
223 if (t->left)
224 entry->append("-", 1);
225
226 if (t->zero)
227 entry->append("0", 1);
228
8846a2b4
CT
229 if (t->widthMin >= 0)
230 storeAppendPrintf(entry, "%d", t->widthMin);
38e16f92 231
8846a2b4
CT
232 if (t->widthMax >= 0)
233 storeAppendPrintf(entry, ".%d", t->widthMax);
38e16f92
AJ
234
235 if (arg)
236 storeAppendPrintf(entry, "{%s}", arg);
237
aa99e35e 238 storeAppendPrintf(entry, "%s", t->label);
38e16f92
AJ
239
240 if (t->space)
241 entry->append(" ", 1);
242 }
243 }
244
245 entry->append("\n", 1);
246 }
247
248}
249
250static void
251log_quoted_string(const char *str, char *out)
252{
253 char *p = out;
254
255 while (*str) {
256 int l = strcspn(str, "\"\\\r\n\t");
257 memcpy(p, str, l);
258 str += l;
259 p += l;
260
261 switch (*str) {
262
263 case '\0':
264 break;
265
266 case '\r':
a38ec4b1
FC
267 *p = '\\';
268 ++p;
269 *p = 'r';
270 ++p;
cb4185f1 271 ++str;
38e16f92
AJ
272 break;
273
274 case '\n':
a38ec4b1
FC
275 *p = '\\';
276 ++p;
277 *p = 'n';
278 ++p;
cb4185f1 279 ++str;
38e16f92
AJ
280 break;
281
282 case '\t':
a38ec4b1
FC
283 *p = '\\';
284 ++p;
285 *p = 't';
286 ++p;
cb4185f1 287 ++str;
38e16f92
AJ
288 break;
289
290 default:
a38ec4b1
FC
291 *p = '\\';
292 ++p;
293 *p = *str;
294 ++p;
cb4185f1 295 ++str;
38e16f92
AJ
296 break;
297 }
298 }
299
cb4185f1 300 *p = '\0';
38e16f92
AJ
301}
302
303void
f4698e0b 304Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
38e16f92
AJ
305{
306 char tmp[1024];
307 String sb;
308
309 for (Token *fmt = format; fmt != NULL; fmt = fmt->next) { /* for each token */
310 const char *out = NULL;
311 int quote = 0;
312 long int outint = 0;
313 int doint = 0;
314 int dofree = 0;
315 int64_t outoff = 0;
316 int dooff = 0;
317
318 switch (fmt->type) {
319
320 case LFT_NONE:
321 out = "";
322 break;
323
324 case LFT_STRING:
325 out = fmt->data.string;
326 break;
327
328 case LFT_CLIENT_IP_ADDRESS:
d4204018
AJ
329 al->getLogClientIp(tmp, sizeof(tmp));
330 out = tmp;
38e16f92
AJ
331 break;
332
333 case LFT_CLIENT_FQDN:
334 if (al->cache.caddr.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
335 out = "-";
336 else
337 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
338 if (!out) {
339 out = al->cache.caddr.NtoA(tmp,1024);
340 }
341
342 break;
343
344 case LFT_CLIENT_PORT:
345 if (al->request) {
346 outint = al->request->client_addr.GetPort();
347 doint = 1;
348 }
349 break;
350
38e16f92 351 case LFT_CLIENT_EUI:
8652f8e7 352#if USE_SQUID_EUI
38e16f92
AJ
353 // TODO make the ACL checklist have a direct link to any TCP details.
354 if (al->request && al->request->clientConnectionManager.valid() && al->request->clientConnectionManager->clientConnection != NULL) {
355 if (al->request->clientConnectionManager->clientConnection->remote.IsIPv4())
356 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
357 else
358 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
359 out = tmp;
360 }
8652f8e7
AJ
361#else
362 out = "-";
38e16f92 363#endif
8652f8e7 364 break;
38e16f92 365
8652f8e7
AJ
366 case LFT_SERVER_IP_ADDRESS:
367 if (al->hier.tcpServer != NULL) {
368 out = al->hier.tcpServer->remote.NtoA(tmp,sizeof(tmp));
369 }
370 break;
38e16f92 371
8652f8e7 372 case LFT_SERVER_FQDN_OR_PEER_NAME:
38e16f92 373 out = al->hier.host;
38e16f92
AJ
374 break;
375
8652f8e7
AJ
376 case LFT_SERVER_PORT:
377 if (al->hier.tcpServer != NULL) {
378 outint = al->hier.tcpServer->remote.GetPort();
379 doint = 1;
380 }
381 break;
38e16f92 382
28417506
CT
383 case LFT_LOCAL_LISTENING_IP: {
384 // avoid logging a dash if we have reliable info
47176c0b 385 const bool interceptedAtKnownPort = al->request ?
450fe1cb 386 (al->request->flags.spoofClientIp ||
45e5102d 387 al->request->flags.intercepted) && al->cache.port :
47176c0b 388 false;
28417506
CT
389 if (interceptedAtKnownPort) {
390 const bool portAddressConfigured = !al->cache.port->s.IsAnyAddr();
391 if (portAddressConfigured)
392 out = al->cache.port->s.NtoA(tmp, sizeof(tmp));
393 } else if (al->tcpClient != NULL)
394 out = al->tcpClient->local.NtoA(tmp, sizeof(tmp));
395 }
396 break;
397
8652f8e7 398 case LFT_CLIENT_LOCAL_IP:
a14f38d0
AJ
399 if (al->tcpClient != NULL) {
400 out = al->tcpClient->local.NtoA(tmp,sizeof(tmp));
38e16f92 401 }
38e16f92
AJ
402 break;
403
28417506
CT
404 case LFT_LOCAL_LISTENING_PORT:
405 if (al->cache.port) {
406 outint = al->cache.port->s.GetPort();
407 doint = 1;
408 }
409 break;
410
8652f8e7 411 case LFT_CLIENT_LOCAL_PORT:
a14f38d0
AJ
412 if (al->tcpClient != NULL) {
413 outint = al->tcpClient->local.GetPort();
38e16f92
AJ
414 doint = 1;
415 }
38e16f92
AJ
416 break;
417
8652f8e7
AJ
418 case LFT_SERVER_LOCAL_IP_OLD_27:
419 case LFT_SERVER_LOCAL_IP:
a14f38d0
AJ
420 if (al->hier.tcpServer != NULL) {
421 out = al->hier.tcpServer->local.NtoA(tmp,sizeof(tmp));
38e16f92
AJ
422 }
423 break;
424
8652f8e7 425 case LFT_SERVER_LOCAL_PORT:
a14f38d0
AJ
426 if (al->hier.tcpServer != NULL) {
427 outint = al->hier.tcpServer->local.GetPort();
38e16f92
AJ
428 doint = 1;
429 }
430
431 break;
432
433 case LFT_TIME_SECONDS_SINCE_EPOCH:
434 // some platforms store time in 32-bit, some 64-bit...
435 outoff = static_cast<int64_t>(current_time.tv_sec);
436 dooff = 1;
437 break;
438
439 case LFT_TIME_SUBSECOND:
440 outint = current_time.tv_usec / fmt->divisor;
441 doint = 1;
442 break;
443
38e16f92
AJ
444 case LFT_TIME_LOCALTIME:
445
446 case LFT_TIME_GMT: {
447 const char *spec;
448
449 struct tm *t;
450 spec = fmt->data.timespec;
451
452 if (fmt->type == LFT_TIME_LOCALTIME) {
453 if (!spec)
454 spec = "%d/%b/%Y:%H:%M:%S %z";
455 t = localtime(&squid_curtime);
456 } else {
457 if (!spec)
458 spec = "%d/%b/%Y:%H:%M:%S";
459
460 t = gmtime(&squid_curtime);
461 }
462
463 strftime(tmp, sizeof(tmp), spec, t);
464
465 out = tmp;
466 }
467
468 break;
469
470 case LFT_TIME_TO_HANDLE_REQUEST:
471 outint = al->cache.msec;
472 doint = 1;
473 break;
474
475 case LFT_PEER_RESPONSE_TIME:
476 if (al->hier.peer_response_time < 0) {
477 out = "-";
478 } else {
479 outoff = al->hier.peer_response_time;
480 dooff = 1;
481 }
482 break;
483
484 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME:
485 if (al->hier.total_response_time < 0) {
486 out = "-";
487 } else {
488 outoff = al->hier.total_response_time;
489 dooff = 1;
490 }
491 break;
492
493 case LFT_DNS_WAIT_TIME:
494 if (al->request && al->request->dnsWait >= 0) {
495 outint = al->request->dnsWait;
496 doint = 1;
497 }
498 break;
499
500 case LFT_REQUEST_HEADER:
501
502 if (al->request)
503 sb = al->request->header.getByName(fmt->data.header.header);
504
505 out = sb.termedBuf();
506
507 quote = 1;
508
509 break;
510
511 case LFT_ADAPTED_REQUEST_HEADER:
512
513 if (al->request)
514 sb = al->adapted_request->header.getByName(fmt->data.header.header);
515
516 out = sb.termedBuf();
517
518 quote = 1;
519
520 break;
521
522 case LFT_REPLY_HEADER:
523 if (al->reply)
524 sb = al->reply->header.getByName(fmt->data.header.header);
525
526 out = sb.termedBuf();
527
528 quote = 1;
529
530 break;
531
532#if USE_ADAPTATION
31971e6a 533 case LFT_ADAPTATION_SUM_XACT_TIMES:
38e16f92
AJ
534 if (al->request) {
535 Adaptation::History::Pointer ah = al->request->adaptHistory();
536 if (ah != NULL)
537 ah->sumLogString(fmt->data.string, sb);
538 out = sb.termedBuf();
539 }
540 break;
541
31971e6a 542 case LFT_ADAPTATION_ALL_XACT_TIMES:
38e16f92
AJ
543 if (al->request) {
544 Adaptation::History::Pointer ah = al->request->adaptHistory();
545 if (ah != NULL)
546 ah->allLogString(fmt->data.string, sb);
547 out = sb.termedBuf();
548 }
549 break;
550
551 case LFT_ADAPTATION_LAST_HEADER:
552 if (al->request) {
553 const Adaptation::History::Pointer ah = al->request->adaptHistory();
554 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
555 sb = ah->allMeta.getByName(fmt->data.header.header);
556 }
557
558 // XXX: here and elsewhere: move such code inside the if guard
559 out = sb.termedBuf();
560
561 quote = 1;
562
563 break;
564
565 case LFT_ADAPTATION_LAST_HEADER_ELEM:
566 if (al->request) {
567 const Adaptation::History::Pointer ah = al->request->adaptHistory();
568 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
569 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
570 }
571
572 out = sb.termedBuf();
573
574 quote = 1;
575
576 break;
577
578 case LFT_ADAPTATION_LAST_ALL_HEADERS:
579 out = al->adapt.last_meta;
580
581 quote = 1;
582
583 break;
584#endif
585
586#if ICAP_CLIENT
587 case LFT_ICAP_ADDR:
588 if (!out)
589 out = al->icap.hostAddr.NtoA(tmp,1024);
590 break;
591
592 case LFT_ICAP_SERV_NAME:
593 out = al->icap.serviceName.termedBuf();
594 break;
595
596 case LFT_ICAP_REQUEST_URI:
597 out = al->icap.reqUri.termedBuf();
598 break;
599
600 case LFT_ICAP_REQUEST_METHOD:
601 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
602 break;
603
604 case LFT_ICAP_BYTES_SENT:
605 outoff = al->icap.bytesSent;
606 dooff = 1;
607 break;
608
609 case LFT_ICAP_BYTES_READ:
610 outoff = al->icap.bytesRead;
611 dooff = 1;
612 break;
613
614 case LFT_ICAP_BODY_BYTES_READ:
615 if (al->icap.bodyBytesRead >= 0) {
616 outoff = al->icap.bodyBytesRead;
617 dooff = 1;
618 }
619 // else if icap.bodyBytesRead < 0, we do not have any http data,
620 // so just print a "-" (204 responses etc)
621 break;
622
623 case LFT_ICAP_REQ_HEADER:
624 if (NULL != al->icap.request) {
625 sb = al->icap.request->header.getByName(fmt->data.header.header);
626 out = sb.termedBuf();
627 quote = 1;
628 }
629 break;
630
631 case LFT_ICAP_REQ_HEADER_ELEM:
632 if (al->request)
633 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
634
635 out = sb.termedBuf();
636
637 quote = 1;
638
639 break;
640
641 case LFT_ICAP_REQ_ALL_HEADERS:
642 if (al->icap.request) {
643 HttpHeaderPos pos = HttpHeaderInitPos;
644 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
645 sb.append(e->name);
646 sb.append(": ");
647 sb.append(e->value);
648 sb.append("\r\n");
649 }
650 out = sb.termedBuf();
651 quote = 1;
652 }
653 break;
654
655 case LFT_ICAP_REP_HEADER:
656 if (NULL != al->icap.reply) {
657 sb = al->icap.reply->header.getByName(fmt->data.header.header);
658 out = sb.termedBuf();
659 quote = 1;
660 }
661 break;
662
663 case LFT_ICAP_REP_HEADER_ELEM:
664 if (NULL != al->icap.reply)
665 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
666
667 out = sb.termedBuf();
668
669 quote = 1;
670
671 break;
672
673 case LFT_ICAP_REP_ALL_HEADERS:
674 if (al->icap.reply) {
675 HttpHeaderPos pos = HttpHeaderInitPos;
676 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
677 sb.append(e->name);
678 sb.append(": ");
679 sb.append(e->value);
680 sb.append("\r\n");
681 }
682 out = sb.termedBuf();
683 quote = 1;
684 }
685 break;
686
687 case LFT_ICAP_TR_RESPONSE_TIME:
688 outint = al->icap.trTime;
689 doint = 1;
690 break;
691
692 case LFT_ICAP_IO_TIME:
693 outint = al->icap.ioTime;
694 doint = 1;
695 break;
696
697 case LFT_ICAP_STATUS_CODE:
698 outint = al->icap.resStatus;
699 doint = 1;
700 break;
701
702 case LFT_ICAP_OUTCOME:
703 out = al->icap.outcome;
704 break;
705
706 case LFT_ICAP_TOTAL_TIME:
707 outint = al->icap.processingTime;
708 doint = 1;
709 break;
710#endif
711 case LFT_REQUEST_HEADER_ELEM:
712 if (al->request)
713 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
714
715 out = sb.termedBuf();
716
717 quote = 1;
718
719 break;
720
721 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
722 if (al->adapted_request)
723 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
724
725 out = sb.termedBuf();
726
727 quote = 1;
728
729 break;
730
731 case LFT_REPLY_HEADER_ELEM:
732 if (al->reply)
733 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
734
735 out = sb.termedBuf();
736
737 quote = 1;
738
739 break;
740
741 case LFT_REQUEST_ALL_HEADERS:
742 out = al->headers.request;
743
744 quote = 1;
745
746 break;
747
748 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
749 out = al->headers.adapted_request;
750
751 quote = 1;
752
753 break;
754
755 case LFT_REPLY_ALL_HEADERS:
756 out = al->headers.reply;
757
758 quote = 1;
759
760 break;
761
762 case LFT_USER_NAME:
bd85ea1f 763 out = strOrNull(al->cache.authuser);
38e16f92 764 if (!out)
bd85ea1f 765 out = strOrNull(al->cache.extuser);
38e16f92 766#if USE_SSL
38e16f92 767 if (!out)
bd85ea1f 768 out = strOrNull(al->cache.ssluser);
38e16f92 769#endif
38e16f92 770 if (!out)
bd85ea1f 771 out = strOrNull(al->cache.rfc931);
38e16f92
AJ
772 break;
773
774 case LFT_USER_LOGIN:
bd85ea1f 775 out = strOrNull(al->cache.authuser);
38e16f92
AJ
776 break;
777
778 case LFT_USER_IDENT:
bd85ea1f 779 out = strOrNull(al->cache.rfc931);
38e16f92
AJ
780 break;
781
782 case LFT_USER_EXTERNAL:
bd85ea1f 783 out = strOrNull(al->cache.extuser);
38e16f92
AJ
784 break;
785
786 /* case LFT_USER_REALM: */
787 /* case LFT_USER_SCHEME: */
788
789 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
790 // but compiler complains if ommited
791 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
792 case LFT_HTTP_SENT_STATUS_CODE:
793 outint = al->http.code;
794
795 doint = 1;
796
797 break;
798
799 case LFT_HTTP_RECEIVED_STATUS_CODE:
955394ce 800 if (al->hier.peer_reply_status == Http::scNone) {
38e16f92
AJ
801 out = "-";
802 } else {
803 outint = al->hier.peer_reply_status;
804 doint = 1;
805 }
806 break;
807 /* case LFT_HTTP_STATUS:
808 * out = statusline->text;
809 * quote = 1;
810 * break;
811 */
812 case LFT_HTTP_BODY_BYTES_READ:
813 if (al->hier.bodyBytesRead >= 0) {
814 outoff = al->hier.bodyBytesRead;
815 dooff = 1;
816 }
817 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
818 // a peer server so just print a "-" (eg requests served from cache,
819 // or internal error messages).
820 break;
821
822 case LFT_SQUID_STATUS:
823 if (al->http.timedout || al->http.aborted) {
02c8dde5 824 snprintf(tmp, sizeof(tmp), "%s%s", LogTags_str[al->cache.code],
38e16f92
AJ
825 al->http.statusSfx());
826 out = tmp;
827 } else {
02c8dde5 828 out = LogTags_str[al->cache.code];
38e16f92
AJ
829 }
830
831 break;
832
833 case LFT_SQUID_ERROR:
834 if (al->request && al->request->errType != ERR_NONE)
835 out = errorPageName(al->request->errType);
836 break;
837
838 case LFT_SQUID_ERROR_DETAIL:
2f3e52b5
CT
839#if USE_SSL
840 if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
841 if (! (out = Ssl::GetErrorName(al->request->errDetail))) {
842 snprintf(tmp, sizeof(tmp), "SSL_ERR=%d", al->request->errDetail);
843 out = tmp;
844 }
e83cdc25 845 } else
2f3e52b5 846#endif
e83cdc25
A
847 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
848 if (al->request->errDetail > ERR_DETAIL_START &&
849 al->request->errDetail < ERR_DETAIL_MAX)
850 out = errorDetailName(al->request->errDetail);
851 else {
852 if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
853 snprintf(tmp, sizeof(tmp), "%s=0x%X",
854 errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
855 else
856 snprintf(tmp, sizeof(tmp), "%s=%d",
857 errorDetailName(al->request->errDetail), al->request->errDetail);
858 out = tmp;
859 }
38e16f92 860 }
38e16f92
AJ
861 break;
862
863 case LFT_SQUID_HIERARCHY:
864 if (al->hier.ping.timedout)
865 mb.append("TIMEOUT_", 8);
866
867 out = hier_code_str[al->hier.code];
868
869 break;
870
871 case LFT_MIME_TYPE:
872 out = al->http.content_type;
873
874 break;
875
876 case LFT_CLIENT_REQ_METHOD:
877 if (al->request) {
878 out = al->request->method.image();
879 quote = 1;
880 }
881 break;
882
883 case LFT_CLIENT_REQ_URI:
884 // original client URI
885 if (al->request) {
886 out = urlCanonical(al->request);
887 quote = 1;
888 }
889 break;
890
891 case LFT_REQUEST_URLPATH_OLD_31:
892 case LFT_CLIENT_REQ_URLPATH:
893 if (al->request) {
894 out = al->request->urlpath.termedBuf();
895 quote = 1;
896 }
897 break;
898
899 case LFT_CLIENT_REQ_VERSION:
900 if (al->request) {
901 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
902 out = tmp;
903 }
904 break;
905
906 case LFT_REQUEST_METHOD:
907 out = al->_private.method_str;
908 break;
909
910 case LFT_REQUEST_URI:
911 out = al->url;
912 break;
913
914 case LFT_REQUEST_VERSION_OLD_2X:
915 case LFT_REQUEST_VERSION:
916 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
917 out = tmp;
918 break;
919
920 case LFT_SERVER_REQ_METHOD:
921 if (al->adapted_request) {
922 out = al->adapted_request->method.image();
923 quote = 1;
924 }
925 break;
926
927 case LFT_SERVER_REQ_URI:
928 // adapted request URI sent to server/peer
929 if (al->adapted_request) {
930 out = urlCanonical(al->adapted_request);
931 quote = 1;
932 }
933 break;
934
935 case LFT_SERVER_REQ_URLPATH:
936 if (al->adapted_request) {
937 out = al->adapted_request->urlpath.termedBuf();
938 quote = 1;
939 }
940 break;
941
942 case LFT_SERVER_REQ_VERSION:
943 if (al->adapted_request) {
944 snprintf(tmp, sizeof(tmp), "%d.%d",
945 (int) al->adapted_request->http_ver.major,
946 (int) al->adapted_request->http_ver.minor);
947 out = tmp;
948 }
949 break;
950
951 case LFT_REQUEST_SIZE_TOTAL:
952 outoff = al->cache.requestSize;
953 dooff = 1;
954 break;
955
956 /*case LFT_REQUEST_SIZE_LINE: */
957 case LFT_REQUEST_SIZE_HEADERS:
958 outoff = al->cache.requestHeadersSize;
959 dooff =1;
960 break;
961 /*case LFT_REQUEST_SIZE_BODY: */
962 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
963
964 case LFT_REPLY_SIZE_TOTAL:
965 outoff = al->cache.replySize;
966 dooff = 1;
967 break;
968
969 case LFT_REPLY_HIGHOFFSET:
970 outoff = al->cache.highOffset;
971
972 dooff = 1;
973
974 break;
975
976 case LFT_REPLY_OBJECTSIZE:
977 outoff = al->cache.objectSize;
978
979 dooff = 1;
980
981 break;
982
983 /*case LFT_REPLY_SIZE_LINE: */
984 case LFT_REPLY_SIZE_HEADERS:
985 outint = al->cache.replyHeadersSize;
986 doint = 1;
987 break;
988 /*case LFT_REPLY_SIZE_BODY: */
989 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
990
991 case LFT_TAG:
992 if (al->request)
993 out = al->request->tag.termedBuf();
994
995 quote = 1;
996
997 break;
998
999 case LFT_IO_SIZE_TOTAL:
1000 outint = al->cache.requestSize + al->cache.replySize;
1001 doint = 1;
1002 break;
1003
1004 case LFT_EXT_LOG:
1005 if (al->request)
1006 out = al->request->extacl_log.termedBuf();
1007
1008 quote = 1;
1009
1010 break;
1011
1012 case LFT_SEQUENCE_NUMBER:
1013 outoff = logSequenceNumber;
1014 dooff = 1;
1015 break;
1016
08097970
AR
1017#if USE_SSL
1018 case LFT_SSL_BUMP_MODE: {
1019 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1020 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1021 out = Ssl::bumpMode(mode);
1022 break;
1023 }
71cae389 1024
f4698e0b
CT
1025 case LFT_SSL_USER_CERT_SUBJECT:
1026 if (X509 *cert = al->cache.sslClientCert.get()) {
1027 if (X509_NAME *subject = X509_get_subject_name(cert)) {
1028 X509_NAME_oneline(subject, tmp, sizeof(tmp));
1029 out = tmp;
1030 }
1031 }
1032 break;
1033
1034 case LFT_SSL_USER_CERT_ISSUER:
1035 if (X509 *cert = al->cache.sslClientCert.get()) {
1036 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
1037 X509_NAME_oneline(issuer, tmp, sizeof(tmp));
1038 out = tmp;
1039 }
1040 }
1041 break;
08097970 1042#endif
d7f4a0b7
CT
1043 case LFT_NOTE:
1044 if (fmt->data.string) {
cf9f0261
CT
1045#if USE_ADAPTATION
1046 Adaptation::History::Pointer ah = al->request->adaptHistory();
1047 if (ah != NULL && ah->metaHeaders != NULL) {
1048 if (const char *meta = ah->metaHeaders->find(fmt->data.string))
1049 sb.append(meta);
1050 }
1051#endif
1052 if (al->helperNotes != NULL) {
1053 if (const char *note = al->helperNotes->find(fmt->data.string)) {
1054 if (sb.size())
1055 sb.append(", ");
1056 sb.append(note);
1057 }
1058 }
1059 if (al->configNotes != NULL) {
1060 if (const char *note = al->configNotes->find(fmt->data.string)) {
1061 if (sb.size())
1062 sb.append(", ");
1063 sb.append(note);
1064 }
1065 }
d7f4a0b7
CT
1066 out = sb.termedBuf();
1067 quote = 1;
1068 } else {
cf9f0261
CT
1069#if USE_ADAPTATION
1070 Adaptation::History::Pointer ah = al->request->adaptHistory();
1071 if (ah != NULL && ah->metaHeaders != NULL && !ah->metaHeaders->empty())
1072 sb.append(ah->metaHeaders->toString());
1073#endif
1074 if (al->helperNotes != NULL && !al->helperNotes->empty())
1075 sb.append(al->helperNotes->toString());
1076 if (al->configNotes != NULL && !al->configNotes->empty())
1077 sb.append(al->configNotes->toString());
d7f4a0b7
CT
1078 out = sb.termedBuf();
1079 quote = 1;
1080 }
1081 break;
08097970 1082
38e16f92
AJ
1083 case LFT_PERCENT:
1084 out = "%";
1085
1086 break;
1087 }
1088
1089 if (dooff) {
8846a2b4 1090 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
38e16f92
AJ
1091 out = tmp;
1092
1093 } else if (doint) {
8846a2b4 1094 snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
38e16f92
AJ
1095 out = tmp;
1096 }
1097
1098 if (out && *out) {
1099 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1100 char *newout = NULL;
1101 int newfree = 0;
1102
1103 switch (fmt->quote) {
1104
1105 case LOG_QUOTE_NONE:
1106 newout = rfc1738_escape_unescaped(out);
1107 break;
1108
1109 case LOG_QUOTE_QUOTES: {
1110 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1111 if (out_len >= sizeof(tmp)) {
1112 newout = (char *)xmalloc(out_len);
1113 newfree = 1;
1114 } else
1115 newout = tmp;
1116 log_quoted_string(out, newout);
1117 }
1118 break;
1119
1120 case LOG_QUOTE_MIMEBLOB:
1121 newout = QuoteMimeBlob(out);
1122 newfree = 1;
1123 break;
1124
1125 case LOG_QUOTE_URL:
1126 newout = rfc1738_escape(out);
1127 break;
1128
1129 case LOG_QUOTE_RAW:
1130 break;
1131 }
1132
1133 if (newout) {
1134 if (dofree)
1135 safe_free(out);
1136
1137 out = newout;
1138
1139 dofree = newfree;
1140 }
1141 }
1142
c32c6db7 1143 // enforce width limits if configured
8846a2b4
CT
1144 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff;
1145 if (haveMaxWidth || fmt->widthMin) {
1146 const int minWidth = fmt->widthMin >= 0 ?
b8ad91f5 1147 fmt->widthMin :0;
c32c6db7 1148 const int maxWidth = haveMaxWidth ?
8846a2b4 1149 fmt->widthMax : strlen(out);
c32c6db7 1150
38e16f92 1151 if (fmt->left)
c32c6db7 1152 mb.Printf("%-*.*s", minWidth, maxWidth, out);
38e16f92 1153 else
c32c6db7 1154 mb.Printf("%*.*s", minWidth, maxWidth, out);
38e16f92
AJ
1155 } else
1156 mb.append(out, strlen(out));
1157 } else {
1158 mb.append("-", 1);
1159 }
1160
1161 if (fmt->space)
1162 mb.append(" ", 1);
1163
1164 sb.clean();
1165
1166 if (dofree)
1167 safe_free(out);
1168 }
1169}