lighttpd1.4/src/etag.c

188 lines
3.8 KiB
C
Raw Normal View History

#include "first.h"
#include "buffer.h"
#include "etag.h"
#include <sys/stat.h>
#include <string.h>
int etag_is_equal(const buffer *etag, const char *line, int weak_ok) {
enum {
START = 0,
CHECK,
CHECK_QUOTED,
SKIP,
SKIP_QUOTED,
TAIL
} state = START;
const char *current;
const char *tok_start;
const char *tok = NULL;
int matched;
if ('*' == line[0] && '\0' == line[1]) {
return 1;
}
if (!etag || buffer_string_is_empty(etag)) return 0;
tok_start = etag->ptr;
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 */
return 0;
}
2019-11-16 01:26:54 +00:00
int etag_create(buffer *etag, const struct stat *st, int flags) {
if (0 == flags) return 0;
buffer_clear(etag);
if (flags & ETAG_USE_INODE) {
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(etag, st->st_ino);
buffer_append_string_len(etag, CONST_STR_LEN("-"));
}
if (flags & ETAG_USE_SIZE) {
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(etag, st->st_size);
buffer_append_string_len(etag, CONST_STR_LEN("-"));
}
if (flags & ETAG_USE_MTIME) {
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(etag, st->st_mtime);
#ifdef st_mtime /* use high-precision timestamp if available */
#if defined(__APPLE__) && defined(__MACH__)
buffer_append_int(etag, st->st_mtimespec.tv_nsec);
#else
buffer_append_int(etag, st->st_mtim.tv_nsec);
#endif
#endif
}
return 0;
}
2019-12-05 08:16:25 +00:00
int etag_mutate(buffer *mut, const buffer *etag) {
size_t i, len;
uint32_t h;
len = buffer_string_length(etag);
for (h=0, i=0; i < len; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]);
buffer_copy_string_len(mut, CONST_STR_LEN("\""));
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
buffer_append_int(mut, h);
buffer_append_string_len(mut, CONST_STR_LEN("\""));
return 0;
}