server/src/kernel/sqlite.c

289 lines
9.5 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
#include <platform.h>
#include <kernel/config.h>
#include <kernel/faction.h>
#include <util/unicode.h>
#include <util/base36.h>
#include <util/log.h>
#include <sqlite3.h>
#include <md5.h>
#include <assert.h>
#include <string.h>
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
faction *get_faction_by_id(int uid)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
faction *f;
for (f = factions; f; f = f->next) {
if (f->subscription == uid) {
2010-08-08 10:06:34 +02:00
return f;
}
}
return NULL;
}
#define SQL_EXPECT(res, val) if (res!=val) { return val; }
#define MAX_EMAIL_LENGTH 64
#define MD5_LENGTH 32
2011-03-07 08:02:35 +01:00
#define MD5_LENGTH_0 (MD5_LENGTH+1) /* MD5 + zero-terminator */
2010-08-08 10:06:34 +02:00
#define MAX_FACTION_NAME 64
#define MAX_FACTION_NAME_0 (MAX_FACTION_NAME+1)
typedef struct stmt_cache {
2011-03-07 08:02:35 +01:00
sqlite3 *db;
sqlite3_stmt *stmt;
const char *sql;
2010-08-08 10:06:34 +02:00
int inuse;
} stmt_cache;
#define MAX_STMT_CACHE 64
static stmt_cache cache[MAX_STMT_CACHE];
static int cache_insert;
2011-03-07 08:02:35 +01:00
static sqlite3_stmt *stmt_cache_get(sqlite3 * db, const char *sql)
2010-08-08 10:06:34 +02:00
{
int i;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
for (i = 0; i != MAX_STMT_CACHE && cache[i].db; ++i) {
if (cache[i].sql == sql && cache[i].db == db) {
2010-08-08 10:06:34 +02:00
cache[i].inuse = 1;
sqlite3_reset(cache[i].stmt);
2010-08-08 10:06:34 +02:00
return cache[i].stmt;
}
}
2011-03-07 08:02:35 +01:00
if (i == MAX_STMT_CACHE) {
2010-08-08 10:06:34 +02:00
while (cache[cache_insert].inuse) {
cache[cache_insert].inuse = 0;
2011-03-07 08:02:35 +01:00
cache_insert = (cache_insert + 1) & (MAX_STMT_CACHE - 1);
2010-08-08 10:06:34 +02:00
}
i = cache_insert;
sqlite3_finalize(cache[i].stmt);
2010-08-08 10:06:34 +02:00
}
cache[i].inuse = 1;
cache[i].db = db;
cache[i].sql = sql;
sqlite3_prepare_v2(db, sql, -1, &cache[i].stmt, NULL);
2010-08-08 10:06:34 +02:00
return cache[i].stmt;
}
typedef struct db_faction {
sqlite3_uint64 id_faction;
sqlite3_uint64 id_email;
int no;
2011-03-07 08:02:35 +01:00
const char *email;
const char *passwd_md5;
const char *name;
2010-08-08 10:06:34 +02:00
} db_faction;
static int
2011-03-07 08:02:35 +01:00
db_update_email(sqlite3 * db, const faction * f, const db_faction * dbstate,
bool force, /* [OUT] */ sqlite3_uint64 * id_email)
2010-08-08 10:06:34 +02:00
{
bool update = force;
2010-08-08 10:06:34 +02:00
int res = SQLITE_OK;
char email_lc[MAX_EMAIL_LENGTH];
if (update) {
unicode_utf8_tolower(email_lc, sizeof(email_lc), f->email);
} else {
2011-03-07 08:02:35 +01:00
if (strcmp(dbstate->email, f->email) != 0) {
2010-08-08 10:06:34 +02:00
unicode_utf8_tolower(email_lc, sizeof(email_lc), f->email);
2011-03-07 08:02:35 +01:00
if (strcmp(dbstate->email, email_lc) != 0) {
2010-08-08 10:06:34 +02:00
update = true;
}
}
}
if (update) {
char email_md5[MD5_LENGTH_0];
int i;
md5_state_t ms;
md5_byte_t digest[16];
2011-03-07 08:02:35 +01:00
const char sql_insert_email[] =
2010-08-08 10:06:34 +02:00
"INSERT OR FAIL INTO email (email,md5) VALUES (?,?)";
2011-03-07 08:02:35 +01:00
const char sql_select_email[] = "SELECT id FROM email WHERE md5=?";
2010-08-08 10:06:34 +02:00
sqlite3_stmt *stmt_insert_email = stmt_cache_get(db, sql_insert_email);
sqlite3_stmt *stmt_select_email = stmt_cache_get(db, sql_select_email);
md5_init(&ms);
2011-03-07 08:02:35 +01:00
md5_append(&ms, (md5_byte_t *) email_lc, (int)strlen(email_lc));
2010-08-08 10:06:34 +02:00
md5_finish(&ms, digest);
2011-03-07 08:02:35 +01:00
for (i = 0; i != 16; ++i)
sprintf(email_md5 + 2 * i, "%.02x", digest[i]);
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
res =
sqlite3_bind_text(stmt_insert_email, 1, email_lc, -1, SQLITE_TRANSIENT);
res =
sqlite3_bind_text(stmt_insert_email, 2, email_md5, MD5_LENGTH,
SQLITE_TRANSIENT);
2010-08-08 10:06:34 +02:00
res = sqlite3_step(stmt_insert_email);
2011-03-07 08:02:35 +01:00
if (res == SQLITE_DONE) {
2010-08-08 10:06:34 +02:00
*id_email = sqlite3_last_insert_rowid(db);
} else {
2011-03-07 08:02:35 +01:00
res =
sqlite3_bind_text(stmt_select_email, 1, email_md5, MD5_LENGTH,
SQLITE_TRANSIENT);
2010-08-08 10:06:34 +02:00
res = sqlite3_step(stmt_select_email);
SQL_EXPECT(res, SQLITE_ROW);
*id_email = sqlite3_column_int64(stmt_select_email, 0);
}
}
return SQLITE_OK;
}
/*
int db_get_game(sqlite3 *db) {
int game_id = 0;
const char sql_game[] =
"SELECT id FROM game WHERE name=?";
sqlite3_stmt *stmt_game = stmt_cache(db, sql_game);
res = sqlite3_bind_text(stmt_game, 1, gamename, -1, SQLITE_TRANSIENT);
SQL_EXPECT(res, SQLITE_OK);
2010-08-08 10:06:34 +02:00
res = sqlite3_step(stmt_select);
return game_id;
}
*/
int db_update_factions(sqlite3 * db, bool force, int game_id)
{
const char sql_select[] =
"SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction"
" WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)";
sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select);
faction *f;
int res;
res = sqlite3_bind_int(stmt_select, 1, game_id);
SQL_EXPECT(res, SQLITE_OK);
res = sqlite3_bind_int(stmt_select, 2, turn - 2);
SQL_EXPECT(res, SQLITE_OK);
for (;;) {
sqlite3_uint64 id_faction;
int lastturn;
const char * no_b36;
res = sqlite3_step(stmt_select);
if (res != SQLITE_ROW)
break;
id_faction = sqlite3_column_int64(stmt_select, 0);
lastturn = sqlite3_column_int(stmt_select, 6);
no_b36 = (const char *)sqlite3_column_text(stmt_select, 2);
f = get_faction_by_id((int)id_faction);
if (!f) {
int no = atoi36(no_b36);
f = findfaction(no);
if (f) {
f->subscription = (int)id_faction;
}
}
if (!f || !f->alive) {
if (lastturn == 0) {
const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?";
sqlite3_stmt *stmt = stmt_cache_get(db, sql_update);
lastturn = f ? f->lastorders : turn - 1;
sqlite3_bind_int(stmt, 1, lastturn);
sqlite3_bind_int64(stmt, 2, id_faction);
res = sqlite3_step(stmt);
SQL_EXPECT(res, SQLITE_DONE);
}
} else {
md5_state_t ms;
md5_byte_t digest[16];
int i;
char passwd_md5[MD5_LENGTH_0];
sqlite3_uint64 id_email;
bool update = force;
db_faction dbstate;
fset(f, FFL_MARK);
dbstate.id_faction = id_faction;
dbstate.id_email = sqlite3_column_int64(stmt_select, 1);
dbstate.no = no_b36 ? atoi36(no_b36) : -1;
dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3);
dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4);
dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5);
id_email = dbstate.id_email;
res = db_update_email(db, f, &dbstate, force, &id_email);
SQL_EXPECT(res, SQLITE_OK);
md5_init(&ms);
md5_append(&ms, (md5_byte_t *) f->passw, (int)strlen(f->passw));
md5_finish(&ms, digest);
for (i = 0; i != 16; ++i)
sprintf(passwd_md5 + 2 * i, "%.02x", digest[i]);
if (!update) {
update = ((id_email != 0 && dbstate.id_email != id_email)
|| dbstate.no != f->no || dbstate.passwd_md5 == NULL
|| strcmp(passwd_md5, dbstate.passwd_md5) != 0 || dbstate.name == NULL
|| strncmp(f->name, dbstate.name, MAX_FACTION_NAME) != 0);
}
if (update) {
const char sql_update_faction[] =
"UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?";
sqlite3_stmt *stmt_update_faction =
stmt_cache_get(db, sql_update_faction);
res = sqlite3_bind_int64(stmt_update_faction, 1, id_email);
SQL_EXPECT(res, SQLITE_OK);
res =
sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH,
SQLITE_TRANSIENT);
SQL_EXPECT(res, SQLITE_OK);
res =
sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1,
SQLITE_TRANSIENT);
SQL_EXPECT(res, SQLITE_OK);
res =
sqlite3_bind_text(stmt_update_faction, 4, f->name, -1,
SQLITE_TRANSIENT);
SQL_EXPECT(res, SQLITE_OK);
res = sqlite3_bind_int(stmt_update_faction, 5, turn - f->age);
SQL_EXPECT(res, SQLITE_OK);
res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription);
SQL_EXPECT(res, SQLITE_OK);
res = sqlite3_step(stmt_update_faction);
SQL_EXPECT(res, SQLITE_DONE);
}
}
2010-08-08 10:06:34 +02:00
}
for (f = factions; f; f = f->next) {
if (!fval(f, FFL_MARK)) {
log_error("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email);
} else {
freset(f, FFL_MARK);
}
2010-08-08 10:06:34 +02:00
}
return SQLITE_OK;
2010-08-08 10:06:34 +02:00
}
int db_update_scores(sqlite3 * db, bool force)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const char *sql_ins =
"INSERT OR FAIL INTO score (value,faction_id,turn) VALUES (?,?,?)";
sqlite3_stmt *stmt_ins = stmt_cache_get(db, sql_ins);
const char *sql_upd =
"UPDATE score set value=? WHERE faction_id=? AND turn=?";
sqlite3_stmt *stmt_upd = stmt_cache_get(db, sql_upd);
faction *f;
2010-08-08 10:06:34 +02:00
sqlite3_exec(db, "BEGIN", 0, 0, 0);
2011-03-07 08:02:35 +01:00
for (f = factions; f; f = f->next) {
2010-08-08 10:06:34 +02:00
int res;
sqlite3_bind_int(stmt_ins, 1, f->score);
sqlite3_bind_int64(stmt_ins, 2, f->subscription);
sqlite3_bind_int(stmt_ins, 3, turn);
res = sqlite3_step(stmt_ins);
2011-03-07 08:02:35 +01:00
if (res == SQLITE_CONSTRAINT) {
2010-08-08 10:06:34 +02:00
sqlite3_bind_int(stmt_upd, 1, f->score);
sqlite3_bind_int64(stmt_upd, 2, f->subscription);
sqlite3_bind_int(stmt_upd, 3, turn);
res = sqlite3_step(stmt_upd);
sqlite3_reset(stmt_upd);
}
sqlite3_reset(stmt_ins);
}
sqlite3_exec(db, "COMMIT", 0, 0, 0);
return SQLITE_OK;
}