2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "buffer.h"
|
|
|
|
#include "etag.h"
|
2008-02-26 16:21:20 +00:00
|
|
|
|
2017-06-22 01:41:59 +00:00
|
|
|
#include <sys/stat.h>
|
2009-10-11 14:31:42 +00:00
|
|
|
#include <string.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2019-03-17 21:25:07 +00:00
|
|
|
int etag_is_equal(const buffer *etag, const char *line, int weak_ok) {
|
2015-07-05 16:59:01 +00:00
|
|
|
enum {
|
|
|
|
START = 0,
|
|
|
|
CHECK,
|
|
|
|
CHECK_QUOTED,
|
|
|
|
SKIP,
|
|
|
|
SKIP_QUOTED,
|
|
|
|
TAIL
|
|
|
|
} state = START;
|
|
|
|
|
|
|
|
const char *current;
|
2015-07-05 20:19:17 +00:00
|
|
|
const char *tok_start;
|
2015-07-05 16:59:01 +00:00
|
|
|
const char *tok = NULL;
|
|
|
|
int matched;
|
|
|
|
|
|
|
|
if ('*' == line[0] && '\0' == line[1]) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!etag || buffer_string_is_empty(etag)) return 0;
|
2015-07-05 20:19:17 +00:00
|
|
|
tok_start = etag->ptr;
|
2015-07-05 16:59:01 +00:00
|
|
|
|
|
|
|
if ('W' == tok_start[0]) {
|
|
|
|
if (!weak_ok || '/' != tok_start[1]) return 0; /* bad etag */
|
|
|
|
tok_start = tok_start + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ('"' != tok_start[0]) return 0; /* bad etag */
|
|
|
|
/* we start comparing after the first '"' */
|
|
|
|
++tok_start;
|
|
|
|
|
|
|
|
for (current = line; *current; ++current) {
|
|
|
|
switch (state) {
|
|
|
|
case START:
|
|
|
|
/* wait for etag to start; ignore whitespace and ',' */
|
|
|
|
switch (*current) {
|
|
|
|
case 'W':
|
|
|
|
/* weak etag always starts with 'W/"' */
|
|
|
|
if ('/' != *++current) return 0; /* bad etag list */
|
|
|
|
if ('"' != *++current) return 0; /* bad etag list */
|
|
|
|
if (!weak_ok) {
|
|
|
|
state = SKIP;
|
|
|
|
} else {
|
|
|
|
state = CHECK;
|
|
|
|
tok = tok_start;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
/* strong etag starts with '"' */
|
|
|
|
state = CHECK;
|
|
|
|
tok = tok_start;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
case ',':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0; /* bad etag list */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHECK:
|
|
|
|
/* compare etags (after the beginning '"')
|
|
|
|
* quoted-pairs must match too (i.e. quoted in both strings):
|
|
|
|
* > (RFC 2616:) both validators MUST be identical in every way
|
|
|
|
*/
|
|
|
|
matched = *tok && *tok == *current;
|
|
|
|
++tok;
|
|
|
|
switch (*current) {
|
|
|
|
case '\\':
|
|
|
|
state = matched ? CHECK_QUOTED : SKIP_QUOTED;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
if (*tok) {
|
|
|
|
/* bad etag - string should end after '"' */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (matched) {
|
|
|
|
/* matching etag: strings were equal */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = TAIL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!matched) {
|
|
|
|
/* strings not matching, skip remainder of etag */
|
|
|
|
state = SKIP;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHECK_QUOTED:
|
|
|
|
if (!*tok || *tok != *current) {
|
|
|
|
/* strings not matching, skip remainder of etag */
|
|
|
|
state = SKIP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++tok;
|
|
|
|
state = CHECK;
|
|
|
|
break;
|
|
|
|
case SKIP:
|
|
|
|
/* wait for final (not quoted) '"' */
|
|
|
|
switch (*current) {
|
|
|
|
case '\\':
|
|
|
|
state = SKIP_QUOTED;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
state = TAIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SKIP_QUOTED:
|
|
|
|
state = SKIP;
|
|
|
|
break;
|
|
|
|
case TAIL:
|
|
|
|
/* search for ',', ignore white space */
|
|
|
|
switch (*current) {
|
|
|
|
case ',':
|
|
|
|
state = START;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0; /* bad etag list */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* no matching etag found */
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-16 01:26:54 +00:00
|
|
|
int etag_create(buffer *etag, const struct stat *st, int flags) {
|
2007-06-15 15:51:16 +00:00
|
|
|
if (0 == flags) return 0;
|
2007-07-03 17:16:21 +00:00
|
|
|
|
2018-11-23 05:37:38 +00:00
|
|
|
buffer_clear(etag);
|
2007-07-03 17:16:21 +00:00
|
|
|
|
2007-06-15 15:51:16 +00:00
|
|
|
if (flags & ETAG_USE_INODE) {
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_append_int(etag, st->st_ino);
|
2007-06-15 15:51:16 +00:00
|
|
|
buffer_append_string_len(etag, CONST_STR_LEN("-"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & ETAG_USE_SIZE) {
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_append_int(etag, st->st_size);
|
2007-06-15 15:51:16 +00:00
|
|
|
buffer_append_string_len(etag, CONST_STR_LEN("-"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & ETAG_USE_MTIME) {
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_append_int(etag, st->st_mtime);
|
2019-04-24 07:35:26 +00:00
|
|
|
#ifdef st_mtime /* use high-precision timestamp if available */
|
2019-05-13 05:45:42 +00:00
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
|
|
buffer_append_int(etag, st->st_mtimespec.tv_nsec);
|
|
|
|
#else
|
2019-04-24 07:35:26 +00:00
|
|
|
buffer_append_int(etag, st->st_mtim.tv_nsec);
|
|
|
|
#endif
|
2019-05-13 05:45:42 +00:00
|
|
|
#endif
|
2007-06-15 15:51:16 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-05 08:16:25 +00:00
|
|
|
int etag_mutate(buffer *mut, const buffer *etag) {
|
2015-02-08 19:10:44 +00:00
|
|
|
size_t i, len;
|
2007-08-15 09:49:15 +00:00
|
|
|
uint32_t h;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 19:10:44 +00:00
|
|
|
len = buffer_string_length(etag);
|
|
|
|
for (h=0, i=0; i < len; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-06-12 09:18:17 +00:00
|
|
|
buffer_copy_string_len(mut, CONST_STR_LEN("\""));
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_append_int(mut, h);
|
2005-06-12 09:18:17 +00:00
|
|
|
buffer_append_string_len(mut, CONST_STR_LEN("\""));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|