Implement authorization #1
@@ -1,5 +1,3 @@
|
|||||||
#include "path.c"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool is_dir;
|
bool is_dir;
|
||||||
SV name;
|
SV name;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ static Noh_String build_path_(bool url, Noh_String_View *first, ...) {
|
|||||||
Noh_String_View elem = *elem_;
|
Noh_String_View elem = *elem_;
|
||||||
while (elem.count > 0) {
|
while (elem.count > 0) {
|
||||||
Noh_String_View component = noh_sv_chop_by_delim(&elem, '/');
|
Noh_String_View component = noh_sv_chop_by_delim(&elem, '/');
|
||||||
if (noh_sv_eq(component, sv(".."))) continue;
|
if (noh_sv_eq(component, noh_sv_from_cstr(".."))) continue;
|
||||||
|
|
||||||
if (component.count > 0) {
|
if (component.count > 0) {
|
||||||
if (start) start = false; else noh_string_append_cstr(&result, "/");
|
if (start) start = false; else noh_string_append_cstr(&result, "/");
|
||||||
|
|||||||
92
src/server.c
92
src/server.c
@@ -5,25 +5,82 @@
|
|||||||
#define NOH_IMPLEMENTATION
|
#define NOH_IMPLEMENTATION
|
||||||
#include "noh.h"
|
#include "noh.h"
|
||||||
|
|
||||||
#define NO_AUTH
|
Noh_Arena temp;
|
||||||
|
|
||||||
|
#include "path.c"
|
||||||
|
#include "users.c"
|
||||||
|
|
||||||
|
#define SV Noh_String_View
|
||||||
|
#define sv noh_sv_from_cstr
|
||||||
|
|
||||||
/* To disable Basic auth for testing:
|
/* To disable Basic auth for testing:
|
||||||
#define NO_AUTH
|
#define NO_AUTH
|
||||||
This will ignore any auth headers and always log in with username demo.
|
This will ignore any auth headers and always log in with username demo.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SV Noh_String_View
|
|
||||||
#define sv noh_sv_from_cstr
|
|
||||||
|
|
||||||
Noh_Arena temp;
|
|
||||||
|
|
||||||
#include "id_store.c"
|
|
||||||
#include "library.c"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SV base_path;
|
SV base_path;
|
||||||
SV library_name;
|
SV library_name;
|
||||||
} Settings;
|
} Settings;
|
||||||
|
|
||||||
|
// Authorizes the username from the Basic authorization info in the provided connection.
|
||||||
|
// The username data is guaranteed to live as long as the connection lives.
|
||||||
|
static bool authorize_user(struct MHD_Connection *connection, SV *username, Settings *settings) {
|
||||||
|
#ifdef NO_AUTH
|
||||||
|
(void)connection;
|
||||||
|
|
||||||
|
*username = sv("demo");
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
|
||||||
|
if (!auth) return false;
|
||||||
|
|
||||||
|
SV provided_username = { .elems = auth->username, .count = auth->username_len };
|
||||||
|
SV provided_password = { .elems = auth->password, .count = auth->password_len };
|
||||||
|
|
||||||
|
// Read the users file.
|
||||||
|
noh_arena_save(&temp);
|
||||||
|
Noh_String_View file_name = noh_sv_from_cstr("users.conf");
|
||||||
|
Noh_String users_file_path = build_path(false, &settings->base_path, &file_name);
|
||||||
|
noh_string_append_null(&users_file_path);
|
||||||
|
|
||||||
|
Noh_String hashed = {0};
|
||||||
|
Users users = {0};
|
||||||
|
Noh_String users_file = {0};
|
||||||
|
if (!read_users_file(users_file_path.elems, &users_file, &users)) noh_return_defer(false);
|
||||||
|
|
||||||
|
// Find the user in the users list.
|
||||||
|
bool found_user = false;
|
||||||
|
User *user = NULL;
|
||||||
|
for (size_t i = 0; i < users.count && !found_user; i++) {
|
||||||
|
user = &users.elems[i];
|
||||||
|
|
||||||
|
if (noh_sv_eq(provided_username, user->username)) found_user = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_user) noh_return_defer(false);
|
||||||
|
|
||||||
|
// Hash the provided password.
|
||||||
|
hashed = hash_password(provided_username, provided_password);
|
||||||
|
|
||||||
|
// Check the hash.
|
||||||
|
if (!noh_sv_eq(noh_sv_from_string(hashed), user->hash)) noh_return_defer(false);
|
||||||
|
|
||||||
|
*username = provided_username;
|
||||||
|
|
||||||
|
defer:
|
||||||
|
noh_string_free(&hashed);
|
||||||
|
noh_da_free(&users);
|
||||||
|
noh_string_free(&users_file_path);
|
||||||
|
noh_string_free(&users_file);
|
||||||
|
noh_arena_reset(&temp);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message, bool xml) {
|
static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message, bool xml) {
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer(
|
struct MHD_Response *response = MHD_create_response_from_buffer(
|
||||||
message.count, (void*)message.elems, MHD_RESPMEM_MUST_COPY);
|
message.count, (void*)message.elems, MHD_RESPMEM_MUST_COPY);
|
||||||
@@ -47,6 +104,8 @@ static enum MHD_Result send_epub_file(struct MHD_Connection *connection, int fil
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "id_store.c"
|
||||||
|
#include "library.c"
|
||||||
#include "opds_handler.c"
|
#include "opds_handler.c"
|
||||||
#include "get_handler.c"
|
#include "get_handler.c"
|
||||||
|
|
||||||
@@ -65,20 +124,7 @@ static enum MHD_Result request_handler(
|
|||||||
noh_log(NOH_INFO, "Request: %s %s %s", version, method, _url);
|
noh_log(NOH_INFO, "Request: %s %s %s", version, method, _url);
|
||||||
|
|
||||||
SV username;
|
SV username;
|
||||||
#ifdef NO_AUTH
|
if (!authorize_user(connection, &username, &settings)) return send_response(connection, 401, sv(""), false);
|
||||||
username = sv("demo");
|
|
||||||
#else
|
|
||||||
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
|
|
||||||
if (!auth) return send_response(connection, 401, sv(""), false);
|
|
||||||
|
|
||||||
username = (SV){ .elems = auth->username, .count = auth->username_len };
|
|
||||||
SV password = { .elems = auth->password, .count = auth->password_len };
|
|
||||||
|
|
||||||
// TODO: Read username from user config.
|
|
||||||
if (!noh_sv_eq(password, sv("jaja"))) return send_response(connection, 401, sv(""), false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
noh_log(NOH_INFO, "Authenticated as "Nsv_Fmt, Nsv_Arg(username));
|
|
||||||
|
|
||||||
if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv(""), false);
|
if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv(""), false);
|
||||||
|
|
||||||
|
|||||||
129
src/userctl.c
129
src/userctl.c
@@ -1,15 +1,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sha256.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#define NOH_IMPLEMENTATION
|
#define NOH_IMPLEMENTATION
|
||||||
#include "noh.h"
|
#include "noh.h"
|
||||||
|
|
||||||
#define SV Noh_String_View
|
Noh_Arena temp;
|
||||||
#define sv noh_sv_from_cstr
|
|
||||||
|
|
||||||
#include "path.c"
|
#include "path.c"
|
||||||
|
#include "users.c"
|
||||||
|
|
||||||
Noh_Arena temp;
|
#define SV Noh_String_View
|
||||||
|
#define sv noh_sv_from_cstr
|
||||||
|
|
||||||
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:");
|
||||||
@@ -35,42 +35,6 @@ static void show_usage(char *program_name, char *error) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Noh_String hash_password(Noh_String_View username, Noh_String_View password) {
|
|
||||||
// Build the sha input string.
|
|
||||||
Noh_String input = {0};
|
|
||||||
noh_string_append_sv(&input, username);
|
|
||||||
noh_string_append_cstr(&input, "<><>");
|
|
||||||
noh_string_append_sv(&input, password);
|
|
||||||
|
|
||||||
// Calculate the hash.
|
|
||||||
SHA256_CTX ctx = {0};
|
|
||||||
BYTE hash[32];
|
|
||||||
sha256_init(&ctx);
|
|
||||||
sha256_update(&ctx, (BYTE*)input.elems, input.count);
|
|
||||||
sha256_final(&ctx, hash);
|
|
||||||
|
|
||||||
// Build the result string.
|
|
||||||
noh_arena_save(&temp);
|
|
||||||
Noh_String result = {0};
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
noh_string_append_cstr(&result, noh_arena_sprintf(&temp, "%02x", hash[i]));
|
|
||||||
}
|
|
||||||
noh_arena_reset(&temp);
|
|
||||||
|
|
||||||
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 remove_directory(char *path) {
|
bool remove_directory(char *path) {
|
||||||
noh_arena_save(&temp);
|
noh_arena_save(&temp);
|
||||||
bool result = true;
|
bool result = true;
|
||||||
@@ -103,55 +67,6 @@ bool remove_directory(char *path) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_users_file(char *file_path, Users *users) {
|
|
||||||
Noh_String users_file = {0};
|
|
||||||
|
|
||||||
noh_log(NOH_INFO, "Reading users file: %s", file_path);
|
|
||||||
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);
|
|
||||||
|
|
||||||
Noh_String_View line = {0};
|
|
||||||
for (size_t i = 0; users_file_sv.count > 0; i++) {
|
|
||||||
line = noh_sv_chop_line(&users_file_sv);
|
|
||||||
|
|
||||||
// Ignore empty lines.
|
|
||||||
noh_sv_trim_space(&line);
|
|
||||||
if (line.count == 0) continue;
|
|
||||||
|
|
||||||
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);
|
|
||||||
noh_string_free(&users_file);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
noh_da_append(users, ((User){ .username = un, .hash = line }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
noh_string_append_cstr(&users_file, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = noh_string_write_file(&users_file, file_path);
|
|
||||||
noh_string_free(&users_file);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Prompts a user with a question and returns their response.
|
/// Prompts a user with a question and returns their response.
|
||||||
bool prompt_user(char *question) {
|
bool prompt_user(char *question) {
|
||||||
noh_arena_save(&temp);
|
noh_arena_save(&temp);
|
||||||
@@ -168,18 +83,22 @@ bool prompt_user(char *question) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_String_View password) {
|
bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_String_View password) {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
// Read the file.
|
// Read the file.
|
||||||
Noh_String_View file_name = noh_sv_from_cstr("users.conf");
|
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 users_file_path = build_path(false, &library_path, &file_name);
|
||||||
noh_string_append_null(&users_file_path);
|
noh_string_append_null(&users_file_path);
|
||||||
|
|
||||||
// Build users map.
|
// Build users map.
|
||||||
|
Noh_String hashed = {0};
|
||||||
Users users = {0};
|
Users users = {0};
|
||||||
if (!read_users_file(users_file_path.elems, &users)) return false;
|
Noh_String users_file = {0};
|
||||||
bool result = true;
|
Noh_String user_folder_path = {0};
|
||||||
|
if (!read_users_file(users_file_path.elems, &users_file, &users)) noh_return_defer(false);
|
||||||
|
|
||||||
// Hash the password.
|
// Hash the password.
|
||||||
Noh_String hashed = hash_password(username, password);
|
hashed = hash_password(username, password);
|
||||||
|
|
||||||
// Find the user in the map of users.
|
// Find the user in the map of users.
|
||||||
for (size_t i = 0; i < users.count; i++) {
|
for (size_t i = 0; i < users.count; i++) {
|
||||||
@@ -197,7 +116,7 @@ bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_S
|
|||||||
if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false);
|
if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false);
|
||||||
|
|
||||||
// Create user folder if needed.
|
// Create user folder if needed.
|
||||||
Noh_String user_folder_path = build_path(false, &library_path, &username);
|
user_folder_path = build_path(false, &library_path, &username);
|
||||||
noh_string_append_null(&user_folder_path);
|
noh_string_append_null(&user_folder_path);
|
||||||
if (!noh_mkdir_if_needed(user_folder_path.elems)) noh_return_defer(false);
|
if (!noh_mkdir_if_needed(user_folder_path.elems)) noh_return_defer(false);
|
||||||
|
|
||||||
@@ -210,25 +129,32 @@ bool register_user(Noh_String_View library_path, Noh_String_View username, Noh_S
|
|||||||
if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false);
|
if (!write_users_file(users_file_path.elems, &users, NULL)) noh_return_defer(false);
|
||||||
|
|
||||||
// Create user folder if needed.
|
// Create user folder if needed.
|
||||||
Noh_String user_folder_path = build_path(false, &library_path, &username);
|
user_folder_path = build_path(false, &library_path, &username);
|
||||||
noh_string_append_null(&user_folder_path);
|
noh_string_append_null(&user_folder_path);
|
||||||
if (!noh_mkdir_if_needed(user_folder_path.elems)) noh_return_defer(false);
|
if (!noh_mkdir_if_needed(user_folder_path.elems)) noh_return_defer(false);
|
||||||
|
|
||||||
defer:
|
defer:
|
||||||
|
noh_string_free(&users_file_path);
|
||||||
|
noh_string_free(&user_folder_path);
|
||||||
noh_da_free(&users);
|
noh_da_free(&users);
|
||||||
|
noh_string_free(&users_file);
|
||||||
|
noh_string_free(&hashed);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool delete_user(Noh_String_View library_path, Noh_String_View username) {
|
bool delete_user(Noh_String_View library_path, Noh_String_View username) {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
// Read the file.
|
// Read the file.
|
||||||
Noh_String_View file_name = noh_sv_from_cstr("users.conf");
|
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 users_file_path = build_path(false, &library_path, &file_name);
|
||||||
noh_string_append_null(&users_file_path);
|
noh_string_append_null(&users_file_path);
|
||||||
|
|
||||||
// Build users map.
|
// Build users map.
|
||||||
|
Noh_String user_folder_path = {0};
|
||||||
Users users = {0};
|
Users users = {0};
|
||||||
if (!read_users_file(users_file_path.elems, &users)) return false;
|
Noh_String users_file = {0};
|
||||||
bool result = true;
|
if (!read_users_file(users_file_path.elems, &users_file, &users)) noh_return_defer(false);
|
||||||
|
|
||||||
// Check if the user exists.
|
// Check if the user exists.
|
||||||
User *user_to_delete = NULL;
|
User *user_to_delete = NULL;
|
||||||
@@ -256,7 +182,7 @@ bool delete_user(Noh_String_View library_path, Noh_String_View username) {
|
|||||||
if (!write_users_file(users_file_path.elems, &users, user_to_delete)) noh_return_defer(false);
|
if (!write_users_file(users_file_path.elems, &users, user_to_delete)) noh_return_defer(false);
|
||||||
|
|
||||||
// Prompt to delete the user's library folder.
|
// Prompt to delete the user's library folder.
|
||||||
Noh_String user_folder_path = build_path(false, &library_path, &username);
|
user_folder_path = build_path(false, &library_path, &username);
|
||||||
noh_string_append_null(&user_folder_path);
|
noh_string_append_null(&user_folder_path);
|
||||||
if (!noh_file_exists(user_folder_path.elems)) noh_return_defer(true);
|
if (!noh_file_exists(user_folder_path.elems)) noh_return_defer(true);
|
||||||
|
|
||||||
@@ -266,7 +192,10 @@ bool delete_user(Noh_String_View library_path, Noh_String_View username) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer:
|
defer:
|
||||||
|
noh_string_free(&user_folder_path);
|
||||||
|
noh_string_free(&users_file_path);
|
||||||
noh_da_free(&users);
|
noh_da_free(&users);
|
||||||
|
noh_string_free(&users_file);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,11 +207,13 @@ bool list_users(Noh_String_View library_path) {
|
|||||||
|
|
||||||
// Build users map.
|
// Build users map.
|
||||||
Users users = {0};
|
Users users = {0};
|
||||||
if (!read_users_file(users_file_path.elems, &users)) return false;
|
Noh_String users_file = {0};
|
||||||
|
if (!read_users_file(users_file_path.elems, &users_file, &users)) return false;
|
||||||
|
|
||||||
if (users.count == 0) {
|
if (users.count == 0) {
|
||||||
noh_log(NOH_INFO, "No users registered.");
|
noh_log(NOH_INFO, "No users registered.");
|
||||||
noh_da_free(&users);
|
noh_da_free(&users);
|
||||||
|
noh_string_free(&users_file);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +225,9 @@ bool list_users(Noh_String_View library_path) {
|
|||||||
}
|
}
|
||||||
noh_arena_reset(&temp);
|
noh_arena_reset(&temp);
|
||||||
|
|
||||||
|
noh_string_free(&users_file_path);
|
||||||
noh_da_free(&users);
|
noh_da_free(&users);
|
||||||
|
noh_string_free(&users_file);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
87
src/users.c
Normal file
87
src/users.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#include <sha256.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Noh_String_View username;
|
||||||
|
Noh_String_View hash;
|
||||||
|
} User;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
User *elems;
|
||||||
|
size_t count;
|
||||||
|
size_t capacity;
|
||||||
|
} Users;
|
||||||
|
|
||||||
|
Noh_String hash_password(Noh_String_View username, Noh_String_View password) {
|
||||||
|
// Build the sha input string.
|
||||||
|
Noh_String input = {0};
|
||||||
|
noh_string_append_sv(&input, username);
|
||||||
|
noh_string_append_cstr(&input, "<><>");
|
||||||
|
noh_string_append_sv(&input, password);
|
||||||
|
|
||||||
|
// Calculate the hash.
|
||||||
|
SHA256_CTX ctx = {0};
|
||||||
|
BYTE hash[32];
|
||||||
|
sha256_init(&ctx);
|
||||||
|
sha256_update(&ctx, (BYTE*)input.elems, input.count);
|
||||||
|
sha256_final(&ctx, hash);
|
||||||
|
|
||||||
|
// Build the result string.
|
||||||
|
noh_arena_save(&temp);
|
||||||
|
Noh_String result = {0};
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
noh_string_append_cstr(&result, noh_arena_sprintf(&temp, "%02x", hash[i]));
|
||||||
|
}
|
||||||
|
noh_arena_reset(&temp);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the users file into users_file. Free this string when the data is no longer needed.
|
||||||
|
// Note that the string needs to be initialized and empty before calling this function.
|
||||||
|
// The users struct will contain string views referencing the data in this string.
|
||||||
|
bool read_users_file(char *file_path, Noh_String *users_file, Users *users) {
|
||||||
|
noh_log(NOH_INFO, "Reading users file: %s", file_path);
|
||||||
|
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);
|
||||||
|
|
||||||
|
Noh_String_View line = {0};
|
||||||
|
for (size_t i = 0; users_file_sv.count > 0; i++) {
|
||||||
|
line = noh_sv_chop_line(&users_file_sv);
|
||||||
|
|
||||||
|
// Ignore empty lines.
|
||||||
|
noh_sv_trim_space(&line);
|
||||||
|
if (line.count == 0) continue;
|
||||||
|
|
||||||
|
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);
|
||||||
|
noh_string_free(users_file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
noh_da_append(users, ((User){ .username = un, .hash = line }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
noh_string_append_cstr(&users_file, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = noh_string_write_file(&users_file, file_path);
|
||||||
|
noh_string_free(&users_file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user