diff options
Diffstat (limited to 'src/c')
-rw-r--r-- | src/c/Makefile.am | 23 | ||||
-rw-r--r-- | src/c/cgi.c | 21 | ||||
-rw-r--r-- | src/c/fastcgi.c | 12 | ||||
-rw-r--r-- | src/c/http.c | 37 | ||||
-rw-r--r-- | src/c/memmem.c | 15 | ||||
-rw-r--r-- | src/c/memmem.h | 23 | ||||
-rw-r--r-- | src/c/request.c | 9 | ||||
-rw-r--r-- | src/c/static.c | 12 | ||||
-rw-r--r-- | src/c/urweb.c | 850 |
9 files changed, 770 insertions, 232 deletions
diff --git a/src/c/Makefile.am b/src/c/Makefile.am index f4d9bef8..ff4b6eaf 100644 --- a/src/c/Makefile.am +++ b/src/c/Makefile.am @@ -1,21 +1,26 @@ lib_LTLIBRARIES = liburweb.la liburweb_http.la liburweb_cgi.la liburweb_fastcgi.la liburweb_static.la -liburweb_la_SOURCES = memmem.c openssl.c urweb.c request.c queue.c +liburweb_la_SOURCES = memmem.c memmem.h openssl.c urweb.c request.c queue.c liburweb_http_la_SOURCES = http.c liburweb_cgi_la_SOURCES = cgi.c liburweb_fastcgi_la_SOURCES = fastcgi.c fastcgi.h liburweb_static_la_SOURCES = static.c -AM_CPPFLAGS = -I$(srcdir)/../../include/urweb $(OPENSSL_INCLUDES) -AM_CFLAGS = -Wimplicit -Wall -Werror -Wno-format-security -Wno-deprecated-declarations -U_FORTIFY_SOURCE $(PTHREAD_CFLAGS) +AM_CPPFLAGS = -I$(srcdir)/../../include/urweb $(OPENSSL_INCLUDES) $(ICU_INCLUDES) +AM_CFLAGS = -Wall -Wunused-parameter -Werror -Wno-format-security -Wno-deprecated-declarations -U_FORTIFY_SOURCE $(PTHREAD_CFLAGS) liburweb_la_LDFLAGS = $(AM_LDFLAGS) $(OPENSSL_LDFLAGS) \ - -export-symbols-regex '^(client_pruner|pthread_create_big|strcmp_nullsafe|uw_.*)' -liburweb_la_LIBADD = $(PTHREAD_LIBS) -lm $(OPENSSL_LIBS) + -export-symbols-regex '^(client_pruner|pthread_create_big|strcmp_nullsafe|uw_.*)' \ + -version-info 1:0:0 +liburweb_la_LIBADD = $(PTHREAD_LIBS) -lm $(OPENSSL_LIBS) $(ICU_LIBS) -licui18n -licuuc -licudata -licuio liburweb_http_la_LIBADD = liburweb.la -liburweb_http_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' +liburweb_http_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' \ + -version-info 1:0:0 liburweb_cgi_la_LIBADD = liburweb.la -liburweb_cgi_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' +liburweb_cgi_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' \ + -version-info 1:0:0 liburweb_fastcgi_la_LIBADD = liburweb.la -liburweb_fastcgi_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' +liburweb_fastcgi_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' \ + -version-info 1:0:0 liburweb_static_la_LIBADD = liburweb.la -liburweb_static_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' +liburweb_static_la_LDFLAGS = -export-symbols-regex '^(main|uw_.*)' \ + -version-info 1:0:0 diff --git a/src/c/cgi.c b/src/c/cgi.c index d060532c..4d0f82b0 100644 --- a/src/c/cgi.c +++ b/src/c/cgi.c @@ -17,6 +17,8 @@ static char *uppercased; static size_t uppercased_len; static char *get_header(void *data, const char *h) { + (void)data; + size_t len = strlen(h); char *s, *r; const char *saved_h = h; @@ -41,16 +43,21 @@ static char *get_header(void *data, const char *h) { } static char *get_env(void *data, const char *name) { + (void)data; return getenv(name); } -static void on_success(uw_context ctx) { } +static void on_success(uw_context ctx) { + (void)ctx; +} static void on_failure(uw_context ctx) { uw_write_header(ctx, "Status: 500 Internal Server Error\r\n"); } static void log_error(void *data, const char *fmt, ...) { + (void)data; + va_list ap; va_start(ap, fmt); @@ -58,11 +65,16 @@ static void log_error(void *data, const char *fmt, ...) { } static void log_debug(void *data, const char *fmt, ...) { + (void)data; + (void)fmt; } static uw_loggers ls = {NULL, log_error, log_debug}; int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + uw_context ctx = uw_request_new_context(0, &uw_application, &ls); uw_request_context rc = uw_new_request_context(); request_result rr; @@ -130,12 +142,17 @@ void *uw_init_client_data() { } void uw_free_client_data(void *data) { + (void)data; } void uw_copy_client_data(void *dst, void *src) { + (void)dst; + (void)src; } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { + (void)data; + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); @@ -144,6 +161,8 @@ void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { } void uw_post_expunge(uw_context ctx, void *data) { + (void)ctx; + (void)data; } int uw_supports_direct_status = 0; diff --git a/src/c/fastcgi.c b/src/c/fastcgi.c index c37debf7..196b3d51 100644 --- a/src/c/fastcgi.c +++ b/src/c/fastcgi.c @@ -127,7 +127,9 @@ static FCGI_Record *fastcgi_recv(FCGI_Input *i) { } } -static void on_success(uw_context ctx) { } +static void on_success(uw_context ctx) { + (void)ctx; +} static void on_failure(uw_context ctx) { uw_write_header(ctx, "Status: 500 Internal Server Error\r\n"); @@ -554,6 +556,7 @@ static void help(char *cmd) { } static void sigint(int signum) { + (void)signum; printf("Exiting....\n"); exit(0); } @@ -674,12 +677,17 @@ void *uw_init_client_data() { } void uw_free_client_data(void *data) { + (void)data; } void uw_copy_client_data(void *dst, void *src) { + (void)dst; + (void)src; } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { + (void)data; + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); @@ -688,6 +696,8 @@ void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { } void uw_post_expunge(uw_context ctx, void *data) { + (void)ctx; + (void)data; } int uw_supports_direct_status = 0; diff --git a/src/c/http.c b/src/c/http.c index 21ad809f..de2f1376 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -11,6 +11,7 @@ #include <unistd.h> #include <signal.h> #include <stdarg.h> +#include <sys/un.h> #include <pthread.h> @@ -46,6 +47,7 @@ static char *get_header(void *data, const char *h) { } static char *get_env(void *data, const char *name) { + (void)data; return getenv(name); } @@ -58,24 +60,30 @@ static void on_failure(uw_context ctx) { } static void log_error(void *data, const char *fmt, ...) { + (void)data; + va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); + fflush(stderr); } static void log_debug(void *data, const char *fmt, ...) { + (void)data; + if (!quiet) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); + fflush(stdout); } } static uw_loggers ls = {NULL, log_error, log_debug}; -static unsigned max_buf_size = 1024 * 1024; // That's 1MB. +static unsigned max_buf_size = 10 * 1024 * 1024; // That's 10MB. static void *worker(void *data) { int me = *(int *)data; @@ -328,10 +336,11 @@ static void *worker(void *data) { } static void help(char *cmd) { - printf("Usage: %s [-p <port>] [-a <IPv4 address>] [-A <IPv6 address>] [-t <thread count>] [-m <bytes>] [-k] [-q] [-T SEC]\nThe '-k' option turns on HTTP keepalive.\nThe '-q' option turns off some chatter on stdout.\nThe '-T' option sets socket recv timeout (0 disables timeout, default is 5 sec).\nThe '-m' sets the maximum size (in bytes) for any buffer used to hold HTTP data sent by clients. (The default is 1 MB.)\n", cmd); + printf("Usage: %s [-p <port>] [-a <IPv4 address>] [-A <IPv6 address>] [-u <UNIX socket>] [-t <thread count>] [-m <bytes>] [-k] [-q] [-T SEC]\nThe '-k' option turns on HTTP keepalive.\nThe '-q' option turns off some chatter on stdout.\nThe '-T' option sets socket recv timeout (0 disables timeout, default is 5 sec).\nThe '-m' sets the maximum size (in bytes) for any buffer used to hold HTTP data sent by clients. (The default is 1 MB.)\n", cmd); } static void sigint(int signum) { + (void)signum; printf("Exiting....\n"); exit(0); } @@ -340,6 +349,7 @@ union uw_sockaddr { struct sockaddr sa; struct sockaddr_in ipv4; struct sockaddr_in6 ipv6; + struct sockaddr_un un; }; int main(int argc, char *argv[]) { @@ -359,7 +369,7 @@ int main(int argc, char *argv[]) { my_addr.sa.sa_family = AF_INET; my_addr.ipv4.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP - while ((opt = getopt(argc, argv, "hp:a:A:t:kqT:m:")) != -1) { + while ((opt = getopt(argc, argv, "hp:a:A:u:t:kqT:m:")) != -1) { switch (opt) { case '?': fprintf(stderr, "Unknown command-line option\n"); @@ -397,6 +407,15 @@ int main(int argc, char *argv[]) { } break; + case 'u': + my_addr.sa.sa_family = AF_UNIX; + if (!strncpy(my_addr.un.sun_path, optarg, sizeof(my_addr.un.sun_path)-1)) { + fprintf(stderr, "Invalid UNIX socket filename\n"); + help(argv[0]); + return 1; + } + break; + case 't': nthreads = atoi(optarg); if (nthreads <= 0) { @@ -466,6 +485,11 @@ int main(int argc, char *argv[]) { my_size = sizeof(my_addr.ipv6); my_addr.ipv6.sin6_port = htons(uw_port); break; + + case AF_UNIX: + unlink(my_addr.un.sun_path); + my_size = sizeof(my_addr.un); + break; } if (bind(sockfd, &my_addr.sa, my_size) < 0) { @@ -542,12 +566,17 @@ void *uw_init_client_data() { } void uw_free_client_data(void *data) { + (void)data; } void uw_copy_client_data(void *dst, void *src) { + (void)dst; + (void)src; } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { + (void)data; + uw_ensure_transaction(ctx); uw_get_app(ctx)->expunger(ctx, cli); @@ -556,6 +585,8 @@ void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { } void uw_post_expunge(uw_context ctx, void *data) { + (void)ctx; + (void)data; } int uw_supports_direct_status = 1; diff --git a/src/c/memmem.c b/src/c/memmem.c index f31f4e31..efddd0c1 100644 --- a/src/c/memmem.c +++ b/src/c/memmem.c @@ -1,4 +1,6 @@ -#include "config.h" +#include "memmem.h" + +#ifndef HAVE_MEMMEM /* $NetBSD$ */ @@ -38,8 +40,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -// Function renamed by Adam Chlipala in 2016. - #include <sys/cdefs.h> #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD$"); @@ -54,13 +54,8 @@ __RCSID("$NetBSD$"); #define NULL ((char *)0) #endif -/* - * urweb_memmem() returns the location of the first occurence of data - * pattern b2 of size len2 in memory block b1 of size len1 or - * NULL if none is found. - */ void * -urweb_memmem(const void *b1, size_t len1, const void *b2, size_t len2) +memmem(const void *b1, size_t len1, const void *b2, size_t len2) { /* Sanity check */ if(!(b1 != NULL && b2 != NULL && len1 != 0 && len2 != 0)) @@ -85,3 +80,5 @@ urweb_memmem(const void *b1, size_t len1, const void *b2, size_t len2) return NULL; } + +#endif // !defined(HAVE_MEMMEM) diff --git a/src/c/memmem.h b/src/c/memmem.h new file mode 100644 index 00000000..0ddbb494 --- /dev/null +++ b/src/c/memmem.h @@ -0,0 +1,23 @@ +#ifndef URWEB_MEMMEM_H +#define URWEB_MEMMEM_H + +#include "config.h" + +#ifdef HAVE_MEMMEM + +#include <string.h> + +#else // !defined(HAVE_MEMMEM) + +#include <stddef.h> + +/* + * memmem() returns the location of the first occurence of data + * pattern b2 of size len2 in memory block b1 of size len1 or + * NULL if none is found. + */ +void *memmem(const void *b1, size_t len1, const void *b2, size_t len2); + +#endif // !defined(HAVE_MEMMEM) + +#endif // URWEB_MEMMEM_H diff --git a/src/c/request.c b/src/c/request.c index a7f23851..195b3cdc 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -11,13 +11,12 @@ #include <pthread.h> +#include "memmem.h" #include "urweb.h" #include "request.h" #define MAX_RETRIES 5 -void *urweb_memmem(const void *b1, size_t len1, const void *b2, size_t len2); - static int try_rollback(uw_context ctx, int will_retry, void *logger_data, uw_logger log_error) { int r = uw_rollback(ctx, will_retry); @@ -78,6 +77,8 @@ uw_context uw_request_new_context(int id, uw_app *app, uw_loggers *ls) { } static void *ticker(void *data) { + (void)data; + while (1) { usleep(100000); ++uw_time; @@ -133,6 +134,8 @@ static unsigned long long stackSize; int pthread_create_big(pthread_t *outThread, void *foo, void *threadFunc, void *arg) { + (void)foo; + if (stackSize > 0) { int err; pthread_attr_t stackSizeAttribute; @@ -418,7 +421,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, } } - part = urweb_memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); + part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); if (!part) { log_error(logger_data, "Missing boundary after multipart payload\n"); return FAILED; diff --git a/src/c/static.c b/src/c/static.c index d70881e2..76fe4129 100644 --- a/src/c/static.c +++ b/src/c/static.c @@ -8,6 +8,8 @@ extern uw_app uw_application; static void log_(void *data, const char *fmt, ...) { + (void)data; + va_list ap; va_start(ap, fmt); @@ -17,6 +19,8 @@ static void log_(void *data, const char *fmt, ...) { static uw_loggers loggers = {NULL, log_, log_}; static char *get_header(void *data, const char *h) { + (void)data; + (void)h; return NULL; } @@ -56,15 +60,23 @@ void *uw_init_client_data() { } void uw_free_client_data(void *data) { + (void)data; } void uw_copy_client_data(void *dst, void *src) { + (void)dst; + (void)src; } void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) { + (void)ctx; + (void)cli; + (void)data; } void uw_post_expunge(uw_context ctx, void *data) { + (void)ctx; + (void)data; } int uw_supports_direct_status = 0; diff --git a/src/c/urweb.c b/src/c/urweb.c index 6f2dde38..0db5fc80 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -13,13 +13,17 @@ #include <stdint.h> #include <sys/types.h> #include <sys/socket.h> -#include <openssl/des.h> #include <openssl/rand.h> +#include <openssl/sha.h> #include <time.h> #include <math.h> #include <pthread.h> +#include <unicode/utf8.h> +#include <unicode/ustring.h> +#include <unicode/uchar.h> + #include "types.h" #include "uthash.h" @@ -514,6 +518,11 @@ struct uw_context { uw_Sqlcache_Unlock *cacheUnlock; int remoteSock; + + int file_cache_missed; + // Set if we are recovering from a miss in the file cache in handling an SQL + // query that only returns hashes of files. If so, this time around we will + // run queries to return actual file contents instead. }; size_t uw_headers_max = SIZE_MAX; @@ -608,6 +617,8 @@ uw_context uw_init(int id, uw_loggers *lg) { ctx->cacheUnlock = NULL; + ctx->file_cache_missed = 0; + return ctx; } @@ -730,7 +741,10 @@ void uw_close(uw_context ctx) { } uw_Basis_string uw_Basis_requestHeader(uw_context ctx, uw_Basis_string h) { - return ctx->get_header(ctx->get_header_data, h); + if (ctx->get_header) + return ctx->get_header(ctx->get_header_data, h); + else + return NULL; } void uw_set_headers(uw_context ctx, char *(*get_header)(void *, const char *), void *get_header_data) { @@ -889,9 +903,12 @@ char *uw_error_message(uw_context ctx) { return ctx->error_message; } -void uw_set_error_message(uw_context ctx, const char *msg) { - strncpy(ctx->error_message, msg, sizeof(ctx->error_message)); - ctx->error_message[sizeof(ctx->error_message)-1] = 0; +void uw_set_error_message(uw_context ctx, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + vsnprintf(ctx->error_message, ERROR_BUF_LEN, fmt, ap); + ctx->error_message[ERROR_BUF_LEN-1] = 0; } static input *INP(uw_context ctx) { @@ -1519,6 +1536,7 @@ uw_Basis_string uw_Basis_maybe_onunload(uw_context ctx, uw_Basis_string s) { } const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) { + (void)u; if (ctx->client == NULL) { if (ctx->needs_sig) { char *sig = ctx->app->cookie_sig(ctx); @@ -1545,94 +1563,90 @@ const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) { } } +uw_Basis_bool uw_Basis_isprint(uw_context ctx, uw_Basis_char ch); + +static void jsifyChar(char **buffer_ptr, uw_context ctx, uw_Basis_char c1) { + char* buffer = *buffer_ptr; + + switch (c1) { + case '"': + strcpy(buffer, "\\\""); + buffer += 2; + break; + case '\'': + strcpy(buffer, "\\047"); + buffer += 4; + break; + case '\\': + strcpy(buffer, "\\\\"); + buffer += 2; + break; + case '<': + strcpy(buffer, "\\074"); + buffer += 4; + break; + case '&': + strcpy(buffer, "\\046"); + buffer += 4; + break; + default: + if (uw_Basis_isprint(ctx, c1)) { + int offset = 0; + U8_APPEND_UNSAFE(buffer, offset, c1); + buffer += offset; + } else { + if(65536 > c1) { + sprintf(buffer, "\\u%04x", c1); + buffer += 6; + } else { + sprintf(buffer, "\\u{%06x}", c1); + buffer += 10; + } + } + } + + *buffer_ptr = buffer; +} + uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) { char *r, *s2; + uw_Basis_char c; - uw_check_heap(ctx, strlen(s) * 4 + 3); + uw_check_heap(ctx, strlen(s) * 10 + 3); r = s2 = ctx->heap.front; *s2++ = '"'; - for (; *s; s++) { - unsigned char c = *s; - - switch (c) { - case '"': - strcpy(s2, "\\\""); - s2 += 2; - break; - case '\'': - strcpy(s2, "\\047"); - s2 += 4; - break; - case '\\': - strcpy(s2, "\\\\"); - s2 += 2; - break; - case '<': - strcpy(s2, "\\074"); - s2 += 4; - break; - case '&': - strcpy(s2, "\\046"); - s2 += 4; - break; - default: - if (isprint((int)c) || c >= 128) - *s2++ = c; - else { - sprintf(s2, "\\%03o", c); - s2 += 4; - } + int offset = 0; + while(s[offset] != 0) + { + U8_NEXT(s, offset, -1, c); + + jsifyChar(&s2, ctx, c); } - } strcpy(s2, "\""); ctx->heap.front = s2 + 2; + return r; } +uw_Basis_int uw_Basis_ord(uw_context ctx, uw_Basis_char c); + uw_Basis_string uw_Basis_jsifyChar(uw_context ctx, uw_Basis_char c1) { - unsigned char c = c1; char *r, *s2; - uw_check_heap(ctx, 7); + uw_check_heap(ctx, 10); r = s2 = ctx->heap.front; + *s2++ = '"'; - - switch (c) { - case '"': - strcpy(s2, "\\\""); - s2 += 2; - break; - case '\'': - strcpy(s2, "\\047"); - s2 += 4; - break; - case '\\': - strcpy(s2, "\\\\"); - s2 += 2; - break; - case '<': - strcpy(s2, "\\074"); - s2 += 4; - break; - case '&': - strcpy(s2, "\\046"); - s2 += 4; - break; - default: - if (isprint((int)c) || c >= 128) - *s2++ = c; - else { - sprintf(s2, "\\%03o", (unsigned char)c); - s2 += 4; - } - } + + jsifyChar(&s2, ctx, c1); strcpy(s2, "\""); ctx->heap.front = s2 + 2; + return r; } @@ -1676,6 +1690,7 @@ uw_Basis_string uw_Basis_jsifyString_ws(uw_context ctx, uw_Basis_string s) { strcpy(s2, "\""); ctx->script.front = s2 + 1; + return r; } @@ -1847,6 +1862,7 @@ char *uw_Basis_attrifyChar(uw_context ctx, uw_Basis_char c) { } char *uw_Basis_attrifyCss_class(uw_context ctx, uw_Basis_css_class s) { + (void)ctx; return s; } @@ -1942,29 +1958,61 @@ char *uw_Basis_urlifyFloat(uw_context ctx, uw_Basis_float n) { return r; } +static void aux_urlifyChar(char** ptr, uw_Basis_char c) { + char* p = *ptr; + + if((uint32_t)(c) <= 0x7f) { + sprintf(p, ".%02X", (uint8_t)(c)); + p += 3; + } else { + if((uint32_t)(c) <= 0x7ff) { + sprintf(p, ".%02X", (uint8_t)(((c)>>6)|0xc0)); + p += 3; + } else { + if((uint32_t)(c) <= 0xffff) { + sprintf(p, ".%02X", (uint8_t)(((c)>>12)|0xe0)); + p += 3; + } else { + sprintf(p, ".%02X", (uint8_t)(((c)>>18)|0xf0)); + p += 3; + sprintf(p, ".%02X", (uint8_t)((((c)>>12)&0x3f)|0x80)); + p += 3; + } + sprintf(p, ".%02X", (uint8_t)((((c)>>6)&0x3f)|0x80)); + p += 3; + } + sprintf(p, ".%02X", (uint8_t)(((c)&0x3f)|0x80)); + p += 3; + } + + *ptr = p; +} + char *uw_Basis_urlifyString(uw_context ctx, uw_Basis_string s) { char *r, *p; if (s[0] == '\0') return "_"; - uw_check_heap(ctx, strlen(s) * 3 + 1 + !!(s[0] == '_')); + uw_check_heap(ctx, strlen(s) * 12 + 1 + !!(s[0] == '_')); r = p = ctx->heap.front; if (s[0] == '_') *p++ = '_'; - for (; *s; s++) { - unsigned char c = *s; - - if (c == ' ') + uw_Basis_char c; + int offset = 0, curr = 0; + while (s[offset] != 0) { + U8_NEXT(s, offset, -1, c); + + if (U8_IS_SINGLE(s[curr]) && s[curr] == ' ') *p++ = '+'; - else if (isalnum(c)) - *p++ = c; + else if (U8_IS_SINGLE(s[curr]) && isalnum(s[curr])) + *p++ = s[curr]; else { - sprintf(p, ".%02X", c); - p += 3; + aux_urlifyChar(&p, c); } + curr = offset; } *p++ = 0; @@ -1973,7 +2021,8 @@ char *uw_Basis_urlifyString(uw_context ctx, uw_Basis_string s) { } char *uw_Basis_urlifyBool(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) + (void)ctx; + if (!b) return "0"; else return "1"; @@ -2033,6 +2082,29 @@ uw_unit uw_Basis_urlifyTime_w(uw_context ctx, uw_Basis_time t) { return uw_Basis_urlifyInt_w(ctx, (uw_Basis_int)t.seconds * 1000000 + t.microseconds); } +uw_unit uw_Basis_urlifyChar_w(uw_context ctx, uw_Basis_char c) { + if (c == '\0') { + uw_check(ctx, 1); + uw_writec_unsafe(ctx, '_'); + return uw_unit_v; + } + + uw_check(ctx, 12 + !!(c == '_')); + + if (c == '_') + uw_writec_unsafe(ctx, '_'); + + if (c == ' ') + uw_writec_unsafe(ctx, '+'); + else if (isalnum(c) && c <= 0x7f) + uw_writec_unsafe(ctx, c); + else { + aux_urlifyChar(&(ctx->page.front), c); + } + + return uw_unit_v; +} + uw_unit uw_Basis_urlifyString_w(uw_context ctx, uw_Basis_string s) { if (s[0] == '\0') { uw_check(ctx, 1); @@ -2040,29 +2112,31 @@ uw_unit uw_Basis_urlifyString_w(uw_context ctx, uw_Basis_string s) { return uw_unit_v; } - uw_check(ctx, strlen(s) * 3 + !!(s[0] == '_')); + uw_check(ctx, strlen(s) * 12 + !!(s[0] == '_')); if (s[0] == '_') uw_writec_unsafe(ctx, '_'); - for (; *s; s++) { - unsigned char c = *s; - - if (c == ' ') + uw_Basis_char c; + int offset = 0, curr = 0; + while (s[offset] != 0) { + U8_NEXT(s, offset, -1, c); + + if (U8_IS_SINGLE(s[curr]) && s[curr] == ' ') uw_writec_unsafe(ctx, '+'); - else if (isalnum(c)) - uw_writec_unsafe(ctx, c); - else { - sprintf(ctx->page.front, ".%02X", c); - ctx->page.front += 3; + else if (U8_IS_SINGLE(s[curr]) && isalnum(s[curr])) + uw_writec_unsafe(ctx, s[curr]); + else { + aux_urlifyChar(&(ctx->page.front), c); } + curr = offset; } return uw_unit_v; } uw_unit uw_Basis_urlifyBool_w(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) + if (!b) uw_writec(ctx, '0'); else uw_writec(ctx, '1'); @@ -2093,6 +2167,8 @@ static char *uw_unurlify_advance(char *s) { } uw_Basis_int uw_Basis_unurlifyInt(uw_context ctx, char **s) { + (void)ctx; + char *new_s = uw_unurlify_advance(*s); uw_Basis_int r; @@ -2102,6 +2178,8 @@ uw_Basis_int uw_Basis_unurlifyInt(uw_context ctx, char **s) { } uw_Basis_float uw_Basis_unurlifyFloat(uw_context ctx, char **s) { + (void)ctx; + char *new_s = uw_unurlify_advance(*s); uw_Basis_float r; @@ -2165,6 +2243,8 @@ static uw_Basis_string uw_unurlifyString_to(int fromClient, uw_context ctx, char } uw_Basis_bool uw_Basis_unurlifyBool(uw_context ctx, char **s) { + (void)ctx; + char *new_s = uw_unurlify_advance(*s); uw_Basis_bool r; @@ -2191,7 +2271,25 @@ uw_Basis_string uw_Basis_unurlifyString(uw_context ctx, char **s) { return r; } +uw_Basis_char uw_Basis_unurlifyChar(uw_context ctx, char **s) { + char *new_s = uw_unurlify_advance(*s); + char *r; + int len; + + len = strlen(*s); + uw_check_heap(ctx, len + 1); + + r = ctx->heap.front; + ctx->heap.front = uw_unurlifyString_to(0, ctx, ctx->heap.front, *s); + *s = new_s; + if (strlen(r) == 1) + return r[0]; + else + uw_error(ctx, FATAL, "Unurlified character is multiple characters long"); +} + uw_Basis_unit uw_Basis_unurlifyUnit(uw_context ctx, char **s) { + (void)ctx; *s = uw_unurlify_advance(*s); return uw_unit_v; } @@ -2232,25 +2330,40 @@ uw_unit uw_Basis_htmlifyInt_w(uw_context ctx, uw_Basis_int n) { return uw_unit_v; } -char *uw_Basis_htmlifySpecialChar(uw_context ctx, unsigned char ch) { +char *uw_Basis_htmlifySpecialChar(uw_context ctx, uw_Basis_char ch) { unsigned int n = ch; int len; char *r; - uw_check_heap(ctx, INTS_MAX+3); + uw_check_heap(ctx, INTS_MAX+3 + 1); r = ctx->heap.front; - sprintf(r, "&#%u;%n", n, &len); + len = sprintf(r, "&#%u;", n); ctx->heap.front += len+1; + return r; } -uw_unit uw_Basis_htmlifySpecialChar_w(uw_context ctx, unsigned char ch) { +uw_unit uw_Basis_htmlifySpecialChar_w(uw_context ctx, uw_Basis_char ch) { unsigned int n = ch; - int len; + int len = 0; uw_check(ctx, INTS_MAX+3); - sprintf(ctx->page.front, "&#%u;%n", n, &len); + + if(uw_Basis_isprint(ctx, ch)) { + + int32_t len_written = 0; + UErrorCode err = U_ZERO_ERROR; + + u_strToUTF8(ctx->page.front, 5, &len_written, (const UChar*)&ch, 1, &err); + len = len_written; + } + + // either it's a non-printable character, or we failed to convert to UTF-8 + if(len == 0) { + len = sprintf(ctx->page.front, "&#%u;", n); + } ctx->page.front += len; + return uw_unit_v; } @@ -2298,23 +2411,35 @@ uw_unit uw_Basis_jsifyInt_w(uw_context ctx, uw_Basis_int n) { char *uw_Basis_htmlifyString(uw_context ctx, const char *s) { char *r, *s2; + uw_Basis_char c1; + int oldoffset = 0, offset = 0, offset2 = 0, len = 0; + + uw_check_heap(ctx, strlen(s) * (INTS_MAX + 3) + 1); - uw_check_heap(ctx, strlen(s) * 5 + 1); - - for (r = s2 = ctx->heap.front; *s; s++) { - unsigned char c = *s; - - switch (c) { - case '<': - strcpy(s2, "<"); - s2 += 4; - break; - case '&': - strcpy(s2, "&"); - s2 += 5; - break; - default: - *s2++ = c; + r = s2 = ctx->heap.front; + + while (s[offset] != 0) { + oldoffset = offset; + U8_NEXT(s, offset, -1, c1); + + if ((offset - oldoffset == 1) && uw_Basis_isprint(ctx, c1)) { + switch (c1) { + case '<': + strcpy(s2, "<"); + s2 += 4; + break; + case '&': + strcpy(s2, "&"); + s2 += 5; + break; + default: + offset2 = 0; + U8_APPEND_UNSAFE(s2, offset2, c1); + s2 += offset2; + } + } else { + len = sprintf(s2, "&#%u;", c1); + s2 += len; } } @@ -2325,34 +2450,44 @@ char *uw_Basis_htmlifyString(uw_context ctx, const char *s) { uw_unit uw_Basis_htmlifyString_w(uw_context ctx, uw_Basis_string s) { uw_check(ctx, strlen(s) * 6); - - for (; *s; s++) { - unsigned char c = *s; - - switch (c) { - case '<': - uw_write_unsafe(ctx, "<"); - break; - case '&': - uw_write_unsafe(ctx, "&"); - break; - default: - uw_writec_unsafe(ctx, c); + int offset = 0, oldoffset = 0; + uw_Basis_char c1; + + while(s[offset] != 0){ + oldoffset = offset; + U8_NEXT(s, offset, -1, c1); + + if ((offset - oldoffset == 1) && uw_Basis_isprint(ctx, c1)) { + + switch (c1) { + case '<': + uw_write_unsafe(ctx, "<"); + break; + case '&': + uw_write_unsafe(ctx, "&"); + break; + default: + uw_writec_unsafe(ctx, c1); + } } + else { + uw_Basis_htmlifySpecialChar_w(ctx, c1); + } } return uw_unit_v; } uw_Basis_string uw_Basis_htmlifyBool(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) + (void)ctx; + if (!b) return "False"; else return "True"; } uw_unit uw_Basis_htmlifyBool_w(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) { + if (!b) { uw_check(ctx, 6); strcpy(ctx->page.front, "False"); ctx->page.front += 5; @@ -2401,63 +2536,115 @@ uw_unit uw_Basis_htmlifySource_w(uw_context ctx, uw_Basis_source src) { } uw_Basis_char uw_Basis_strsub(uw_context ctx, uw_Basis_string s, uw_Basis_int n) { + uw_Basis_char c; + int offset = 0; + while (n >= 0) { - if (*s == 0) + + if (s[offset] == 0) uw_error(ctx, FATAL, "Out-of-bounds strsub"); + U8_NEXT(s, offset, -1, c); + if (n == 0) - return *s; + return c; --n; - ++s; } uw_error(ctx, FATAL, "Negative strsub bound"); } uw_Basis_string uw_Basis_strsuffix(uw_context ctx, uw_Basis_string s, uw_Basis_int n) { + int offset = 0; while (n >= 0) { - if (*s == 0 || n == 0) - return s; + if (s[offset] == 0 || n == 0) + return s + offset; + U8_FWD_1(s, offset, -1); --n; - ++s; } uw_error(ctx, FATAL, "Negative strsuffix bound"); } uw_Basis_int uw_Basis_strlen(uw_context ctx, uw_Basis_string s) { - return strlen(s); + (void)ctx; + int offset = 0, iterations = 0; + while (s[offset] != 0) { + U8_FWD_1(s, offset, -1); + ++iterations; + } + return iterations; } uw_Basis_bool uw_Basis_strlenGe(uw_context ctx, uw_Basis_string s, uw_Basis_int n) { + (void)ctx; + int offset = 0; while (n > 0) { - if (*s == 0) + if (s[offset] == 0) return uw_Basis_False; - + + U8_FWD_1(s, offset, -1); --n; - ++s; } return uw_Basis_True; } +static int aux_strchr(uw_Basis_string s, uw_Basis_char ch, int *o_offset) { + int u8idx = 0, offset = 0, offsetpr = 0; + uw_Basis_char c; + + while (s[offset] != 0) { + U8_NEXT(s, offset, -1, c); + if (c == ch) { + *o_offset = offsetpr; + return u8idx; + } + + offsetpr = offset; + ++u8idx; + } + + *o_offset = -1; + return -1; +} + uw_Basis_string uw_Basis_strchr(uw_context ctx, uw_Basis_string s, uw_Basis_char ch) { - return strchr(s, ch); + (void)ctx; + int offset = -1; + if (aux_strchr(s, ch, &offset) > -1) { + return s + offset; + } + return NULL; } uw_Basis_int uw_Basis_strcspn(uw_context ctx, uw_Basis_string s, uw_Basis_string chs) { - return strcspn(s, chs); + (void)ctx; + int offset = 0, u8idx = 0, offsetChs = 0; + uw_Basis_char c; + + while (s[offset] != 0) { + U8_NEXT(s, offset, -1, c); + if (aux_strchr(chs, c, &offsetChs) > -1) { + return u8idx; + } + ++u8idx; + } + + return u8idx; } uw_Basis_int *uw_Basis_strindex(uw_context ctx, uw_Basis_string s, uw_Basis_char ch) { - uw_Basis_string r = strchr(s, ch); - if (r == NULL) + (void)ctx; + int offset = -1; + int r = aux_strchr(s, ch, &offset); + if (r == -1) return NULL; else { uw_Basis_int *nr = uw_malloc(ctx, sizeof(uw_Basis_int)); - *nr = r - s; + *nr = r; return nr; } } @@ -2468,13 +2655,19 @@ uw_Basis_int *uw_Basis_strsindex(uw_context ctx, const char *haystack, const cha return NULL; else { uw_Basis_int *nr = uw_malloc(ctx, sizeof(uw_Basis_int)); - *nr = r - haystack; + int src = r - haystack, offset = 0, utf8idx = 0; + while (offset < src) { + U8_FWD_1(haystack, offset, -1); + ++utf8idx; + } + + *nr = utf8idx; return nr; } } uw_Basis_string uw_Basis_strcat(uw_context ctx, uw_Basis_string s1, uw_Basis_string s2) { - int len = uw_Basis_strlen(ctx, s1) + uw_Basis_strlen(ctx, s2) + 1; + int len = strlen(s1) + strlen(s2) + 1; char *s; uw_check_heap(ctx, len); @@ -2489,8 +2682,8 @@ uw_Basis_string uw_Basis_strcat(uw_context ctx, uw_Basis_string s1, uw_Basis_str } uw_Basis_string uw_Basis_substring(uw_context ctx, uw_Basis_string s, uw_Basis_int start, uw_Basis_int len) { - size_t full_len = uw_Basis_strlen(ctx, s); - + int full_len = uw_Basis_strlen(ctx, s); + if (start < 0) uw_error(ctx, FATAL, "substring: Negative start index"); if (len < 0) @@ -2498,32 +2691,53 @@ uw_Basis_string uw_Basis_substring(uw_context ctx, uw_Basis_string s, uw_Basis_i if (start + len > full_len) uw_error(ctx, FATAL, "substring: Start index plus length is too large"); - if (start + len == full_len) - return &s[start]; - else { - uw_Basis_string r = uw_malloc(ctx, len+1); - memcpy(r, s+start, len); - r[len] = 0; + int offset = 0; + U8_FWD_N(s, offset, -1, start); + + if (start + len == full_len) { + return s + offset; + } else { + int end = offset; + U8_FWD_N(s, end, -1, len); + + int actual_len = end - offset; + + uw_Basis_string r = uw_malloc(ctx, actual_len + 1); + memcpy(r, s + offset, actual_len); + r[actual_len] = 0; return r; } - } uw_Basis_string uw_Basis_str1(uw_context ctx, uw_Basis_char ch) { char *r; - - uw_check_heap(ctx, 2); + int req = U8_LENGTH(ch); + int offset = 0; + + uw_check_heap(ctx, req + 1); r = ctx->heap.front; - r[0] = ch; - r[1] = 0; - ctx->heap.front += 2; + U8_APPEND_UNSAFE(r, offset, ch); + r[req] = 0; - return r; + ctx->heap.front += req + 1; + return r; +} + +uw_Basis_string uw_Basis_ofUnicode(uw_context ctx, uw_Basis_int n) { + UChar buf16[] = {n}; + uw_Basis_string out = uw_malloc(ctx, 3); + int32_t outLen; + UErrorCode pErrorCode = 0; + + if (u_strToUTF8(out, 3, &outLen, buf16, 1, &pErrorCode) == NULL || outLen == 0) + uw_error(ctx, FATAL, "Bad Unicode string to unescape (error %s)", u_errorName(pErrorCode)); + + return out; } uw_Basis_string uw_strdup(uw_context ctx, uw_Basis_string s1) { - int len = uw_Basis_strlen(ctx, s1) + 1; + int len = strlen(s1) + 1; char *s; uw_check_heap(ctx, len); @@ -2650,7 +2864,6 @@ uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) { uw_Basis_string uw_Basis_sqlifyChar(uw_context ctx, uw_Basis_char c) { char *r, *s2; - uw_check_heap(ctx, 5 + uw_Estrings + strlen(uw_sqlsuffixChar)); r = s2 = ctx->heap.front; @@ -2794,7 +3007,8 @@ uw_Basis_string uw_Basis_sqlifyStringN(uw_context ctx, uw_Basis_string s) { } char *uw_Basis_sqlifyBool(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) + (void)ctx; + if (!b) return "FALSE"; else return "TRUE"; @@ -2878,7 +3092,7 @@ char *uw_Basis_ensqlBool(uw_Basis_bool b) { static uw_Basis_int true = 1; static uw_Basis_int false = 0; - if (b == uw_Basis_False) + if (!b) return (char *)&false; else return (char *)&true; @@ -2907,14 +3121,12 @@ uw_Basis_string uw_Basis_floatToString(uw_context ctx, uw_Basis_float n) { } uw_Basis_string uw_Basis_charToString(uw_context ctx, uw_Basis_char ch) { - char *r = uw_malloc(ctx, 2); - r[0] = ch; - r[1] = 0; - return r; + return uw_Basis_str1(ctx, ch); } uw_Basis_string uw_Basis_boolToString(uw_context ctx, uw_Basis_bool b) { - if (b == uw_Basis_False) + (void)ctx; + if (!b) return "False"; else return "True"; @@ -2969,16 +3181,18 @@ uw_Basis_char *uw_Basis_stringToChar(uw_context ctx, uw_Basis_string s) { uw_Basis_char *r = uw_malloc(ctx, 1); r[0] = 0; return r; - } else if (s[1] != 0) + } else if (uw_Basis_strlenGe(ctx, s, 2)) return NULL; else { uw_Basis_char *r = uw_malloc(ctx, 1); - r[0] = s[0]; + int offset = 0; + U8_NEXT(s, offset, -1, *r); return r; } } uw_Basis_bool *uw_Basis_stringToBool(uw_context ctx, uw_Basis_string s) { + (void)ctx; static uw_Basis_bool true = uw_Basis_True; static uw_Basis_bool false = uw_Basis_False; @@ -3097,10 +3311,14 @@ uw_Basis_float uw_Basis_stringToFloat_error(uw_context ctx, uw_Basis_string s) { uw_Basis_char uw_Basis_stringToChar_error(uw_context ctx, uw_Basis_string s) { if (s[0] == 0) return 0; - else if (s[1] != 0) + else if (uw_Basis_strlenGe(ctx, s, 2)) uw_error(ctx, FATAL, "Can't parse char: %s", uw_Basis_htmlifyString(ctx, s)); - else - return s[0]; + else { + uw_Basis_char c; + int offset = 0; + U8_NEXT(s, offset, -1, c); + return c; + } } uw_Basis_bool uw_Basis_stringToBool_error(uw_context ctx, uw_Basis_string s) { @@ -3203,10 +3421,19 @@ uw_Basis_blob uw_Basis_stringToBlob_error(uw_context ctx, uw_Basis_string s, siz s += 2; while (*s) { + char a = s[0]; + s += 1; + char b; + if (*s){ + b = s[0]; + } else { + b = 0; + } int n; - sscanf(s, "%02x", &n); + char buf[3] = {a, b, 0}; + n = strtol(buf, NULL, 16); *r++ = n; - s += 2; + s += 1; } } else { while (*s) { @@ -3353,6 +3580,8 @@ static delta *allocate_delta(uw_context ctx, unsigned client) { } uw_Basis_channel uw_Basis_new_channel(uw_context ctx, uw_unit u) { + (void)u; + if (ctx->client == NULL) uw_error(ctx, FATAL, "Attempt to create channel on request not associated with a persistent connection"); @@ -3622,6 +3851,8 @@ int uw_commit(uw_context ctx) { } } + ctx->file_cache_missed = 0; + return 0; } @@ -3929,37 +4160,59 @@ int uw_streq(uw_Basis_string s1, uw_Basis_string s2) { } uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) { + (void)u; ctx->usedSig = 1; return ctx->app->cookie_sig(ctx); } uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) { + (void)ctx; return f.name; } uw_Basis_string uw_Basis_fileMimeType(uw_context ctx, uw_Basis_file f) { + (void)ctx; return f.type; } uw_Basis_int uw_Basis_blobSize(uw_context ctx, uw_Basis_blob b) { + (void)ctx; return b.size; } uw_Basis_blob uw_Basis_textBlob(uw_context ctx, uw_Basis_string s) { + (void)ctx; uw_Basis_blob b = {strlen(s), s}; return b; } +uw_Basis_string uw_Basis_textOfBlob(uw_context ctx, uw_Basis_blob b) { + size_t i; + uw_Basis_string r; + + for (i = 0; i < b.size; ++i) + if (b.data[i] == 0) + return NULL; + + r = uw_malloc(ctx, b.size + 1); + memcpy(r, b.data, b.size); + r[b.size] = 0; + return r; +} + uw_Basis_blob uw_Basis_fileData(uw_context ctx, uw_Basis_file f) { + (void)ctx; return f.data; } uw_Basis_string uw_Basis_postType(uw_context ctx, uw_Basis_postBody pb) { + (void)ctx; return pb.type; } uw_Basis_string uw_Basis_postData(uw_context ctx, uw_Basis_postBody pb) { + (void)ctx; return pb.data; } @@ -4156,24 +4409,29 @@ uw_Basis_string uw_Basis_mstrcat(uw_context ctx, ...) { const uw_Basis_time uw_Basis_minTime = {}; uw_Basis_time uw_Basis_now(uw_context ctx) { + (void)ctx; uw_Basis_time r = { time(NULL) }; return r; } uw_Basis_time uw_Basis_addSeconds(uw_context ctx, uw_Basis_time tm, uw_Basis_int n) { + (void)ctx; tm.seconds += n; return tm; } uw_Basis_int uw_Basis_diffInSeconds(uw_context ctx, uw_Basis_time tm1, uw_Basis_time tm2) { + (void)ctx; return difftime(tm2.seconds, tm1.seconds); } uw_Basis_int uw_Basis_toMilliseconds(uw_context ctx, uw_Basis_time tm) { + (void)ctx; return tm.seconds * 1000 + tm.microseconds / 1000; } uw_Basis_time uw_Basis_fromMilliseconds(uw_context ctx, uw_Basis_int n) { + (void)ctx; uw_Basis_time tm = {n / 1000, n % 1000 * 1000}; return tm; } @@ -4183,54 +4441,63 @@ uw_Basis_int uw_Basis_diffInMilliseconds(uw_context ctx, uw_Basis_time tm1, uw_B } uw_Basis_int uw_Basis_toSeconds(uw_context ctx, uw_Basis_time tm) { + (void)ctx; return tm.seconds; } uw_Basis_time uw_Basis_fromDatetime(uw_context ctx, uw_Basis_int year, uw_Basis_int month, uw_Basis_int day, uw_Basis_int hour, uw_Basis_int minute, uw_Basis_int second) { + (void)ctx; struct tm tm = { .tm_year = year - 1900, .tm_mon = month, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second, .tm_isdst = -1 }; - uw_Basis_time r = { timelocal(&tm) }; + uw_Basis_time r = { mktime(&tm) }; return r; } uw_Basis_int uw_Basis_datetimeYear(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_year + 1900; } uw_Basis_int uw_Basis_datetimeMonth(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_mon; } uw_Basis_int uw_Basis_datetimeDay(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_mday; } uw_Basis_int uw_Basis_datetimeHour(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_hour; } uw_Basis_int uw_Basis_datetimeMinute(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_min; } uw_Basis_int uw_Basis_datetimeSecond(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_sec; } uw_Basis_int uw_Basis_datetimeDayOfWeek(uw_context ctx, uw_Basis_time time) { + (void)ctx; struct tm tm; localtime_r(&time.seconds, &tm); return tm.tm_wday; @@ -4272,73 +4539,109 @@ void uw_set_global(uw_context ctx, char *name, void *data, void (*free)(void*)) } uw_Basis_bool uw_Basis_isalnum(uw_context ctx, uw_Basis_char c) { - return !!isalnum((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_POSIX_ALNUM); } uw_Basis_bool uw_Basis_isalpha(uw_context ctx, uw_Basis_char c) { - return !!isalpha((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_ALPHABETIC); } uw_Basis_bool uw_Basis_isblank(uw_context ctx, uw_Basis_char c) { - return !!isblank((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_POSIX_BLANK); } uw_Basis_bool uw_Basis_iscntrl(uw_context ctx, uw_Basis_char c) { - return !!iscntrl((int)c); + (void)ctx; + return !!(u_charType(c)==U_CONTROL_CHAR); } uw_Basis_bool uw_Basis_isdigit(uw_context ctx, uw_Basis_char c) { - return !!isdigit((int)c); + (void)ctx; + return !!u_isdigit(c); } uw_Basis_bool uw_Basis_isgraph(uw_context ctx, uw_Basis_char c) { - return !!isgraph((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_POSIX_GRAPH); } uw_Basis_bool uw_Basis_islower(uw_context ctx, uw_Basis_char c) { - return !!islower((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_LOWERCASE); } uw_Basis_bool uw_Basis_isprint(uw_context ctx, uw_Basis_char c) { - return !!isprint((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_POSIX_PRINT); } uw_Basis_bool uw_Basis_ispunct(uw_context ctx, uw_Basis_char c) { - return !!ispunct((int)c); + (void)ctx; + return !!u_ispunct(c); } uw_Basis_bool uw_Basis_isspace(uw_context ctx, uw_Basis_char c) { - return !!isspace((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_WHITE_SPACE); } uw_Basis_bool uw_Basis_isupper(uw_context ctx, uw_Basis_char c) { - return !!isupper((int)c); + (void)ctx; + return !!u_hasBinaryProperty(c, UCHAR_UPPERCASE); } uw_Basis_bool uw_Basis_isxdigit(uw_context ctx, uw_Basis_char c) { - return !!isxdigit((int)c); + (void)ctx; + return !!(c <= 0x7f && u_isxdigit(c)); } uw_Basis_char uw_Basis_tolower(uw_context ctx, uw_Basis_char c) { - return tolower((int)c); + (void)ctx; + return u_tolower(c); } uw_Basis_char uw_Basis_toupper(uw_context ctx, uw_Basis_char c) { - return toupper((int)c); + (void)ctx; + return u_toupper(c); } uw_Basis_int uw_Basis_ord(uw_context ctx, uw_Basis_char c) { - return (unsigned char)c; + (void)ctx; + return (uw_Basis_int)c; +} + +uw_Basis_bool uw_Basis_iscodepoint(uw_context ctx, uw_Basis_int n) { + (void)ctx; + return !!(n <= 0x10FFFF); +} + +uw_Basis_bool uw_Basis_issingle(uw_context ctx, uw_Basis_char c) { + (void)ctx; + return !!(c < 128); } uw_Basis_char uw_Basis_chr(uw_context ctx, uw_Basis_int n) { - return n; + (void)ctx; + uw_Basis_char ch = (uw_Basis_char)n; + + if (n > 0x10FFFF) { + uw_error(ctx, FATAL, "The integer %lld is not a valid char codepoint", n); + } + + return ch; } uw_Basis_string uw_Basis_currentUrl(uw_context ctx) { return ctx->current_url; } +uw_Basis_string uw_Basis_anchorUrl(uw_context ctx, uw_Basis_string s) { + return uw_Basis_strcat(ctx, uw_Basis_strcat(ctx, ctx->current_url, "#"), s); +} + void uw_set_currentUrl(uw_context ctx, char *s) { ctx->current_url = s; } @@ -4431,16 +4734,13 @@ failure_kind uw_runCallback(uw_context ctx, void (*callback)(uw_context)) { return r; } -uw_Basis_string uw_Basis_crypt(uw_context ctx, uw_Basis_string key, uw_Basis_string salt) { - char buf[14]; - return uw_strdup(ctx, DES_fcrypt(key, salt, buf)); -} - uw_Basis_bool uw_Basis_eq_time(uw_context ctx, uw_Basis_time t1, uw_Basis_time t2) { + (void)ctx; return !!(t1.seconds == t2.seconds && t1.microseconds == t2.microseconds); } uw_Basis_bool uw_Basis_lt_time(uw_context ctx, uw_Basis_time t1, uw_Basis_time t2) { + (void)ctx; return !!(t1.seconds < t2.seconds || (t1.seconds == t2.seconds && t1.microseconds < t2.microseconds)); } @@ -4505,66 +4805,82 @@ uw_Basis_string uw_Basis_fresh(uw_context ctx) { } uw_Basis_float uw_Basis_floatFromInt(uw_context ctx, uw_Basis_int n) { + (void)ctx; return n; } uw_Basis_int uw_Basis_ceil(uw_context ctx, uw_Basis_float n) { + (void)ctx; return ceil(n); } uw_Basis_int uw_Basis_trunc(uw_context ctx, uw_Basis_float n) { + (void)ctx; return trunc(n); } uw_Basis_int uw_Basis_round(uw_context ctx, uw_Basis_float n) { + (void)ctx; return round(n); } uw_Basis_int uw_Basis_floor(uw_context ctx, uw_Basis_float n) { + (void)ctx; return floor(n); } uw_Basis_float uw_Basis_pow(uw_context ctx, uw_Basis_float n, uw_Basis_float m) { + (void)ctx; return pow(n,m); } uw_Basis_float uw_Basis_sqrt(uw_context ctx, uw_Basis_float n) { + (void)ctx; return sqrt(n); } uw_Basis_float uw_Basis_sin(uw_context ctx, uw_Basis_float n) { + (void)ctx; return sin(n); } uw_Basis_float uw_Basis_cos(uw_context ctx, uw_Basis_float n) { + (void)ctx; return cos(n); } uw_Basis_float uw_Basis_log(uw_context ctx, uw_Basis_float n) { + (void)ctx; return log(n); } uw_Basis_float uw_Basis_exp(uw_context ctx, uw_Basis_float n) { + (void)ctx; return exp(n); } uw_Basis_float uw_Basis_asin(uw_context ctx, uw_Basis_float n) { + (void)ctx; return asin(n); } uw_Basis_float uw_Basis_acos(uw_context ctx, uw_Basis_float n) { + (void)ctx; return acos(n); } uw_Basis_float uw_Basis_atan(uw_context ctx, uw_Basis_float n) { + (void)ctx; return atan(n); } uw_Basis_float uw_Basis_atan2(uw_context ctx, uw_Basis_float n, uw_Basis_float m) { + (void)ctx; return atan2(n, m); } uw_Basis_float uw_Basis_abs(uw_context ctx, uw_Basis_float n) { + (void)ctx; return fabs(n); } @@ -4573,7 +4889,7 @@ uw_Basis_string uw_Basis_atom(uw_context ctx, uw_Basis_string s) { for (p = s; *p; ++p) { char c = *p; - if (!isalnum((int)c) && c != '+' && c != '-' && c != '.' && c != '%' && c != '#') + if (!U8_IS_SINGLE(c) || (!isalnum((int)c) && c != '+' && c != '-' && c != '.' && c != '%' && c != '#')) uw_error(ctx, FATAL, "Disallowed character in CSS atom"); } @@ -4585,8 +4901,8 @@ uw_Basis_string uw_Basis_css_url(uw_context ctx, uw_Basis_string s) { for (p = s; *p; ++p) { char c = *p; - if (!isalnum((int)c) && c != ':' && c != '/' && c != '.' && c != '_' && c != '+' - && c != '-' && c != '%' && c != '?' && c != '&' && c != '=' && c != '#') + if (!U8_IS_SINGLE(c) || (!isalnum((int)c) && c != ':' && c != '/' && c != '.' && c != '_' && c != '+' + && c != '-' && c != '%' && c != '?' && c != '&' && c != '=' && c != '#')) uw_error(ctx, FATAL, "Disallowed character in CSS URL"); } @@ -4599,12 +4915,12 @@ uw_Basis_string uw_Basis_property(uw_context ctx, uw_Basis_string s) { if (!*s) uw_error(ctx, FATAL, "Empty CSS property"); - if (!islower((int)s[0]) && s[0] != '_') + if (!U8_IS_SINGLE(s[0]) || (!islower((int)s[0]) && s[0] != '_')) uw_error(ctx, FATAL, "Bad initial character in CSS property"); for (p = s; *p; ++p) { char c = *p; - if (!islower((int)c) && !isdigit((int)c) && c != '_' && c != '-') + if (!U8_IS_SINGLE(c) || (!islower((int)c) && !isdigit((int)c) && c != '_' && c != '-')) uw_error(ctx, FATAL, "Disallowed character in CSS property"); } @@ -4612,14 +4928,17 @@ uw_Basis_string uw_Basis_property(uw_context ctx, uw_Basis_string s) { } uw_Basis_string uw_Basis_fieldName(uw_context ctx, uw_Basis_postField f) { + (void)ctx; return f.name; } uw_Basis_string uw_Basis_fieldValue(uw_context ctx, uw_Basis_postField f) { + (void)ctx; return f.value; } uw_Basis_string uw_Basis_remainingFields(uw_context ctx, uw_Basis_postField f) { + (void)ctx; return f.remaining; } @@ -4635,13 +4954,13 @@ uw_Basis_postField *uw_Basis_firstFormField(uw_context ctx, uw_Basis_string s) { f = uw_malloc(ctx, sizeof(uw_Basis_postField)); unurl = s; - f->name = uw_Basis_unurlifyString(ctx, &unurl); + f->name = uw_Basis_unurlifyString_fromClient(ctx, &unurl); s = strchr(s, 0); if (!s) uw_error(ctx, FATAL, "firstFormField: Missing null terminator"); ++s; unurl = s; - f->value = uw_Basis_unurlifyString(ctx, &unurl); + f->value = uw_Basis_unurlifyString_fromClient(ctx, &unurl); s = strchr(s, 0); if (!s) uw_error(ctx, FATAL, "firstFormField: Missing null terminator"); @@ -4654,7 +4973,7 @@ uw_Basis_string uw_Basis_blessData(uw_context ctx, uw_Basis_string s) { char *p = s; for (; *p; ++p) - if (!isalnum(*p) && *p != '-' && *p != '_') + if (!U8_IS_SINGLE(*p) || (!isalnum(*p) && *p != '-' && *p != '_')) uw_error(ctx, FATAL, "Illegal HTML5 data-* attribute: %s", s); return s; @@ -4754,6 +5073,7 @@ static char *uw_Sqlcache_keyCopy(char *buf, char *key) { // The NUL-terminated prefix of [key] below always looks something like "_k1_k2_k3..._kn". uw_Sqlcache_Value *uw_Sqlcache_check(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) { + (void)ctx; int doBump = random() % 1024 == 0; if (doBump) { pthread_rwlock_wrlock(&cache->lockIn); @@ -4836,6 +5156,8 @@ static void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw } static void uw_Sqlcache_flushCommitOne(uw_Sqlcache_Cache *cache, char **keys) { + (void)cache; + (void)keys; } static void uw_Sqlcache_commit(void *data) { @@ -4854,6 +5176,7 @@ static void uw_Sqlcache_commit(void *data) { } static void uw_Sqlcache_free(void *data, int dontCare) { + (void)dontCare; uw_context ctx = (uw_context)data; uw_Sqlcache_Update *update = ctx->cacheUpdate; while (update) { @@ -4929,6 +5252,7 @@ void uw_Sqlcache_store(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys, uw } void uw_Sqlcache_flush(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) { + (void)ctx; // A flush has to happen immediately so that subsequent stores in the same transaction fail. // This is safe to do because we will always call [uw_Sqlcache_wlock] earlier. // If the transaction fails, the only harm done is a few extra cache misses. @@ -4978,3 +5302,117 @@ int strcmp_nullsafe(const char *str1, const char *str2) { else return 1; } + +static int is_valid_hash(uw_Basis_string hash) { + for (; *hash; ++hash) + if (!U8_IS_SINGLE(*hash) || !isxdigit(*hash)) + return 0; + + return 1; +} + +uw_unit uw_Basis_cache_file(uw_context ctx, uw_Basis_blob contents) { + char *dir = ctx->app->file_cache, path[1024], tempfile[1024]; + unsigned char *res, *hash; + char *hash_encoded; + int fd, len, i; + ssize_t written_so_far = 0; + + if (!dir) + return uw_unit_v; + + hash = uw_malloc(ctx, SHA512_DIGEST_LENGTH); + res = SHA512((unsigned char *)contents.data, contents.size, hash); + if (!res) + uw_error(ctx, FATAL, "Can't hash file contents"); + + hash_encoded = uw_malloc(ctx, SHA512_DIGEST_LENGTH * 2 + 1); + for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) + sprintf(hash_encoded + 2 * i, "%02x", (int)hash[i]); + hash_encoded[SHA512_DIGEST_LENGTH * 2] = 0; + + len = snprintf(tempfile, sizeof tempfile, "%s/tmpXXXXXX", dir); + if (len < 0 || len >= sizeof tempfile) + uw_error(ctx, FATAL, "Error assembling file path for cache (temporary)"); + + fd = mkstemp(tempfile); + if (fd < 0) + uw_error(ctx, FATAL, "Error creating temporary file %s for cache", tempfile); + + while (written_so_far < contents.size) { + ssize_t written_just_now = write(fd, contents.data + written_so_far, contents.size - written_so_far); + if (written_just_now <= 0) { + close(fd); + uw_error(ctx, FATAL, "Error writing all bytes to cached file"); + } + written_so_far += written_just_now; + } + + close(fd); + + len = snprintf(path, sizeof path, "%s/%s", dir, hash_encoded); + if (len < 0 || len >= sizeof path) + uw_error(ctx, FATAL, "Error assembling file path for cache"); + + if (rename(tempfile, path)) + uw_error(ctx, FATAL, "Error renaming temporary file into cache"); + + return uw_unit_v; +} + +uw_Basis_blob uw_Basis_check_filecache(uw_context ctx, uw_Basis_string hash) { + char path[1024], *dir = ctx->app->file_cache, *filedata; + int len; + long size, read_so_far = 0; + FILE *fp; + uw_Basis_blob res; + + // Hashes come formatted for printing by Postgres, which means they start with + // two extra characters. Let's remove them. + if (hash[0] == '\\' && hash[1] == 'x') + hash += 2; + + if (!dir) + uw_error(ctx, FATAL, "Checking file cache when no directory is set"); + + if (!is_valid_hash(hash)) + uw_error(ctx, FATAL, "Checking file cache with invalid hash %s", hash); + + len = snprintf(path, sizeof path, "%s/%s", dir, hash); + if (len < 0 || len >= sizeof path) + uw_error(ctx, FATAL, "Error assembling file path for cache"); + + fp = fopen(path, "r"); + if (!fp) { + ctx->file_cache_missed = 1; + uw_error(ctx, UNLIMITED_RETRY, "Missed in the file cache for hash %s", hash); + } + uw_push_cleanup(ctx, (void (*)(void *))fclose, fp); + + if (fseek(fp, 0L, SEEK_END)) + uw_error(ctx, FATAL, "Error seeking to end of cached file"); + + size = ftell(fp); + if (size < 0) + uw_error(ctx, FATAL, "Error getting size of cached file"); + + rewind(fp); + filedata = uw_malloc(ctx, size); + + while (read_so_far < size) { + size_t just_read = fread(filedata + read_so_far, 1, size - read_so_far, fp); + if (just_read <= 0) + uw_error(ctx, FATAL, "Error reading all bytes of cached file"); + read_so_far += just_read; + } + + uw_pop_cleanup(ctx); + + res.size = size; + res.data = filedata; + return res; +} + +uw_Basis_bool uw_Basis_filecache_missed(uw_context ctx) { + return !!(ctx->file_cache_missed); +} |