updates CMake configuration for SQLite; sqlite: open or create table function.

This commit is contained in:
2026-04-29 18:47:28 +03:00
parent 39ff135367
commit 03f6db22b1
11 changed files with 164 additions and 11 deletions
+22
View File
@@ -0,0 +1,22 @@
add_library(db STATIC)
if (USE_POSTGRESQL)
target_link_libraries(db
PRIVATE
PostgreSQL::PostgreSQL
)
else()
target_link_libraries(db
PRIVATE
sqlite
)
endif()
target_sources(db
PRIVATE
"db.c"
PUBLIC
FILE_SET HEADERS
FILES
"db.h"
)
+5
View File
@@ -0,0 +1,5 @@
#ifdef USE_POSTGRESQL
#include "db_postgresql.c"
#else
#include "db_sqlite.c"
#endif
+54
View File
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */
#ifndef DB_H
#define DB_H
#include "../main.h"
#include <stdint.h>
/* Auth */
typedef struct {
char *name;
char *pass;
} i_auth_t;
typedef struct {
char privileges;
uint32_t uid;
} o_auth_t;
/* Get file */
typedef struct {
uint32_t id;
uint32_t user_id;
char name[FILE_NAME_LEN];
} i_get_file_db;
enum sort_direction { ASC, DESC };
enum sort_by { ID, NAME, CREATED_AT, USER_ID };
typedef struct {
uint32_t user_id; // TODO: implement
char name[FILE_NAME_LEN]; // TODO: implement
uint32_t limit;
uint32_t offset;
enum sort_by sort_by;
enum sort_direction sort_direction;
} i_get_files_db;
typedef struct {
char uname[UNAME_LEN];
char pass[PASS_LEN];
char email[EMAIL_LEN];
} i_db_user_create;
int32_t init_db_connection();
int32_t db_save_file(session *s);
int32_t db_user_auth(i_auth_t *credentials, o_auth_t *response);
s_file_t *db_get_file(i_get_file_db *arg);
uint64_t db_get_files_data(i_get_files_db *arg, fl_t **fl_start,
uint64_t *full_count);
int32_t db_user_create(i_db_user_create *);
#endif
+438
View File
@@ -0,0 +1,438 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */
#include <db.h>
#include "../main.h"
#include <endian.h>
#include <fcntl.h>
#include <libpq-fe.h>
#include <netinet/in.h>
#include <openssl/sha.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define UNUSED(x) (void)(x)
#define Q_LEN 128
#define BIN 1
#define TEXT 0
#define ENCR_SIZE 2048
static PGconn *conn = NULL;
static PGresult *res = NULL;
static void clearRes(void) {
PQclear(res);
res = NULL;
}
static int terminate(int code) {
if (code != 0)
fprintf(stderr, "%s\n", PQerrorMessage(conn));
if (res != NULL)
PQclear(res);
if (conn != NULL)
PQfinish(conn);
return code;
}
static int exit_query(int code) {
if (code != 0)
fprintf(stderr, "%s\n", PQerrorMessage(conn));
if (res != NULL)
PQclear(res);
return code;
}
static int exit_query_2(int code) {
if (code == 0)
fprintf(stderr, "%s\n", PQerrorMessage(conn));
if (res != NULL)
PQclear(res);
return code;
}
static void *exit_query_3(void *ptr) {
fprintf(stderr, "%s\n", PQerrorMessage(conn));
if (res != NULL)
PQclear(res);
return ptr;
}
static void processNotice(void *arg, const char *message) {
UNUSED(arg);
UNUSED(message);
// do nothing
}
int init_db_connection() {
// TODO: get from external configs
conn = PQconnectdb("user=postgres password=dobro host=127.0.0.1 dbname=bbs");
if (PQstatus(conn) != CONNECTION_OK)
return terminate(1);
int server_ver = PQserverVersion(conn);
char *user = PQuser(conn);
char *db_name = PQdb(conn);
// res = PQexec(conn, "SELECT pg_catalog.set_config('search_path', '',
// false)"); if (PQresultStatus(res) != PGRES_TUPLES_OK) {
// return terminate(2);
// }
printf("Connection established! server version: %d, user: %s, db: %s\n",
server_ver, user, db_name);
return exit_query(0);
}
void SHA256_raw_to_string(const unsigned char *passHashed, char *restrict out) {
int i;
for (i = 0; i < 4; i++) {
uint64_t *num_pointer = (uint64_t *)(passHashed + i * 8);
sprintf(out + 16 * i, "%016lx", htobe64(*num_pointer));
}
}
void string_to_SHA256(const char *str, char *restrict out) {
unsigned char md[SHA256_DIGEST_LENGTH];
unsigned char *passHashed = SHA256((unsigned char *)str, strlen(str), md);
SHA256_raw_to_string(passHashed, out);
}
int32_t db_user_auth(i_auth_t *c, o_auth_t *r) {
const char *paramValues[1];
paramValues[0] = c->name;
char passHashed[SHA256_DIGEST_LENGTH * 2];
res = PQexecParams(conn,
"SELECT id, username, password, privileges "
"FROM users "
"WHERE username= $1",
1, NULL, paramValues, NULL, NULL, 0);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query(1);
const int tup_len = PQntuples(res);
if (tup_len == 0) {
return exit_query(4);
}
const char *pass = PQgetvalue(res, 0, 2);
string_to_SHA256(c->pass, passHashed);
if (pass == NULL) {
/* TODO: return */
}
if (!strcmp(passHashed, pass)) {
r->privileges = PQgetvalue(res, 0, 3)[0];
r->uid = atoi(PQgetvalue(res, 0, 0));
clearRes();
char u_buf[128];
sprintf(u_buf, "UPDATE users SET last_login = NOW() WHERE id = %u", r->uid);
res = PQexec(conn, u_buf);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query(3);
return exit_query(0);
}
return exit_query(2);
}
int32_t db_user_create(i_db_user_create *args) {
const char *paramValues[3];
int32_t paramLengths[3];
char passHashed[SHA256_DIGEST_LENGTH * 2];
paramValues[0] = args->uname;
paramValues[2] = args->email;
paramLengths[0] = strlen(args->uname);
paramLengths[2] = strlen(args->email);
uint32_t ret_value;
string_to_SHA256(args->pass, passHashed);
paramValues[1] = passHashed;
paramLengths[1] = 64;
/* Check username */
res = PQexecParams(conn, "SELECT id FROM users WHERE username=$1", 1, NULL,
paramValues, paramLengths, NULL, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_2(-1);
if (PQntuples(res) > 0) {
return exit_query_2(-2);
}
/* Check email */
res = PQexecParams(conn, "SELECT id FROM users WHERE email=$1", 1, NULL,
paramValues+2, paramLengths+2, NULL, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_2(-1);
if (PQntuples(res) > 0) {
return exit_query_2(-3);
}
clearRes();
res = PQexecParams(conn,
"INSERT INTO users (username, password, email, "
"privileges, created_at, last_login)"
" VALUES ($1, $2, $3, 1, NOW(), NOW()) RETURNING id",
3, NULL, paramValues, paramLengths, NULL, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query(-4);
ret_value = atoi(PQgetvalue(res, 0, 0));
clearRes();
return ret_value;
}
int32_t db_save_file(session *s) {
const char *paramValues[6];
int paramFormats[6];
int paramLengths[6];
/* int collision_id = 0; */
s_file_t *sf = s->file;
char empty_str[] = "";
int32_t uid_n = htonl(s->uid);
uint64_t size_n = htobe64(sf->size);
int32_t hash_n = htonl(sf->hash);
int32_t perm_n = htonl(sf->permissions);
paramValues[0] = (char *)&uid_n;
paramValues[1] = sf->name;
paramValues[2] = (char *)&size_n;
paramValues[3] = (char *)&hash_n;
if (sf->description != NULL) {
paramValues[4] = sf->description;
} else {
paramValues[4] = empty_str;
}
paramValues[5] = (char *)&perm_n;
paramLengths[0] = sizeof(int32_t);
paramLengths[1] = strlen(sf->name);
paramLengths[2] = sizeof(uint64_t);
paramLengths[3] = sizeof(int32_t);
if (sf->description != NULL) {
paramLengths[4] = strlen(sf->description);
} else {
paramLengths[4] = 0;
}
paramLengths[5] = sizeof(int32_t);
paramFormats[0] = BIN;
paramFormats[1] = TEXT;
paramFormats[2] = BIN;
paramFormats[3] = BIN;
paramFormats[4] = TEXT;
paramFormats[5] = BIN;
res = PQexecParams(conn,
"INSERT INTO files(user_id, name, size, created_at, "
"hash, description, permissions) "
"VALUES ($1, $2, $3, NOW(), $4, $5, $6) "
"RETURNING id",
6, NULL, paramValues, paramLengths, paramFormats, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_2(0);
int id = atoi(PQgetvalue(res, 0, 0));
clearRes();
return id;
}
// TODO: get file
s_file_t *db_get_file(i_get_file_db *arg) {
s_file_t *sf;
char s_field[24];
char value[64] = "";
const char *paramValues[1];
char query[512];
uint32_t q_len = 0;
if (arg->id) {
strcpy(s_field, "id");
sprintf(value, "%u", arg->id);
} else if (strlen(arg->name)) {
strcpy(s_field, "name");
strcpy(value, arg->name);
} else if (arg->user_id) {
strcpy(s_field, "user_id");
sprintf(value, "%u", arg->user_id);
} else {
fprintf(stderr, "db_get_file: none of the args is specified!\n");
return NULL;
}
paramValues[0] = value;
sprintf(
query,
"SELECT files.id, user_id, name, size, description, permissions, hash "
"FROM files JOIN users ON user_id = users.id WHERE files.%s = $1%n",
s_field, &q_len);
res = PQexecParams(conn, query, 1, NULL, paramValues, NULL, NULL, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_3(NULL);
sf = malloc(sizeof(s_file_t));
sf->id = atoi(PQgetvalue(res, 0, 0));
sf->owner_id = atoi(PQgetvalue(res, 0, 1));
char *name = PQgetvalue(res, 0, 2);
sf->name = malloc(strlen(name) + 1);
strcpy(sf->name, name);
sf->size = atoll(PQgetvalue(res, 0, 3));
char *description = PQgetvalue(res, 0, 4);
sf->description = malloc(strlen(description) + 1);
strcpy(sf->description, description);
sf->permissions = atoi(PQgetvalue(res, 0, 5));
sf->hash = atoi(PQgetvalue(res, 0, 6));
clearRes();
return sf;
}
uint64_t db_get_files_data(i_get_files_db *arg, fl_t **fl_start,
uint64_t *full_count) {
fl_t *fl_current;
char query[512];
char sort_by[16] = "id";
char sort_dir[5] = "ASC";
const char *paramValues[3];
int paramLengths[3];
int paramFormats[3];
int i;
uint64_t count;
uint64_t n_limit = htobe64(arg->limit);
uint64_t n_offset = htobe64(arg->offset);
bool by_name = strlen(arg->name) > 0;
uint32_t params_num = 3;
switch (arg->sort_by) {
case NAME:
strcpy(sort_by, "name");
break;
case CREATED_AT:
strcpy(sort_by, "created_at");
break;
case USER_ID:
strcpy(sort_by, "user_id");
break;
case ID: // id is by default
default:
break;
}
if (arg->sort_direction == DESC) {
strcpy(sort_dir, "DESC");
}
paramValues[0] = (char *)&n_limit;
paramValues[1] = (char *)&n_offset;
paramValues[2] = arg->name;
paramLengths[0] = sizeof(n_limit);
paramLengths[1] = sizeof(n_offset);
paramLengths[2] = strlen(arg->name);
paramFormats[0] = BIN;
paramFormats[1] = BIN;
paramFormats[2] = TEXT;
if (!by_name) {
params_num = 2;
sprintf(query,
"SELECT files.id, user_id, name, size, description, permissions, "
"hash, username "
"FROM files JOIN users ON user_id = users.id "
"ORDER BY files.%s %s LIMIT $1 OFFSET $2",
sort_by, sort_dir);
} else {
sprintf(query,
"SELECT files.id, user_id, name, size, description, permissions, "
"hash, username "
"FROM files JOIN users ON user_id = users.id "
"WHERE name ILIKE '%%' || $3 || '%%'"
"ORDER BY files.%s %s LIMIT $1 OFFSET $2",
sort_by, sort_dir);
}
res = PQexecParams(conn, query, params_num, NULL, paramValues, paramLengths,
paramFormats, TEXT);
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_2(0);
for (i = 0; i < PQntuples(res); i++) {
fl_t *l_item = malloc(sizeof(fl_t));
l_item->next = NULL;
l_item->id = atoi(PQgetvalue(res, i, 0));
l_item->owner_id = atoi(PQgetvalue(res, i, 1));
char *name = PQgetvalue(res, i, 2);
l_item->name = malloc(strlen(name) + 1);
strcpy(l_item->name, name);
l_item->size = atoll(PQgetvalue(res, i, 3));
char *description = PQgetvalue(res, i, 4);
l_item->description = malloc(strlen(description) + 1);
strcpy(l_item->description, description);
l_item->permissions = atoi(PQgetvalue(res, i, 5));
l_item->hash = atoi(PQgetvalue(res, i, 6));
char *owner = PQgetvalue(res, i, 7);
l_item->owner = malloc(strlen(owner) + 1);
strcpy(l_item->owner, owner);
if (*fl_start == NULL) {
*fl_start = l_item;
} else {
fl_current->next = l_item;
}
fl_current = l_item;
}
count = PQntuples(res);
clearRes();
if (by_name) {
res = PQexecParams(conn,
"SELECT COUNT(id) "
"FROM files "
"WHERE name ILIKE '%%' || $1 || '%%'",
1, NULL, paramValues + 2, paramLengths + 2,
paramFormats + 2, TEXT);
} else {
res = PQexec(conn, "SELECT COUNT(id) "
"FROM files ");
}
if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res))
return exit_query_2(0);
*full_count = atoll(PQgetvalue(res, 0, 0));
clearRes();
return count;
}
+123
View File
@@ -0,0 +1,123 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */
#include "../main.h"
#include <db.h>
#include <endian.h>
#include <openssl/sha.h>
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
#define UNUSED(x) (void)(x)
#define Q_LEN 128
#define BIN 1
#define TEXT 0
#define ENCR_SIZE 2048
static sqlite3 *db;
void print_err(char **err) {
if (*err != NULL) {
printf("%s\n", *err);
sqlite3_free(*err);
*err = NULL;
}
}
int check_and_create_tables() {
int res = 0;
char *err = NULL;
res = sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS 'users' ("
" 'id' SERIAL,"
" 'username' VARCHAR(50) NOT NULL,"
" 'password' CHARACTER(64) NOT NULL,"
" 'email' VARCHAR(255) NOT NULL,"
" 'created_at' TIMESTAMP NOT NULL,"
" 'last_login' TIMESTAMP NULL,"
" 'privileges' SMALLINT NOT NULL,"
" CONSTRAINT 'users_pkey' PRIMARY KEY ('id'),"
" CONSTRAINT 'users_email_key' UNIQUE ('email'),"
" CONSTRAINT 'users_username_key' UNIQUE ('username')"
"); ",
NULL, NULL, &err);
print_err(&err);
res = sqlite3_exec(db,
"CREATE UNIQUE INDEX IF NOT EXISTS 'username_idx'"
" ON 'users' ("
" 'username' ASC"
" );",
NULL, NULL, &err);
print_err(&err);
res = sqlite3_exec(db,
"CREATE UNIQUE INDEX IF NOT EXISTS 'email_idx' "
"ON 'users' ("
"'email' ASC"
");",
NULL, NULL, &err);
print_err(&err);
res = sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS 'files' ("
" 'id' SERIAL,"
" 'user_id' INTEGER NULL,"
" 'name' VARCHAR(255) NOT NULL,"
" 'size' BIGINT NOT NULL,"
" 'created_at' DATE NOT NULL,"
" 'description' TEXT NULL,"
" 'permissions' INTEGER NULL,"
" 'hash' INTEGER NULL,"
" CONSTRAINT 'files_pkey' PRIMARY KEY ('id'),"
" CONSTRAINT 'fk_user' FOREIGN KEY ('user_id')"
" REFERENCES 'users' ('id') ON DELETE NO ACTION"
" ON UPDATE NO ACTION"
");",
NULL, NULL, &err);
print_err(&err);
return res;
}
int init_db_connection(void) {
int res = sqlite3_open("db.sql", &db);
char *err = NULL;
if (!res) {
res = check_and_create_tables();
}
return 0;
}
void SHA256_raw_to_string(const unsigned char *passHashed, char *restrict out) {
int i;
for (i = 0; i < 4; i++) {
uint64_t *num_pointer = (uint64_t *)(passHashed + i * 8);
sprintf(out + 16 * i, "%016lx", htobe64(*num_pointer));
}
}
void string_to_SHA256(const char *str, char *restrict out) {
unsigned char md[SHA256_DIGEST_LENGTH];
unsigned char *passHashed = SHA256((unsigned char *)str, strlen(str), md);
SHA256_raw_to_string(passHashed, out);
}
int32_t db_user_auth(i_auth_t *c, o_auth_t *r) { return 0; }
int32_t db_user_create(i_db_user_create *args) { return 0; }
int32_t db_save_file(session *s) { return 0; }
// TODO: get file
s_file_t *db_get_file(i_get_file_db *arg) {
s_file_t *sf = NULL;
return sf;
}
uint64_t db_get_files_data(i_get_files_db *arg, fl_t **fl_start,
uint64_t *full_count) {
fl_t *fl_current = NULL;
return 0;
}