Make a big amalgamation of HTTP, WL, sqlite alongside cweb

This commit is contained in:
2025-09-24 10:51:12 +02:00
parent 0790a11b00
commit f2d5eef6d2
23 changed files with 275535 additions and 361 deletions
View File
View File
View File
View File
+34 -2
View File
@@ -6,7 +6,7 @@
#ifndef WL_NOINCLUDE
#include "wl.h"
#endif // WL_NOINCLUDE
#endif
/////////////////////////////////////////////////////////////////////////
// BASIC
@@ -3588,7 +3588,7 @@ WL_Compiler *wl_compiler_init(WL_Arena *arena)
return compiler;
}
WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String content)
WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String path, WL_String content)
{
if (compiler->err)
return (WL_AddResult) { .type=WL_ADD_ERROR };
@@ -3599,6 +3599,38 @@ WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String content)
return (WL_AddResult) { .type=WL_ADD_ERROR };
}
// Make include paths relative to the parent file
if (path.len > 0) {
String parent = { path.ptr, path.len };
char sep = '/';
while (parent.len > 0 && parent.ptr[parent.len-1] != sep)
parent.len--;
if (parent.len > 0) {
Node *include = pres.includes;
while (include) {
char *dst = alloc(compiler->arena, parent.len + include->include_path.len + 1, 1);
if (dst == NULL) {
// TODO
}
memcpy(dst,
parent.ptr,
parent.len);
memcpy(dst + parent.len,
include->include_path.ptr,
include->include_path.len);
include->include_path = (String) { dst, parent.len + include->include_path.len };
include = include->include_next;
}
}
}
CompiledFile compiled_file = {
.file = compiler->waiting_file,
.root = pres.node,
+1 -1
View File
@@ -46,7 +46,7 @@ typedef struct {
} WL_EvalResult;
WL_Compiler* wl_compiler_init (WL_Arena *arena);
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content);
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String path, WL_String content);
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
WL_String wl_compiler_error (WL_Compiler *compiler);
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
+2 -5
View File
@@ -12,11 +12,8 @@ all: cweb.c cweb.h cozisnews$(EXT)
cweb.c cweb.h: src/main.c src/main.h
python amalg.py
sqlite3.o: demo/sqlite3.c
gcc -o $@ -c $<
cozisnews$(EXT): demo/main.c cweb.c cweb.h sqlite3.o
gcc -o $@ demo/main.c demo/chttp.c demo/wl.c cweb.c sqlite3.o $(FLAGS) -Idemo
cozisnews$(EXT): demo/main.c cweb.c cweb.h
gcc -o $@ demo/main.c cweb.c $(FLAGS)
clean:
rm *.o *.out *.exe
+51 -4
View File
@@ -12,7 +12,7 @@ class Amalgamator:
self.out += "// " + file + "\n"
self.out += "////////////////////////////////////////////////////////////////////////////////////////\n"
self.out += "\n"
#self.out += "#line 1 \"" + file + "\"\n"
self.out += "#line 1 \"" + file + "\"\n"
self.out += open(file).read()
if len(self.out) > 0 and self.out[len(self.out)-1] != '\n':
@@ -35,8 +35,55 @@ header.save("cweb.h")
source = Amalgamator()
source.append_text("#include \"cweb.h\"\n")
source.append_text("#define CRYPT_BLOWFISH_NOINCLUDE")
source.append_file("src/crypt_blowfish.h")
source.append_file("src/crypt_blowfish.c")
source.append_text("#define WL_NOINCLUDE\n")
source.append_text("#define HTTP_NOINCLUDE\n")
source.append_text("#define CRYPT_BLOWFISH_NOINCLUDE\n")
source.append_file("3p/chttp.h")
source.append_file("3p/chttp.c")
source.append_text("#undef MIN\n")
source.append_text("#undef MAX\n")
source.append_text("#undef ASSERT\n")
source.append_text("#undef SIZEOF\n")
source.append_text("#undef TRACE\n")
source.append_file("3p/sqlite3.c")
source.append_text("#undef MIN\n")
source.append_text("#undef MAX\n")
source.append_text("#undef ASSERT\n")
source.append_text("#undef SIZEOF\n")
source.append_text("#undef TRACE\n")
source.append_text("#define Scanner WL_Scanner\n")
source.append_text("#define Token WL_Token\n")
source.append_text("#define is_space is_space__wl\n")
source.append_text("#define is_digit is_digit__wl\n")
source.append_text("#define is_alpha is_alpha__wl\n")
source.append_text("#define is_printable is_printable__wl\n")
source.append_text("#define is_hex_digit is_hex_digit__wl\n")
source.append_text("#define hex_digit_to_int hex_digit_to_int__wl\n")
source.append_text("#define consume_str consume_str__wl\n")
source.append_file("3p/wl.h")
source.append_file("3p/wl.c")
source.append_text("#undef Scanner\n")
source.append_text("#undef Token\n")
source.append_text("#undef is_space\n")
source.append_text("#undef is_digit\n")
source.append_text("#undef is_alpha\n")
source.append_text("#undef is_printable\n")
source.append_text("#undef is_hex_digit\n")
source.append_text("#undef hex_digit_to_int\n")
source.append_text("#undef MIN\n")
source.append_text("#undef MAX\n")
source.append_text("#undef ASSERT\n")
source.append_text("#undef SIZEOF\n")
source.append_file("3p/crypt_blowfish.h")
source.append_file("3p/crypt_blowfish.c")
source.append_file("src/main.c")
source.save("cweb.c")
+275174 -119
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -7,6 +7,7 @@
// src/main.h
////////////////////////////////////////////////////////////////////////////////////////
#line 1 "src/main.h"
#include <stdint.h>
#include <stdbool.h>
@@ -231,7 +232,12 @@ void cweb_global_free(void);
CWEB *cweb_init(CWEB_String addr, uint16_t port);
void cweb_free(CWEB *cweb);
int cweb_enable_database(CWEB *cweb, CWEB_String file);
// Open an SQLite instance in file "database_file" and run the DDL script at "schema_file".
// Note that "database_file" may be ":memory:".
int cweb_enable_database(CWEB *cweb, CWEB_String database_file, CWEB_String schema_file);
// Log all evaluated SQL statements to stdout
void cweb_trace_sql(CWEB *cweb, bool enable);
// Pause execution until a request is available.
// TODO: When does this function return NULL?
@@ -302,8 +308,8 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args);
void cweb_free_query_result(CWEB_QueryResult *res);
// Calculates the bcrypt hash of the specified password
int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *hash);
int cweb_hash_password(CWEB_String pass, int cost, CWEB_PasswordHash *hash);
// Checks whether the password matches the given hash
int cweb_check_password(char *pass, int passlen, CWEB_PasswordHash hash);
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash);
#endif // CWEB_AMALGAMATION
+50 -96
View File
@@ -1,68 +1,7 @@
#include <stddef.h>
#include "cweb.h"
#if 0
#define WL_STR(X) ((WL_String) { (X), (int) sizeof(X)-1})
#define HTML_STR(X) html_str(HTTP_STR(#X))
static CWEB_String html_str(CWEB_String str)
{
str = cweb_trim(str);
if (str.len > 0 && str.ptr[0] == '(') {
str.ptr++;
str.len--;
}
if (str.len > 0 && str.ptr[str.len-1] == ')')
str.len--;
return str;
}
int load_file(char *file, char **data, long *size)
{
FILE *f = fopen(file, "rb");
if (f == NULL) return -1;
fseek(f, 0, SEEK_END);
*size = ftell(f);
fseek(f, 0, SEEK_SET);
*data = malloc(*size + 1);
fread(*data, 1, *size, f);
(*data)[*size] = '\0';
fclose(f);
return 0;
}
static void *alloc(WL_Arena *arena, int num, int align)
{
int pad = -(uintptr_t) (arena->ptr + arena->cur) & (align-1);
if (arena->len - arena->cur < num + pad)
return NULL;
void *ptr = arena->ptr + arena->cur + pad;
arena->cur += num + pad;
return ptr;
}
static bool is_hex_digit(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
static int hex_digit_to_int(char c)
{
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return c - '0';
}
#endif
#define USERNAME_LIMIT 64
bool valid_name(CWEB_String str)
@@ -125,14 +64,14 @@ static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass)
return 0;
}
ret = cweb_check_password(pass.ptr, pass.len, hash);
ret = cweb_check_password(pass, hash);
if (ret < 0) {
cweb_free_query_result(&res);
return -500;
return -1;
}
if (ret > 0) {
cweb_free_query_result(&res);
return -400;
return 0;
}
return user_id;
@@ -148,17 +87,22 @@ static void endpoint_api_login(CWEB *cweb, CWEB_Request *req)
CWEB_String name = cweb_get_param_s(req, CWEB_STR("username"));
CWEB_String pass = cweb_get_param_s(req, CWEB_STR("password"));
if (!valid_name(name) || !valid_pass(pass)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid credentials</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid credentials"));
return;
}
int ret = user_exists(cweb, name, pass);
if (ret < 0) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid credentials</div>"));
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
if (ret == 0) {
cweb_respond_basic(req, 400, CWEB_STR("Invalid credentials"));
return;
}
if (cweb_set_user_id(req, ret) < 0) {
// TODO
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
cweb_respond_redirect(req, CWEB_STR("/index"));
@@ -177,29 +121,32 @@ static void endpoint_api_signup(CWEB *cweb, CWEB_Request *req)
CWEB_String pass2 = cweb_get_param_s(req, CWEB_STR("password2"));
if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid credentials</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid credentials"));
return;
}
if (!cweb_streq(pass1, pass2)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">The password was repeated incorrectly</div>"));
cweb_respond_basic(req, 400, CWEB_STR("The password was repeated incorrectly"));
return;
}
CWEB_PasswordHash hash;
int ret = cweb_hash_password(pass1.ptr, pass1.len, 12, &hash);
int ret = cweb_hash_password(pass1, 12, &hash);
if (ret) {
cweb_respond_basic(req, 400, CWEB_STR("<div class=\"error\">Internal error</div>"));
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
int64_t insert_id = cweb_database_insert(cweb, "INSERT INTO Users(username, email, hash) VALUES (?, ?, ?)", name, email, hash);
if (insert_id < 0) {
// TODO: What if the user exists?
cweb_respond_basic(req, 400, CWEB_STR("<div class=\"error\">Internal error</div>"));
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
if (cweb_set_user_id(req, insert_id) < 0) {
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
cweb_set_user_id(req, insert_id);
cweb_respond_redirect(req, CWEB_STR("/index"));
}
@@ -229,19 +176,16 @@ static void endpoint_api_post(CWEB *cweb, CWEB_Request *req)
cweb_respond_basic(req, 400, CWEB_STR("Invalid request"));
return;
}
if (!valid_post_title(title)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid title</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid title"));
return;
}
if (!valid_link(link)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid link</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid link"));
return;
}
if (!valid_post_content(content)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid content</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid content"));
return;
}
@@ -256,19 +200,20 @@ static void endpoint_api_post(CWEB *cweb, CWEB_Request *req)
user_id, title, is_link, content);
if (insert_id < 0) {
cweb_respond_basic(req, 500, CWEB_STR("<div class=\"error\">Internal error</div>"));
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
int post_id = (int) insert_id;
cweb_respond_redirect(req, cweb_format(req, "/post?id={}", post_id));
CWEB_String target = cweb_format(req, "/post?id={}", post_id);
cweb_respond_redirect(req, target);
}
static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req)
{
int user_id = cweb_get_user_id(req);
if (user_id == -1) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">You are not logged in</div>"));
cweb_respond_basic(req, 400, CWEB_STR("You are not logged in"));
return;
}
@@ -278,13 +223,13 @@ static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req)
CWEB_String csrf2 = cweb_get_param_s(req, CWEB_STR("csrf"));
if (!cweb_streq(cweb_get_csrf(req), csrf2)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid request</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid request"));
return;
}
content = cweb_trim(content);
if (!valid_comment_content(content)) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Invalid content</div>"));
cweb_respond_basic(req, 400, CWEB_STR("Invalid content"));
return;
}
@@ -292,18 +237,19 @@ static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req)
if (parent_comment == -1) insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post);
else insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post, parent_comment) VALUES (?, ?, ?, ?)", user_id, content, parent_post, parent_comment);
if (insert_id < 0) {
cweb_respond_basic(req, 200, CWEB_STR("<div class=\"error\">Internal error</div>"));
cweb_respond_basic(req, 500, CWEB_STR("Internal error"));
return;
}
cweb_respond_redirect(req, cweb_format(req, "/post?id={}", parent_post));
CWEB_String target = cweb_format(req, "/post?id={}", parent_post);
cweb_respond_redirect(req, target);
}
static void endpoint_index(CWEB *cweb, CWEB_Request *req)
{
(void) cweb;
cweb_respond_template(req, 200, CWEB_STR("pages/index.wl"), -1);
cweb_respond_template(req, 200, CWEB_STR("demo/pages/index.wl"), -1);
}
static void endpoint_write(CWEB *cweb, CWEB_Request *req)
@@ -315,7 +261,7 @@ static void endpoint_write(CWEB *cweb, CWEB_Request *req)
return;
}
cweb_respond_template(req, 200, CWEB_STR("pages/write.wl"), -1);
cweb_respond_template(req, 200, CWEB_STR("demo/pages/write.wl"), -1);
}
static void endpoint_login(CWEB *cweb, CWEB_Request *req)
@@ -327,7 +273,7 @@ static void endpoint_login(CWEB *cweb, CWEB_Request *req)
return;
}
cweb_respond_template(req, 200, CWEB_STR("pages/login.wl"), -1);
cweb_respond_template(req, 200, CWEB_STR("demo/pages/login.wl"), -1);
}
static void endpoint_signup(CWEB *cweb, CWEB_Request *req)
@@ -339,14 +285,14 @@ static void endpoint_signup(CWEB *cweb, CWEB_Request *req)
return;
}
cweb_respond_template(req, 200, CWEB_STR("pages/signup.wl"), -1);
cweb_respond_template(req, 200, CWEB_STR("demo/pages/signup.wl"), -1);
}
static void endpoint_post(CWEB *cweb, CWEB_Request *req)
{
int post_id = cweb_get_param_i(req, CWEB_STR("id"));
if (post_id < 0) {
cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1);
cweb_respond_template(req, 404, CWEB_STR("demo/pages/notfound.wl"), -1);
return;
}
@@ -366,18 +312,18 @@ static void endpoint_post(CWEB *cweb, CWEB_Request *req)
}
if (num == 0) {
cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1);
cweb_respond_template(req, 404, CWEB_STR("demo/pages/notfound.wl"), -1);
return;
}
cweb_respond_template(req, 200, CWEB_STR("pages/post.wl"), post_id);
cweb_respond_template(req, 200, CWEB_STR("demo/pages/post.wl"), post_id);
}
static void endpoint_fallback(CWEB *cweb, CWEB_Request *req)
{
(void) cweb;
cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1);
cweb_respond_template(req, 404, CWEB_STR("demo/pages/notfound.wl"), -1);
}
int main(void)
@@ -394,6 +340,14 @@ int main(void)
return -1;
}
if (cweb_enable_database(cweb, CWEB_STR(":memory:"), CWEB_STR("demo/schema.sql")) < 0) {
cweb_free(cweb);
cweb_global_free();
return -1;
}
cweb_trace_sql(cweb, true);
for (;;) {
CWEB_Request *req = cweb_wait(cweb);
if (0) {}
+1 -1
View File
@@ -1,4 +1,4 @@
include "pages/page.wl"
include "page.wl"
let posts = $query("SELECT P.id, P.title, P.is_link, P.content, (SELECT COUNT(*) FROM Comments as C WHERE c.parent_post=P.id) as num_comments, CURRENT_TIMESTAMP as date FROM Posts as P")
+3 -3
View File
@@ -1,5 +1,5 @@
include "pages/page.wl"
include "pages/login_and_signup_style.wl"
include "page.wl"
include "login_and_signup_style.wl"
let main =
<main>
@@ -7,7 +7,7 @@ let main =
<div id="response"></div>
<form hx-post="/api/login" hx-target="#response">
<form action="/api/login" method="POST">
<input type="text" name="username" placeholder="username" />
<input type="password" name="password" placeholder="password" />
<input type="submit" value="Log-In" />
+1 -1
View File
@@ -1,4 +1,4 @@
include "pages/page.wl"
include "page.wl"
let style =
<style>
+1 -1
View File
@@ -1,4 +1,4 @@
include "pages/page.wl"
include "page.wl"
let posts = $query("SELECT U.username, P.title, P.content FROM Posts as P, Users as U WHERE P.id=? AND U.id=P.author", $post_id)
let comments = $query("SELECT C.id, U.username, C.content, C.parent_post, C.parent_comment FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id=C.author", $post_id)
+1 -1
View File
@@ -1,4 +1,4 @@
include "pages/page.wl"
include "page.wl"
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
let comments = $query("SELECT C.id, U.username, C.content, C.parent_post, C.parent_comment FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id == C.author", $post_id)
+3 -3
View File
@@ -1,5 +1,5 @@
include "pages/page.wl"
include "pages/login_and_signup_style.wl"
include "page.wl"
include "login_and_signup_style.wl"
let main =
<main>
@@ -7,7 +7,7 @@ let main =
<div id="response"></div>
<form hx-post="/api/signup" hx-target="#response">
<form action="/api/signup" method="POST">
<input type="text" name="username" placeholder="username" />
<input type="email" name="email" placeholder="email" />
<input type="password" name="password1" placeholder="password" />
+1 -1
View File
@@ -1,4 +1,4 @@
include "pages/page.wl"
include "page.wl"
let style =
<style>
+29
View File
@@ -0,0 +1,29 @@
CREATE TABLE IF NOT EXISTS Users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
hash TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
signup_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS Posts (
id INTEGER PRIMARY KEY,
author INTEGER NOT NULL,
title TEXT NOT NULL,
is_link BOOLEAN NOT NULL,
content TEXT NOT NULL,
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (author) REFERENCES Users(id)
);
CREATE TABLE IF NOT EXISTS Comments (
id INTEGER PRIMARY KEY,
parent_post INTEGER NOT NULL,
parent_comment INTEGER,
author INTEGER NOT NULL,
content TEXT NOT NULL,
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (author) REFERENCES Users(id),
FOREIGN KEY (parent_post) REFERENCES Posts(id),
FOREIGN KEY (parent_comment) REFERENCES Comments(id)
);
+166 -117
View File
@@ -1,4 +1,6 @@
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#ifdef __linux__
#include <errno.h>
@@ -9,6 +11,7 @@
#include <windows.h>
#endif
#ifndef CWEB_AMALGAMATION
#ifdef CWEB_ENABLE_DATABASE
#include "sqlite3.h"
#endif
@@ -18,7 +21,8 @@
#endif
#include "chttp.h"
#include "cweb.h"
#include "main.h"
#endif
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
@@ -93,6 +97,7 @@ struct CWEB {
#ifdef CWEB_ENABLE_DATABASE
sqlite3 *db;
SQLiteCache *dbcache;
bool trace_sql;
#endif
#ifdef CWEB_ENABLE_TEMPLATE
@@ -158,6 +163,87 @@ CWEB_VArg cweb_varg_from_pb (bool *pb) { return (CWEB_VArg) { CWEB_VA
CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr) { return (CWEB_VArg) { CWEB_VARG_TYPE_PSTR, .pstr=pstr }; }
CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash) { return (CWEB_VArg) { CWEB_VARG_TYPE_PHASH, .phash=phash }; }
typedef struct {
char *dst;
int cap;
int len;
} StaticOutputBuffer;
static void append_to_output(StaticOutputBuffer *out, char *src, int len)
{
int unused = out->cap - out->len;
if (unused > 0)
memcpy(out->dst + out->len, src, MIN(len, unused));
out->len += len;
}
static void append_to_output_u64(StaticOutputBuffer *out, uint64_t n)
{
// TODO
}
static void append_to_output_s64(StaticOutputBuffer *out, int64_t n)
{
// TODO
}
static void append_to_output_f64(StaticOutputBuffer *out, double n)
{
// TODO
}
static void append_to_output_ptr(StaticOutputBuffer *out, void *p)
{
// TODO
}
static void value_to_output(StaticOutputBuffer *out, CWEB_VArg arg)
{
switch (arg.type) {
case CWEB_VARG_TYPE_C : append_to_output(out, &arg.c, 1); break;
case CWEB_VARG_TYPE_S : append_to_output_s64(out, arg.s); break;
case CWEB_VARG_TYPE_I : append_to_output_s64(out, arg.i); break;
case CWEB_VARG_TYPE_L : append_to_output_s64(out, arg.l); break;
case CWEB_VARG_TYPE_LL : append_to_output_s64(out, arg.ll); break;
case CWEB_VARG_TYPE_SC : append_to_output_s64(out, arg.sc); break;
case CWEB_VARG_TYPE_SS : append_to_output_s64(out, arg.ss); break;
case CWEB_VARG_TYPE_SI : append_to_output_s64(out, arg.si); break;
case CWEB_VARG_TYPE_SL : append_to_output_s64(out, arg.sl); break;
case CWEB_VARG_TYPE_SLL : append_to_output_s64(out, arg.sll); break;
case CWEB_VARG_TYPE_UC : append_to_output_u64(out, arg.uc); break;
case CWEB_VARG_TYPE_US : append_to_output_u64(out, arg.us); break;
case CWEB_VARG_TYPE_UI : append_to_output_u64(out, arg.ui); break;
case CWEB_VARG_TYPE_UL : append_to_output_u64(out, arg.ul); break;
case CWEB_VARG_TYPE_ULL : append_to_output_u64(out, arg.ull); break;
case CWEB_VARG_TYPE_F : append_to_output_f64(out, arg.f); break;
case CWEB_VARG_TYPE_D : append_to_output_u64(out, arg.d); break;
case CWEB_VARG_TYPE_B : append_to_output(out, arg.b ? "true" : "false", arg.b ? 4: 5);break;
case CWEB_VARG_TYPE_STR : append_to_output(out, arg.str.ptr, arg.str.len); break;
case CWEB_VARG_TYPE_HASH : append_to_output(out, arg.hash.data, strlen(arg.hash.data)); break;
case CWEB_VARG_TYPE_PC : append_to_output_ptr(out, arg.pc); break;
case CWEB_VARG_TYPE_PS : append_to_output_ptr(out, arg.ps); break;
case CWEB_VARG_TYPE_PI : append_to_output_ptr(out, arg.pi); break;
case CWEB_VARG_TYPE_PL : append_to_output_ptr(out, arg.pl); break;
case CWEB_VARG_TYPE_PLL : append_to_output_ptr(out, arg.pll); break;
case CWEB_VARG_TYPE_PSC : append_to_output_ptr(out, arg.psc); break;
case CWEB_VARG_TYPE_PSS : append_to_output_ptr(out, arg.pss); break;
case CWEB_VARG_TYPE_PSI : append_to_output_ptr(out, arg.psi); break;
case CWEB_VARG_TYPE_PSL : append_to_output_ptr(out, arg.psl); break;
case CWEB_VARG_TYPE_PSLL : append_to_output_ptr(out, arg.psll); break;
case CWEB_VARG_TYPE_PUC : append_to_output_ptr(out, arg.puc); break;
case CWEB_VARG_TYPE_PUS : append_to_output_ptr(out, arg.pus); break;
case CWEB_VARG_TYPE_PUI : append_to_output_ptr(out, arg.pui); break;
case CWEB_VARG_TYPE_PUL : append_to_output_ptr(out, arg.pul); break;
case CWEB_VARG_TYPE_PULL : append_to_output_ptr(out, arg.pull); break;
case CWEB_VARG_TYPE_PF : append_to_output_ptr(out, arg.pf); break;
case CWEB_VARG_TYPE_PD : append_to_output_ptr(out, arg.pd); break;
case CWEB_VARG_TYPE_PB : append_to_output_ptr(out, arg.pb); break;
case CWEB_VARG_TYPE_PSTR : append_to_output_ptr(out, arg.pstr); break;
case CWEB_VARG_TYPE_PHASH: append_to_output_ptr(out, arg.phash); break;
}
}
/////////////////////////////////////////////////////////////////
// FILE SYSTEM
////////////////////////////////////////////////////////////////
@@ -260,13 +346,13 @@ static int generate_random_bytes(char *dst, int cap)
// PASSWORD
////////////////////////////////////////////////////////////////
int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *hash)
int cweb_hash_password(CWEB_String pass, int cost, CWEB_PasswordHash *hash)
{
char passzt[128];
if (passlen >= (int) sizeof(passzt))
if (pass.len >= (int) sizeof(passzt))
return -1;
memcpy(passzt, pass, passlen);
passzt[passlen] = '\0';
memcpy(passzt, pass.ptr, pass.len);
passzt[pass.len] = '\0';
char random[16];
int ret = generate_random_bytes(random, (int) sizeof(random));
@@ -282,13 +368,13 @@ int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *has
return 0;
}
int cweb_check_password(char *pass, int passlen, CWEB_PasswordHash hash)
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash)
{
char passzt[128];
if (passlen >= (int) sizeof(passzt))
if (pass.len >= (int) sizeof(passzt))
return -1;
memcpy(passzt, pass, passlen);
passzt[passlen] = '\0';
memcpy(passzt, pass.ptr, pass.len);
passzt[pass.len] = '\0';
CWEB_PasswordHash new_hash;
if (_crypt_blowfish_rn(passzt, hash.data, new_hash.data, sizeof(new_hash.data)) == NULL)
@@ -322,6 +408,8 @@ struct SessionStorage {
Session items[];
};
#ifndef CWEB_AMALGAMATION
static bool is_hex_digit(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
@@ -336,6 +424,8 @@ static int hex_digit_to_int(char c)
return c - '0';
}
#endif
static void unpack_token(char *src, int srclen, char *dst, int dstlen)
{
ASSERT(2 * srclen == dstlen);
@@ -585,7 +675,7 @@ static int sqlite3utils_prepare(SQLiteCache *cache, sqlite3_stmt **pstmt, char *
sqlite3_stmt *stmt;
int ret = sqlite3_prepare_v2(cache->db, fmt, -1, &stmt, NULL);
if (ret != SQLITE_OK) {
//fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(cache->db), __FILE__, __LINE__); // TODO
return ret;
}
@@ -617,30 +707,32 @@ static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache,
for (int i = 0; i < args.len; i++) {
CWEB_VArg arg = args.ptr[i];
switch (arg.type) {
case CWEB_VARG_TYPE_C : ret = sqlite3_bind_text (stmt, i+1, &arg.c, 1, NULL); break;
case CWEB_VARG_TYPE_S : ret = sqlite3_bind_int (stmt, i+1, arg.s); break;
case CWEB_VARG_TYPE_I : ret = sqlite3_bind_int (stmt, i+1, arg.i); break;
case CWEB_VARG_TYPE_L : ret = sqlite3_bind_int64 (stmt, i+1, arg.l); break;
case CWEB_VARG_TYPE_LL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ll); break;
case CWEB_VARG_TYPE_SC : ret = sqlite3_bind_int (stmt, i+1, arg.sc); break;
case CWEB_VARG_TYPE_SS : ret = sqlite3_bind_int (stmt, i+1, arg.ss); break;
case CWEB_VARG_TYPE_SI : ret = sqlite3_bind_int (stmt, i+1, arg.si); break;
case CWEB_VARG_TYPE_SL : ret = sqlite3_bind_int64 (stmt, i+1, arg.sl); break;
case CWEB_VARG_TYPE_SLL: ret = sqlite3_bind_int (stmt, i+1, arg.sll); break;
case CWEB_VARG_TYPE_UC : ret = sqlite3_bind_int (stmt, i+1, arg.uc); break;
case CWEB_VARG_TYPE_US : ret = sqlite3_bind_int (stmt, i+1, arg.us); break;
case CWEB_VARG_TYPE_UI : ret = sqlite3_bind_int64 (stmt, i+1, arg.ui); break;
case CWEB_VARG_TYPE_UL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ul); break;
case CWEB_VARG_TYPE_ULL: ret = sqlite3_bind_int64 (stmt, i+1, arg.ull); break;
case CWEB_VARG_TYPE_F : ret = sqlite3_bind_double(stmt, i+1, arg.f); break;
case CWEB_VARG_TYPE_D : ret = sqlite3_bind_double(stmt, i+1, arg.d); break;
case CWEB_VARG_TYPE_B : ret = sqlite3_bind_int (stmt, i+1, arg.b); break;
case CWEB_VARG_TYPE_STR: ret = sqlite3_bind_text (stmt, i+1, arg.str.ptr, arg.str.len, NULL); break;
case CWEB_VARG_TYPE_C : ret = sqlite3_bind_text (stmt, i+1, &arg.c, 1, NULL); break;
case CWEB_VARG_TYPE_S : ret = sqlite3_bind_int (stmt, i+1, arg.s); break;
case CWEB_VARG_TYPE_I : ret = sqlite3_bind_int (stmt, i+1, arg.i); break;
case CWEB_VARG_TYPE_L : ret = sqlite3_bind_int64 (stmt, i+1, arg.l); break;
case CWEB_VARG_TYPE_LL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ll); break;
case CWEB_VARG_TYPE_SC : ret = sqlite3_bind_int (stmt, i+1, arg.sc); break;
case CWEB_VARG_TYPE_SS : ret = sqlite3_bind_int (stmt, i+1, arg.ss); break;
case CWEB_VARG_TYPE_SI : ret = sqlite3_bind_int (stmt, i+1, arg.si); break;
case CWEB_VARG_TYPE_SL : ret = sqlite3_bind_int64 (stmt, i+1, arg.sl); break;
case CWEB_VARG_TYPE_SLL : ret = sqlite3_bind_int (stmt, i+1, arg.sll); break;
case CWEB_VARG_TYPE_UC : ret = sqlite3_bind_int (stmt, i+1, arg.uc); break;
case CWEB_VARG_TYPE_US : ret = sqlite3_bind_int (stmt, i+1, arg.us); break;
case CWEB_VARG_TYPE_UI : ret = sqlite3_bind_int64 (stmt, i+1, arg.ui); break;
case CWEB_VARG_TYPE_UL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ul); break;
case CWEB_VARG_TYPE_ULL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ull); break;
case CWEB_VARG_TYPE_F : ret = sqlite3_bind_double(stmt, i+1, arg.f); break;
case CWEB_VARG_TYPE_D : ret = sqlite3_bind_double(stmt, i+1, arg.d); break;
case CWEB_VARG_TYPE_B : ret = sqlite3_bind_int (stmt, i+1, arg.b); break;
case CWEB_VARG_TYPE_STR : ret = sqlite3_bind_text (stmt, i+1, arg.str.ptr, arg.str.len, NULL); break;
case CWEB_VARG_TYPE_HASH: ret = sqlite3_bind_text (stmt, i+1, arg.hash.data, -1, NULL); break;
default:
// TODO
ASSERT(0); // TODO
break;
}
if (ret != SQLITE_OK) {
fprintf(stderr, "Failed to bind paremeter: %s (%s:%d)\n", sqlite3_errmsg(cache->db), __FILE__, __LINE__); // TODO
sqlite3_reset(stmt);
return ret;
}
@@ -652,9 +744,29 @@ static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache,
#endif // CWEB_ENABLE_DATABASE
static void dump_sql(char *fmt, CWEB_VArgs args)
{
printf("SQL :: %s", fmt);
if (args.len > 0) {
printf(" (");
for (int i = 0; i < args.len; i++) {
char mem[128];
StaticOutputBuffer buf = { mem, sizeof(mem), 0 };
value_to_output(&buf, args.ptr[i]);
printf("%.*s", buf.len, buf.dst);
if (i+1 < args.len)
printf(", ");
}
printf(")");
}
printf("\n");
}
int64_t cweb_database_insert_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
{
#ifdef CWEB_ENABLE_DATABASE
if (cweb->trace_sql) dump_sql(fmt, args);
sqlite3_stmt *stmt;
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
if (ret != SQLITE_OK)
@@ -668,6 +780,7 @@ int64_t cweb_database_insert_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
int64_t insert_id = sqlite3_last_insert_rowid(cweb->db);
if (insert_id < 0) {
fprintf(stderr, "Insert ID is invalid: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
sqlite3_reset(stmt);
return -1;
}
@@ -682,6 +795,8 @@ int64_t cweb_database_insert_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
CWEB_QueryResult cweb_database_select_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
{
#ifdef CWEB_ENABLE_DATABASE
if (cweb->trace_sql) dump_sql(fmt, args);
sqlite3_stmt *stmt;
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
if (ret != SQLITE_OK)
@@ -865,6 +980,7 @@ CWEB *cweb_init(CWEB_String addr, uint16_t port)
#ifdef CWEB_ENABLE_DATABASE
cweb->db = NULL;
cweb->dbcache = NULL;
cweb->trace_sql = false;
#endif
cweb->allow_insecure_login = false;
@@ -893,17 +1009,24 @@ void cweb_version(void)
printf("%s\n", sqlite3_libversion());
}
int cweb_enable_database(CWEB *cweb, CWEB_String file)
void cweb_trace_sql(CWEB *cweb, bool enable)
{
#ifdef CWEB_ENABLE_DATABASE
cweb->trace_sql = enable;
#endif
}
int cweb_enable_database(CWEB *cweb, CWEB_String database_file, CWEB_String schema_file)
{
#ifdef CWEB_ENABLE_DATABASE
if (cweb->db != NULL)
return -1; // Already enabled
char file_copy[1<<12];
if (file.len >= SIZEOF(file_copy))
if (database_file.len >= SIZEOF(file_copy))
return -1;
memcpy(file_copy, file.ptr, file.len);
file_copy[file.len] = '\0';
memcpy(file_copy, database_file.ptr, database_file.len);
file_copy[database_file.len] = '\0';
int ret = sqlite3_open(file_copy, &cweb->db);
if (ret != SQLITE_OK) {
@@ -912,7 +1035,7 @@ int cweb_enable_database(CWEB *cweb, CWEB_String file)
return -1;
}
LoadedFile *schema = load_file(file);
LoadedFile *schema = load_file(schema_file);
if (schema == NULL) {
sqlite3_close(cweb->db);
cweb->db = NULL;
@@ -921,6 +1044,7 @@ int cweb_enable_database(CWEB *cweb, CWEB_String file)
ret = sqlite3_exec(cweb->db, schema->data, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
free_loaded_files(schema);
sqlite3_close(cweb->db);
cweb->db = NULL;
return -1;
@@ -998,7 +1122,7 @@ static int set_auth_cookie_if_necessary(CWEB_Request *req)
{
if (req->just_created_session) {
char cookie[1<<9];
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_token=%.*s; Path=/; HttpOnly; Secure", req->sess.len, req->sess.ptr);
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_token=%.*s; Path=/; HttpOnly%s", req->sess.len, req->sess.ptr, req->cweb->allow_insecure_login ? "" : "; Secure");
if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie))
return -500;
http_response_builder_header(req->builder, (HTTP_String) { cookie, cookie_len });
@@ -1020,86 +1144,6 @@ void cweb_respond_basic(CWEB_Request *req, int status, CWEB_String content)
http_response_builder_done(req->builder);
}
typedef struct {
char *dst;
int cap;
int len;
} StaticOutputBuffer;
static void append_to_output(StaticOutputBuffer *out, char *src, int len)
{
int unused = out->cap - out->len;
if (unused > 0)
memcpy(out->dst + out->len, src, MIN(len, unused));
out->len += len;
}
static void append_to_output_u64(StaticOutputBuffer *out, uint64_t n)
{
// TODO
}
static void append_to_output_s64(StaticOutputBuffer *out, int64_t n)
{
// TODO
}
static void append_to_output_f64(StaticOutputBuffer *out, double n)
{
// TODO
}
static void append_to_output_ptr(StaticOutputBuffer *out, void *p)
{
// TODO
}
static void value_to_output(StaticOutputBuffer *out, CWEB_VArg arg)
{
switch (arg.type) {
case CWEB_VARG_TYPE_C : append_to_output(out, &arg.c, 1); break;
case CWEB_VARG_TYPE_S : append_to_output_s64(out, arg.s); break;
case CWEB_VARG_TYPE_I : append_to_output_s64(out, arg.i); break;
case CWEB_VARG_TYPE_L : append_to_output_s64(out, arg.l); break;
case CWEB_VARG_TYPE_LL : append_to_output_s64(out, arg.ll); break;
case CWEB_VARG_TYPE_SC : append_to_output_s64(out, arg.sc); break;
case CWEB_VARG_TYPE_SS : append_to_output_s64(out, arg.ss); break;
case CWEB_VARG_TYPE_SI : append_to_output_s64(out, arg.si); break;
case CWEB_VARG_TYPE_SL : append_to_output_s64(out, arg.sl); break;
case CWEB_VARG_TYPE_SLL : append_to_output_s64(out, arg.sll); break;
case CWEB_VARG_TYPE_UC : append_to_output_u64(out, arg.uc); break;
case CWEB_VARG_TYPE_US : append_to_output_u64(out, arg.us); break;
case CWEB_VARG_TYPE_UI : append_to_output_u64(out, arg.ui); break;
case CWEB_VARG_TYPE_UL : append_to_output_u64(out, arg.ul); break;
case CWEB_VARG_TYPE_ULL : append_to_output_u64(out, arg.ull); break;
case CWEB_VARG_TYPE_F : append_to_output_f64(out, arg.f); break;
case CWEB_VARG_TYPE_D : append_to_output_u64(out, arg.d); break;
case CWEB_VARG_TYPE_B : append_to_output(out, arg.b ? "true" : "false", arg.b ? 4: 5);break;
case CWEB_VARG_TYPE_STR : append_to_output(out, arg.str.ptr, arg.str.len); break;
case CWEB_VARG_TYPE_HASH : append_to_output(out, arg.hash.data, strlen(arg.hash.data)); break;
case CWEB_VARG_TYPE_PC : append_to_output_ptr(out, arg.pc); break;
case CWEB_VARG_TYPE_PS : append_to_output_ptr(out, arg.ps); break;
case CWEB_VARG_TYPE_PI : append_to_output_ptr(out, arg.pi); break;
case CWEB_VARG_TYPE_PL : append_to_output_ptr(out, arg.pl); break;
case CWEB_VARG_TYPE_PLL : append_to_output_ptr(out, arg.pll); break;
case CWEB_VARG_TYPE_PSC : append_to_output_ptr(out, arg.psc); break;
case CWEB_VARG_TYPE_PSS : append_to_output_ptr(out, arg.pss); break;
case CWEB_VARG_TYPE_PSI : append_to_output_ptr(out, arg.psi); break;
case CWEB_VARG_TYPE_PSL : append_to_output_ptr(out, arg.psl); break;
case CWEB_VARG_TYPE_PSLL : append_to_output_ptr(out, arg.psll); break;
case CWEB_VARG_TYPE_PUC : append_to_output_ptr(out, arg.puc); break;
case CWEB_VARG_TYPE_PUS : append_to_output_ptr(out, arg.pus); break;
case CWEB_VARG_TYPE_PUI : append_to_output_ptr(out, arg.pui); break;
case CWEB_VARG_TYPE_PUL : append_to_output_ptr(out, arg.pul); break;
case CWEB_VARG_TYPE_PULL : append_to_output_ptr(out, arg.pull); break;
case CWEB_VARG_TYPE_PF : append_to_output_ptr(out, arg.pf); break;
case CWEB_VARG_TYPE_PD : append_to_output_ptr(out, arg.pd); break;
case CWEB_VARG_TYPE_PB : append_to_output_ptr(out, arg.pb); break;
case CWEB_VARG_TYPE_PSTR : append_to_output_ptr(out, arg.pstr); break;
case CWEB_VARG_TYPE_PHASH: append_to_output_ptr(out, arg.phash); break;
}
}
static void evaluate_format(StaticOutputBuffer *out, CWEB_String format, CWEB_VArgs args)
{
char *src = format.ptr;
@@ -1279,7 +1323,7 @@ static int compile(WL_String path, WL_Program *program, WL_Arena *arena)
loaded_file_tail = &loaded_file->next;
WL_String content = { loaded_file->data, loaded_file->len };
WL_AddResult result = wl_compiler_add(compiler, content);
WL_AddResult result = wl_compiler_add(compiler, path, content);
if (result.type == WL_ADD_ERROR) {
TRACE("Compilation failed (%s)", wl_compiler_error(compiler).ptr);
@@ -1307,6 +1351,11 @@ static int compile(WL_String path, WL_Program *program, WL_Arena *arena)
static int query_routine(WL_Runtime *rt, SQLiteCache *dbcache)
{
if (dbcache == NULL) {
wl_push_none(rt); // Allow not pushing anything on the WL machine
return -1;
}
int num_args = wl_arg_count(rt);
if (num_args == 0)
return 0;
+8 -3
View File
@@ -222,7 +222,12 @@ void cweb_global_free(void);
CWEB *cweb_init(CWEB_String addr, uint16_t port);
void cweb_free(CWEB *cweb);
int cweb_enable_database(CWEB *cweb, CWEB_String file);
// Open an SQLite instance in file "database_file" and run the DDL script at "schema_file".
// Note that "database_file" may be ":memory:".
int cweb_enable_database(CWEB *cweb, CWEB_String database_file, CWEB_String schema_file);
// Log all evaluated SQL statements to stdout
void cweb_trace_sql(CWEB *cweb, bool enable);
// Pause execution until a request is available.
// TODO: When does this function return NULL?
@@ -293,7 +298,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args);
void cweb_free_query_result(CWEB_QueryResult *res);
// Calculates the bcrypt hash of the specified password
int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *hash);
int cweb_hash_password(CWEB_String pass, int cost, CWEB_PasswordHash *hash);
// Checks whether the password matches the given hash
int cweb_check_password(char *pass, int passlen, CWEB_PasswordHash hash);
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash);