General fixes for cWEB and updated WL
This commit is contained in:
@@ -761,14 +761,19 @@ static Node *parse_html(Parser *p)
|
|||||||
|
|
||||||
bool no_body = false;
|
bool no_body = false;
|
||||||
Scanner *s = &p->s;
|
Scanner *s = &p->s;
|
||||||
for (;;) {
|
for (int quote = 0;;) {
|
||||||
|
|
||||||
int off = s->cur;
|
int off = s->cur;
|
||||||
|
|
||||||
bool quotes = false;
|
while (s->cur < s->len && s->src[s->cur] != '\\' && (quote || (s->src[s->cur] != '/' && s->src[s->cur] != '>'))) {
|
||||||
while (s->cur < s->len && s->src[s->cur] != '\\' && (quotes || (s->src[s->cur] != '/' && s->src[s->cur] != '>'))) {
|
if (quote) {
|
||||||
if (s->src[s->cur] == '"')
|
if (quote == s->src[s->cur])
|
||||||
quotes = !quotes;
|
quote = 0;
|
||||||
|
} else {
|
||||||
|
char c = s->src[s->cur];
|
||||||
|
if (c == '"' || c == '\'')
|
||||||
|
quote = c;
|
||||||
|
}
|
||||||
s->cur++;
|
s->cur++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define WL_STR(X) ((WL_String) { (X), (int) sizeof(X)-1 })
|
||||||
|
|
||||||
typedef struct WL_Runtime WL_Runtime;
|
typedef struct WL_Runtime WL_Runtime;
|
||||||
typedef struct WL_Compiler WL_Compiler;
|
typedef struct WL_Compiler WL_Compiler;
|
||||||
|
|
||||||
@@ -45,17 +47,90 @@ typedef struct {
|
|||||||
WL_String str;
|
WL_String str;
|
||||||
} WL_EvalResult;
|
} WL_EvalResult;
|
||||||
|
|
||||||
WL_Compiler* wl_compiler_init (WL_Arena *arena);
|
// Creates a compilation unit for a program
|
||||||
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String path, WL_String content);
|
// The provided arena (which can't be NULL) is
|
||||||
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
// used for all memory allocations until a
|
||||||
WL_String wl_compiler_error (WL_Compiler *compiler);
|
// program is produced or an error occurs.
|
||||||
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
// If not enough memory is provided, NULL is
|
||||||
void wl_dump_program (WL_Program program);
|
// returned.
|
||||||
|
WL_Compiler *wl_compiler_init(WL_Arena *arena);
|
||||||
|
|
||||||
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
// Adds a file to the current compilation unit
|
||||||
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
// and returns
|
||||||
WL_String wl_runtime_error (WL_Runtime *rt);
|
//
|
||||||
void wl_runtime_dump (WL_Runtime *rt);
|
// WL_ADD_ERROR if an error occurred
|
||||||
|
//
|
||||||
|
// WL_ADD_AGAIN if the file included a different
|
||||||
|
// file that also needs to be added to the unit,
|
||||||
|
// in which case the file name is in the "path"
|
||||||
|
// field of the return value.
|
||||||
|
//
|
||||||
|
// WL_ADD_LINK all sources were processed and
|
||||||
|
// the unit is ready for linking
|
||||||
|
//
|
||||||
|
// Note that the source of all files needs to
|
||||||
|
// stay alive until the program is linked as the
|
||||||
|
// compiler may keep references to it until then.
|
||||||
|
WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String path, WL_String content);
|
||||||
|
|
||||||
|
// Links a compilation unit producing an executable.
|
||||||
|
// The output program is just an array of bytes
|
||||||
|
// allocated using the compiler's arena memory and
|
||||||
|
// may be written to disk or any other external system
|
||||||
|
// for caching.
|
||||||
|
//
|
||||||
|
// On error, -1 is returned and the error text can
|
||||||
|
// be retrieved using wl_compiler_error. On success,
|
||||||
|
// 0 is returned.
|
||||||
|
int wl_compiler_link(WL_Compiler *compiler, WL_Program *program);
|
||||||
|
|
||||||
|
// Returns the null-terminated error string for a
|
||||||
|
// compilation unit that failed.
|
||||||
|
WL_String wl_compiler_error(WL_Compiler *compiler);
|
||||||
|
|
||||||
|
// Serializes the AST of the source parsed in this
|
||||||
|
// compilation unit by writing the string to the
|
||||||
|
// rovided buffer and returning the number of bytes
|
||||||
|
// written to it.
|
||||||
|
//
|
||||||
|
// If the provided buffer is too small, the function
|
||||||
|
// fills it up and returns the number of bytes that
|
||||||
|
// would have been written if the buffer was large
|
||||||
|
// enough. On error, -1 is returned.
|
||||||
|
int wl_dump_ast(WL_Compiler *compiler, char *dst, int cap);
|
||||||
|
|
||||||
|
// Writes the bytecode of a program to stdout as a
|
||||||
|
// human-readable string.
|
||||||
|
void wl_dump_program(WL_Program program);
|
||||||
|
|
||||||
|
// Creates an evaluation context for a bytecode program
|
||||||
|
// All memory used while running the program will be
|
||||||
|
// allocated from the provided arena.
|
||||||
|
//
|
||||||
|
// If not enough memory was provided or the program is
|
||||||
|
// invalid, NULL is returned.
|
||||||
|
WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program);
|
||||||
|
|
||||||
|
// Run the program associated to this runtime until an
|
||||||
|
// event happens. The event may be one of:
|
||||||
|
//
|
||||||
|
// WL_EVAL_DONE if execution is complete
|
||||||
|
//
|
||||||
|
// WL_EVAL_ERROR if execution failed
|
||||||
|
//
|
||||||
|
// WL_EVAL_OUTPUT if data is available for output,
|
||||||
|
// in which case the field "str" points to it
|
||||||
|
//
|
||||||
|
// WL_EVAL_SYSVAR if the program requested the value
|
||||||
|
// of an external symbol.
|
||||||
|
//
|
||||||
|
// WL_EVAL_SYSCALL
|
||||||
|
//
|
||||||
|
WL_EvalResult wl_runtime_eval(WL_Runtime *rt);
|
||||||
|
|
||||||
|
WL_String wl_runtime_error(WL_Runtime *rt);
|
||||||
|
|
||||||
|
void wl_runtime_dump(WL_Runtime *rt);
|
||||||
|
|
||||||
bool wl_streq (WL_String a, char *b, int blen);
|
bool wl_streq (WL_String a, char *b, int blen);
|
||||||
int wl_arg_count (WL_Runtime *rt);
|
int wl_arg_count (WL_Runtime *rt);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ifeq ($(OS),Windows_NT)
|
|||||||
FLAGS += -lws2_32 -lbcrypt
|
FLAGS += -lws2_32 -lbcrypt
|
||||||
else
|
else
|
||||||
EXT = .out
|
EXT = .out
|
||||||
FLAGS += -lssl -lcrypto -DHTTPS_ENABLED
|
FLAGS += -lssl -lcrypto -DHTTPS_ENABLED -fsanitize=address,undefined
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: cweb.h cozisnews$(EXT)
|
all: cweb.h cozisnews$(EXT)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
Copyright © 2025 Francesco Cozzuto
|
Copyright © 2025 Francesco Cozzuto
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
copy of this software and associated documentation files (the “Software”),
|
copy of this software and associated documentation files (the “Software”),
|
||||||
to deal in the Software without restriction, including without limitation
|
to deal in the Software without restriction, including without limitation
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
@@ -11,7 +11,7 @@ Software is furnished to do so, subject to the following conditions:
|
|||||||
The above copyright notice and this permission notice shall be included
|
The above copyright notice and this permission notice shall be included
|
||||||
in all copies or substantial portions of the Software.
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
@@ -35,6 +35,8 @@ OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
#define CWEB_STR(X) ((CWEB_String) { (X), (int) sizeof(X)-1 })
|
#define CWEB_STR(X) ((CWEB_String) { (X), (int) sizeof(X)-1 })
|
||||||
|
|
||||||
|
#define CWEB_TRACE(X, ...) fprintf(stderr, ("TRACE: " X "\n"), ##__VA_ARGS__);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
@@ -6486,6 +6488,8 @@ int http_serve(char *addr, int port, HTTP_Router *router)
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define WL_STR(X) ((WL_String) { (X), (int) sizeof(X)-1 })
|
||||||
|
|
||||||
typedef struct WL_Runtime WL_Runtime;
|
typedef struct WL_Runtime WL_Runtime;
|
||||||
typedef struct WL_Compiler WL_Compiler;
|
typedef struct WL_Compiler WL_Compiler;
|
||||||
|
|
||||||
@@ -6530,17 +6534,90 @@ typedef struct {
|
|||||||
WL_String str;
|
WL_String str;
|
||||||
} WL_EvalResult;
|
} WL_EvalResult;
|
||||||
|
|
||||||
WL_Compiler* wl_compiler_init (WL_Arena *arena);
|
// Creates a compilation unit for a program
|
||||||
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String path, WL_String content);
|
// The provided arena (which can't be NULL) is
|
||||||
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
// used for all memory allocations until a
|
||||||
WL_String wl_compiler_error (WL_Compiler *compiler);
|
// program is produced or an error occurs.
|
||||||
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
// If not enough memory is provided, NULL is
|
||||||
void wl_dump_program (WL_Program program);
|
// returned.
|
||||||
|
WL_Compiler *wl_compiler_init(WL_Arena *arena);
|
||||||
|
|
||||||
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
// Adds a file to the current compilation unit
|
||||||
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
// and returns
|
||||||
WL_String wl_runtime_error (WL_Runtime *rt);
|
//
|
||||||
void wl_runtime_dump (WL_Runtime *rt);
|
// WL_ADD_ERROR if an error occurred
|
||||||
|
//
|
||||||
|
// WL_ADD_AGAIN if the file included a different
|
||||||
|
// file that also needs to be added to the unit,
|
||||||
|
// in which case the file name is in the "path"
|
||||||
|
// field of the return value.
|
||||||
|
//
|
||||||
|
// WL_ADD_LINK all sources were processed and
|
||||||
|
// the unit is ready for linking
|
||||||
|
//
|
||||||
|
// Note that the source of all files needs to
|
||||||
|
// stay alive until the program is linked as the
|
||||||
|
// compiler may keep references to it until then.
|
||||||
|
WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String path, WL_String content);
|
||||||
|
|
||||||
|
// Links a compilation unit producing an executable.
|
||||||
|
// The output program is just an array of bytes
|
||||||
|
// allocated using the compiler's arena memory and
|
||||||
|
// may be written to disk or any other external system
|
||||||
|
// for caching.
|
||||||
|
//
|
||||||
|
// On error, -1 is returned and the error text can
|
||||||
|
// be retrieved using wl_compiler_error. On success,
|
||||||
|
// 0 is returned.
|
||||||
|
int wl_compiler_link(WL_Compiler *compiler, WL_Program *program);
|
||||||
|
|
||||||
|
// Returns the null-terminated error string for a
|
||||||
|
// compilation unit that failed.
|
||||||
|
WL_String wl_compiler_error(WL_Compiler *compiler);
|
||||||
|
|
||||||
|
// Serializes the AST of the source parsed in this
|
||||||
|
// compilation unit by writing the string to the
|
||||||
|
// rovided buffer and returning the number of bytes
|
||||||
|
// written to it.
|
||||||
|
//
|
||||||
|
// If the provided buffer is too small, the function
|
||||||
|
// fills it up and returns the number of bytes that
|
||||||
|
// would have been written if the buffer was large
|
||||||
|
// enough. On error, -1 is returned.
|
||||||
|
int wl_dump_ast(WL_Compiler *compiler, char *dst, int cap);
|
||||||
|
|
||||||
|
// Writes the bytecode of a program to stdout as a
|
||||||
|
// human-readable string.
|
||||||
|
void wl_dump_program(WL_Program program);
|
||||||
|
|
||||||
|
// Creates an evaluation context for a bytecode program
|
||||||
|
// All memory used while running the program will be
|
||||||
|
// allocated from the provided arena.
|
||||||
|
//
|
||||||
|
// If not enough memory was provided or the program is
|
||||||
|
// invalid, NULL is returned.
|
||||||
|
WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program);
|
||||||
|
|
||||||
|
// Run the program associated to this runtime until an
|
||||||
|
// event happens. The event may be one of:
|
||||||
|
//
|
||||||
|
// WL_EVAL_DONE if execution is complete
|
||||||
|
//
|
||||||
|
// WL_EVAL_ERROR if execution failed
|
||||||
|
//
|
||||||
|
// WL_EVAL_OUTPUT if data is available for output,
|
||||||
|
// in which case the field "str" points to it
|
||||||
|
//
|
||||||
|
// WL_EVAL_SYSVAR if the program requested the value
|
||||||
|
// of an external symbol.
|
||||||
|
//
|
||||||
|
// WL_EVAL_SYSCALL
|
||||||
|
//
|
||||||
|
WL_EvalResult wl_runtime_eval(WL_Runtime *rt);
|
||||||
|
|
||||||
|
WL_String wl_runtime_error(WL_Runtime *rt);
|
||||||
|
|
||||||
|
void wl_runtime_dump(WL_Runtime *rt);
|
||||||
|
|
||||||
bool wl_streq (WL_String a, char *b, int blen);
|
bool wl_streq (WL_String a, char *b, int blen);
|
||||||
int wl_arg_count (WL_Runtime *rt);
|
int wl_arg_count (WL_Runtime *rt);
|
||||||
@@ -7342,14 +7419,19 @@ static Node *parse_html(Parser *p)
|
|||||||
|
|
||||||
bool no_body = false;
|
bool no_body = false;
|
||||||
Scanner *s = &p->s;
|
Scanner *s = &p->s;
|
||||||
for (;;) {
|
for (int quote = 0;;) {
|
||||||
|
|
||||||
int off = s->cur;
|
int off = s->cur;
|
||||||
|
|
||||||
bool quotes = false;
|
while (s->cur < s->len && s->src[s->cur] != '\\' && (quote || (s->src[s->cur] != '/' && s->src[s->cur] != '>'))) {
|
||||||
while (s->cur < s->len && s->src[s->cur] != '\\' && (quotes || (s->src[s->cur] != '/' && s->src[s->cur] != '>'))) {
|
if (quote) {
|
||||||
if (s->src[s->cur] == '"')
|
if (quote == s->src[s->cur])
|
||||||
quotes = !quotes;
|
quote = 0;
|
||||||
|
} else {
|
||||||
|
char c = s->src[s->cur];
|
||||||
|
if (c == '"' || c == '\'')
|
||||||
|
quote = c;
|
||||||
|
}
|
||||||
s->cur++;
|
s->cur++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13534,6 +13616,49 @@ CWEB_String cweb_trim(CWEB_String s)
|
|||||||
return (CWEB_String) { s2.ptr, s2.len };
|
return (CWEB_String) { s2.ptr, s2.len };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *type_names__[] = {
|
||||||
|
[CWEB_VARG_TYPE_C] = "char",
|
||||||
|
[CWEB_VARG_TYPE_S] = "short",
|
||||||
|
[CWEB_VARG_TYPE_I] = "int",
|
||||||
|
[CWEB_VARG_TYPE_L] = "long",
|
||||||
|
[CWEB_VARG_TYPE_LL] = "long long",
|
||||||
|
[CWEB_VARG_TYPE_SC] = "signed char",
|
||||||
|
[CWEB_VARG_TYPE_SS] = "signed short",
|
||||||
|
[CWEB_VARG_TYPE_SI] = "signed int",
|
||||||
|
[CWEB_VARG_TYPE_SL] = "signed long",
|
||||||
|
[CWEB_VARG_TYPE_SLL] = "signed long long",
|
||||||
|
[CWEB_VARG_TYPE_UC] = "unsigned char",
|
||||||
|
[CWEB_VARG_TYPE_US] = "unsigned short",
|
||||||
|
[CWEB_VARG_TYPE_UI] = "unsigned int",
|
||||||
|
[CWEB_VARG_TYPE_UL] = "unsigned long",
|
||||||
|
[CWEB_VARG_TYPE_ULL] = "unsigned long long",
|
||||||
|
[CWEB_VARG_TYPE_F] = "float",
|
||||||
|
[CWEB_VARG_TYPE_D] = "double",
|
||||||
|
[CWEB_VARG_TYPE_B] = "bool",
|
||||||
|
[CWEB_VARG_TYPE_STR] = "string",
|
||||||
|
[CWEB_VARG_TYPE_HASH] = "hash",
|
||||||
|
[CWEB_VARG_TYPE_PC] = "char*",
|
||||||
|
[CWEB_VARG_TYPE_PS] = "short*",
|
||||||
|
[CWEB_VARG_TYPE_PI] = "int*",
|
||||||
|
[CWEB_VARG_TYPE_PL] = "long*",
|
||||||
|
[CWEB_VARG_TYPE_PLL] = "long long*",
|
||||||
|
[CWEB_VARG_TYPE_PSC] = "signed char*",
|
||||||
|
[CWEB_VARG_TYPE_PSS] = "signed short*",
|
||||||
|
[CWEB_VARG_TYPE_PSI] = "signed int*",
|
||||||
|
[CWEB_VARG_TYPE_PSL] = "signed long*",
|
||||||
|
[CWEB_VARG_TYPE_PSLL] = "signed long long*",
|
||||||
|
[CWEB_VARG_TYPE_PUC] = "unsigned char*",
|
||||||
|
[CWEB_VARG_TYPE_PUS] = "unsigned short*",
|
||||||
|
[CWEB_VARG_TYPE_PUI] = "unsigned int*",
|
||||||
|
[CWEB_VARG_TYPE_PUL] = "unsigned long*",
|
||||||
|
[CWEB_VARG_TYPE_PULL] = "unsigned long long*",
|
||||||
|
[CWEB_VARG_TYPE_PF] = "float*",
|
||||||
|
[CWEB_VARG_TYPE_PD] = "double*",
|
||||||
|
[CWEB_VARG_TYPE_PB] = "bool*",
|
||||||
|
[CWEB_VARG_TYPE_PSTR] = "string*",
|
||||||
|
[CWEB_VARG_TYPE_PHASH] = "hash*",
|
||||||
|
};
|
||||||
|
|
||||||
CWEB_VArg cweb_varg_from_c (char c) { return (CWEB_VArg) { CWEB_VARG_TYPE_C, .c=c }; }
|
CWEB_VArg cweb_varg_from_c (char c) { return (CWEB_VArg) { CWEB_VARG_TYPE_C, .c=c }; }
|
||||||
CWEB_VArg cweb_varg_from_s (short s) { return (CWEB_VArg) { CWEB_VARG_TYPE_S, .s=s }; }
|
CWEB_VArg cweb_varg_from_s (short s) { return (CWEB_VArg) { CWEB_VARG_TYPE_S, .s=s }; }
|
||||||
CWEB_VArg cweb_varg_from_i (int i) { return (CWEB_VArg) { CWEB_VARG_TYPE_I, .i=i }; }
|
CWEB_VArg cweb_varg_from_i (int i) { return (CWEB_VArg) { CWEB_VARG_TYPE_I, .i=i }; }
|
||||||
@@ -13575,7 +13700,6 @@ 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_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 }; }
|
CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash) { return (CWEB_VArg) { CWEB_VARG_TYPE_PHASH, .phash=phash }; }
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *dst;
|
char *dst;
|
||||||
int cap;
|
int cap;
|
||||||
@@ -13728,7 +13852,7 @@ static void free_loaded_files(LoadedFile *loaded_file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// RANDOM
|
// RANDOM
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -13783,14 +13907,18 @@ int cweb_hash_password(CWEB_String pass, int cost, CWEB_PasswordHash *hash)
|
|||||||
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash)
|
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash)
|
||||||
{
|
{
|
||||||
char passzt[128];
|
char passzt[128];
|
||||||
if (pass.len >= (int) sizeof(passzt))
|
if (pass.len >= (int) sizeof(passzt)) {
|
||||||
|
CWEB_TRACE("%s: static buffer limit reached", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
memcpy(passzt, pass.ptr, pass.len);
|
memcpy(passzt, pass.ptr, pass.len);
|
||||||
passzt[pass.len] = '\0';
|
passzt[pass.len] = '\0';
|
||||||
|
|
||||||
CWEB_PasswordHash new_hash;
|
CWEB_PasswordHash new_hash;
|
||||||
if (_crypt_blowfish_rn(passzt, hash.data, new_hash.data, sizeof(new_hash.data)) == NULL)
|
if (_crypt_blowfish_rn(passzt, hash.data, new_hash.data, sizeof(new_hash.data)) == NULL) {
|
||||||
|
CWEB_TRACE("%s: couldn't calculate hash (password=[%.*s], hash=[%.*s])", __func__, pass.len, pass.ptr, (int) strnlen(hash.data, sizeof(hash.data)), hash.data);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(hash.data, new_hash.data)) // TODO: should be constant-time
|
if (strcmp(hash.data, new_hash.data)) // TODO: should be constant-time
|
||||||
return 1;
|
return 1;
|
||||||
@@ -13863,7 +13991,7 @@ static int pack_token(char *src, int srclen, char *dst, int dstlen)
|
|||||||
int low = src[i+1];
|
int low = src[i+1];
|
||||||
if (!is_hex_digit(high) || !is_hex_digit(low))
|
if (!is_hex_digit(high) || !is_hex_digit(low))
|
||||||
return -1;
|
return -1;
|
||||||
dst[i] = (hex_digit_to_int(high) << 4) | (hex_digit_to_int(low) << 0);
|
dst[i >> 1] = (hex_digit_to_int(high) << 4) | (hex_digit_to_int(low) << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -14181,11 +14309,14 @@ int64_t cweb_database_insert_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
|
|||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
||||||
if (ret != SQLITE_OK)
|
if (ret != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "sqlite3 prepare+bind error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ret = sqlite3_step(stmt);
|
ret = sqlite3_step(stmt);
|
||||||
if (ret != SQLITE_DONE) {
|
if (ret != SQLITE_DONE) {
|
||||||
|
fprintf(stderr, "sqlite3_step error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -14211,8 +14342,10 @@ CWEB_QueryResult cweb_database_select_impl(CWEB *cweb, char *fmt, CWEB_VArgs arg
|
|||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
||||||
if (ret != SQLITE_OK)
|
if (ret != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "sqlite3 prepare+bind error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
return (CWEB_QueryResult) { NULL };
|
return (CWEB_QueryResult) { NULL };
|
||||||
|
}
|
||||||
|
|
||||||
return (CWEB_QueryResult) { stmt };
|
return (CWEB_QueryResult) { stmt };
|
||||||
#else
|
#else
|
||||||
@@ -14223,21 +14356,27 @@ CWEB_QueryResult cweb_database_select_impl(CWEB *cweb, char *fmt, CWEB_VArgs arg
|
|||||||
int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
||||||
{
|
{
|
||||||
#ifdef CWEB_ENABLE_DATABASE
|
#ifdef CWEB_ENABLE_DATABASE
|
||||||
if (res->handle == NULL)
|
if (res->handle == NULL) {
|
||||||
|
CWEB_TRACE("%s failed because database is initialized", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int ret = sqlite3_step(res->handle);
|
int ret = sqlite3_step(res->handle);
|
||||||
|
|
||||||
if (ret == SQLITE_DONE)
|
if (ret == SQLITE_DONE) {
|
||||||
|
CWEB_TRACE("%s returned no row", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret != SQLITE_ROW) {
|
if (ret != SQLITE_ROW) {
|
||||||
|
CWEB_TRACE("%s didn't return ROW or DONE (%d)", __func__, ret);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sqlite3_column_count(res->handle) != args.len) {
|
if (sqlite3_column_count(res->handle) != args.len) {
|
||||||
|
CWEB_TRACE("%s returned an unexpected column count", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -14250,6 +14389,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
{
|
{
|
||||||
int64_t x = sqlite3_column_int64(res->handle, i);
|
int64_t x = sqlite3_column_int64(res->handle, i);
|
||||||
if (x < INT_MIN || x > INT_MAX) {
|
if (x < INT_MIN || x > INT_MAX) {
|
||||||
|
CWEB_TRACE("%s couldn't bind integer out of range", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -14262,6 +14402,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
{
|
{
|
||||||
int64_t x = sqlite3_column_int64(res->handle, i);
|
int64_t x = sqlite3_column_int64(res->handle, i);
|
||||||
if (x < LONG_MIN || x > LONG_MAX) {
|
if (x < LONG_MIN || x > LONG_MAX) {
|
||||||
|
CWEB_TRACE("%s couldn't bind integer out of range", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -14300,7 +14441,27 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CWEB_VARG_TYPE_PHASH:
|
||||||
|
{
|
||||||
|
char *ptr = sqlite3_column_text(res->handle, i);
|
||||||
|
int len = sqlite3_column_bytes(res->handle, i);
|
||||||
|
|
||||||
|
CWEB_TRACE("%s: hash value (\"%.*s\", %d)", __func__, len, ptr, len);
|
||||||
|
|
||||||
|
CWEB_PasswordHash *hash = args.ptr[i].phash;
|
||||||
|
if ((int) sizeof(hash->data) <= len) {
|
||||||
|
CWEB_TRACE("%s couldn't bind to hash argument", __func__);
|
||||||
|
sqlite3_reset(res->handle);
|
||||||
|
res->handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(hash->data, 0, sizeof(hash->data));
|
||||||
|
memcpy(hash->data, ptr, len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
CWEB_TRACE("%s couldn't bind to unexpected argument type %s", __func__, type_names__[args.ptr[i].type]);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -14311,6 +14472,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
#else
|
#else
|
||||||
(void) res;
|
(void) res;
|
||||||
(void) args;
|
(void) args;
|
||||||
|
CWEB_TRACE("%s is unsupported", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -55,20 +55,24 @@ static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass)
|
|||||||
CWEB_PasswordHash hash;
|
CWEB_PasswordHash hash;
|
||||||
int ret = cweb_next_query_row(&res, &user_id, &hash);
|
int ret = cweb_next_query_row(&res, &user_id, &hash);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
printf("here %s:%d\n", __FILE__, __LINE__); // TODO: remove
|
||||||
cweb_free_query_result(&res);
|
cweb_free_query_result(&res);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
printf("here %s:%d\n", __FILE__, __LINE__); // TODO: remove
|
||||||
cweb_free_query_result(&res);
|
cweb_free_query_result(&res);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = cweb_check_password(pass, hash);
|
ret = cweb_check_password(pass, hash);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
printf("here %s:%d\n", __FILE__, __LINE__); // TODO: remove
|
||||||
cweb_free_query_result(&res);
|
cweb_free_query_result(&res);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
|
printf("here %s:%d\n", __FILE__, __LINE__); // TODO: remove
|
||||||
cweb_free_query_result(&res);
|
cweb_free_query_result(&res);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -329,7 +333,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
CWEB_String addr = CWEB_STR("127.0.0.1");
|
CWEB_String addr = CWEB_STR("127.0.0.1");
|
||||||
uint16_t port = 8080;
|
uint16_t port = 8080;
|
||||||
CWEB_String database_file = CWEB_STR("demo.db");
|
CWEB_String database_file = CWEB_STR(":memory:");
|
||||||
CWEB_String schema_file = CWEB_STR("demo/schema.sql");
|
CWEB_String schema_file = CWEB_STR("demo/schema.sql");
|
||||||
|
|
||||||
if (cweb_global_init() < 0)
|
if (cweb_global_init() < 0)
|
||||||
|
|||||||
+4
-3
@@ -42,8 +42,9 @@ let style =
|
|||||||
let main =
|
let main =
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
\if len(posts) == 0:
|
\if len(posts) == 0: {
|
||||||
<div id="no-posts">There are no posts yet!</div>
|
<div id="no-posts">There are no posts yet!</div>
|
||||||
|
}
|
||||||
|
|
||||||
\for post in posts: {
|
\for post in posts: {
|
||||||
|
|
||||||
@@ -55,10 +56,10 @@ let main =
|
|||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div>
|
<div>
|
||||||
<a href=\'"'\link\'"'>\escape(post.title)</a>
|
<a href="\{link}">\{escape post.title}</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>\escape(post.date)</span> | <span>\escape(post.num_comments) comments</span>
|
<span>\{escape post.date}</span> | <span>\{escape post.num_comments} comments</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
procedure page(title, login_user_id, style, main)
|
procedure page(title, login_user_id, style, main) {
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>CN - \escape(title)</title>
|
<title>CN - \{escape title}</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
line-height: 200%;
|
line-height: 200%;
|
||||||
@@ -42,7 +42,7 @@ procedure page(title, login_user_id, style, main)
|
|||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
\(style)
|
\{style}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/htmx/1.9.2/htmx.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/htmx/1.9.2/htmx.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -67,9 +67,10 @@ procedure page(title, login_user_id, style, main)
|
|||||||
<a href="/api/logout">log-out</a>
|
<a href="/api/logout">log-out</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
\main
|
\{main}
|
||||||
<footer>
|
<footer>
|
||||||
Made with love by cozis
|
Made with love by cozis
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
}
|
||||||
+19
-19
@@ -180,13 +180,13 @@ let main =
|
|||||||
<main>
|
<main>
|
||||||
<div class="thread-header">
|
<div class="thread-header">
|
||||||
<div class="thread-title">
|
<div class="thread-title">
|
||||||
<span>\escape(post.title)</span>
|
<span>\{escape post.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="thread-meta">
|
<div class="thread-meta">
|
||||||
submitted 3 hours ago by <a href="">\escape(post.username)</a> | <a href="">\len(comments)</a>
|
submitted 3 hours ago by <a href="">\{escape post.username}</a> | <a href="">\{len comments}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="thread-text">
|
<div class="thread-text">
|
||||||
<pre>\escape(post.content)</pre>
|
<pre>\{escape post.content}</pre>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
@@ -194,16 +194,23 @@ let main =
|
|||||||
</summary>
|
</summary>
|
||||||
<div class="add-comment">
|
<div class="add-comment">
|
||||||
<form action="/api/comment" method="POST">
|
<form action="/api/comment" method="POST">
|
||||||
<input type="hidden" name="csrf" value=\'"'\$csrf\'"' />
|
<input type="hidden" name="csrf" value="\{$csrf}" />
|
||||||
<input type="hidden" name="parent_post" value=\'"'\post_id\'"' />
|
<input type="hidden" name="parent_post" value="\{post_id}" />
|
||||||
<textarea name="content" placeholder="Add a comment..."></textarea>
|
<textarea name="content" placeholder="Add a comment..."></textarea>
|
||||||
<input type="submit" vaue="Publish" />
|
<input type="submit" vaue="Publish" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
\if len(root_comments) == 0:
|
||||||
|
<div id="no-comments">
|
||||||
|
No comments
|
||||||
|
</div>
|
||||||
|
else for comment in root_comments:
|
||||||
|
render_comment(post_id, comment)
|
||||||
|
</main>
|
||||||
|
|
||||||
\procedure render_comment(post_id, comment)
|
procedure render_comment(post_id, comment) {
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
<div class="vote-buttons">
|
<div class="vote-buttons">
|
||||||
<a href="">▲</a>
|
<a href="">▲</a>
|
||||||
@@ -211,10 +218,10 @@ let main =
|
|||||||
</div>
|
</div>
|
||||||
<div class="comment-content">
|
<div class="comment-content">
|
||||||
<div class="comment-meta">
|
<div class="comment-meta">
|
||||||
<a href="">\escape(comment.username)</a> 2 hours ago
|
<a href="">\{escape comment.username}</a> 2 hours ago
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-text">
|
<div class="comment-text">
|
||||||
<pre>\escape(comment.content)</pre>
|
<pre>\{escape comment.content}</pre>
|
||||||
</div>
|
</div>
|
||||||
\if $login_user_id != none:
|
\if $login_user_id != none:
|
||||||
<details>
|
<details>
|
||||||
@@ -223,9 +230,9 @@ let main =
|
|||||||
</summary>
|
</summary>
|
||||||
<div class="add-comment">
|
<div class="add-comment">
|
||||||
<form action="/api/comment" method="POST">
|
<form action="/api/comment" method="POST">
|
||||||
<input type="hidden" name="csrf" value=\'"'\$csrf\'"' />
|
<input type="hidden" name="csrf" value="\{$csrf}" />
|
||||||
<input type="hidden" name="parent_post" value=\'"'\post_id\'"' />
|
<input type="hidden" name="parent_post" value="\{post_id}" />
|
||||||
<input type="hidden" name="parent_comment" value=\'"'\comment.id\'"' />
|
<input type="hidden" name="parent_comment" value="\{comment.id}" />
|
||||||
<textarea name="content" placeholder="Add a comment..."></textarea>
|
<textarea name="content" placeholder="Add a comment..."></textarea>
|
||||||
<input type="submit" vaue="Publish" />
|
<input type="submit" vaue="Publish" />
|
||||||
</form>
|
</form>
|
||||||
@@ -237,13 +244,6 @@ let main =
|
|||||||
render_comment(post_id, child)
|
render_comment(post_id, child)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
\if len(root_comments) == 0:
|
|
||||||
<div id="no-comments">
|
|
||||||
No comments
|
|
||||||
</div>
|
|
||||||
else for comment in root_comments:
|
|
||||||
render_comment(post_id, comment)
|
|
||||||
</main>
|
|
||||||
|
|
||||||
page(post.title, $login_user_id, style, main)
|
page(post.title, $login_user_id, style, main)
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
||||||
let lookup = {}
|
|
||||||
|
|
||||||
for comment in comments: {
|
|
||||||
comment.child = []
|
|
||||||
lookup[comment.id] = comment
|
|
||||||
}
|
|
||||||
|
|
||||||
let roots = []
|
|
||||||
|
|
||||||
for comment in comments: {
|
|
||||||
if comment.parent_comment == none:
|
|
||||||
roots << comment
|
|
||||||
else
|
|
||||||
lookup[comment.parent_comment].child << comment
|
|
||||||
}
|
|
||||||
|
|
||||||
comments = roots
|
|
||||||
|
|
||||||
let post = posts[0]
|
|
||||||
|
|
||||||
let style =
|
|
||||||
<style>
|
|
||||||
.child {
|
|
||||||
border-left: 3px solid #ccc;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
form textarea {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
procedure render_comment(parent)
|
|
||||||
<div>
|
|
||||||
<a href="">\parent.username</a>
|
|
||||||
<p>
|
|
||||||
\parent.content
|
|
||||||
</p>
|
|
||||||
<div class="child">
|
|
||||||
\if $login_user_id != none:
|
|
||||||
<form action="/api/comment" method="POST">
|
|
||||||
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
|
||||||
<input type="hidden" name="parent_comment" value=\'"'\parent.id\'"' />
|
|
||||||
<textarea name="content"></textarea>
|
|
||||||
<input type="submit" vaue="Publish" />
|
|
||||||
</form>
|
|
||||||
\for child in parent.child:
|
|
||||||
render_comment(child)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
let main =
|
|
||||||
<main>
|
|
||||||
<h3>\post.title</h3>
|
|
||||||
<p>\post.content</p>
|
|
||||||
<div>
|
|
||||||
<form action="/api/comment" method="POST">
|
|
||||||
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
|
||||||
<textarea name="content"></textarea>
|
|
||||||
<input type="submit" vaue="Publish" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
\if len comments == 0:
|
|
||||||
<span>No comments yet!</span>
|
|
||||||
else for comment in comments:
|
|
||||||
render_comment(comment)
|
|
||||||
</main>
|
|
||||||
|
|
||||||
page(post.title, $login_user_id, style, main)
|
|
||||||
+1
-1
@@ -60,7 +60,7 @@ let main =
|
|||||||
<main>
|
<main>
|
||||||
<form action="/api/post" method="POST">
|
<form action="/api/post" method="POST">
|
||||||
|
|
||||||
<input type="hidden" name="csrf" value=\'"'\$csrf\'"' />
|
<input type="hidden" name="csrf" value="\{$csrf}" />
|
||||||
<input type="text" id="title" name="title" placeholder="Title" required />
|
<input type="text" id="title" name="title" placeholder="Title" required />
|
||||||
|
|
||||||
<div class="checkbox-row">
|
<div class="checkbox-row">
|
||||||
|
|||||||
+89
-9
@@ -122,6 +122,49 @@ CWEB_String cweb_trim(CWEB_String s)
|
|||||||
return (CWEB_String) { s2.ptr, s2.len };
|
return (CWEB_String) { s2.ptr, s2.len };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *type_names__[] = {
|
||||||
|
[CWEB_VARG_TYPE_C] = "char",
|
||||||
|
[CWEB_VARG_TYPE_S] = "short",
|
||||||
|
[CWEB_VARG_TYPE_I] = "int",
|
||||||
|
[CWEB_VARG_TYPE_L] = "long",
|
||||||
|
[CWEB_VARG_TYPE_LL] = "long long",
|
||||||
|
[CWEB_VARG_TYPE_SC] = "signed char",
|
||||||
|
[CWEB_VARG_TYPE_SS] = "signed short",
|
||||||
|
[CWEB_VARG_TYPE_SI] = "signed int",
|
||||||
|
[CWEB_VARG_TYPE_SL] = "signed long",
|
||||||
|
[CWEB_VARG_TYPE_SLL] = "signed long long",
|
||||||
|
[CWEB_VARG_TYPE_UC] = "unsigned char",
|
||||||
|
[CWEB_VARG_TYPE_US] = "unsigned short",
|
||||||
|
[CWEB_VARG_TYPE_UI] = "unsigned int",
|
||||||
|
[CWEB_VARG_TYPE_UL] = "unsigned long",
|
||||||
|
[CWEB_VARG_TYPE_ULL] = "unsigned long long",
|
||||||
|
[CWEB_VARG_TYPE_F] = "float",
|
||||||
|
[CWEB_VARG_TYPE_D] = "double",
|
||||||
|
[CWEB_VARG_TYPE_B] = "bool",
|
||||||
|
[CWEB_VARG_TYPE_STR] = "string",
|
||||||
|
[CWEB_VARG_TYPE_HASH] = "hash",
|
||||||
|
[CWEB_VARG_TYPE_PC] = "char*",
|
||||||
|
[CWEB_VARG_TYPE_PS] = "short*",
|
||||||
|
[CWEB_VARG_TYPE_PI] = "int*",
|
||||||
|
[CWEB_VARG_TYPE_PL] = "long*",
|
||||||
|
[CWEB_VARG_TYPE_PLL] = "long long*",
|
||||||
|
[CWEB_VARG_TYPE_PSC] = "signed char*",
|
||||||
|
[CWEB_VARG_TYPE_PSS] = "signed short*",
|
||||||
|
[CWEB_VARG_TYPE_PSI] = "signed int*",
|
||||||
|
[CWEB_VARG_TYPE_PSL] = "signed long*",
|
||||||
|
[CWEB_VARG_TYPE_PSLL] = "signed long long*",
|
||||||
|
[CWEB_VARG_TYPE_PUC] = "unsigned char*",
|
||||||
|
[CWEB_VARG_TYPE_PUS] = "unsigned short*",
|
||||||
|
[CWEB_VARG_TYPE_PUI] = "unsigned int*",
|
||||||
|
[CWEB_VARG_TYPE_PUL] = "unsigned long*",
|
||||||
|
[CWEB_VARG_TYPE_PULL] = "unsigned long long*",
|
||||||
|
[CWEB_VARG_TYPE_PF] = "float*",
|
||||||
|
[CWEB_VARG_TYPE_PD] = "double*",
|
||||||
|
[CWEB_VARG_TYPE_PB] = "bool*",
|
||||||
|
[CWEB_VARG_TYPE_PSTR] = "string*",
|
||||||
|
[CWEB_VARG_TYPE_PHASH] = "hash*",
|
||||||
|
};
|
||||||
|
|
||||||
CWEB_VArg cweb_varg_from_c (char c) { return (CWEB_VArg) { CWEB_VARG_TYPE_C, .c=c }; }
|
CWEB_VArg cweb_varg_from_c (char c) { return (CWEB_VArg) { CWEB_VARG_TYPE_C, .c=c }; }
|
||||||
CWEB_VArg cweb_varg_from_s (short s) { return (CWEB_VArg) { CWEB_VARG_TYPE_S, .s=s }; }
|
CWEB_VArg cweb_varg_from_s (short s) { return (CWEB_VArg) { CWEB_VARG_TYPE_S, .s=s }; }
|
||||||
CWEB_VArg cweb_varg_from_i (int i) { return (CWEB_VArg) { CWEB_VARG_TYPE_I, .i=i }; }
|
CWEB_VArg cweb_varg_from_i (int i) { return (CWEB_VArg) { CWEB_VARG_TYPE_I, .i=i }; }
|
||||||
@@ -163,7 +206,6 @@ 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_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 }; }
|
CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash) { return (CWEB_VArg) { CWEB_VARG_TYPE_PHASH, .phash=phash }; }
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *dst;
|
char *dst;
|
||||||
int cap;
|
int cap;
|
||||||
@@ -316,7 +358,7 @@ static void free_loaded_files(LoadedFile *loaded_file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// RANDOM
|
// RANDOM
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -371,14 +413,18 @@ int cweb_hash_password(CWEB_String pass, int cost, CWEB_PasswordHash *hash)
|
|||||||
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash)
|
int cweb_check_password(CWEB_String pass, CWEB_PasswordHash hash)
|
||||||
{
|
{
|
||||||
char passzt[128];
|
char passzt[128];
|
||||||
if (pass.len >= (int) sizeof(passzt))
|
if (pass.len >= (int) sizeof(passzt)) {
|
||||||
|
CWEB_TRACE("%s: static buffer limit reached", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
memcpy(passzt, pass.ptr, pass.len);
|
memcpy(passzt, pass.ptr, pass.len);
|
||||||
passzt[pass.len] = '\0';
|
passzt[pass.len] = '\0';
|
||||||
|
|
||||||
CWEB_PasswordHash new_hash;
|
CWEB_PasswordHash new_hash;
|
||||||
if (_crypt_blowfish_rn(passzt, hash.data, new_hash.data, sizeof(new_hash.data)) == NULL)
|
if (_crypt_blowfish_rn(passzt, hash.data, new_hash.data, sizeof(new_hash.data)) == NULL) {
|
||||||
|
CWEB_TRACE("%s: couldn't calculate hash (password=[%.*s], hash=[%.*s])", __func__, pass.len, pass.ptr, (int) strnlen(hash.data, sizeof(hash.data)), hash.data);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(hash.data, new_hash.data)) // TODO: should be constant-time
|
if (strcmp(hash.data, new_hash.data)) // TODO: should be constant-time
|
||||||
return 1;
|
return 1;
|
||||||
@@ -451,7 +497,7 @@ static int pack_token(char *src, int srclen, char *dst, int dstlen)
|
|||||||
int low = src[i+1];
|
int low = src[i+1];
|
||||||
if (!is_hex_digit(high) || !is_hex_digit(low))
|
if (!is_hex_digit(high) || !is_hex_digit(low))
|
||||||
return -1;
|
return -1;
|
||||||
dst[i] = (hex_digit_to_int(high) << 4) | (hex_digit_to_int(low) << 0);
|
dst[i >> 1] = (hex_digit_to_int(high) << 4) | (hex_digit_to_int(low) << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -769,11 +815,14 @@ int64_t cweb_database_insert_impl(CWEB *cweb, char *fmt, CWEB_VArgs args)
|
|||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
||||||
if (ret != SQLITE_OK)
|
if (ret != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "sqlite3 prepare+bind error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ret = sqlite3_step(stmt);
|
ret = sqlite3_step(stmt);
|
||||||
if (ret != SQLITE_DONE) {
|
if (ret != SQLITE_DONE) {
|
||||||
|
fprintf(stderr, "sqlite3_step error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -799,8 +848,10 @@ CWEB_QueryResult cweb_database_select_impl(CWEB *cweb, char *fmt, CWEB_VArgs arg
|
|||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args);
|
||||||
if (ret != SQLITE_OK)
|
if (ret != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "sqlite3 prepare+bind error: %s (%s:%d)\n", sqlite3_errmsg(cweb->db), __FILE__, __LINE__); // TODO
|
||||||
return (CWEB_QueryResult) { NULL };
|
return (CWEB_QueryResult) { NULL };
|
||||||
|
}
|
||||||
|
|
||||||
return (CWEB_QueryResult) { stmt };
|
return (CWEB_QueryResult) { stmt };
|
||||||
#else
|
#else
|
||||||
@@ -811,21 +862,27 @@ CWEB_QueryResult cweb_database_select_impl(CWEB *cweb, char *fmt, CWEB_VArgs arg
|
|||||||
int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
||||||
{
|
{
|
||||||
#ifdef CWEB_ENABLE_DATABASE
|
#ifdef CWEB_ENABLE_DATABASE
|
||||||
if (res->handle == NULL)
|
if (res->handle == NULL) {
|
||||||
|
CWEB_TRACE("%s failed because database is initialized", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int ret = sqlite3_step(res->handle);
|
int ret = sqlite3_step(res->handle);
|
||||||
|
|
||||||
if (ret == SQLITE_DONE)
|
if (ret == SQLITE_DONE) {
|
||||||
|
CWEB_TRACE("%s returned no row", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret != SQLITE_ROW) {
|
if (ret != SQLITE_ROW) {
|
||||||
|
CWEB_TRACE("%s didn't return ROW or DONE (%d)", __func__, ret);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sqlite3_column_count(res->handle) != args.len) {
|
if (sqlite3_column_count(res->handle) != args.len) {
|
||||||
|
CWEB_TRACE("%s returned an unexpected column count", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -838,6 +895,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
{
|
{
|
||||||
int64_t x = sqlite3_column_int64(res->handle, i);
|
int64_t x = sqlite3_column_int64(res->handle, i);
|
||||||
if (x < INT_MIN || x > INT_MAX) {
|
if (x < INT_MIN || x > INT_MAX) {
|
||||||
|
CWEB_TRACE("%s couldn't bind integer out of range", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -850,6 +908,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
{
|
{
|
||||||
int64_t x = sqlite3_column_int64(res->handle, i);
|
int64_t x = sqlite3_column_int64(res->handle, i);
|
||||||
if (x < LONG_MIN || x > LONG_MAX) {
|
if (x < LONG_MIN || x > LONG_MAX) {
|
||||||
|
CWEB_TRACE("%s couldn't bind integer out of range", __func__);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -888,7 +947,27 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CWEB_VARG_TYPE_PHASH:
|
||||||
|
{
|
||||||
|
char *ptr = sqlite3_column_text(res->handle, i);
|
||||||
|
int len = sqlite3_column_bytes(res->handle, i);
|
||||||
|
|
||||||
|
CWEB_TRACE("%s: hash value (\"%.*s\", %d)", __func__, len, ptr, len);
|
||||||
|
|
||||||
|
CWEB_PasswordHash *hash = args.ptr[i].phash;
|
||||||
|
if ((int) sizeof(hash->data) <= len) {
|
||||||
|
CWEB_TRACE("%s couldn't bind to hash argument", __func__);
|
||||||
|
sqlite3_reset(res->handle);
|
||||||
|
res->handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(hash->data, 0, sizeof(hash->data));
|
||||||
|
memcpy(hash->data, ptr, len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
CWEB_TRACE("%s couldn't bind to unexpected argument type %s", __func__, type_names__[args.ptr[i].type]);
|
||||||
sqlite3_reset(res->handle);
|
sqlite3_reset(res->handle);
|
||||||
res->handle = NULL;
|
res->handle = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -899,6 +978,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args)
|
|||||||
#else
|
#else
|
||||||
(void) res;
|
(void) res;
|
||||||
(void) args;
|
(void) args;
|
||||||
|
CWEB_TRACE("%s is unsupported", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#define CWEB_STR(X) ((CWEB_String) { (X), (int) sizeof(X)-1 })
|
#define CWEB_STR(X) ((CWEB_String) { (X), (int) sizeof(X)-1 })
|
||||||
|
|
||||||
|
#define CWEB_TRACE(X, ...) fprintf(stderr, ("TRACE: " X "\n"), ##__VA_ARGS__);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
|
|||||||
Reference in New Issue
Block a user