[core] set REDIRECT_STATUS to error_handler_saved_status (fixes #1828)
[lighttpd.git] / src / etag.c
blob18c296960a1e39a22b3f6e713240907384c5e1c0
1 #include "first.h"
3 #include "buffer.h"
4 #include "etag.h"
6 #if defined HAVE_STDINT_H
7 # include <stdint.h>
8 #elif defined HAVE_INTTYPES_H
9 # include <inttypes.h>
10 #endif
12 #include <string.h>
14 int etag_is_equal(buffer *etag, const char *line, int weak_ok) {
15 enum {
16 START = 0,
17 CHECK,
18 CHECK_QUOTED,
19 SKIP,
20 SKIP_QUOTED,
21 TAIL
22 } state = START;
24 const char *current;
25 const char *tok_start;
26 const char *tok = NULL;
27 int matched;
29 if ('*' == line[0] && '\0' == line[1]) {
30 return 1;
33 if (!etag || buffer_string_is_empty(etag)) return 0;
34 tok_start = etag->ptr;
36 if ('W' == tok_start[0]) {
37 if (!weak_ok || '/' != tok_start[1]) return 0; /* bad etag */
38 tok_start = tok_start + 2;
41 if ('"' != tok_start[0]) return 0; /* bad etag */
42 /* we start comparing after the first '"' */
43 ++tok_start;
45 for (current = line; *current; ++current) {
46 switch (state) {
47 case START:
48 /* wait for etag to start; ignore whitespace and ',' */
49 switch (*current) {
50 case 'W':
51 /* weak etag always starts with 'W/"' */
52 if ('/' != *++current) return 0; /* bad etag list */
53 if ('"' != *++current) return 0; /* bad etag list */
54 if (!weak_ok) {
55 state = SKIP;
56 } else {
57 state = CHECK;
58 tok = tok_start;
60 break;
61 case '"':
62 /* strong etag starts with '"' */
63 state = CHECK;
64 tok = tok_start;
65 break;
66 case ' ':
67 case ',':
68 case '\t':
69 case '\r':
70 case '\n':
71 break;
72 default:
73 return 0; /* bad etag list */
75 break;
76 case CHECK:
77 /* compare etags (after the beginning '"')
78 * quoted-pairs must match too (i.e. quoted in both strings):
79 * > (RFC 2616:) both validators MUST be identical in every way
81 matched = *tok && *tok == *current;
82 ++tok;
83 switch (*current) {
84 case '\\':
85 state = matched ? CHECK_QUOTED : SKIP_QUOTED;
86 break;
87 case '"':
88 if (*tok) {
89 /* bad etag - string should end after '"' */
90 return 0;
92 if (matched) {
93 /* matching etag: strings were equal */
94 return 1;
97 state = TAIL;
98 break;
99 default:
100 if (!matched) {
101 /* strings not matching, skip remainder of etag */
102 state = SKIP;
104 break;
106 break;
107 case CHECK_QUOTED:
108 if (!*tok || *tok != *current) {
109 /* strings not matching, skip remainder of etag */
110 state = SKIP;
111 break;
113 ++tok;
114 state = CHECK;
115 break;
116 case SKIP:
117 /* wait for final (not quoted) '"' */
118 switch (*current) {
119 case '\\':
120 state = SKIP_QUOTED;
121 break;
122 case '"':
123 state = TAIL;
124 break;
126 break;
127 case SKIP_QUOTED:
128 state = SKIP;
129 break;
130 case TAIL:
131 /* search for ',', ignore white space */
132 switch (*current) {
133 case ',':
134 state = START;
135 break;
136 case ' ':
137 case '\t':
138 case '\r':
139 case '\n':
140 break;
141 default:
142 return 0; /* bad etag list */
144 break;
147 /* no matching etag found */
148 return 0;
151 int etag_create(buffer *etag, struct stat *st,etag_flags_t flags) {
152 if (0 == flags) return 0;
154 buffer_reset(etag);
156 if (flags & ETAG_USE_INODE) {
157 buffer_append_int(etag, st->st_ino);
158 buffer_append_string_len(etag, CONST_STR_LEN("-"));
161 if (flags & ETAG_USE_SIZE) {
162 buffer_append_int(etag, st->st_size);
163 buffer_append_string_len(etag, CONST_STR_LEN("-"));
166 if (flags & ETAG_USE_MTIME) {
167 buffer_append_int(etag, st->st_mtime);
170 return 0;
173 int etag_mutate(buffer *mut, buffer *etag) {
174 size_t i, len;
175 uint32_t h;
177 len = buffer_string_length(etag);
178 for (h=0, i=0; i < len; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]);
180 buffer_reset(mut);
181 buffer_copy_string_len(mut, CONST_STR_LEN("\""));
182 buffer_append_int(mut, h);
183 buffer_append_string_len(mut, CONST_STR_LEN("\""));
185 return 0;