/* dblayer.c - Database layer for ck -----------------------------------*- C -*-
*
* This file is part of ck, the config keeper
*
* -----------------------------------------------------------------------------
*
* Copyright (C) 2018 Anastasis Grammenos
* GPLv3 (see LICENCE for the full notice)
*
* -----------------------------------------------------------------------------
*
* Give access to the database.
*
* -------------------------------------------------------------------------- */
#include "dblayer.h"
#include "dbhelper.h"
#include "ckutil.h"
const char * const DB_FILE_NAME = "/ckdb";
/* figure out the database name */
void make_db_name(char *ret, const char *confPath) {
char db_path[STR_L];
strcpy(db_path, confPath);
strcat(db_path, DB_FILE_NAME);
strcpy(ret, db_path);
}
/* Check if the db file exists*/
int db_exists(const UserOpt *opt) {
char db_path[STR_L];
make_db_name(db_path, opt->confDir);
return util_is_file_rw(db_path);
}
/* check if db has the correct tables */
int check_initialized_DB(sqlite3 *db) {
char sql[STR_M];
dbh_form_query_select_all_tables(sql);
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
int program_table_ok, config_table_ok, rel_table_ok = 0;
while (sqlite3_step(stmt) != SQLITE_DONE) {
const unsigned char *tmpbuf = sqlite3_column_text(stmt, 1);
if (strcmp((char *)tmpbuf, TBL_PROGRAM) == 0) {
program_table_ok = 1;
}
if (strcmp((char *)tmpbuf, TBL_CONFIG) == 0) {
config_table_ok = 1;
}
if (strcmp((char *)tmpbuf, TBL_REL) == 0) {
rel_table_ok = 1;
}
}
if (!program_table_ok
|| !config_table_ok
|| !rel_table_ok ) {
return 1;
}
sqlite3_finalize(stmt);
return 0;
}
DB empty_DB(SqlError err) {
return (DB){ .ptr = NULL, .error = err };
}
DB new_DB(sqlite3 *db) {
return (DB){ .ptr = db, .error = SQL_NO_ERR };
}
void close_DB(DB *db) {
sqlite3_close(db->ptr);
}
DB init_make_DB(const UserOpt *opt) {
sqlite3 *db;
char db_path[STR_L];
int rc;
make_db_name(db_path, opt->confDir);
rc = sqlite3_open(db_path, &db);
if (rc != SQLITE_OK) {
return empty_DB(SQL_ERR_NO_DB_FILE);
}
return new_DB(db);
}
DB open_DB(const UserOpt *opt) {
sqlite3 *db;
int rc;
char db_path[STR_L];
make_db_name(db_path, opt->confDir);
rc = sqlite3_open(db_path, &db);
if (rc) {
return empty_DB(SQL_ERR_NO_DB_FILE);
}
if (check_initialized_DB(db)) {
return empty_DB(SQL_ERR_NO_TABLES);
}
return new_DB(db);
}
void init_make_tables(DB *db) {
char sql[STR_L];
dbh_form_query_make_tables(sql);
int rc = sqlite3_exec(db->ptr, sql, 0, 0, 0);
if (rc != SQLITE_OK ) {
PRINT_ERR("Could not create empty db.");
db->error = SQL_ERR_SQLITE;
return;
}
}
int get_next_valid_id_from_table(DB *db, const char* tableName) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_select_id_from(sql, tableName);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_text(stmt, 1, tableName, strlen(tableName), 0);
int id = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
int a = sqlite3_column_int(stmt, 0);
if (a != id) {
break;
}
id++;
}
sqlite3_finalize(stmt);
return id;
}
int insert_to_program_table(DB *db, const char *name) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_L];
dbh_form_query_insert_program(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
PRINT_ERR("while preparing insert to program sql.");
db->error = SQL_ERR_SQLITE;
return -1;
}
int id = get_next_valid_id_from_table(db, TBL_PROGRAM);
if (id == -1) {
db->error = SQL_ERR_SQLITE;
return -1;
}
sqlite3_bind_int(stmt, 1, id);
sqlite3_bind_text(stmt, 2, name, strlen(name), 0);
if (sqlite3_step(stmt) != SQLITE_DONE) {
PRINT_ERR("while excecuting insert to program sql.");
db->error = SQL_ERR_SQLITE;
return -1;
}
sqlite3_finalize(stmt);
return id;
}
int insert_to_config_table(DB *db, const char *path, const int secret, const int prime) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_L];
dbh_form_query_insert_config(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
PRINT_ERR("Error while preparing insert to config sql.");
db->error = SQL_ERR_SQLITE;
return -1;
}
int id = get_next_valid_id_from_table(db, TBL_CONFIG);
if (id == -1) {
db->error = SQL_ERR_SQLITE;
return -1;
}
sqlite3_bind_int(stmt, 1, id);
sqlite3_bind_text(stmt, 2, path, strlen(path), 0);
sqlite3_bind_int(stmt, 3, secret);
sqlite3_bind_int(stmt, 4, prime);
if (sqlite3_step(stmt) != SQLITE_DONE) {
PRINT_ERR("Error while excecuting insert to config sql.");
db->error = SQL_ERR_SQLITE;
return-1;
}
sqlite3_finalize(stmt);
return id;
}
int insert_to_rel_table(DB *db, const int pid, const int cid) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dhb_form_query_insert_relationship(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
db->error = SQL_ERR_SQLITE;
PRINT_ERR("while preparing insert to rel sql.");
return -1;
}
sqlite3_bind_int(stmt, 1, pid);
sqlite3_bind_int(stmt, 2, cid);
if (sqlite3_step(stmt) != SQLITE_DONE) {
db->error = SQL_ERR_SQLITE;
PRINT_ERR("while excecuting insert to rel sql.");
return-1;
}
sqlite3_finalize(stmt);
return 1;
}
/* Returns -2 or error, -1 if program doesn't exist
* else the program ID */
int get_program_id(DB *db, const char* name) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dhb_form_query_find_program(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
PRINT_ERR("Error while preparing get_program_id sql.");
return -2;
}
sqlite3_bind_text(stmt, 1, name, strlen(name), 0);
int id = -1;
while (sqlite3_step(stmt) == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
break;
}
sqlite3_finalize(stmt);
return id;
}
int get_config_id(DB *db, const char* path) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dhb_form_query_find_config(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
PRINT_ERR("while preparing get_config_id sql.");
return -2;
}
sqlite3_bind_text(stmt, 1, path, strlen(path), 0);
int id = -1;
while (sqlite3_step(stmt) == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
break;
}
sqlite3_finalize(stmt);
return id;
}
int add_insert_relationship(DB *db, const int pid, const int cid) {
// do checks
return insert_to_rel_table(db, pid, cid);
}
/* Returns the path of the found config via *ret */
int program_has_primary_config(DB *db, const int pid, char *ret, int *sec) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_L];
char condition[STR_S] = TBL_PROGRAM;
strcat(condition, ".");
strcat(condition, COL_PROGRAM_ID);
char selection[STR_M] = COL_CONFIG_PRIMARY;
strcat(selection, ", ");
strcat(selection, COL_CONFIG_PATH);
strcat(selection, ", ");
strcat(selection, COL_CONFIG_SECRET);
dbh_form_query_select_from_joined_eq(sql, selection, condition);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
PRINT_ERR("whinnle preparing program_has_primary_exists sql.");
return -2;
}
sqlite3_bind_int(stmt, 1, pid);
while (sqlite3_step(stmt) == SQLITE_ROW) {
if (sqlite3_column_int(stmt, 0) == 1) {
if (ret) {
strcpy(ret,(char *)sqlite3_column_text(stmt, 1));
}
if (sec) {
*sec = sqlite3_column_int(stmt, 2);
}
sqlite3_finalize(stmt);
return 1;
}
}
sqlite3_finalize(stmt);
return 0;
}
int add_get_or_insert_config_to_db(DB *db, const int pid, const char *path, const int secret, const int prime) {
int cid = get_config_id(db, path);
if (cid == -2) {
db->error = SQL_ERR_SQLITE;
return -1;
}
/* If config doesnt exist insert it and return it's cid */
if (cid == -1) {
if (program_has_primary_config(db, pid, NULL, NULL) && prime) {
db->error = SQL_ERR_PRIMARY_REDEFINITION;
return -1;
}
return insert_to_config_table(db, path, secret, prime);
}
/* If it exist it means the user has inserted the same path twice */
db->error = SQL_CONFIG_PATH_EXISTS;
return -1;
}
int add_get_or_insert_program_to_db(DB *db, const char *name) {
int pid = get_program_id(db, name);
if (pid == -2) {
db->error = SQL_ERR_SQLITE;
return -1;
}
if (pid == -1) {
return insert_to_program_table(db,name);
}
return pid;
}
int add_transaction_try(DB *db, const AddOpt * const opt) {
__BEGIN_TRANSACTION__
int pid = add_get_or_insert_program_to_db(db, opt->progName);
if (db->error == SQL_ERR_SQLITE) {
PRINT_ERR("Could not insert program to db.\n");
return 1;
}
int cid = add_get_or_insert_config_to_db(db, pid, opt->confPath, opt->secret, opt->prime);
if (db->error == SQL_ERR_SQLITE) {
PRINT_ERR("Could not insert config to db.\n");
return 1;
}
else if (db->error == SQL_CONFIG_PATH_EXISTS) {
PRINT_ERR("This config already exists in the database.\n");
return 1;
}
else if (db->error == SQL_ERR_PRIMARY_REDEFINITION) {
PRINT_ERR("This program already has a primary config.\n");
return 1;
}
add_insert_relationship(db, pid, cid);
if (db->error == SQL_ERR_SQLITE) {
PRINT_ERR("rel update failed\n");
return 1;
}
__END_TRANSACTION__
return 0;
}
int edit_get_prime_config_from_program(DB *db, char *pName, char *ret, int *secret) {
int pid = get_program_id(db, pName);
/* error */
if (pid == -2) {
return -1;
}
/* program exists */
if (pid > -1) {
char path[STR_M];
if (program_has_primary_config(db, pid, path, secret) == 1) {
if (!str_is_empty(path)) {
if (ret) {
char confName[STR_M];
str_make_ck_config_name(confName, path, pName);
strcpy(ret, confName);
}
return 1;
}
}
}
/* No prime config found */
return 0;
}
int list_get_paths(DB *db, cklist *ckl, int attr) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_select_paths_with_attributes(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -2;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
char path[STR_L] = "";
strcat(path, (char *)sqlite3_column_text(stmt, 0));
if (attr) {
/* secret */
if (sqlite3_column_int(stmt, 1)) {
strcat(path, " [s]");
}
/* primary */
if (sqlite3_column_int(stmt, 2)) {
strcat(path, " [p]");
}
}
list_add(ckl, path);
}
sqlite3_finalize(stmt);
return 1;
}
int list_get_programs(DB *db, cklist *ckl) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_select_programs(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -2;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
list_add(ckl, (char *)sqlite3_column_text(stmt, 0));
}
sqlite3_finalize(stmt);
return 1;
}
int list_get_path_program_tree(DB *db, cklist *ckl, int attr) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_select_programs(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -2;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
char programName[STR_M] = "";
strcat(programName, (char *)sqlite3_column_text(stmt, 0));
strcat(programName, ":");
list_add(ckl, programName);
sqlite3_stmt *stmt2;
int rc2;
char sql2[STR_L];
char selection[STR_M] = COL_CONFIG_PATH;
strcat(selection, ",");
strcat(selection, COL_CONFIG_SECRET);
strcat(selection, ",");
strcat(selection, COL_CONFIG_PRIMARY);
char condition[STR_M] = TBL_PROGRAM;
strcat(condition, ".");
strcat(condition, COL_PROGRAM_NAME);
dbh_form_query_select_from_joined_eq(sql2, selection, condition);
rc2 = sqlite3_prepare_v2(db->ptr, sql2, -1, &stmt2, 0);
if (rc2 != SQLITE_OK) {
return -2;
}
sqlite3_bind_text(stmt2, 1, (char *)sqlite3_column_text(stmt, 0), -1, 0);
while (sqlite3_step(stmt2) == SQLITE_ROW) {
char treePath[STR_L] = "|- ";
strcat(treePath, (char *)sqlite3_column_text(stmt2, 0));
if (attr) {
/* secret */
if (sqlite3_column_int(stmt2, 1)) {
strcat(treePath, " [s]");
}
/* primary */
if (sqlite3_column_int(stmt2, 2)) {
strcat(treePath, " [p]");
}
}
list_add(ckl, treePath);
}
sqlite3_finalize(stmt2);
}
sqlite3_finalize(stmt);
return 1;
}
int delete_prog(DB *db, int pid) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_delete_x_from_y(sql, COL_PROGRAM_ID, TBL_PROGRAM);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_int(stmt, 1, pid);
sqlite3_step(stmt);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_finalize(stmt);
return 0;
}
int delete_conf(DB *db, int cid) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_delete_x_from_y(sql, COL_CONFIG_ID, TBL_CONFIG);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_int(stmt, 1, cid);
sqlite3_step(stmt);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_finalize(stmt);
return 0;
}
int get_pid_from_cid(DB *db, int cid) {
int pid = -1;
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_get_pid_from_cid(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_int(stmt, 1, cid);
while (sqlite3_step(stmt) == SQLITE_ROW) {
pid = sqlite3_column_int(stmt, 0);
}
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_finalize(stmt);
return pid;
}
int get_program_relations(DB *db, int pid) {
int count = -1;
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_count_program_relations(sql);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_int(stmt, 1, pid);
while (sqlite3_step(stmt) == SQLITE_ROW) {
count = sqlite3_column_int(stmt, 0);
}
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_finalize(stmt);
return count;
}
/* Removes the relationship of `cid` with the corresponding program.
* Returns the program's pid on succes, negative integer otherwise.
*/
int remove_conf_rel(DB *db, int cid) {
sqlite3_stmt *stmt;
int rc;
int pid = get_pid_from_cid(db, cid);
char sql[STR_M];
dbh_form_query_delete_x_from_y(sql, COL_REL_CONFIG_ID, TBL_REL);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_bind_int(stmt, 1, cid);
sqlite3_step(stmt);
if (rc != SQLITE_OK) {
return -1;
}
sqlite3_finalize(stmt);
return pid;
}
int remove_all_configs(DB *db, int pid) {
sqlite3_stmt *stmt;
int rc;
char sql[STR_M];
dbh_form_query_select_from_joined_eq(sql, COL_REL_CONFIG_ID, COL_REL_PROGRAM_ID);
rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
return -2;
}
sqlite3_bind_int(stmt, 1, pid);
while (sqlite3_step(stmt) == SQLITE_ROW) {
delete_conf(db, sqlite3_column_int(stmt, 0));
remove_conf_rel(db, sqlite3_column_int(stmt, 0));
}
sqlite3_finalize(stmt);
return 0;
}
int del_transaction_try(DB *db, char *arg, int conf) {
__BEGIN_TRANSACTION__
int pid = -1;
if (conf) {
// del conf
int cid = get_config_id(db, arg);
printf("cid: %d\n", cid);
if (cid < 0) {
PRINT_ERR("Config doesn't exist in the database.\n");
}
if (delete_conf(db, cid)) {
PRINT_ERR("Could not delete config from db.\n");
return -1;
}
// handle relations
pid = remove_conf_rel(db, cid);
printf("pid: %d\n", pid);
if (get_program_relations(db, pid) > 0) {
printf("More rels left\n");
goto end;
}
} else {
pid = get_program_id(db, arg);
}
if (pid < 0) {
PRINT_ERR("Program not found in the db.\n");
return -1;
}
/* If we are deleting a proram we should delete everything that
* refferences it (configs and relationships) */
if (!conf) {
remove_all_configs(db, pid);
}
if(delete_prog(db, pid)) {
PRINT_ERR("Could not delete program from db.\n");
}
if (db->error == SQL_ERR_SQLITE) {
return -1;
}
end:
__END_TRANSACTION__
return 0;
}