1 #include "base.h"
2 #include "log.h"
3 #include "buffer.h"
4 
5 #include "plugin.h"
6 
7 #include "inet_ntop_cache.h"
8 
9 #include "sys-socket.h"
10 
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 
14 #include <ctype.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <time.h>
21 
22 #include <stdio.h>
23 
24 #ifdef HAVE_SYSLOG_H
25 # include <syslog.h>
26 #endif
27 
28 typedef struct {
29 	char key;
30 	enum {
31 		FORMAT_UNSET,
32 			FORMAT_UNSUPPORTED,
33 			FORMAT_PERCENT,
34 			FORMAT_REMOTE_HOST,
35 			FORMAT_REMOTE_IDENT,
36 			FORMAT_REMOTE_USER,
37 			FORMAT_TIMESTAMP,
38 			FORMAT_REQUEST_LINE,
39 			FORMAT_STATUS,
40 			FORMAT_BYTES_OUT_NO_HEADER,
41 			FORMAT_HEADER,
42 
43 			FORMAT_REMOTE_ADDR,
44 			FORMAT_LOCAL_ADDR,
45 			FORMAT_COOKIE,
46 			FORMAT_TIME_USED_MS,
47 			FORMAT_ENV,
48 			FORMAT_FILENAME,
49 			FORMAT_REQUEST_PROTOCOL,
50 			FORMAT_REQUEST_METHOD,
51 			FORMAT_SERVER_PORT,
52 			FORMAT_QUERY_STRING,
53 			FORMAT_TIME_USED,
54 			FORMAT_URL,
55 			FORMAT_SERVER_NAME,
56 			FORMAT_HTTP_HOST,
57 			FORMAT_CONNECTION_STATUS,
58 			FORMAT_BYTES_IN,
59 			FORMAT_BYTES_OUT,
60 
61 			FORMAT_RESPONSE_HEADER
62 	} type;
63 } format_mapping;
64 
65 /**
66  *
67  *
68  * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
69  *
70  */
71 
72 static const format_mapping fmap[] =
73 {
74 	{ '%', FORMAT_PERCENT },
75 	{ 'h', FORMAT_REMOTE_HOST },
76 	{ 'l', FORMAT_REMOTE_IDENT },
77 	{ 'u', FORMAT_REMOTE_USER },
78 	{ 't', FORMAT_TIMESTAMP },
79 	{ 'r', FORMAT_REQUEST_LINE },
80 	{ 's', FORMAT_STATUS },
81 	{ 'b', FORMAT_BYTES_OUT_NO_HEADER },
82 	{ 'i', FORMAT_HEADER },
83 
84 	{ 'a', FORMAT_REMOTE_ADDR },
85 	{ 'A', FORMAT_LOCAL_ADDR },
86 	{ 'B', FORMAT_BYTES_OUT_NO_HEADER },
87 	{ 'C', FORMAT_COOKIE },
88 	{ 'D', FORMAT_TIME_USED_MS },
89 	{ 'e', FORMAT_ENV },
90 	{ 'f', FORMAT_FILENAME },
91 	{ 'H', FORMAT_REQUEST_PROTOCOL },
92 	{ 'm', FORMAT_REQUEST_METHOD },
93 	{ 'n', FORMAT_UNSUPPORTED }, /* we have no notes */
94 	{ 'p', FORMAT_SERVER_PORT },
95 	{ 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
96 	{ 'q', FORMAT_QUERY_STRING },
97 	{ 'T', FORMAT_TIME_USED },
98 	{ 'U', FORMAT_URL }, /* w/o querystring */
99 	{ 'v', FORMAT_SERVER_NAME },
100 	{ 'V', FORMAT_HTTP_HOST },
101 	{ 'X', FORMAT_CONNECTION_STATUS },
102 	{ 'I', FORMAT_BYTES_IN },
103 	{ 'O', FORMAT_BYTES_OUT },
104 
105 	{ 'o', FORMAT_RESPONSE_HEADER },
106 
107 	{ '\0', FORMAT_UNSET }
108 };
109 
110 
111 typedef struct {
112 	enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
113 
114 	buffer *string;
115 	int field;
116 } format_field;
117 
118 typedef struct {
119 	format_field **ptr;
120 
121 	size_t used;
122 	size_t size;
123 } format_fields;
124 
125 typedef struct {
126 	buffer *access_logfile;
127 	buffer *format;
128 	unsigned short use_syslog;
129 
130 
131 	int    log_access_fd;
132 	time_t last_generated_accesslog_ts;
133 	time_t *last_generated_accesslog_ts_ptr;
134 
135 
136 	buffer *access_logbuffer;
137 	buffer *ts_accesslog_str;
138 	buffer *ts_accesslog_fmt_str;
139 	unsigned short append_tz_offset;
140 
141 	format_fields *parsed_format;
142 } plugin_config;
143 
144 typedef struct {
145 	PLUGIN_DATA;
146 
147 	plugin_config **config_storage;
148 	plugin_config conf;
149 } plugin_data;
150 
INIT_FUNC(mod_accesslog_init)151 INIT_FUNC(mod_accesslog_init) {
152 	plugin_data *p;
153 
154 	p = calloc(1, sizeof(*p));
155 
156 	return p;
157 }
158 
accesslog_append_escaped(buffer * dest,buffer * str)159 static void accesslog_append_escaped(buffer *dest, buffer *str) {
160 	char *ptr, *start, *end;
161 
162 	/* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
163 	/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
164 	if (str->used == 0) return;
165 	buffer_prepare_append(dest, str->used - 1);
166 
167 	for (ptr = start = str->ptr, end = str->ptr + str->used - 1; ptr < end; ptr++) {
168 		char const c = *ptr;
169 		if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
170 			/* nothing to change, add later as one block */
171 		} else {
172 			/* copy previous part */
173 			if (start < ptr) {
174 				buffer_append_string_len(dest, start, ptr - start);
175 			}
176 			start = ptr + 1;
177 
178 			switch (c) {
179 			case '"':
180 				BUFFER_APPEND_STRING_CONST(dest, "\\\"");
181 				break;
182 			case '\\':
183 				BUFFER_APPEND_STRING_CONST(dest, "\\\\");
184 				break;
185 			case '\b':
186 				BUFFER_APPEND_STRING_CONST(dest, "\\b");
187 				break;
188 			case '\n':
189 				BUFFER_APPEND_STRING_CONST(dest, "\\n");
190 				break;
191 			case '\r':
192 				BUFFER_APPEND_STRING_CONST(dest, "\\r");
193 				break;
194 			case '\t':
195 				BUFFER_APPEND_STRING_CONST(dest, "\\t");
196 				break;
197 			case '\v':
198 				BUFFER_APPEND_STRING_CONST(dest, "\\v");
199 				break;
200 			default: {
201 					/* non printable char => \xHH */
202 					char hh[5] = {'\\','x',0,0,0};
203 					char h = c / 16;
204 					hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
205 					h = c % 16;
206 					hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
207 					buffer_append_string_len(dest, &hh[0], 4);
208 				}
209 				break;
210 			}
211 		}
212 	}
213 
214 	if (start < end) {
215 		buffer_append_string_len(dest, start, end - start);
216 	}
217 }
218 
accesslog_parse_format(server * srv,format_fields * fields,buffer * format)219 static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
220 	size_t i, j, k = 0, start = 0;
221 
222 	if (format->used == 0) return -1;
223 
224 	for (i = 0; i < format->used - 1; i++) {
225 		switch(format->ptr[i]) {
226 		case '%':
227 			if (i > 0 && start != i) {
228 				/* copy the string before this % */
229 				if (fields->size == 0) {
230 					fields->size = 16;
231 					fields->used = 0;
232 					fields->ptr = malloc(fields->size * sizeof(format_field * ));
233 				} else if (fields->used == fields->size) {
234 					fields->size += 16;
235 					fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
236 				}
237 
238 				fields->ptr[fields->used] = malloc(sizeof(format_field));
239 				fields->ptr[fields->used]->type = FIELD_STRING;
240 				fields->ptr[fields->used]->string = buffer_init();
241 
242 				buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
243 
244 				fields->used++;
245 			}
246 
247 			/* we need a new field */
248 
249 			if (fields->size == 0) {
250 				fields->size = 16;
251 				fields->used = 0;
252 				fields->ptr = malloc(fields->size * sizeof(format_field * ));
253 			} else if (fields->used == fields->size) {
254 				fields->size += 16;
255 				fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
256 			}
257 
258 			/* search for the terminating command */
259 			switch (format->ptr[i+1]) {
260 			case '>':
261 			case '<':
262 				/* after the } has to be a character */
263 				if (format->ptr[i+2] == '\0') {
264 					log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
265 					return -1;
266 				}
267 
268 
269 				for (j = 0; fmap[j].key != '\0'; j++) {
270 					if (fmap[j].key != format->ptr[i+2]) continue;
271 
272 					/* found key */
273 
274 					fields->ptr[fields->used] = malloc(sizeof(format_field));
275 					fields->ptr[fields->used]->type = FIELD_FORMAT;
276 					fields->ptr[fields->used]->field = fmap[j].type;
277 					fields->ptr[fields->used]->string = NULL;
278 
279 					fields->used++;
280 
281 					break;
282 				}
283 
284 				if (fmap[j].key == '\0') {
285 					log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
286 					return -1;
287 				}
288 
289 				start = i + 3;
290 				i = start - 1; /* skip the string */
291 
292 				break;
293 			case '{':
294 				/* go forward to } */
295 
296 				for (k = i+2; k < format->used - 1; k++) {
297 					if (format->ptr[k] == '}') break;
298 				}
299 
300 				if (k == format->used - 1) {
301 					log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
302 					return -1;
303 				}
304 
305 				/* after the } has to be a character */
306 				if (format->ptr[k+1] == '\0') {
307 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
308 					return -1;
309 				}
310 
311 				if (k == i + 2) {
312 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
313 					return -1;
314 				}
315 
316 				for (j = 0; fmap[j].key != '\0'; j++) {
317 					if (fmap[j].key != format->ptr[k+1]) continue;
318 
319 					/* found key */
320 
321 					fields->ptr[fields->used] = malloc(sizeof(format_field));
322 					fields->ptr[fields->used]->type = FIELD_FORMAT;
323 					fields->ptr[fields->used]->field = fmap[j].type;
324 					fields->ptr[fields->used]->string = buffer_init();
325 
326 					buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
327 
328 					fields->used++;
329 
330 					break;
331 				}
332 
333 				if (fmap[j].key == '\0') {
334 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
335 					return -1;
336 				}
337 
338 				start = k + 2;
339 				i = start - 1; /* skip the string */
340 
341 				break;
342 			default:
343 				/* after the % has to be a character */
344 				if (format->ptr[i+1] == '\0') {
345 					log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
346 					return -1;
347 				}
348 
349 				for (j = 0; fmap[j].key != '\0'; j++) {
350 					if (fmap[j].key != format->ptr[i+1]) continue;
351 
352 					/* found key */
353 
354 					fields->ptr[fields->used] = malloc(sizeof(format_field));
355 					fields->ptr[fields->used]->type = FIELD_FORMAT;
356 					fields->ptr[fields->used]->field = fmap[j].type;
357 					fields->ptr[fields->used]->string = NULL;
358 
359 					fields->used++;
360 
361 					break;
362 				}
363 
364 				if (fmap[j].key == '\0') {
365 					log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
366 					return -1;
367 				}
368 
369 				start = i + 2;
370 				i = start - 1; /* skip the string */
371 
372 				break;
373 			}
374 
375 			break;
376 		}
377 	}
378 
379 	if (start < i) {
380 		/* copy the string */
381 		if (fields->size == 0) {
382 			fields->size = 16;
383 			fields->used = 0;
384 			fields->ptr = malloc(fields->size * sizeof(format_field * ));
385 		} else if (fields->used == fields->size) {
386 			fields->size += 16;
387 			fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
388 		}
389 
390 		fields->ptr[fields->used] = malloc(sizeof(format_field));
391 		fields->ptr[fields->used]->type = FIELD_STRING;
392 		fields->ptr[fields->used]->string = buffer_init();
393 
394 		buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
395 
396 		fields->used++;
397 	}
398 
399 	return 0;
400 }
401 
FREE_FUNC(mod_accesslog_free)402 FREE_FUNC(mod_accesslog_free) {
403 	plugin_data *p = p_d;
404 	size_t i;
405 
406 	if (!p) return HANDLER_GO_ON;
407 
408 	if (p->config_storage) {
409 
410 		for (i = 0; i < srv->config_context->used; i++) {
411 			plugin_config *s = p->config_storage[i];
412 
413 			if (!s) continue;
414 
415 			if (s->access_logbuffer->used) {
416 				if (s->use_syslog) {
417 # ifdef HAVE_SYSLOG_H
418 					if (s->access_logbuffer->used > 2) {
419 						syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
420 					}
421 # endif
422 				} else if (s->log_access_fd != -1) {
423 					write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
424 				}
425 			}
426 
427 			if (s->log_access_fd != -1) close(s->log_access_fd);
428 
429 			buffer_free(s->ts_accesslog_str);
430 			buffer_free(s->ts_accesslog_fmt_str);
431 			buffer_free(s->access_logbuffer);
432 			buffer_free(s->format);
433 			buffer_free(s->access_logfile);
434 
435 			if (s->parsed_format) {
436 				size_t j;
437 				for (j = 0; j < s->parsed_format->used; j++) {
438 					if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
439 					free(s->parsed_format->ptr[j]);
440 				}
441 				free(s->parsed_format->ptr);
442 				free(s->parsed_format);
443 			}
444 
445 			free(s);
446 		}
447 
448 		free(p->config_storage);
449 	}
450 
451 	free(p);
452 
453 	return HANDLER_GO_ON;
454 }
455 
SETDEFAULTS_FUNC(log_access_open)456 SETDEFAULTS_FUNC(log_access_open) {
457 	plugin_data *p = p_d;
458 	size_t i = 0;
459 
460 	config_values_t cv[] = {
461 		{ "accesslog.filename",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
462 		{ "accesslog.use-syslog",           NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
463 		{ "accesslog.format",               NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
464 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
465 	};
466 
467 	if (!p) return HANDLER_ERROR;
468 
469 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
470 
471 	for (i = 0; i < srv->config_context->used; i++) {
472 		plugin_config *s;
473 
474 		s = calloc(1, sizeof(plugin_config));
475 		s->access_logfile = buffer_init();
476 		s->format = buffer_init();
477 		s->access_logbuffer = buffer_init();
478 		s->ts_accesslog_str = buffer_init();
479 		s->ts_accesslog_fmt_str = buffer_init();
480 		s->log_access_fd = -1;
481 		s->last_generated_accesslog_ts = 0;
482 		s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
483 
484 
485 		cv[0].destination = s->access_logfile;
486 		cv[1].destination = &(s->use_syslog);
487 		cv[2].destination = s->format;
488 
489 		p->config_storage[i] = s;
490 
491 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
492 			return HANDLER_ERROR;
493 		}
494 
495 		if (i == 0 && buffer_is_empty(s->format)) {
496 			/* set a default logfile string */
497 
498 			buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
499 		}
500 
501 		/* parse */
502 
503 		if (s->format->used) {
504 			size_t j, count;
505 
506 			s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
507 
508 			if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
509 
510 				log_error_write(srv, __FILE__, __LINE__, "sb",
511 						"parsing accesslog-definition failed:", s->format);
512 
513 				return HANDLER_ERROR;
514 			}
515 
516 			/* make sure they didn't try to send the timestamp in twice...
517 			 * also, save the format string in a different variable (this
518 			 * will save a few conditionals later)
519 			 */
520 			count = 0;
521 			for (j = 0; j < s->parsed_format->used; j++) {
522 				if (FIELD_FORMAT == s->parsed_format->ptr[j]->type) {
523 					if (FORMAT_TIMESTAMP == s->parsed_format->ptr[j]->field) {
524 						if (!buffer_is_empty(s->parsed_format->ptr[j]->string)) {
525 							buffer_copy_string(s->ts_accesslog_fmt_str, s->parsed_format->ptr[j]->string->ptr);
526 						}
527 
528 						if (++count > 1) {
529 							log_error_write(srv, __FILE__, __LINE__, "sb",
530 								"you may not use the timestamp twice in the same access log:", s->format);
531 
532 							return HANDLER_ERROR;
533 						}
534 					}
535 				}
536 			}
537 
538 #if 0
539 			/* debugging */
540 			for (j = 0; j < s->parsed_format->used; j++) {
541 				switch (s->parsed_format->ptr[j]->type) {
542 				case FIELD_FORMAT:
543 					log_error_write(srv, __FILE__, __LINE__, "ssds",
544 							"config:", "format", s->parsed_format->ptr[j]->field,
545 							s->parsed_format->ptr[j]->string ?
546 							s->parsed_format->ptr[j]->string->ptr : "" );
547 					break;
548 				case FIELD_STRING:
549 					log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
550 					break;
551 				default:
552 					break;
553 				}
554 			}
555 #endif
556 		}
557 
558 		s->append_tz_offset = 0;
559 		if (buffer_is_empty(s->ts_accesslog_fmt_str)) {
560 #if defined(HAVE_STRUCT_TM_GMTOFF)
561 			BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S ");
562 			s->append_tz_offset = 1;
563 #else
564 			BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S +0000]");
565 #endif
566 		}
567 
568 		if (s->use_syslog) {
569 			/* ignore the next checks */
570 			continue;
571 		}
572 
573 		if (s->access_logfile->used < 2) continue;
574 
575 		if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr)))
576 			return HANDLER_ERROR;
577 
578 	}
579 
580 	return HANDLER_GO_ON;
581 }
582 
SIGHUP_FUNC(log_access_cycle)583 SIGHUP_FUNC(log_access_cycle) {
584 	plugin_data *p = p_d;
585 	size_t i;
586 
587 	if (!p->config_storage) return HANDLER_GO_ON;
588 
589 	for (i = 0; i < srv->config_context->used; i++) {
590 		plugin_config *s = p->config_storage[i];
591 
592 		if (s->access_logbuffer->used) {
593 			if (s->use_syslog) {
594 #ifdef HAVE_SYSLOG_H
595 				if (s->access_logbuffer->used > 2) {
596 					/* syslog appends a \n on its own */
597 					syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
598 				}
599 #endif
600 			} else if (s->log_access_fd != -1) {
601 				write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
602 			}
603 
604 			buffer_reset(s->access_logbuffer);
605 		}
606 
607 		if (s->use_syslog == 0 &&
608 		    s->access_logfile->used > 1 &&
609 		    s->access_logfile->ptr[0] != '|') {
610 
611 			close(s->log_access_fd);
612 
613 			if (-1 == (s->log_access_fd =
614 				   open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
615 
616 				log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
617 
618 				return HANDLER_ERROR;
619 			}
620 #ifdef FD_CLOEXEC
621 			fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC);
622 #endif
623 		}
624 	}
625 
626 	return HANDLER_GO_ON;
627 }
628 
629 #define PATCH(x) \
630 	p->conf.x = s->x;
mod_accesslog_patch_connection(server * srv,connection * con,plugin_data * p)631 static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
632 	size_t i, j;
633 	plugin_config *s = p->config_storage[0];
634 
635 	PATCH(access_logfile);
636 	PATCH(format);
637 	PATCH(log_access_fd);
638 	PATCH(last_generated_accesslog_ts_ptr);
639 	PATCH(access_logbuffer);
640 	PATCH(ts_accesslog_str);
641 	PATCH(ts_accesslog_fmt_str);
642 	PATCH(append_tz_offset);
643 	PATCH(parsed_format);
644 	PATCH(use_syslog);
645 
646 	/* skip the first, the global context */
647 	for (i = 1; i < srv->config_context->used; i++) {
648 		data_config *dc = (data_config *)srv->config_context->data[i];
649 		s = p->config_storage[i];
650 
651 		/* condition didn't match */
652 		if (!config_check_cond(srv, con, dc)) continue;
653 
654 		/* merge config */
655 		for (j = 0; j < dc->value->used; j++) {
656 			data_unset *du = dc->value->data[j];
657 
658 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
659 				PATCH(access_logfile);
660 				PATCH(log_access_fd);
661 				PATCH(access_logbuffer);
662 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
663 				PATCH(format);
664 				PATCH(parsed_format);
665 				PATCH(last_generated_accesslog_ts_ptr);
666 				PATCH(ts_accesslog_str);
667 				PATCH(ts_accesslog_fmt_str);
668 				PATCH(append_tz_offset);
669 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
670 				PATCH(use_syslog);
671 				PATCH(access_logbuffer);
672 			}
673 		}
674 	}
675 
676 	return 0;
677 }
678 #undef PATCH
679 
REQUESTDONE_FUNC(log_access_write)680 REQUESTDONE_FUNC(log_access_write) {
681 	plugin_data *p = p_d;
682 	buffer *b;
683 	size_t j;
684 
685 	int newts = 0;
686 	data_string *ds;
687 
688 	mod_accesslog_patch_connection(srv, con, p);
689 
690 	/* No output device, nothing to do */
691 	if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
692 
693 	b = p->conf.access_logbuffer;
694 	if (b->used == 0) {
695 		buffer_copy_string_len(b, CONST_STR_LEN(""));
696 	}
697 
698 	for (j = 0; j < p->conf.parsed_format->used; j++) {
699 		switch(p->conf.parsed_format->ptr[j]->type) {
700 		case FIELD_STRING:
701 			buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string);
702 			break;
703 		case FIELD_FORMAT:
704 			switch(p->conf.parsed_format->ptr[j]->field) {
705 			case FORMAT_TIMESTAMP:
706 
707 				/* cache the generated timestamp */
708 				if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) {
709 					struct tm tm;
710 #if defined(HAVE_STRUCT_TM_GMTOFF)
711 					long scd, hrs, min;
712 #endif
713 
714 					buffer_prepare_copy(p->conf.ts_accesslog_str, 255);
715 #if defined(HAVE_STRUCT_TM_GMTOFF)
716 # ifdef HAVE_LOCALTIME_R
717 					localtime_r(&(srv->cur_ts), &tm);
718 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
719 # else /* HAVE_LOCALTIME_R */
720 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, localtime_r(&(srv->cur_ts)));
721 # endif /* HAVE_LOCALTIME_R */
722 					p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
723 
724 					if (p->conf.append_tz_offset) {
725 						buffer_append_string_len(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-", 1);
726 
727 						scd = abs(tm.tm_gmtoff);
728 						hrs = scd / 3600;
729 						min = (scd % 3600) / 60;
730 
731 						/* hours */
732 						if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
733 						buffer_append_long(p->conf.ts_accesslog_str, hrs);
734 
735 						if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
736 						buffer_append_long(p->conf.ts_accesslog_str, min);
737 						buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
738 					}
739 #else /* HAVE_STRUCT_TM_GMTOFF */
740 # ifdef HAVE_GMTIME_R
741 					gmtime_r(&(srv->cur_ts), &tm);
742 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
743 # else /* HAVE_GMTIME_R */
744 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, gmtime(&(srv->cur_ts)));
745 # endif /* HAVE_GMTIME_R */
746 					p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
747 #endif /* HAVE_STRUCT_TM_GMTOFF */
748 
749 					*(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
750 					newts = 1;
751 				}
752 
753 				buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
754 
755 				break;
756 			case FORMAT_REMOTE_HOST:
757 
758 				/* handle inet_ntop cache */
759 
760 				buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
761 
762 				break;
763 			case FORMAT_REMOTE_IDENT:
764 				/* ident */
765 				buffer_append_string_len(b, CONST_STR_LEN("-"));
766 				break;
767 			case FORMAT_REMOTE_USER:
768 				if (con->authed_user->used > 1) {
769 					buffer_append_string_buffer(b, con->authed_user);
770 				} else {
771 					buffer_append_string_len(b, CONST_STR_LEN("-"));
772 				}
773 				break;
774 			case FORMAT_REQUEST_LINE:
775 				if (con->request.request_line->used) {
776 					accesslog_append_escaped(b, con->request.request_line);
777 				}
778 				break;
779 			case FORMAT_STATUS:
780 				buffer_append_long(b, con->http_status);
781 				break;
782 
783 			case FORMAT_BYTES_OUT_NO_HEADER:
784 				if (con->bytes_written > 0) {
785 					buffer_append_off_t(b,
786 							    con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
787 				} else {
788 					buffer_append_string_len(b, CONST_STR_LEN("-"));
789 				}
790 				break;
791 			case FORMAT_HEADER:
792 				if (NULL != (ds = (data_string *)array_get_element(con->request.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
793 					accesslog_append_escaped(b, ds->value);
794 				} else {
795 					buffer_append_string_len(b, CONST_STR_LEN("-"));
796 				}
797 				break;
798 			case FORMAT_RESPONSE_HEADER:
799 				if (NULL != (ds = (data_string *)array_get_element(con->response.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
800 					accesslog_append_escaped(b, ds->value);
801 				} else {
802 					buffer_append_string_len(b, CONST_STR_LEN("-"));
803 				}
804 				break;
805 			case FORMAT_ENV:
806 				if (NULL != (ds = (data_string *)array_get_element(con->environment, p->conf.parsed_format->ptr[j]->string->ptr))) {
807 					accesslog_append_escaped(b, ds->value);
808 				} else {
809 					buffer_append_string_len(b, CONST_STR_LEN("-"));
810 				}
811 				break;
812 			case FORMAT_FILENAME:
813 				if (con->physical.path->used > 1) {
814 					buffer_append_string_buffer(b, con->physical.path);
815 				} else {
816 					buffer_append_string_len(b, CONST_STR_LEN("-"));
817 				}
818 				break;
819 			case FORMAT_BYTES_OUT:
820 				if (con->bytes_written > 0) {
821 					buffer_append_off_t(b, con->bytes_written);
822 				} else {
823 					buffer_append_string_len(b, CONST_STR_LEN("-"));
824 				}
825 				break;
826 			case FORMAT_BYTES_IN:
827 				if (con->bytes_read > 0) {
828 					buffer_append_off_t(b, con->bytes_read);
829 				} else {
830 					buffer_append_string_len(b, CONST_STR_LEN("-"));
831 				}
832 				break;
833 			case FORMAT_TIME_USED:
834 				buffer_append_long(b, srv->cur_ts - con->request_start);
835 				break;
836 			case FORMAT_SERVER_NAME:
837 				if (con->server_name->used > 1) {
838 					buffer_append_string_buffer(b, con->server_name);
839 				} else {
840 					buffer_append_string_len(b, CONST_STR_LEN("-"));
841 				}
842 				break;
843 			case FORMAT_HTTP_HOST:
844 				if (con->uri.authority->used > 1) {
845 					accesslog_append_escaped(b, con->uri.authority);
846 				} else {
847 					buffer_append_string_len(b, CONST_STR_LEN("-"));
848 				}
849 				break;
850 			case FORMAT_REQUEST_PROTOCOL:
851 				buffer_append_string_len(b,
852 						     con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
853 				break;
854 			case FORMAT_REQUEST_METHOD:
855 				buffer_append_string(b, get_http_method_name(con->request.http_method));
856 				break;
857 			case FORMAT_PERCENT:
858 				buffer_append_string_len(b, CONST_STR_LEN("%"));
859 				break;
860 			case FORMAT_SERVER_PORT:
861 				{
862 					const char *colon;
863 					buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
864 					if (srvtoken->ptr[0] == '[') {
865 						colon = strstr(srvtoken->ptr, "]:");
866 					} else {
867 						colon = strchr(srvtoken->ptr, ':');
868 					}
869 					if (colon) {
870 						buffer_append_string(b, colon+1);
871 					} else {
872 						buffer_append_long(b, srv->srvconf.port);
873 					}
874 				}
875 				break;
876 			case FORMAT_QUERY_STRING:
877 				accesslog_append_escaped(b, con->uri.query);
878 				break;
879 			case FORMAT_URL:
880 				accesslog_append_escaped(b, con->uri.path_raw);
881 				break;
882 			case FORMAT_CONNECTION_STATUS:
883 				switch(con->keep_alive) {
884 				case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break;
885 				default: buffer_append_string_len(b, CONST_STR_LEN("+")); break;
886 				}
887 				break;
888 			default:
889 				/*
890 				 { 'a', FORMAT_REMOTE_ADDR },
891 				 { 'A', FORMAT_LOCAL_ADDR },
892 				 { 'C', FORMAT_COOKIE },
893 				 { 'D', FORMAT_TIME_USED_MS },
894 				 */
895 
896 				break;
897 			}
898 			break;
899 		default:
900 			break;
901 		}
902 	}
903 
904 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
905 
906 	if (p->conf.use_syslog ||  /* syslog doesn't cache */
907 	    (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
908 	    newts ||
909 	    b->used > BUFFER_MAX_REUSE_SIZE) {
910 		if (p->conf.use_syslog) {
911 #ifdef HAVE_SYSLOG_H
912 			if (b->used > 2) {
913 				/* syslog appends a \n on its own */
914 				syslog(LOG_INFO, "%*s", (int) b->used - 2, b->ptr);
915 			}
916 #endif
917 		} else if (p->conf.log_access_fd != -1) {
918 			write(p->conf.log_access_fd, b->ptr, b->used - 1);
919 		}
920 		buffer_reset(b);
921 	}
922 
923 	return HANDLER_GO_ON;
924 }
925 
926 
927 int mod_accesslog_plugin_init(plugin *p);
mod_accesslog_plugin_init(plugin * p)928 int mod_accesslog_plugin_init(plugin *p) {
929 	p->version     = LIGHTTPD_VERSION_ID;
930 	p->name        = buffer_init_string("accesslog");
931 
932 	p->init        = mod_accesslog_init;
933 	p->set_defaults= log_access_open;
934 	p->cleanup     = mod_accesslog_free;
935 
936 	p->handle_request_done  = log_access_write;
937 	p->handle_sighup        = log_access_cycle;
938 
939 	p->data        = NULL;
940 
941 	return 0;
942 }
943