diff --git a/backend/.gitignore b/backend/.gitignore
index 5e19c1d..2f64681 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -1,2 +1,3 @@
build/
database.db
+Session.vim
diff --git a/backend/src/controllers.h b/backend/src/controllers.h
index 8e917fd..3a1d8b7 100644
--- a/backend/src/controllers.h
+++ b/backend/src/controllers.h
@@ -38,5 +38,7 @@ void route_post_user_register(HttpCtx* ctx);
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
-#define RESPOND_BAD_REQUEST(HTTP_CTX) \
- RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false}")
+#define RESPOND_BAD_REQUEST(HTTP_CTX, MSG) \
+ RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false,\"msg\":\"%s\"}", (MSG))
+#define RESPOND_SERVER_ERROR(HTTP_CTX) \
+ RESPOND_JSON(HTTP_CTX, 500, "{\"ok\":false,\"msg\":\"server error\"}")
diff --git a/backend/src/controllers/auth.c b/backend/src/controllers/auth.c
new file mode 100644
index 0000000..754ee6a
--- /dev/null
+++ b/backend/src/controllers/auth.c
@@ -0,0 +1,10 @@
+#include "../controllers.h"
+#include "../http_server.h"
+#include "../models_json.h"
+#include "../str_util.h"
+
+void route_post_auth_login(HttpCtx* ctx)
+{
+
+ Cx* cx = http_ctx_user_ctx(ctx);
+}
diff --git a/backend/src/controllers/general.c b/backend/src/controllers/general.c
new file mode 100644
index 0000000..c876953
--- /dev/null
+++ b/backend/src/controllers/general.c
@@ -0,0 +1,48 @@
+#include "../controllers.h"
+#include "../http_server.h"
+#include "../models_json.h"
+
+void route_get_index(HttpCtx* ctx)
+{
+ Cx* cx = http_ctx_user_ctx(ctx);
+
+ RESPOND_HTML(ctx, 200,
+ "
Number = %d
",
+ cx->number);
+}
+
+void route_post_set_number(HttpCtx* ctx)
+{
+ Cx* cx = http_ctx_user_ctx(ctx);
+
+ const char* body_text = http_ctx_req_body(ctx);
+ JsonParser parser;
+ json_parser_construct(&parser, body_text, strlen(body_text));
+ JsonValue* body = json_parser_parse(&parser);
+ json_parser_destroy(&parser);
+
+ if (!json_object_has(body, "value")) {
+ RESPOND_JSON(
+ ctx, 200, "{\"ok\": false, \"msg\": \"no 'value' key\"}\r\n");
+ goto l0_return;
+ }
+
+ int64_t value = json_int(json_object_get(body, "value"));
+ cx->number = (int)value;
+
+ RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n");
+
+l0_return:
+ json_free(body);
+}
+
+void route_get_not_found(HttpCtx* ctx)
+{
+ RESPOND_HTML(ctx, 404,
+ "404 Not "
+ "Found
GET "
+ "%s
",
+ http_ctx_req_path(ctx));
+}
diff --git a/backend/src/controllers/users.c b/backend/src/controllers/users.c
new file mode 100644
index 0000000..910a476
--- /dev/null
+++ b/backend/src/controllers/users.c
@@ -0,0 +1,66 @@
+#include "../controllers.h"
+#include "../http_server.h"
+#include "../models_json.h"
+#include "../str_util.h"
+#include
+
+void route_post_user_register(HttpCtx* ctx)
+{
+ Cx* cx = http_ctx_user_ctx(ctx);
+
+ const char* body_str = http_ctx_req_body(ctx);
+
+ JsonValue* body_json = json_parse(body_str, strlen(body_str));
+
+ UsersRegisterReq req;
+ if (users_register_req_from_json(&req, body_json) != 0) {
+ RESPOND_BAD_REQUEST(ctx, "bad request");
+ json_free(body_json);
+ return;
+ }
+ json_free(body_json);
+
+ if (strlen(req.name) == 0 || strlen(req.email) == 0
+ || strlen(req.password) > MAX_HASH_INPUT_LEN) {
+ RESPOND_BAD_REQUEST(ctx, "bad request");
+ users_register_req_free(&req);
+ return;
+ }
+
+ Ids ids;
+ ids_construct(&ids);
+ if (db_users_with_email(cx->db, &ids, req.email) != DbRes_Ok) {
+ RESPOND_SERVER_ERROR(ctx);
+ ids_destroy(&ids);
+ users_register_req_free(&req);
+ return;
+ }
+ if (ids.size > 0) {
+ RESPOND_BAD_REQUEST(ctx, "email in use");
+ ids_destroy(&ids);
+ users_register_req_free(&req);
+ return;
+ }
+ ids_destroy(&ids);
+
+ char* password_hash = str_hash(req.password);
+
+ if (db_user_insert(cx->db,
+ &(User) {
+ .id = 0,
+ .name = req.name,
+ .email = req.email,
+ .password_hash = password_hash,
+ .balance_dkk_cent = 0,
+ })
+ != DbRes_Ok) {
+
+ RESPOND_SERVER_ERROR(ctx);
+ free(password_hash);
+ users_register_req_free(&req);
+ return;
+ }
+ free(password_hash);
+ users_register_req_free(&req);
+ RESPOND_JSON(ctx, 200, "{\"ok\":true}");
+}
diff --git a/backend/src/db.h b/backend/src/db.h
index d07b66d..dfba81e 100644
--- a/backend/src/db.h
+++ b/backend/src/db.h
@@ -15,7 +15,7 @@ typedef enum {
typedef struct Db Db;
/// `user.id` field is ignored.
-DbRes db_user_insert(Db* db, User* user);
+DbRes db_user_insert(Db* db, const User* user);
DbRes db_user_from_id(Db* db, User* user, int64_t id);
/// Expects `ids` to be constructed.
DbRes db_users_with_email(Db* db, Ids* ids, const char* email);
diff --git a/backend/src/db_sqlite.c b/backend/src/db_sqlite.c
index ad49ecb..7ef6c55 100644
--- a/backend/src/db_sqlite.c
+++ b/backend/src/db_sqlite.c
@@ -65,7 +65,7 @@ static inline void disconnect(sqlite3* connection)
disconnect(connection); \
}
-DbRes db_user_insert(Db* db, User* user)
+DbRes db_user_insert(Db* db, const User* user)
{
static_assert(sizeof(User) == 40, "model has changed");
@@ -76,7 +76,7 @@ DbRes db_user_insert(Db* db, User* user)
sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection,
"INSERT INTO users (name, email, password_hash, balance_dkk_cent) "
- "VALUES (?, ?, ?)",
+ "VALUES (?, ?, ?, ?)",
-1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);
diff --git a/backend/src/str_util.c b/backend/src/str_util.c
index 2989eba..6894ea6 100644
--- a/backend/src/str_util.c
+++ b/backend/src/str_util.c
@@ -72,17 +72,27 @@ char* string_copy(const String* string)
return copy;
}
-static inline StrHash str_hash_with_salt(const char* str, const uint8_t* salt)
+#define STR_HASH_SALT_SIZE 32
+#define STR_HASH_HASH_SIZE 32
+#define STR_HASH_STR_LEN 128
+
+typedef struct {
+ uint8_t salt[STR_HASH_SALT_SIZE];
+ uint8_t hash[STR_HASH_HASH_SIZE];
+} HashData;
+
+static inline HashData hashdata_from_str_and_salt(
+ const char* str, const uint8_t* salt)
{
- if (strlen(str) >= MAX_HASH_INPUT_LEN - 1) {
+ if (strlen(str) >= MAX_HASH_INPUT_LEN) {
fprintf(stderr, "error: tried to hash too long input\n");
exit(1);
}
- StrHash hash;
+ HashData hash;
memcpy(hash.salt, salt, STR_HASH_SALT_SIZE);
- uint8_t input[MAX_HASH_INPUT_LEN + STR_HASH_SALT_SIZE] = { 0 };
+ uint8_t input[MAX_HASH_INPUT_LEN + 1 + STR_HASH_SALT_SIZE] = { 0 };
memcpy(input, hash.salt, STR_HASH_SALT_SIZE);
memcpy(&input[STR_HASH_SALT_SIZE], str, strlen(str));
@@ -90,20 +100,20 @@ static inline StrHash str_hash_with_salt(const char* str, const uint8_t* salt)
return hash;
}
-StrHash str_hash(const char* str)
+static inline HashData hashdata_from_str(const char* str)
{
uint8_t salt[STR_HASH_SALT_SIZE];
RAND_bytes(salt, STR_HASH_SALT_SIZE);
- return str_hash_with_salt(str, salt);
+ return hashdata_from_str_and_salt(str, salt);
}
-bool str_hash_is_equal(StrHash hash, const char* str)
+static inline bool hashdata_is_equal(HashData hash, const char* str)
{
- StrHash other = str_hash_with_salt(str, hash.salt);
+ HashData other = hashdata_from_str_and_salt(str, hash.salt);
return memcmp(hash.hash, other.hash, STR_HASH_HASH_SIZE) == 0;
}
-char* str_hash_to_string(StrHash hash)
+static inline char* hashdata_to_string(HashData hash)
{
char* result = calloc(STR_HASH_STR_LEN + 1, sizeof(char));
for (size_t i = 0; i < STR_HASH_SALT_SIZE; ++i) {
@@ -121,7 +131,7 @@ char* str_hash_to_string(StrHash hash)
return result;
}
-StrHash str_hash_from_string(const char* str)
+static inline HashData hashdata_from_hash_string(const char* str)
{
uint8_t result[64] = { 0 };
size_t result_i = 0;
@@ -133,7 +143,7 @@ StrHash str_hash_from_string(const char* str)
result_i += 1;
}
- StrHash hash;
+ HashData hash;
// memcpy((uint8_t*)&hash, result, sizeof(result));
for (size_t i = 0; i < 32; ++i) {
hash.salt[i] = result[i];
@@ -141,3 +151,15 @@ StrHash str_hash_from_string(const char* str)
}
return hash;
}
+
+char* str_hash(const char* input)
+{
+ HashData data = hashdata_from_str(input);
+ return hashdata_to_string(data);
+}
+
+bool str_hash_equal(const char* hash, const char* input)
+{
+ HashData data = hashdata_from_hash_string(hash);
+ return hashdata_is_equal(data, input);
+}
diff --git a/backend/src/str_util.h b/backend/src/str_util.h
index 44aaacc..0ebe580 100644
--- a/backend/src/str_util.h
+++ b/backend/src/str_util.h
@@ -33,18 +33,7 @@ char* string_copy(const String* string);
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
-#define MAX_HASH_INPUT_LEN 256
+#define MAX_HASH_INPUT_LEN 256 - 1
-#define STR_HASH_SALT_SIZE 32
-#define STR_HASH_HASH_SIZE 32
-#define STR_HASH_STR_LEN 128
-
-typedef struct {
- uint8_t salt[STR_HASH_SALT_SIZE];
- uint8_t hash[STR_HASH_HASH_SIZE];
-} StrHash;
-
-StrHash str_hash(const char* str);
-bool str_hash_is_equal(StrHash hash, const char* str);
-char* str_hash_to_string(StrHash hash);
-StrHash str_hash_from_string(const char* str);
+char* str_hash(const char* input);
+bool str_hash_equal(const char* hash, const char* input);