422 lines
10 KiB
C
422 lines
10 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sqlite3.h>
|
|
|
|
#include <cJSON.h>
|
|
#include <logger.h>
|
|
#include <database.h>
|
|
#include <models/schedule.h>
|
|
#include <models/junction_tag.h>
|
|
#include <models/tag.h>
|
|
|
|
static int
|
|
db_update_insert(schedule_t *schedule, sqlite3_stmt *stmt)
|
|
{
|
|
int rc;
|
|
uint16_t *periods_blob = schedule_periods_to_blob(schedule);
|
|
int blob_size = (int)sizeof(uint16_t) * ((periods_blob[0] * 2) + 1);
|
|
|
|
sqlite3_bind_int(stmt, 1, schedule->id);
|
|
sqlite3_bind_blob(stmt, 2, schedule->uid, sizeof(uuid_t), SQLITE_STATIC);
|
|
sqlite3_bind_text(stmt, 3, schedule->name, -1, SQLITE_STATIC);
|
|
sqlite3_bind_blob(stmt, 4, periods_blob, blob_size, SQLITE_STATIC);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
free(periods_blob);
|
|
|
|
return rc != SQLITE_DONE;
|
|
}
|
|
static schedule_t*
|
|
schedule_db_select_mapper(sqlite3_stmt *stmt)
|
|
{
|
|
const uint16_t *periods_blob;
|
|
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
|
{
|
|
const char *name = sqlite3_column_name(stmt, i);
|
|
switch(name[0])
|
|
{
|
|
case 'i': // id
|
|
new_schedule->id = sqlite3_column_int(stmt, i);
|
|
break;
|
|
case 'n': // name
|
|
strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
|
new_schedule->name[127] = '\0';
|
|
break;
|
|
case 'p': // periods
|
|
periods_blob = sqlite3_column_blob(stmt, i);
|
|
new_schedule->periods_count = periods_blob[0];
|
|
new_schedule->periods = malloc(sizeof(period_t) * periods_blob[0]);
|
|
|
|
for(int i = 0; i < periods_blob[0]; ++i)
|
|
{
|
|
new_schedule->periods[i].start = periods_blob[(i * 2) + 1];
|
|
new_schedule->periods[i].end = periods_blob[(i * 2) + 2];
|
|
}
|
|
|
|
break;
|
|
case 'u': // uid
|
|
uuid_copy(new_schedule->uid, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
|
break;
|
|
default: // ignore columns not implemented
|
|
break;
|
|
}
|
|
}
|
|
return new_schedule;
|
|
}
|
|
|
|
static schedule_t**
|
|
schedule_db_select(sqlite3_stmt *stmt)
|
|
{
|
|
schedule_t **all_schedules = malloc(sizeof(schedule_t*));
|
|
|
|
int row = 0;
|
|
|
|
while(true)
|
|
{
|
|
int s;
|
|
|
|
s = sqlite3_step(stmt);
|
|
if (s == SQLITE_ROW)
|
|
{
|
|
schedule_t *new_schedule = schedule_db_select_mapper(stmt);
|
|
row++;
|
|
|
|
all_schedules = (schedule_t**)realloc(all_schedules, sizeof(schedule_t*) * (row + 1));
|
|
all_schedules[row - 1] = new_schedule;
|
|
}
|
|
else
|
|
{
|
|
if(s == SQLITE_DONE)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("error selecting schedules from database: %s\n", sqlite3_errstr(s));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
all_schedules[row] = NULL;
|
|
return all_schedules;
|
|
}
|
|
|
|
int
|
|
schedule_save(schedule_t *schedule)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
if(schedule->id)
|
|
{
|
|
sqlite3_prepare_v2(global_database, "UPDATE schedules SET uid = ?2, name = ?3, periods = ?4 WHERE id=?1;", -1, &stmt, NULL);
|
|
}
|
|
else
|
|
{
|
|
sqlite3_prepare_v2(global_database, "INSERT INTO schedules(uid, name, periods) values (?2, ?3, ?4);", -1, &stmt, NULL);
|
|
}
|
|
|
|
int result = db_update_insert(schedule, stmt);
|
|
|
|
if(result)
|
|
{
|
|
if(schedule->id)
|
|
{
|
|
LOG_ERROR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
schedule->id = sqlite3_last_insert_rowid(global_database);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int
|
|
schedule_remove(schedule_t *schedule)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
if(!schedule->id)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
sqlite3_prepare_v2(global_database, "DELETE FROM schedules WHERE id=?1;", -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, schedule->id);
|
|
|
|
int rc = sqlite3_step(stmt);
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return rc != SQLITE_DONE;
|
|
}
|
|
|
|
int
|
|
schedule_is_protected(schedule_t *schedule)
|
|
{
|
|
uuid_t tmp_uuid;
|
|
|
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
memcpy(tmp_uuid, "off", 3);
|
|
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
memcpy(tmp_uuid, "on", 2);
|
|
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
schedule_free(schedule_t *schedule)
|
|
{
|
|
free(schedule->periods);
|
|
free(schedule);
|
|
}
|
|
|
|
void
|
|
schedule_free_list(schedule_t **schedules)
|
|
{
|
|
for(int i = 0; schedules[i] != NULL; ++i)
|
|
{
|
|
schedule_free(schedules[i]);
|
|
}
|
|
free(schedules);
|
|
}
|
|
|
|
uint16_t*
|
|
schedule_periods_to_blob(schedule_t *schedule)
|
|
{
|
|
uint16_t *blob = malloc(sizeof(uint16_t) * ((schedule->periods_count * 2) + 1));
|
|
|
|
blob[0] = schedule->periods_count;
|
|
|
|
for(int i = 0; i < schedule->periods_count; i++)
|
|
{
|
|
blob[(i * 2) + 1] = schedule->periods[i].start;
|
|
blob[(i * 2) + 2] = schedule->periods[i].end;
|
|
}
|
|
return blob;
|
|
}
|
|
|
|
cJSON*
|
|
schedule_to_json(schedule_t *schedule)
|
|
{
|
|
cJSON *json = cJSON_CreateObject();
|
|
|
|
cJSON *json_name = cJSON_CreateString(schedule->name);
|
|
if(json_name == NULL)
|
|
{
|
|
cJSON_Delete(json);
|
|
return NULL;
|
|
}
|
|
cJSON_AddItemToObject(json, "name", json_name);
|
|
|
|
char uuid_str[UUID_STR_LEN];
|
|
schedule_uid_unparse(schedule->uid, uuid_str);
|
|
cJSON *json_id = cJSON_CreateString(uuid_str);
|
|
if(json_name == NULL)
|
|
{
|
|
cJSON_Delete(json);
|
|
return NULL;
|
|
}
|
|
cJSON_AddItemToObject(json, "id", json_id);
|
|
|
|
cJSON *json_periods = cJSON_CreateArray();
|
|
if(json_periods == NULL)
|
|
{
|
|
cJSON_Delete(json);
|
|
return NULL;
|
|
}
|
|
cJSON_AddItemToObject(json, "periods", json_periods);
|
|
|
|
for(int i = 0; i < schedule->periods_count; ++i)
|
|
{
|
|
cJSON *json_period = cJSON_CreateObject();
|
|
if (json_period == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
char start_str[8], end_str[8];
|
|
period_t *period = &schedule->periods[i];
|
|
sprintf(start_str, "%02d:%02d", period->start / 60, period->start % 60);
|
|
sprintf(end_str, "%02d:%02d", period->end / 60, period->end % 60);
|
|
|
|
cJSON *json_period_start = cJSON_CreateString(start_str);
|
|
if (json_period_start == NULL)
|
|
{
|
|
LOG_DEBUG("failed to add start period from string '%s'\n", start_str);
|
|
cJSON_Delete(json_period);
|
|
continue;
|
|
}
|
|
cJSON_AddItemToObject(json_period, "start", json_period_start);
|
|
|
|
cJSON *json_period_end = cJSON_CreateString(end_str);
|
|
if (json_period_end == NULL)
|
|
{
|
|
LOG_DEBUG("failed to add end period from string '%s'\n", end_str);
|
|
cJSON_Delete(json_period);
|
|
continue;
|
|
}
|
|
cJSON_AddItemToObject(json_period, "end", json_period_end);
|
|
|
|
cJSON_AddItemToArray(json_periods, json_period);
|
|
}
|
|
|
|
cJSON *json_tags = cJSON_CreateArray();
|
|
int *tags_ids = junction_tag_get_tags_for_schedule_id(schedule->id);
|
|
if(tags_ids != NULL)
|
|
{
|
|
for(int i = 0; tags_ids[i] != 0; ++i)
|
|
{
|
|
char *tag = tag_get_tag(tags_ids[i]);
|
|
if(tag == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cJSON *json_tag = cJSON_CreateString(tag);
|
|
if (json_tag == NULL)
|
|
{
|
|
LOG_DEBUG("failed to add tag from string '%s'\n", tag);
|
|
free(tag);
|
|
continue;
|
|
}
|
|
cJSON_AddItemToArray(json_tags, json_tag);
|
|
free(tag);
|
|
}
|
|
free(tags_ids);
|
|
}
|
|
cJSON_AddItemToObject(json, "tags", json_tags);
|
|
|
|
return json;
|
|
}
|
|
|
|
schedule_t*
|
|
schedule_get_by_id_or_off(int id)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE id = ?1;", -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
schedule_t **sql_result = schedule_db_select(stmt);
|
|
|
|
schedule_t *result = sql_result[0];
|
|
free(sql_result);
|
|
|
|
if(result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
uuid_t tmp_uuid;
|
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
memcpy(tmp_uuid, "off", 3);
|
|
|
|
return schedule_get_by_uid(tmp_uuid);
|
|
}
|
|
|
|
schedule_t*
|
|
schedule_get_by_id(int id)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE id = ?1;", -1, &stmt, NULL);
|
|
sqlite3_bind_int(stmt, 1, id);
|
|
|
|
schedule_t **sql_result = schedule_db_select(stmt);
|
|
|
|
schedule_t *result = sql_result[0];
|
|
free(sql_result);
|
|
|
|
return result;
|
|
}
|
|
|
|
schedule_t*
|
|
schedule_get_by_uid(uuid_t uid)
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE uid = ?1;", -1, &stmt, NULL);
|
|
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
|
|
|
schedule_t **sql_result = schedule_db_select(stmt);
|
|
|
|
schedule_t *result = sql_result[0];
|
|
free(sql_result);
|
|
|
|
return result;
|
|
}
|
|
|
|
schedule_t**
|
|
schedule_get_all()
|
|
{
|
|
sqlite3_stmt *stmt;
|
|
|
|
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules;", -1, &stmt, NULL);
|
|
|
|
return schedule_db_select(stmt);
|
|
|
|
}
|
|
|
|
int
|
|
schedule_uid_parse(const char *uid_str, uuid_t result)
|
|
{
|
|
if(strcmp("off", uid_str) == 0)
|
|
{
|
|
memset(result, 0, sizeof(uuid_t));
|
|
memcpy(result, "off", 3);
|
|
return 0;
|
|
}
|
|
if(strcmp("on", uid_str) == 0)
|
|
{
|
|
memset(result, 0, sizeof(uuid_t));
|
|
memcpy(result, "on", 2);
|
|
return 0;
|
|
}
|
|
|
|
if(uuid_parse(uid_str, result))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
schedule_uid_unparse(const uuid_t uid, char *result)
|
|
{
|
|
uuid_t tmp_uuid;
|
|
|
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
memcpy(tmp_uuid, "off", 3);
|
|
if(uuid_compare(uid, tmp_uuid) == 0)
|
|
{
|
|
strcpy(result, "off");
|
|
return;
|
|
}
|
|
|
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
memcpy(tmp_uuid, "on", 2);
|
|
if(uuid_compare(uid, tmp_uuid) == 0)
|
|
{
|
|
strcpy(result, "on");
|
|
return;
|
|
}
|
|
|
|
uuid_unparse(uid, result);
|
|
}
|