#define _GNU_SOURCE #include #include #include #include #include #include "db.h" #include "schema.sql.h" #define SQLITE(func,...) do{if(sqlite3_##func(__VA_ARGS__)!=SQLITE_OK){die_sqlite("sqlite3_" #func);}}while(0) #define DATABASE_VERSION 1 #define PASSHASH_OPSLIMIT 3 #define PASSHASH_MEMLIMIT crypto_pwhash_MEMLIMIT_INTERACTIVE sqlite3 *database=NULL; __attribute__((noreturn)) static void die_sqlite(const char *func){ die("%s: %s",func,sqlite3_errmsg(database)); } struct hashed_password { char str[crypto_pwhash_STRBYTES + 1]; int length; // -1 if hashing failed }; static struct hashed_password hash_password(const char *pass) { struct hashed_password result; int ret = crypto_pwhash_str( result.str, pass, strlen(pass), PASSHASH_OPSLIMIT, PASSHASH_MEMLIMIT); if (ret != 0) { result.length = -1; } else { result.length = crypto_pwhash_STRBYTES; } return result; } void db_init(void){ SQLITE(config,SQLITE_CONFIG_SERIALIZED); SQLITE(initialize); SQLITE(open_v2,"db.db",&database,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,NULL); SQLITE(busy_timeout,database,500); SQLITE(exec,database,"pragma foreign_keys = 1",NULL,NULL,NULL); sqlite3_stmt *stmt; int res = sqlite3_prepare_v2(database, "select version from Meta", -1, &stmt, NULL); if (res == SQLITE_OK && sqlite3_step(stmt) == SQLITE_ROW) { i64 version = sqlite3_column_int64(stmt, 0); SQLITE(finalize, stmt); if (version != DATABASE_VERSION) { die("Database version incompatible: database %" PRIi64 ", application %d", version, DATABASE_VERSION); } } else { if (res == SQLITE_OK) SQLITE(finalize, stmt); char *str=malloc(schema_sql_len+1,char); memcpy(str,schema_sql,schema_sql_len); str[schema_sql_len]='\0'; SQLITE(exec,database,str,NULL,NULL,NULL); free(str); SQLITE(prepare_v2, database, "insert into Meta (version) values (?)", -1, &stmt, NULL); SQLITE(bind_int64, stmt, 1, DATABASE_VERSION); if (sqlite3_step(stmt) != SQLITE_DONE) { die("Could not set database version: %s", sqlite3_errmsg(database)); } } } void db_reinit(void){ SQLITE(open_v2,"db.db",&database,SQLITE_OPEN_READWRITE,NULL); SQLITE(busy_timeout,database,500); } void db_close(void){ sqlite3_close(database); SQLITE(shutdown); database=NULL; } static char* gen_room_name(void){ const int name_len=8; const char *alphabet="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const int alpha_len=strlen(alphabet); char *name=malloc(name_len+1,char); for(int i=0;i=0); sqlite3_stmt *stmt; if(beforeid<0){ SQLITE(prepare_v2,database, "select id, user, time, message " "from Messages " "where room = ? " "order by time desc " "limit ?" ,-1,&stmt,NULL); SQLITE(bind_int64,stmt,1,roomid); SQLITE(bind_int64,stmt,2,count); } else { SQLITE(prepare_v2,database, "select id, user, time, message " "from Messages " "where room = ? and time < (select time from Messages where id = ?) " "order by time desc " "limit ?" ,-1,&stmt,NULL); SQLITE(bind_int64,stmt,1,roomid); SQLITE(bind_int64,stmt,2,beforeid); SQLITE(bind_int64,stmt,3,count); } struct db_message_list ml; i64 cap=count; ml.count=0; ml.list=malloc(cap,struct db_message); int ret; while((ret=sqlite3_step(stmt))==SQLITE_ROW){ if(ml.count==cap){ die("sqlite gave too many rows while 'limit %" PRIi64 "' was present",count); } ml.list[ml.count].msgid=sqlite3_column_int64(stmt,0); ml.list[ml.count].roomid=roomid; ml.list[ml.count].userid=sqlite3_column_int64(stmt,1); ml.list[ml.count].timestamp=sqlite3_column_int64(stmt,2); ml.list[ml.count].message=strdup((const char*)sqlite3_column_text(stmt,3)); ml.count++; } if(ret!=SQLITE_DONE)die_sqlite("sqlite3_step"); SQLITE(finalize,stmt); return ml; } void db_nullify_name_id(struct db_name_id ni){ if(ni.name)free(ni.name); } void db_nullify_room_list(struct db_room_list rl){ for(i64 i=0;i