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