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