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