Implement userctl user registering

This commit is contained in:
2026-06-17 17:34:07 +02:00
parent 68bb993d1a
commit 998c0bd994
3 changed files with 164 additions and 36 deletions

View File

@@ -1,3 +1,5 @@
#include "path.c"
typedef struct { typedef struct {
bool is_dir; bool is_dir;
SV name; SV name;
@@ -41,34 +43,6 @@ static void set_modified_time(Library_Entry *entry) {
} }
} }
#define build_path(url, first, ...) build_path_((url), (first), __VA_ARGS__, NULL)
static Noh_String build_path_(bool url, SV *first, ...) {
va_list args;
SV *elem_ = first;
va_start(args, first);
bool start = true;
Noh_String result = {0};
if (url) noh_string_append_cstr(&result, "/");
while (elem_ != NULL) {
SV elem = *elem_;
while (elem.count > 0) {
SV component = noh_sv_chop_by_delim(&elem, '/');
if (noh_sv_eq(component, sv(".."))) continue;
if (component.count > 0) {
if (start) start = false; else noh_string_append_cstr(&result, "/");
noh_string_append_sv(&result, component);
}
}
elem_ = va_arg(args, SV *);
}
va_end(args);
return result;
}
/** /**
* Creates a Library entry from a dirent structure. * Creates a Library entry from a dirent structure.
*/ */

27
src/path.c Normal file
View File

@@ -0,0 +1,27 @@
#define build_path(url, first, ...) build_path_((url), (first), __VA_ARGS__, NULL)
static Noh_String build_path_(bool url, Noh_String_View *first, ...) {
va_list args;
Noh_String_View *elem_ = first;
va_start(args, first);
bool start = true;
Noh_String result = {0};
if (url) noh_string_append_cstr(&result, "/");
while (elem_ != NULL) {
Noh_String_View elem = *elem_;
while (elem.count > 0) {
Noh_String_View component = noh_sv_chop_by_delim(&elem, '/');
if (noh_sv_eq(component, sv(".."))) continue;
if (component.count > 0) {
if (start) start = false; else noh_string_append_cstr(&result, "/");
noh_string_append_sv(&result, component);
}
}
elem_ = va_arg(args, Noh_String_View *);
}
va_end(args);
return result;
}

View File

