]> git.ipfire.org Git - thirdparty/squid.git/blob - src/format/Format.cc
Bug 2279: Log Format options to log server source IP and port
[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 for (struct TokenTableEntry *te = TokenTable; te->config != NULL; te++) {
230 if (te->token_type == type) {
231 storeAppendPrintf(entry, "%s", te->config);
232 break;
233 }
234 }
235
236 if (t->space)
237 entry->append(" ", 1);
238 }
239 }
240
241 entry->append("\n", 1);
242 }
243
244 }
245
246 static void
247 log_quoted_string(const char *str, char *out)
248 {
249 char *p = out;
250
251 while (*str) {
252 int l = strcspn(str, "\"\\\r\n\t");
253 memcpy(p, str, l);
254 str += l;
255 p += l;
256
257 switch (*str) {
258
259 case '\0':
260 break;
261
262 case '\r':
263 *p++ = '\\';
264 *p++ = 'r';
265 str++;
266 break;
267
268 case '\n':
269 *p++ = '\\';
270 *p++ = 'n';
271 str++;
272 break;
273
274 case '\t':
275 *p++ = '\\';
276 *p++ = 't';
277 str++;
278 break;
279
280 default:
281 *p++ = '\\';
282 *p++ = *str;
283 str++;
284 break;
285 }
286 }
287
288 *p++ = '\0';
289 }
290
291 void
292 Format::Format::assemble(MemBuf &mb, AccessLogEntry *al, int logSequenceNumber) const
293 {
294 char tmp[1024];
295 String sb;
296
297 for (Token *fmt = format; fmt != NULL; fmt = fmt->next) { /* for each token */
298 const char *out = NULL;
299 int quote = 0;
300 long int outint = 0;
301 int doint = 0;
302 int dofree = 0;
303 int64_t outoff = 0;
304 int dooff = 0;
305
306 switch (fmt->type) {
307
308 case LFT_NONE:
309 out = "";
310 break;
311
312 case LFT_STRING:
313 out = fmt->data.string;
314 break;
315
316 case LFT_CLIENT_IP_ADDRESS:
317 if (al->cache.caddr.IsNoAddr()) // e.g., ICAP OPTIONS lack client
318 out = "-";
319 else
320 out = al->cache.caddr.NtoA(tmp,1024);
321 break;
322
323 case LFT_CLIENT_FQDN:
324 if (al->cache.caddr.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
325 out = "-";
326 else
327 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
328 if (!out) {
329 out = al->cache.caddr.NtoA(tmp,1024);
330 }
331
332 break;
333
334 case LFT_CLIENT_PORT:
335 if (al->request) {
336 outint = al->request->client_addr.GetPort();
337 doint = 1;
338 }
339 break;
340
341 case LFT_CLIENT_EUI:
342 #if USE_SQUID_EUI
343 // TODO make the ACL checklist have a direct link to any TCP details.
344 if (al->request && al->request->clientConnectionManager.valid() && al->request->clientConnectionManager->clientConnection != NULL) {
345 if (al->request->clientConnectionManager->clientConnection->remote.IsIPv4())
346 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
347 else
348 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
349 out = tmp;
350 }
351 #else
352 out = "-";
353 #endif
354 break;
355
356 case LFT_SERVER_IP_ADDRESS:
357 if (al->hier.tcpServer != NULL) {
358 out = al->hier.tcpServer->remote.NtoA(tmp,sizeof(tmp));
359 }
360 break;
361
362 case LFT_SERVER_FQDN_OR_PEER_NAME:
363 out = al->hier.host;
364 break;
365
366 case LFT_SERVER_PORT:
367 if (al->hier.tcpServer != NULL) {
368 outint = al->hier.tcpServer->remote.GetPort();
369 doint = 1;
370 }
371 break;
372
373 case LFT_CLIENT_LOCAL_IP_OLD_31:
374 case LFT_CLIENT_LOCAL_IP:
375 if (al->tcpClient != NULL) {
376 out = al->tcpClient->local.NtoA(tmp,sizeof(tmp));
377 }
378 break;
379
380 case LFT_CLIENT_LOCAL_PORT_OLD_31:
381 case LFT_CLIENT_LOCAL_PORT:
382 if (al->tcpClient != NULL) {
383 outint = al->tcpClient->local.GetPort();
384 doint = 1;
385 }
386 break;
387
388 case LFT_SERVER_LOCAL_IP_OLD_27:
389 case LFT_SERVER_LOCAL_IP:
390 if (al->hier.tcpServer != NULL) {
391 out = al->hier.tcpServer->local.NtoA(tmp,sizeof(tmp));
392 }
393 break;
394
395 case LFT_SERVER_LOCAL_PORT:
396 if (al->hier.tcpServer != NULL) {
397 outint = al->hier.tcpServer->local.GetPort();
398 doint = 1;
399 }
400
401 break;
402
403 case LFT_TIME_SECONDS_SINCE_EPOCH:
404 // some platforms store time in 32-bit, some 64-bit...
405 outoff = static_cast<int64_t>(current_time.tv_sec);
406 dooff = 1;
407 break;
408
409 case LFT_TIME_SUBSECOND:
410 outint = current_time.tv_usec / fmt->divisor;
411 doint = 1;
412 break;
413
414
415 case LFT_TIME_LOCALTIME:
416
417 case LFT_TIME_GMT: {
418 const char *spec;
419
420 struct tm *t;
421 spec = fmt->data.timespec;
422
423 if (fmt->type == LFT_TIME_LOCALTIME) {
424 if (!spec)
425 spec = "%d/%b/%Y:%H:%M:%S %z";
426 t = localtime(&squid_curtime);
427 } else {
428 if (!spec)
429 spec = "%d/%b/%Y:%H:%M:%S";
430
431 t = gmtime(&squid_curtime);
432 }
433
434 strftime(tmp, sizeof(tmp), spec, t);
435
436 out = tmp;
437 }
438
439 break;
440
441 case LFT_TIME_TO_HANDLE_REQUEST:
442 outint = al->cache.msec;
443 doint = 1;
444 break;
445
446 case LFT_PEER_RESPONSE_TIME:
447 if (al->hier.peer_response_time < 0) {
448 out = "-";
449 } else {
450 outoff = al->hier.peer_response_time;
451 dooff = 1;
452 }
453 break;
454
455 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME:
456 if (al->hier.total_response_time < 0) {
457 out = "-";
458 } else {
459 outoff = al->hier.total_response_time;
460 dooff = 1;
461 }
462 break;
463
464 case LFT_DNS_WAIT_TIME:
465 if (al->request && al->request->dnsWait >= 0) {
466 outint = al->request->dnsWait;
467 doint = 1;
468 }
469 break;
470
471 case LFT_REQUEST_HEADER:
472
473 if (al->request)
474 sb = al->request->header.getByName(fmt->data.header.header);
475
476 out = sb.termedBuf();
477
478 quote = 1;
479
480 break;
481
482 case LFT_ADAPTED_REQUEST_HEADER:
483
484 if (al->request)
485 sb = al->adapted_request->header.getByName(fmt->data.header.header);
486
487 out = sb.termedBuf();
488
489 quote = 1;
490
491 break;
492
493 case LFT_REPLY_HEADER:
494 if (al->reply)
495 sb = al->reply->header.getByName(fmt->data.header.header);
496
497 out = sb.termedBuf();
498
499 quote = 1;
500
501 break;
502
503 #if USE_ADAPTATION
504 case LTF_ADAPTATION_SUM_XACT_TIMES:
505 if (al->request) {
506 Adaptation::History::Pointer ah = al->request->adaptHistory();
507 if (ah != NULL)
508 ah->sumLogString(fmt->data.string, sb);
509 out = sb.termedBuf();
510 }
511 break;
512
513 case LTF_ADAPTATION_ALL_XACT_TIMES:
514 if (al->request) {
515 Adaptation::History::Pointer ah = al->request->adaptHistory();
516 if (ah != NULL)
517 ah->allLogString(fmt->data.string, sb);
518 out = sb.termedBuf();
519 }
520 break;
521
522 case LFT_ADAPTATION_LAST_HEADER:
523 if (al->request) {
524 const Adaptation::History::Pointer ah = al->request->adaptHistory();
525 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
526 sb = ah->allMeta.getByName(fmt->data.header.header);
527 }
528
529 // XXX: here and elsewhere: move such code inside the if guard
530 out = sb.termedBuf();
531
532 quote = 1;
533
534 break;
535
536 case LFT_ADAPTATION_LAST_HEADER_ELEM:
537 if (al->request) {
538 const Adaptation::History::Pointer ah = al->request->adaptHistory();
539 if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
540 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
541 }
542
543 out = sb.termedBuf();
544
545 quote = 1;
546
547 break;
548
549 case LFT_ADAPTATION_LAST_ALL_HEADERS:
550 out = al->adapt.last_meta;
551
552 quote = 1;
553
554 break;
555 #endif
556
557 #if ICAP_CLIENT
558 case LFT_ICAP_ADDR:
559 if (!out)
560 out = al->icap.hostAddr.NtoA(tmp,1024);
561 break;
562
563 case LFT_ICAP_SERV_NAME:
564 out = al->icap.serviceName.termedBuf();
565 break;
566
567 case LFT_ICAP_REQUEST_URI:
568 out = al->icap.reqUri.termedBuf();
569 break;
570
571 case LFT_ICAP_REQUEST_METHOD:
572 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
573 break;
574
575 case LFT_ICAP_BYTES_SENT:
576 outoff = al->icap.bytesSent;
577 dooff = 1;
578 break;
579
580 case LFT_ICAP_BYTES_READ:
581 outoff = al->icap.bytesRead;
582 dooff = 1;
583 break;
584
585 case LFT_ICAP_BODY_BYTES_READ:
586 if (al->icap.bodyBytesRead >= 0) {
587 outoff = al->icap.bodyBytesRead;
588 dooff = 1;
589 }
590 // else if icap.bodyBytesRead < 0, we do not have any http data,
591 // so just print a "-" (204 responses etc)
592 break;
593
594 case LFT_ICAP_REQ_HEADER:
595 if (NULL != al->icap.request) {
596 sb = al->icap.request->header.getByName(fmt->data.header.header);
597 out = sb.termedBuf();
598 quote = 1;
599 }
600 break;
601
602 case LFT_ICAP_REQ_HEADER_ELEM:
603 if (al->request)
604 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
605
606 out = sb.termedBuf();
607
608 quote = 1;
609
610 break;
611
612 case LFT_ICAP_REQ_ALL_HEADERS:
613 if (al->icap.request) {
614 HttpHeaderPos pos = HttpHeaderInitPos;
615 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
616 sb.append(e->name);
617 sb.append(": ");
618 sb.append(e->value);
619 sb.append("\r\n");
620 }
621 out = sb.termedBuf();
622 quote = 1;
623 }
624 break;
625
626 case LFT_ICAP_REP_HEADER:
627 if (NULL != al->icap.reply) {
628 sb = al->icap.reply->header.getByName(fmt->data.header.header);
629 out = sb.termedBuf();
630 quote = 1;
631 }
632 break;
633
634 case LFT_ICAP_REP_HEADER_ELEM:
635 if (NULL != al->icap.reply)
636 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
637
638 out = sb.termedBuf();
639
640 quote = 1;
641
642 break;
643
644 case LFT_ICAP_REP_ALL_HEADERS:
645 if (al->icap.reply) {
646 HttpHeaderPos pos = HttpHeaderInitPos;
647 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
648 sb.append(e->name);
649 sb.append(": ");
650 sb.append(e->value);
651 sb.append("\r\n");
652 }
653 out = sb.termedBuf();
654 quote = 1;
655 }
656 break;
657
658 case LFT_ICAP_TR_RESPONSE_TIME:
659 outint = al->icap.trTime;
660 doint = 1;
661 break;
662
663 case LFT_ICAP_IO_TIME:
664 outint = al->icap.ioTime;
665 doint = 1;
666 break;
667
668 case LFT_ICAP_STATUS_CODE:
669 outint = al->icap.resStatus;
670 doint = 1;
671 break;
672
673 case LFT_ICAP_OUTCOME:
674 out = al->icap.outcome;
675 break;
676
677 case LFT_ICAP_TOTAL_TIME:
678 outint = al->icap.processingTime;
679 doint = 1;
680 break;
681 #endif
682 case LFT_REQUEST_HEADER_ELEM:
683 if (al->request)
684 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
685
686 out = sb.termedBuf();
687
688 quote = 1;
689
690 break;
691
692 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
693 if (al->adapted_request)
694 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
695
696 out = sb.termedBuf();
697
698 quote = 1;
699
700 break;
701
702 case LFT_REPLY_HEADER_ELEM:
703 if (al->reply)
704 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
705
706 out = sb.termedBuf();
707
708 quote = 1;
709
710 break;
711
712 case LFT_REQUEST_ALL_HEADERS:
713 out = al->headers.request;
714
715 quote = 1;
716
717 break;
718
719 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
720 out = al->headers.adapted_request;
721
722 quote = 1;
723
724 break;
725
726 case LFT_REPLY_ALL_HEADERS:
727 out = al->headers.reply;
728
729 quote = 1;
730
731 break;
732
733 case LFT_USER_NAME:
734 out = QuoteUrlEncodeUsername(al->cache.authuser);
735
736 if (!out)
737 out = QuoteUrlEncodeUsername(al->cache.extuser);
738
739 #if USE_SSL
740
741 if (!out)
742 out = QuoteUrlEncodeUsername(al->cache.ssluser);
743
744 #endif
745
746 if (!out)
747 out = QuoteUrlEncodeUsername(al->cache.rfc931);
748
749 dofree = 1;
750
751 break;
752
753 case LFT_USER_LOGIN:
754 out = QuoteUrlEncodeUsername(al->cache.authuser);
755
756 dofree = 1;
757
758 break;
759
760 case LFT_USER_IDENT:
761 out = QuoteUrlEncodeUsername(al->cache.rfc931);
762
763 dofree = 1;
764
765 break;
766
767 case LFT_USER_EXTERNAL:
768 out = QuoteUrlEncodeUsername(al->cache.extuser);
769
770 dofree = 1;
771
772 break;
773
774 /* case LFT_USER_REALM: */
775 /* case LFT_USER_SCHEME: */
776
777 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
778 // but compiler complains if ommited
779 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
780 case LFT_HTTP_SENT_STATUS_CODE:
781 outint = al->http.code;
782
783 doint = 1;
784
785 break;
786
787 case LFT_HTTP_RECEIVED_STATUS_CODE:
788 if (al->hier.peer_reply_status == HTTP_STATUS_NONE) {
789 out = "-";
790 } else {
791 outint = al->hier.peer_reply_status;
792 doint = 1;
793 }
794 break;
795 /* case LFT_HTTP_STATUS:
796 * out = statusline->text;
797 * quote = 1;
798 * break;
799 */
800 case LFT_HTTP_BODY_BYTES_READ:
801 if (al->hier.bodyBytesRead >= 0) {
802 outoff = al->hier.bodyBytesRead;
803 dooff = 1;
804 }
805 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
806 // a peer server so just print a "-" (eg requests served from cache,
807 // or internal error messages).
808 break;
809
810 case LFT_SQUID_STATUS:
811 if (al->http.timedout || al->http.aborted) {
812 snprintf(tmp, sizeof(tmp), "%s%s", log_tags[al->cache.code],
813 al->http.statusSfx());
814 out = tmp;
815 } else {
816 out = log_tags[al->cache.code];
817 }
818
819 break;
820
821 case LFT_SQUID_ERROR:
822 if (al->request && al->request->errType != ERR_NONE)
823 out = errorPageName(al->request->errType);
824 break;
825
826 case LFT_SQUID_ERROR_DETAIL:
827 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
828 if (al->request->errDetail > ERR_DETAIL_START &&
829 al->request->errDetail < ERR_DETAIL_MAX)
830 out = errorDetailName(al->request->errDetail);
831 else {
832 if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
833 snprintf(tmp, sizeof(tmp), "%s=0x%X",
834 errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
835 else
836 snprintf(tmp, sizeof(tmp), "%s=%d",
837 errorDetailName(al->request->errDetail), al->request->errDetail);
838 out = tmp;
839 }
840 }
841 break;
842
843 case LFT_SQUID_HIERARCHY:
844 if (al->hier.ping.timedout)
845 mb.append("TIMEOUT_", 8);
846
847 out = hier_code_str[al->hier.code];
848
849 break;
850
851 case LFT_MIME_TYPE:
852 out = al->http.content_type;
853
854 break;
855
856 case LFT_CLIENT_REQ_METHOD:
857 if (al->request) {
858 out = al->request->method.image();
859 quote = 1;
860 }
861 break;
862
863 case LFT_CLIENT_REQ_URI:
864 // original client URI
865 if (al->request) {
866 out = urlCanonical(al->request);
867 quote = 1;
868 }
869 break;
870
871 case LFT_REQUEST_URLPATH_OLD_31:
872 case LFT_CLIENT_REQ_URLPATH:
873 if (al->request) {
874 out = al->request->urlpath.termedBuf();
875 quote = 1;
876 }
877 break;
878
879 case LFT_CLIENT_REQ_VERSION:
880 if (al->request) {
881 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
882 out = tmp;
883 }
884 break;
885
886 case LFT_REQUEST_METHOD:
887 out = al->_private.method_str;
888 break;
889
890 case LFT_REQUEST_URI:
891 out = al->url;
892 break;
893
894 case LFT_REQUEST_VERSION_OLD_2X:
895 case LFT_REQUEST_VERSION:
896 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
897 out = tmp;
898 break;
899
900 case LFT_SERVER_REQ_METHOD:
901 if (al->adapted_request) {
902 out = al->adapted_request->method.image();
903 quote = 1;
904 }
905 break;
906
907 case LFT_SERVER_REQ_URI:
908 // adapted request URI sent to server/peer
909 if (al->adapted_request) {
910 out = urlCanonical(al->adapted_request);
911 quote = 1;
912 }
913 break;
914
915 case LFT_SERVER_REQ_URLPATH:
916 if (al->adapted_request) {
917 out = al->adapted_request->urlpath.termedBuf();
918 quote = 1;
919 }
920 break;
921
922 case LFT_SERVER_REQ_VERSION:
923 if (al->adapted_request) {
924 snprintf(tmp, sizeof(tmp), "%d.%d",
925 (int) al->adapted_request->http_ver.major,
926 (int) al->adapted_request->http_ver.minor);
927 out = tmp;
928 }
929 break;
930
931 case LFT_REQUEST_SIZE_TOTAL:
932 outoff = al->cache.requestSize;
933 dooff = 1;
934 break;
935
936 /*case LFT_REQUEST_SIZE_LINE: */
937 case LFT_REQUEST_SIZE_HEADERS:
938 outoff = al->cache.requestHeadersSize;
939 dooff =1;
940 break;
941 /*case LFT_REQUEST_SIZE_BODY: */
942 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
943
944 case LFT_REPLY_SIZE_TOTAL:
945 outoff = al->cache.replySize;
946 dooff = 1;
947 break;
948
949 case LFT_REPLY_HIGHOFFSET:
950 outoff = al->cache.highOffset;
951
952 dooff = 1;
953
954 break;
955
956 case LFT_REPLY_OBJECTSIZE:
957 outoff = al->cache.objectSize;
958
959 dooff = 1;
960
961 break;
962
963 /*case LFT_REPLY_SIZE_LINE: */
964 case LFT_REPLY_SIZE_HEADERS:
965 outint = al->cache.replyHeadersSize;
966 doint = 1;
967 break;
968 /*case LFT_REPLY_SIZE_BODY: */
969 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
970
971 case LFT_TAG:
972 if (al->request)
973 out = al->request->tag.termedBuf();
974
975 quote = 1;
976
977 break;
978
979 case LFT_IO_SIZE_TOTAL:
980 outint = al->cache.requestSize + al->cache.replySize;
981 doint = 1;
982 break;
983
984 case LFT_EXT_LOG:
985 if (al->request)
986 out = al->request->extacl_log.termedBuf();
987
988 quote = 1;
989
990 break;
991
992 case LFT_SEQUENCE_NUMBER:
993 outoff = logSequenceNumber;
994 dooff = 1;
995 break;
996
997 case LFT_PERCENT:
998 out = "%";
999
1000 break;
1001 }
1002
1003 if (dooff) {
1004 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero ? (int) fmt->width : 0, outoff);
1005 out = tmp;
1006
1007 } else if (doint) {
1008 snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero ? (int) fmt->width : 0, outint);
1009 out = tmp;
1010 }
1011
1012 if (out && *out) {
1013 if (quote || fmt->quote != LOG_QUOTE_NONE) {
1014 char *newout = NULL;
1015 int newfree = 0;
1016
1017 switch (fmt->quote) {
1018
1019 case LOG_QUOTE_NONE:
1020 newout = rfc1738_escape_unescaped(out);
1021 break;
1022
1023 case LOG_QUOTE_QUOTES: {
1024 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1025 if (out_len >= sizeof(tmp)) {
1026 newout = (char *)xmalloc(out_len);
1027 newfree = 1;
1028 } else
1029 newout = tmp;
1030 log_quoted_string(out, newout);
1031 }
1032 break;
1033
1034 case LOG_QUOTE_MIMEBLOB:
1035 newout = QuoteMimeBlob(out);
1036 newfree = 1;
1037 break;
1038
1039 case LOG_QUOTE_URL:
1040 newout = rfc1738_escape(out);
1041 break;
1042
1043 case LOG_QUOTE_RAW:
1044 break;
1045 }
1046
1047 if (newout) {
1048 if (dofree)
1049 safe_free(out);
1050
1051 out = newout;
1052
1053 dofree = newfree;
1054 }
1055 }
1056
1057 if (fmt->width) {
1058 if (fmt->left)
1059 mb.Printf("%-*s", (int) fmt->width, out);
1060 else
1061 mb.Printf("%*s", (int) fmt->width, out);
1062 } else
1063 mb.append(out, strlen(out));
1064 } else {
1065 mb.append("-", 1);
1066 }
1067
1068 if (fmt->space)
1069 mb.append(" ", 1);
1070
1071 sb.clean();
1072
1073 if (dofree)
1074 safe_free(out);
1075 }
1076 }