From db095e224c08996d78a90c032a1311d7a5dd8dd4 Mon Sep 17 00:00:00 2001 From: alexhudson Date: Mon, 6 Aug 2007 19:59:24 +0000 Subject: [PATCH] Slight grab-bag; * some sqlite helper functions added to msgapi. versions in store could/should be deprecated, possibly post-1.0 though * remove some hard-coded stuff from auth, replace with sqlite db * various small fixes --- include/msgapi.h | 33 ++++ src/agents/mailprox/imap.c | 1 - src/agents/mailprox/mailprox.c | 2 + src/agents/mailprox/pop.c | 1 - src/apps/config/config.c | 15 +- src/libs/msgapi/Bongo.rules | 1 + src/libs/msgapi/msgauth.c | 143 ++++++++++++++-- src/libs/msgapi/sqldb.c | 300 +++++++++++++++++++++++++++++++++ 8 files changed, 479 insertions(+), 17 deletions(-) create mode 100644 src/libs/msgapi/sqldb.c diff --git a/include/msgapi.h b/include/msgapi.h index 3a7149b..f2686c7 100644 --- a/include/msgapi.h +++ b/include/msgapi.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -48,10 +49,42 @@ EXPORT BOOL MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uin EXPORT int MsgAuthFindCookie(const char *username, const char *token); EXPORT BOOL MsgAuthDeleteCookie(const char *username, const char *token); +// SQL Routines + +typedef struct _MsgSQLStatement { + sqlite3_stmt *stmt; + void *userdata; +} MsgSQLStatement; + +typedef struct _MsgSQLHandle { + sqlite3 *db; + struct { + /* NOTE: begin and end must be the first and last stmts (see DStoreClose()) */ + MsgSQLStatement begin; + MsgSQLStatement abort; + MsgSQLStatement end; + } stmts; + + BongoMemStack *memstack; + int transactionDepth; + int lockTimeoutMs; +} MsgSQLHandle; + +#define MSGSQL_STMT_SLEEP_MS 250 + +MsgSQLHandle *MsgSQLOpen(char *path, BongoMemStack *memstack, int locktimeoutms); +void MsgSQLClose(MsgSQLHandle *handle); +BongoMemStack *MsgSQLGetMemStack(MsgSQLHandle *handle); +void MsgSQLSetMemStack(MsgSQLHandle *handle, BongoMemStack *memstack); +void MsgSQLSetLockTimeout(MsgSQLHandle *handle, int timeoutms); +MsgSQLStatement *MsgSQLPrepare(MsgSQLHandle *handle, const char *statement, MsgSQLStatement *stmt); + // Misc. util functions +// FIXME: This API is wrong, should include agent name EXPORT BOOL MsgSetRecoveryFlag(void); EXPORT BOOL MsgGetRecoveryFlag(void); + EXPORT BOOL MsgGetServerCredential(char *buffer); #define MSGSRV_LOCAL_SERVER NULL diff --git a/src/agents/mailprox/imap.c b/src/agents/mailprox/imap.c index bea6bda..1adf2b4 100644 --- a/src/agents/mailprox/imap.c +++ b/src/agents/mailprox/imap.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "mailprox.h" diff --git a/src/agents/mailprox/mailprox.c b/src/agents/mailprox/mailprox.c index a6046e9..c8f134c 100644 --- a/src/agents/mailprox/mailprox.c +++ b/src/agents/mailprox/mailprox.c @@ -21,6 +21,8 @@ #include +#define LOGGERNAME "mailprox" + #include #include #include diff --git a/src/agents/mailprox/pop.c b/src/agents/mailprox/pop.c index 6d34ebb..7354109 100644 --- a/src/agents/mailprox/pop.c +++ b/src/agents/mailprox/pop.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "mailprox.h" diff --git a/src/apps/config/config.c b/src/apps/config/config.c index f547c83..80d969c 100644 --- a/src/apps/config/config.c +++ b/src/apps/config/config.c @@ -175,7 +175,7 @@ ImportSystemBackupFile(const StoreClient *client, const char *path) XplConsolePrintf(_("ERROR: Couldn't write\n")); } else { if (! SetAdminRights(client, fullpath)) { - XplConsolePrintf(_("ERROR: rigts\n")); + XplConsolePrintf(_("ERROR: Couldn't set rights on store file\n")); } } MemFree(file); @@ -187,7 +187,17 @@ ImportSystemBackupFile(const StoreClient *client, const char *path) } void -InitialStoreConfiguration() { +InitializeDataArea(void) +{ + XplConsolePrintf(_("Initializing user database...\n")); + if (MsgAuthInitDB() != 0) { + XplConsolePrintf(_("ERROR: Couldn't create user database\n")); + exit(1); + } +} + +void +InitialStoreConfiguration(void) { char path[XPL_MAX_PATH]; int store_pid; char *args[3]; @@ -540,6 +550,7 @@ main(int argc, char *argv[]) { switch(command) { case 1: + InitializeDataArea(); InitialStoreConfiguration(); break; case 2: diff --git a/src/libs/msgapi/Bongo.rules b/src/libs/msgapi/Bongo.rules index fa69a18..90552c8 100644 --- a/src/libs/msgapi/Bongo.rules +++ b/src/libs/msgapi/Bongo.rules @@ -9,6 +9,7 @@ libbongomsgapi_la_SOURCES := \ src/libs/msgapi/msgcollector.c \ src/libs/msgapi/msgdate.c \ src/libs/msgapi/msghttp.c \ + src/libs/msgapi/sqldb.c \ src/libs/msgapi/msghttp.h libbongomsgapi_la_CPPFLAGS := $(AM_CPPFLAGS) \ diff --git a/src/libs/msgapi/msgauth.c b/src/libs/msgapi/msgauth.c index 609c77e..c6f88c0 100644 --- a/src/libs/msgapi/msgauth.c +++ b/src/libs/msgapi/msgauth.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,16 +16,104 @@ #include #include +typedef struct { + MsgSQLStatement find_user; + MsgSQLStatement auth_user; +} MsgAuthStatements; + +MsgAuthStatements msgauth_stmts; + +int +MsgAuthDBPath(char *path, size_t size) +{ + return snprintf(path, size, "%s/%s", XPL_DEFAULT_DBF_DIR, "userdb.sqlite"); +} + +/* returns 0 on success */ +int +MsgAuthInitDB(void) +{ + int dcode; + char path[XPL_MAX_PATH + 1]; + MsgSQLHandle *handle; + struct stat buf; + + MsgAuthDBPath(&path, XPL_MAX_PATH); + if (stat(path, &buf) == 0) { + // FIXME: db already exists - for now, remove, but could/should be more gentle + unlink(path); + } + + handle = MsgSQLOpen(path, NULL, 1000); + + if (NULL == handle) return -2; + + if (MsgSQLBeginTransaction(handle)) goto fail; + + // FIXME: need to store passwords as hashes, really? + dcode = sqlite3_exec (handle->db, + "PRAGMA user_version=0;" + "CREATE TABLE users (username TEXT DEFAULT NULL," + " password TEXT DEFAULT NULL," + " email TEXT DEFAULT NULL" + ");" + "INSERT INTO users (username, password, email)" + " VALUES ('admin', 'bongo', 'admin'); ", + NULL, NULL, NULL); + if (SQLITE_OK != dcode) goto fail; + + + if (MsgSQLCommitTransaction(handle)) goto fail; + + MsgSQLClose(handle); + return 0; + +fail: + // FIXME: logging + printf("MsgAuthInitDB error: %s\n", sqlite3_errmsg(handle->db)); + + MsgSQLAbortTransaction(handle); + MsgSQLClose(handle); + return -1; +} + /** * Determine whether or not a user exists in our user database * SECURITY: We don't want to disclose this to unknown people + * Do we need this function? Often gets called before trying a + * password, which is unnecessary - might be this can be removed. + * This an MsgAuthVerifyPassword() are basically the same. * \param user The username to check * \return Whether or not the user exists */ BOOL -MsgAuthFindUser(const char *user) { - if (!strcmp(user, "admin")) - return TRUE; +MsgAuthFindUser(const char *user) +{ + MsgSQLStatement *stmt; + char path[XPL_MAX_PATH + 1]; + MsgSQLHandle *handle; + int users = -1; + + MsgAuthDBPath(&path, XPL_MAX_PATH); + handle = MsgSQLOpen(path, NULL, 1000); + + // Log error? + if (NULL == handle) return FALSE; + + stmt = MsgSQLPrepare (handle, "SELECT count(username) FROM users WHERE username = ?;", + &msgauth_stmts.find_user); + MsgSQLBindString(stmt, 1, user, TRUE); + + if (MsgSQLResults(handle, stmt) >= 0) { + // should only have one result column + users = sqlite3_column_int(stmt->stmt, 0); + } + + MsgSQLFinalize(stmt); + MsgSQLClose(handle); + + // one and only one user - any other condition is 'bad'... + if (users == 1) return TRUE; return FALSE; } @@ -36,9 +125,34 @@ MsgAuthFindUser(const char *user) { * \return Whether or not the username matches the password */ BOOL -MsgAuthVerifyPassword(const char *user, const char *password) { - if ((!strcmp(user, "admin")) && (!strcmp(password, "bongo"))) - return TRUE; +MsgAuthVerifyPassword(const char *user, const char *password) +{ + MsgSQLStatement *stmt; + char path[XPL_MAX_PATH + 1]; + MsgSQLHandle *handle; + int users = -1; + + MsgAuthDBPath(&path, XPL_MAX_PATH); + handle = MsgSQLOpen(path, NULL, 1000); + + // Log error? + if (NULL == handle) return FALSE; + + stmt = MsgSQLPrepare (handle, "SELECT count(username) FROM users WHERE username = ? AND password = ?;", + &msgauth_stmts.auth_user); + MsgSQLBindString(stmt, 1, user, TRUE); + MsgSQLBindString(stmt, 2, password, TRUE); + + if (MsgSQLResults(handle, stmt) >= 0) { + // should only have one result column + users = sqlite3_column_int(stmt->stmt, 0); + } + + MsgSQLFinalize(stmt); + MsgSQLClose(handle); + + // one and only one user - any other condition is 'bad'... + if (users == 1) return TRUE; return FALSE; } @@ -51,7 +165,8 @@ MsgAuthVerifyPassword(const char *user, const char *password) { * \return Whether or not we successfully changed the password */ BOOL -MsgAuthSetPassword(const char *user, const char *oldpassword, const char *newpassword) { +MsgAuthSetPassword(const char *user, const char *oldpassword, const char *newpassword) +{ return FALSE; } @@ -62,7 +177,8 @@ MsgAuthSetPassword(const char *user, const char *oldpassword, const char *newpas * \return Whether this was successful */ BOOL -MsgAuthGetUserStore(const char *user, struct sockaddr_in *store) { +MsgAuthGetUserStore(const char *user, struct sockaddr_in *store) +{ memset(store, 0, sizeof(store)); store->sin_addr.s_addr = inet_addr("127.0.0.1"); store->sin_family = AF_INET; @@ -76,7 +192,8 @@ MsgAuthGetUserStore(const char *user, struct sockaddr_in *store) { * \return Whether this was successful */ BOOL -MsgAuthUserList(BongoArray **list) { +MsgAuthUserList(BongoArray **list) +{ BongoArray *userlist; userlist = BongoArrayNew(sizeof (char *), 0); @@ -128,7 +245,7 @@ MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uint64_t timeou XplHashFinal(&context, XPLHASH_LOWERCASE, cookie->token, XPLHASH_MD5_LENGTH); // save it to our cookie list - MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + MsgAuthCookiePath(username, path, XPL_MAX_PATH); cookiefile = fopen(path, "a"); if (! cookiefile) return FALSE; @@ -163,7 +280,7 @@ MsgAuthFindCookie(const char *username, const char *token) now = time(NULL); - MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + MsgAuthCookiePath(username, path, XPL_MAX_PATH); cookiefile = fopen(path, "r"); if (! cookiefile) return 2; @@ -207,9 +324,9 @@ MsgAuthDeleteCookie(const char *username, const char *token) MsgAuthCookie **cookieset; uint64_t now; - now = BongoCalTimeAsUint64(BongoCalTimeNow(NULL)); + now = time(NULL); - MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + MsgAuthCookiePath(username, path, XPL_MAX_PATH); // first, read cookies into memory cookiefile = fopen(path, "w"); diff --git a/src/libs/msgapi/sqldb.c b/src/libs/msgapi/sqldb.c new file mode 100644 index 0000000..e893620 --- /dev/null +++ b/src/libs/msgapi/sqldb.c @@ -0,0 +1,300 @@ +/* Bongo Project licensing applies to this file, see COPYING + * (C) 2006 Novell + * (C) 2007 Alex Hudson + */ + +/** \file + * SQLite Helper API + */ + +/* This code all adapted from the Store, but made generic. + * Eventually, the store should probably use this + */ + +#include +#include +#include + +MsgSQLHandle * +MsgSQLOpen(char *path, BongoMemStack *memstack, int locktimeoutms) +{ + MsgSQLHandle *handle = NULL; + int create = 0; + + create = access(path, 0); + + if (!(handle = MemMalloc(sizeof(MsgSQLHandle))) || + memset(handle, 0, sizeof(MsgSQLHandle)), 0 || + SQLITE_OK != sqlite3_open(path, &handle->db)) { + // FIXME Logging + XplConsolePrintf("msgapi: Failed to open database \"%s\".\r\n", path); + goto fail; + } + + handle->memstack = memstack; + handle->lockTimeoutMs = locktimeoutms; + +// FIXME +/* if (create && DStoreCreateDB(handle)) { + printf("Couldn't open db"); + goto fail; + } + + if (UpgradeDB(handle)) { + goto fail; + } +*/ + return handle; + +fail: + if (handle) { + if (handle->db) { + sqlite3_close(handle->db); + } + + MemFree(handle); + } + return NULL; +} + +void +MsgSQLClose(MsgSQLHandle *handle) +{ + MsgSQLReset(handle); + if (SQLITE_BUSY == sqlite3_close(handle->db)) { + // FIXME logging + XplConsolePrintf ("msgapi: couldn't close database\r\n"); + } + + MemFree(handle); +} + +void +MsgSQLFinalize(MsgSQLStatement *stmt) +{ + if (stmt->stmt) { + sqlite3_finalize(stmt->stmt); + stmt->stmt = NULL; + } +} + +void +MsgSQLReset(MsgSQLHandle *handle) +{ + MsgSQLStatement *stmt; + + for (stmt = &handle->stmts.begin; + stmt <= &handle->stmts.end; + stmt++) + { + if (stmt->stmt) { + sqlite3_finalize(stmt->stmt); + stmt->stmt = NULL; + } + } +} + +BongoMemStack * +MsgSQLGetMemStack(MsgSQLHandle *handle) +{ + return handle->memstack; +} + +void +MsgSQLSetMemStack(MsgSQLHandle *handle, BongoMemStack *memstack) +{ + handle->memstack = memstack; +} + + +void +MsgSQLSetLockTimeout(MsgSQLHandle *handle, int timeoutms) +{ + handle->lockTimeoutMs = timeoutms; +} + +// returns 0 on success, -2 db busy, -1 on error +int +MsgSQLBeginTransaction(MsgSQLHandle *handle) +{ + MsgSQLStatement *stmt; + int result; + int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS; + + if (handle->transactionDepth) { + /* FIXME: ensure it's an exclusive transaction */ + abort (); + return 0; + } + + stmt = MsgSQLPrepare(handle, "BEGIN EXCLUSIVE TRANSACTION;", &handle->stmts.begin); + if (!stmt) { + //DStoreStmtError(handle, stmt); + return -1; + } + + do { + result = sqlite3_step(stmt->stmt); + sqlite3_reset(stmt->stmt); + } while (SQLITE_BUSY == result && --count && (XplDelay(MSGSQL_STMT_SLEEP_MS), 1)); + + switch (result) { + case SQLITE_DONE: + ++handle->transactionDepth; + return 0; + case SQLITE_BUSY: + return -2; + default: + //DStoreStmtError(handle, stmt); + return -1; + } +} + +// returns 0 on success, -1 on error +int +MsgSQLCommitTransaction(MsgSQLHandle *handle) +{ + MsgSQLStatement *stmt; + int result; + int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS; + + stmt = MsgSQLPrepare(handle, "END TRANSACTION;", &handle->stmts.end); + if (!stmt) { + // DStoreStmtError(handle, stmt); + return -1; + } + + do { + result = sqlite3_step(stmt->stmt); + sqlite3_reset(stmt->stmt); + } while (SQLITE_BUSY == result && --count && (XplDelay(MSGSQL_STMT_SLEEP_MS), 1)); + + --handle->transactionDepth; + + if (SQLITE_DONE != result) { + // DStoreStmtError(handle, stmt); + return -1; + } + return 0; +} + +// returns 0 on success, -1 on error +int +MsgSQLAbortTransaction(MsgSQLHandle *handle) +{ + MsgSQLStatement *stmt; + int result; + + stmt = MsgSQLPrepare(handle, "ROLLBACK TRANSACTION;", &handle->stmts.abort); + if (!stmt) { + // DStoreStmtError(handle, stmt); + return -1; + } + + result = sqlite3_step(stmt->stmt); + if (SQLITE_DONE != result) { + // DStoreStmtError(handle, stmt); + } + sqlite3_reset(stmt->stmt); + --handle->transactionDepth; + return SQLITE_DONE == result ? 0 : -1; +} + +int +MsgSQLCancelTransactions(MsgSQLHandle *handle) +{ + while (handle->transactionDepth) { + if (MsgSQLAbortTransaction(handle)) { + return -1; + } + } + return 0; +} + +MsgSQLStatement * +MsgSQLPrepare(MsgSQLHandle *handle, const char *statement, MsgSQLStatement *stmt) +{ + int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS; + + // stmt->filter = NULL; + if (stmt->stmt) { + return stmt; + } + + while (count--) { + switch (sqlite3_prepare(handle->db, statement, -1, &stmt->stmt, NULL)) { + case SQLITE_OK: + return stmt; + case SQLITE_BUSY: + XplDelay(MSGSQL_STMT_SLEEP_MS); + continue; + default: + // FIXME + //XplConsolePrintf("SQL Prepare statement \"%s\" failed; %s\r\n", + // statement, sqlite3_errmsg(handle->db)); + return NULL; + } + } + + return NULL; +} + +int +MsgSQLBindString(MsgSQLStatement *stmt, int var, const char *str, BOOL nullify) +{ + if (str) { + return sqlite3_bind_text(stmt->stmt, var, str, strlen(str), SQLITE_STATIC); + } else { + if (nullify) { + return sqlite3_bind_null(stmt->stmt, var); + } else { + return sqlite3_bind_text(stmt->stmt, var, "", 0, SQLITE_STATIC); + } + } +} + +int +MsgSQLExecute(MsgSQLHandle *handle, MsgSQLStatement *_stmt) +{ + + sqlite3_stmt *stmt = _stmt->stmt; + int result; + + result = sqlite3_step(stmt); + if (SQLITE_DONE == result) { + return 0; + } else { + XplConsolePrintf("Sql: %s\r\n", sqlite3_errmsg(handle->db)); + return -1; + } +} + +int +MsgSQLResults(MsgSQLHandle *handle, MsgSQLStatement *_stmt) +{ + sqlite3_stmt *stmt = _stmt->stmt; + int result; + int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS; + + while (count--) { + result = sqlite3_step(stmt); + switch (result) { + case SQLITE_DONE: + return 0; // no more results + break; + case SQLITE_ROW: + return 1; + break; + case SQLITE_BUSY: + XplDelay(MSGSQL_STMT_SLEEP_MS); + continue; + default: + // FIXME + //XplConsolePrintf("SQL Prepare statement \"%s\" failed; %s\r\n", + // statement, sqlite3_errmsg(handle->db)); + return -1; + } + } + + return -1; +}