@@ -3,11 +3,26 @@
#define NOH_IMPLEMENTATION #define NOH_IMPLEMENTATION
#include "noh.h" #include "noh.h"
#define SV Noh_String_View
#define sv noh_sv_from_cstr
#include "path.c"
static void show_usage(char *program_name, char *error) { static void show_usage(char *program_name, char *error) {
noh_log(NOH_INFO, "Usage:"); noh_log(NOH_INFO, "Usage:");
noh_log(NOH_INFO, "%s username password", program_name); noh_log(NOH_INFO, "%s command [args]", program_name);
noh_log(NOH_INFO, "");
noh_log(NOH_INFO, "Commands:");
noh_log(NOH_INFO, "%s register library_path username password", program_name);
noh_log(NOH_INFO, " library_path: The path to the library where the users file and user subfolders are located.");
noh_log(NOH_INFO, " username: The username of the user to register."); noh_log(NOH_INFO, " username: The username of the user to register.");
noh_log(NOH_INFO, " password: The password of the user to register."); noh_log(NOH_INFO, " password: The password of the user to register.");
noh_log(NOH_INFO, "Registers a new user, or changes their password if they already exist.");
noh_log(NOH_INFO, "");
noh_log(NOH_INFO, "%s delete library_path username", program_name);
noh_log(NOH_INFO, " library_path: The path to the library where the users file and user subfolders are located.");
noh_log(NOH_INFO, " username: The username of the user to delete.");
noh_log(NOH_INFO, "Deletes a user, optionally also deleting their library folder.");
noh_log(NOH_INFO, ""); noh_log(NOH_INFO, "");
noh_log(NOH_ERROR, "%s", error); noh_log(NOH_ERROR, "%s", error);
exit(1); exit(1);
@@ -38,21 +53,133 @@ Noh_String hash_password(Noh_Arena *arena, Noh_String_View username, Noh_String_
return result; return result;
} }
typedef struct {
Noh_String_View username;
Noh_String_View hash;
} User;
typedef struct {
User *elems;
size_t count;
size_t capacity;
} Users;
bool read_users_file(char *file_path, Users *users) {
Noh_String users_file = {0};
if (!noh_file_exists(file_path)) return true;
if (!noh_string_read_file(&users_file, file_path)) return false;
Noh_String_View users_file_sv = noh_sv_from_string(users_file);
for (size_t i = 0; users_file_sv.count > 0; i++) {
Noh_String_View line = noh_sv_chop_line(&users_file_sv);
Noh_String_View un = noh_sv_chop_by_delim(&line, ':');
if (line.count == 0) {
noh_log(NOH_ERROR, "%s %zu: Line not in username:hash format.", file_path, i);
return false;
}
noh_da_append(users, ((User){ .username = un, .hash = line }));
}
return true;
}
bool write_users_file(char *file_path, Users *users) {
Noh_String users_file = {0};
for (size_t i = 0; i < users->count; i++) {
User *u = &users->elems[i];
noh_string_append_sv(&users_file, u->username);
noh_string_append_cstr(&users_file, ":");
noh_string_append_sv(&users_file, u->hash);
noh_string_append_cstr(&users_file, "\n");
}
return noh_string_write_file(&users_file, file_path);
}
bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_String_View password) {
// Read the file.
Noh_String_View file_name = noh_sv_from_cstr("users.txt");
Noh_String users_file_path = build_path(false, &library_path, &file_name);
noh_string_append_null(&users_file_path);
// Build users map.
Users users = {0};
if (!read_users_file(users_file_path.elems, &users)) return false;
bool result = true;
// Hash the password.
Noh_Arena arena = noh_arena_init(1 KB);
Noh_String hashed = hash_password(&arena, username, password);
// Find the user in the map of users.
for (size_t i = 0; i < users.count; i++) {
User *user = &users.elems[i];
if (!noh_sv_eq_ci(user->username, username)) continue;
// The user exists. Update password after confirmation.
noh_log(NOH_INFO, "User already exists. Update password? [yn]");
char response;
if (!scanf(" %c", &response)) {
noh_log(NOH_ERROR, "Error reading response from stdin.");
noh_return_defer(false);
}
if (response != 'y') {
noh_log(NOH_INFO, "Ok, not changing anything.");
noh_return_defer(true);
}
noh_log(NOH_INFO, "Updating user password.");
user->hash = noh_sv_from_string(hashed);
if (!write_users_file(users_file_path.elems, &users)) noh_return_defer(false);
noh_return_defer(true);
}
// User was not found, add it to the list.
noh_log(NOH_INFO, "Adding a new user.");
noh_da_append(&users, ((User){ .username = username, .hash = noh_sv_from_string(hashed) }));
if (!write_users_file(users_file_path.elems, &users)) noh_return_defer(false);
defer:
noh_da_free(&users);
return result;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
// Parse input parameters. // Parse input parameters.
char *program_name = noh_shift_args(&argc, &argv); char *program_name = noh_shift_args(&argc, &argv);
Noh_Arena arena = noh_arena_init(64 KB);
if (argc < 1) show_usage(program_name, "Command not provided");
Noh_String_View command = noh_sv_from_cstr(noh_shift_args(&argc, &argv));
if (argc < 1) show_usage(program_name, "Library path not provided.");
Noh_String_View library_path = noh_sv_from_cstr(noh_shift_args(&argc, &argv));
if (argc < 1) show_usage(program_name, "Username not provided."); if (argc < 1) show_usage(program_name, "Username not provided.");
Noh_String_View username = noh_sv_from_cstr(noh_shift_args(&argc, &argv)); Noh_String_View username = noh_sv_from_cstr(noh_shift_args(&argc, &argv));
if (argc < 1) show_usage(program_name, "Password not provided."); if (noh_sv_eq_ci(command, noh_sv_from_cstr("register"))) {
Noh_String_View password = noh_sv_from_cstr(noh_shift_args(&argc, &argv)); if (argc < 1) show_usage(program_name, "Password not provided.");
Noh_String_View password = noh_sv_from_cstr(noh_shift_args(&argc, &argv));
Noh_Arena arena = noh_arena_init(64 KB); if (!register_user(library_path, username, password)) return 1;
Noh_String result = hash_password(&arena, username, password); return 0;
noh_string_append_null(&result);
printf("%s\n", result.elems);
return 0; } else if (noh_sv_eq_ci(command, sv("delete"))) {
noh_assert(false);
} else {
show_usage(program_name, noh_arena_sprintf(&arena, "Unknown command: "Nsv_Fmt, Nsv_Arg(command)));
}
} }