From 8132ecd9d8b27d7ecc64ce39850ef4bf7cd97f3d Mon Sep 17 00:00:00 2001 From: ThoNohT Date: Wed, 17 Jun 2026 17:34:07 +0200 Subject: [PATCH] Implement deleting a user --- src/path.c | 2 +- src/userctl.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/path.c b/src/path.c index 5608c40..9b06186 100644 --- a/src/path.c +++ b/src/path.c @@ -5,7 +5,7 @@ static Noh_String build_path_(bool url, Noh_String_View *first, ...) { va_start(args, first); bool start = true; Noh_String result = {0}; - if (url) noh_string_append_cstr(&result, "/"); + if (url || noh_sv_starts_with(*first, noh_sv_from_cstr("/"))) noh_string_append_cstr(&result, "/"); while (elem_ != NULL) { Noh_String_View elem = *elem_; diff --git a/src/userctl.c b/src/userctl.c index 48c543c..3e4b752 100644 --- a/src/userctl.c +++ b/src/userctl.c @@ -1,5 +1,6 @@ #include #include +#include #define NOH_IMPLEMENTATION #include "noh.h" @@ -70,6 +71,38 @@ typedef struct { size_t capacity; } Users; +bool remove_directory(char *path) { + noh_arena_save(&temp); + bool result = true; + + DIR *dir = opendir(path); + if (!dir) return false; + + struct dirent *p; + while ((p = readdir(dir))) { + // Ignore . and .. file entries. + if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue; + + char *sub_path = noh_arena_sprintf(&temp, "%s/%s", path, p->d_name); + + // Delete files and recurse subfolders. + struct stat statbuf; + if (!stat(sub_path, &statbuf)) { + if (S_ISDIR(statbuf.st_mode)) + result &= remove_directory(sub_path); + else + result &= unlink(sub_path) == 0; + } + } + + closedir(dir); + + if (result) result = rmdir(path); + + noh_arena_reset(&temp); + return result; +} + bool read_users_file(char *file_path, Users *users) { Noh_String users_file = {0}; @@ -99,11 +132,13 @@ bool read_users_file(char *file_path, Users *users) { return true; } -bool write_users_file(char *file_path, Users *users) { +bool write_users_file(char *file_path, Users *users, User *user_to_skip) { Noh_String users_file = {0}; for (size_t i = 0; i < users->count; i++) { User *u = &users->elems[i]; + if (u == user_to_skip) continue; + noh_string_append_sv(&users_file, u->username); noh_string_append_cstr(&users_file, ":"); noh_string_append_sv(&users_file, u->hash); @@ -149,14 +184,14 @@ bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_S if (!noh_sv_eq_ci(user->username, username)) continue; // The user exists. Update password after confirmation. - if (!prompt_user("User already exists. Update password? [yn]")) { + if (!prompt_user("User already exists. Update password?")) { 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); + if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false); noh_return_defer(true); } @@ -164,7 +199,58 @@ bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_S // 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); + if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false); + +defer: + noh_da_free(&users); + return result; +} + +bool delete_user(Noh_String_View library_path, Noh_String_View username) { + // Read the file. + Noh_String_View file_name = noh_sv_from_cstr("users.conf"); + 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; + + // Check if the user exists. + User *user_to_delete = NULL; + for (size_t i = 0; i < users.count; i++) { + User *user = &users.elems[i]; + if (!noh_sv_eq_ci(user->username, username)) continue; + + user_to_delete = user; + break; + } + + // User does not exist, don't change anything. + if (!user_to_delete) { + noh_log(NOH_INFO, "User does not exist."); + noh_return_defer(true); + } + + // The user exists. Prompt to confirm deletion. + if (!prompt_user("User found, delete the user?")) { + noh_log(NOH_INFO, "Ok, not changing anything."); + noh_return_defer(true); + } + + noh_log(NOH_INFO, "Deleting user."); + if (!write_users_file(users_file_path.elems, &users, user_to_delete)) noh_return_defer(false); + + // Prompt to delete the user's library folder. + Noh_String user_folder_path = build_path(false, &library_path, &username); + noh_string_append_null(&user_folder_path); + if (!noh_file_exists(user_folder_path.elems)) noh_return_defer(true); + + if (prompt_user("Library folder exists. Delete it?")) { + noh_log(NOH_INFO, "Deleting library folder."); + return remove_directory(user_folder_path.elems); + } defer: noh_da_free(&users); @@ -218,22 +304,22 @@ int main(int argc, char **argv) { if (argc < 1) show_usage(program_name, "Password not provided."); Noh_String_View password = noh_sv_from_cstr(noh_shift_args(&argc, &argv)); - return register_user(library_path, username, password); + return register_user(library_path, username, password) ? 0 : 1; } else if (noh_sv_eq_ci(command, sv("delete"))) { if (argc < 1) show_usage(program_name, "Username not provided."); Noh_String_View username = noh_sv_from_cstr(noh_shift_args(&argc, &argv)); - (void)username; - noh_assert(false); + return delete_user(library_path, username) ? 0 : 1; } else if (noh_sv_eq_ci(command, sv("list"))) { - return list_users(library_path); + return list_users(library_path) ? 0 : 1; } else { noh_arena_save(&temp); show_usage(program_name, noh_arena_sprintf(&temp, "Unknown command: "Nsv_Fmt, Nsv_Arg(command))); noh_arena_reset(&temp); + return 1; } }