#include #include #include #include #include #include #include #include #include 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(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); }