[tests] skip mod-secdownload HMAC-SHA1,HMAC-SHA256
[lighttpd.git] / src / etag.c
bloba89c4e51b0acdb18cc13421a3511e7ba8e57a597
1 #include "first.h"
3 #include "buffer.h"
4 #include "etag.h"
6 #include <sys/stat.h>
7 #include <string.h>
9 int etag_is_equal(const buffer *etag, const char *line, int weak_ok) {
10 enum {
11 START = 0,
12 CHECK,
13 CHECK_QUOTED,
14 SKIP,
15 SKIP_QUOTED,
16 TAIL
17 } state = START;
19 const char *current;
20 const char *tok_start;
21 const char *tok = NULL;
22 int matched;
24 if ('*' == line[0] && '\0' == line[1]) {
25 return 1;
28 if (!etag || buffer_string_is_empty(etag)) return 0;
29 tok_start = etag->ptr;
31 if ('W' == tok_start[0]) {
32 if (!weak_ok || '/' != tok_start[1]) return 0; /* bad etag */
33 tok_start = tok_start + 2;
36 if ('"' != tok_start[0]) return 0; /* bad etag */
37 /* we start comparing after the first '"' */
38 ++tok_start;
40 for (current = line; *current; ++current) {
41 switch (state) {
42 case START:
43 /* wait for etag to start; ignore whitespace and ',' */
44 switch (*current) {
45 case 'W':
46 /* weak etag always starts with 'W/"' */
47 if ('/' != *++current) return 0; /* bad etag list */
48 if ('"' != *++current) return 0; /* bad etag list */
49 if (!weak_ok) {
50 state = SKIP;
51 } else {
52 state = CHECK;
53 tok = tok_start;
55 break;
56 case '"':
57 /* strong etag starts with '"' */
58 state = CHECK;
59 tok = tok_start;
60 break;
61 case ' ':
62 case ',':
63 case '\t':
64 case '\r':
65 case '\n':
66 break;
67 default:
68 return 0; /* bad etag list */
70 break;
71 case CHECK:
72 /* compare etags (after the beginning '"')
73 * quoted-pairs must match too (i.e. quoted in both strings):
74 * > (RFC 2616:) both validators MUST be identical in every way
76 matched = *tok && *tok == *current;
77 ++tok;
78 switch (*current) {
79 case '\\':
80 state = matched ? CHECK_QUOTED : SKIP_QUOTED;
81 break;
82 case '"':
83 if (*tok) {
84 /* bad etag - string should end after '"' */
85 return 0;
87 if (matched) {
88 /* matching etag: strings were equal */
89 return 1;
92 state = TAIL;
93 break;
94 default:
95 if (!matched) {
96 /* strings not matching, skip remainder of etag */
97 state = SKIP;
99 break;
101 break;
102 case CHECK_QUOTED:
103 if (!*tok || *tok != *current) {
104 /* strings not matching, skip remainder of etag */
105 state = SKIP;
106 break;
108 ++tok;
109 state = CHECK;
110 break;
111 case SKIP:
112 /* wait for final (not quoted) '"' */
113 switch (*current) {
114 case '\\':
115 state = SKIP_QUOTED;
116 break;
117 case '"':
118 state = TAIL;
119 break;
121 break;
122 case SKIP_QUOTED:
123 state = SKIP;
124 break;
125 case TAIL:
126 /* search for ',', ignore white space */
127 switch (*current) {
128 case ',':
129 state = START;
130 break;
131 case ' ':
132 case '\t':
133 case '\r':
134 case '\n':
135 break;
136 default:
137 return 0; /* bad etag list */
139 break;
142 /* no matching etag found */
143 return 0;
146 int etag_create(buffer *etag, const struct stat *st, etag_flags_t flags) {
147 if (0 == flags) return 0;
149 buffer_clear(etag);
151 if (flags & ETAG_USE_INODE) {
152 buffer_append_int(etag, st->st_ino);
153 buffer_append_string_len(etag, CONST_STR_LEN("-"));
156 if (flags & ETAG_USE_SIZE) {
157 buffer_append_int(etag, st->st_size);
158 buffer_append_string_len(etag, CONST_STR_LEN("-"));
161 if (flags & ETAG_USE_MTIME) {
162 buffer_append_int(etag, st->st_mtime);
163 #ifdef st_mtime /* use high-precision timestamp if available */
164 buffer_append_int(etag, st->st_mtim.tv_nsec);
165 #endif
168 return 0;
171 int etag_mutate(buffer *mut, buffer *etag) {
172 size_t i, len;
173 uint32_t h;
175 len = buffer_string_length(etag);
176 for (h=0, i=0; i < len; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]);
178 buffer_copy_string_len(mut, CONST_STR_LEN("\""));
179 buffer_append_int(mut, h);
180 buffer_append_string_len(mut, CONST_STR_LEN("\""));
182 return 0;