From c3e8184c7e85b65de03dcb21fccd955b2978c99f Mon Sep 17 00:00:00 2001 From: Edouard Tisserant Date: Sat, 30 Apr 2016 17:59:16 +0200 Subject: [PATCH] Fixed Primary Key of identity table. Was (address) and know is (address, user_id). Added 'virtual' user_id (const string + address) as a side effect, in case user_id isn't given and no user have been created with same address. --- src/keymanagement.c | 60 +++++++++++++++++++------ src/pEpEngine.c | 107 +++++++++++++++++++++++++++++++------------- src/pEpEngine.h | 24 +++++++++- src/pEp_internal.h | 1 + 4 files changed, 146 insertions(+), 46 deletions(-) diff --git a/src/keymanagement.c b/src/keymanagement.c index 4de3692d..da5efe1c 100644 --- a/src/keymanagement.c +++ b/src/keymanagement.c @@ -70,7 +70,43 @@ DYNAMIC_API PEP_STATUS update_identity( if (!(session && identity && !EMPTYSTR(identity->address))) return PEP_ILLEGAL_VALUE; - status = get_identity(session, identity->address, &stored_identity); + int _no_user_id = EMPTYSTR(identity->user_id); + + if (_no_user_id) + { + free(identity->user_id); + + status = get_best_user(session, + identity->address, + &identity->user_id); + + // Default user_id, aka Virtual user_id + if (status == PEP_CANNOT_FIND_IDENTITY) + { + identity->user_id = calloc(1, identity->address_size + 5); + if (!identity->user_id) + { + return PEP_OUT_OF_MEMORY; + } + snprintf(identity->user_id, identity->address_size + 5, + "TOFU_%s", identity->address); + } + else if (status != PEP_STATUS_OK) + { + return status; + } + + if(identity->user_id) + { + identity->user_id_size = strlen(identity->user_id); + } + } + + status = get_identity(session, + identity->address, + identity->user_id, + &stored_identity); + assert(status != PEP_OUT_OF_MEMORY); if (status == PEP_OUT_OF_MEMORY) return PEP_OUT_OF_MEMORY; @@ -82,15 +118,6 @@ DYNAMIC_API PEP_STATUS update_identity( if (status == PEP_OUT_OF_MEMORY) return PEP_OUT_OF_MEMORY; - if (EMPTYSTR(identity->user_id)) { - free(identity->user_id); - identity->user_id = strndup(stored_identity->user_id, stored_identity->user_id_size); - assert(identity->user_id); - if (identity->user_id == NULL) - return PEP_OUT_OF_MEMORY; - identity->user_id_size = stored_identity->user_id_size; - } - if (EMPTYSTR(identity->username)) { free(identity->username); identity->username = strndup(stored_identity->username, stored_identity->username_size); @@ -218,10 +245,15 @@ DYNAMIC_API PEP_STATUS update_identity( identity->username_size = 9; } - status = set_identity(session, identity); - assert(status == PEP_STATUS_OK); - if (status != PEP_STATUS_OK) { - return status; + // Identity doesn't get stored if is was just about checking existing + // user by address (i.e. no user id but already stored) + if (!(_no_user_id && stored_identity)) + { + status = set_identity(session, identity); + assert(status == PEP_STATUS_OK); + if (status != PEP_STATUS_OK) { + return status; + } } } diff --git a/src/pEpEngine.c b/src/pEpEngine.c index 84624f67..8c096467 100644 --- a/src/pEpEngine.c +++ b/src/pEpEngine.c @@ -13,6 +13,7 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) static const char *sql_log; static const char *sql_trustword; static const char *sql_get_identity; + static const char *sql_get_best_user; static const char *sql_set_person; static const char *sql_set_pgp_keypair; static const char *sql_set_identity; @@ -140,14 +141,15 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) " comment text\n" ");\n" "create table if not exists identity (\n" - " address text primary key,\n" + " address text,\n" " user_id text\n" " references person (id)\n" " on delete cascade,\n" " main_key_id text\n" " references pgp_keypair (fpr)\n" " on delete set null,\n" - " comment text\n" + " comment text,\n" + " primary key (address, user_id)\n" ");\n" "create table if not exists trust (\n" " user_id text not null\n" @@ -186,13 +188,21 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) sql_log = "insert into log (title, entity, description, comment)" "values (?1, ?2, ?3, ?4);"; - sql_get_identity = "select fpr, identity.user_id, username, comm_type, lang" + sql_get_identity = "select fpr, username, comm_type, lang" " from identity" " join person on id = identity.user_id" " join pgp_keypair on fpr = identity.main_key_id" " join trust on id = trust.user_id" " and pgp_keypair_fpr = identity.main_key_id" - " where address = ?1 ;"; + " where address = ?1 and identity.user_id = ?2;"; + + sql_get_best_user = "select identity.user_id" + " from identity" + " join trust on trust.user_id = identity.user_id" + " and pgp_keypair_fpr = identity.main_key_id" + " where address = ?1" + " order by comm_type DESC, identity.rowid DESC" + " limit 1;"; sql_trustword = "select id, word from wordlist where lang = lower(?1) " "and id = ?2 ;"; @@ -201,21 +211,21 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) "values (?1, ?2, ?3) ;"; sql_set_pgp_keypair = "insert or replace into pgp_keypair (fpr) " - "values (?1) ;"; + "values (upper(replace(?1,' ',''))) ;"; sql_set_identity = "insert or replace into identity (address, main_key_id, " - "user_id) values (?1, ?2, ?3) ;"; + "user_id) values (?1, upper(replace(?2,' ','')), ?3) ;"; sql_set_trust = "insert or replace into trust (user_id, pgp_keypair_fpr, comm_type) " - "values (?1, ?2, ?3) ;"; + "values (?1, upper(replace(?2,' ','')), ?3) ;"; - sql_get_trust = "select user_id, comm_type from trust where user_id = ?1 " - "and pgp_keypair_fpr = ?2 ;"; + sql_get_trust = "select comm_type from trust where user_id = ?1 " + "and pgp_keypair_fpr = upper(replace(?2,' ','')) ;"; - sql_least_trust = "select min(comm_type) from trust where pgp_keypair_fpr = ?1 ;"; + sql_least_trust = "select min(comm_type) from trust where pgp_keypair_fpr = upper(replace(?1,' ','')) ;"; sql_mark_as_compromized = "update trust not indexed set comm_type = 15" - " where pgp_keypair_fpr = ?1 ;"; + " where pgp_keypair_fpr = upper(replace(?1,' ','')) ;"; sql_crashdump = "select timestamp, title, entity, description, comment" " from log order by timestamp desc limit ?1 ;"; @@ -226,13 +236,13 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) // blacklist - sql_blacklist_add = "insert or replace into blacklist_keys (fpr) values (?1) ;" - "delete from identity where main_key_id = ?1 ;" - "delete from pgp_keypair where fpr = ?1 ;"; + sql_blacklist_add = "insert or replace into blacklist_keys (fpr) values (upper(replace(?1,' ',''))) ;" + "delete from identity where main_key_id = upper(replace(?1,' ','')) ;" + "delete from pgp_keypair where fpr = upper(replace(?1,' ','')) ;"; - sql_blacklist_delete = "delete from blacklist_keys where fpr = ?1 ;"; + sql_blacklist_delete = "delete from blacklist_keys where fpr = upper(replace(?1,' ','')) ;"; - sql_blacklist_is_listed = "select count(*) from blacklist_keys where fpr = ?1 ;"; + sql_blacklist_is_listed = "select count(*) from blacklist_keys where fpr = upper(replace(?1,' ','')) ;"; sql_blacklist_retrieve = "select * from blacklist_keys ;"; } @@ -249,6 +259,10 @@ DYNAMIC_API PEP_STATUS init(PEP_SESSION *session) (int)strlen(sql_get_identity), &_session->get_identity, NULL); assert(int_result == SQLITE_OK); + int_result = sqlite3_prepare_v2(_session->db, sql_get_best_user, + (int)strlen(sql_get_best_user), &_session->get_best_user, NULL); + assert(int_result == SQLITE_OK); + int_result = sqlite3_prepare_v2(_session->db, sql_set_person, (int)strlen(sql_set_person), &_session->set_person, NULL); assert(int_result == SQLITE_OK); @@ -360,6 +374,8 @@ DYNAMIC_API void release(PEP_SESSION session) sqlite3_finalize(session->trustword); if (session->get_identity) sqlite3_finalize(session->get_identity); + if (session->get_best_user) + sqlite3_finalize(session->get_best_user); if (session->set_person) sqlite3_finalize(session->set_person); if (session->set_pgp_keypair) @@ -671,7 +687,9 @@ void free_identity(pEp_identity *identity) } DYNAMIC_API PEP_STATUS get_identity( - PEP_SESSION session, const char *address, + PEP_SESSION session, + const char *address, + const char *user_id, pEp_identity **identity ) { @@ -689,6 +707,7 @@ DYNAMIC_API PEP_STATUS get_identity( sqlite3_reset(session->get_identity); sqlite3_bind_text(session->get_identity, 1, address, -1, SQLITE_STATIC); + sqlite3_bind_text(session->get_identity, 2, user_id, -1, SQLITE_STATIC); result = sqlite3_step(session->get_identity); switch (result) { @@ -696,15 +715,15 @@ DYNAMIC_API PEP_STATUS get_identity( _identity = new_identity( address, (const char *) sqlite3_column_text(session->get_identity, 0), - (const char *) sqlite3_column_text(session->get_identity, 1), - (const char *) sqlite3_column_text(session->get_identity, 2) + user_id, + (const char *) sqlite3_column_text(session->get_identity, 1) ); assert(_identity); if (_identity == NULL) return PEP_OUT_OF_MEMORY; - _identity->comm_type = (PEP_comm_type) sqlite3_column_int(session->get_identity, 3); - _lang = (const char *) sqlite3_column_text(session->get_identity, 4); + _identity->comm_type = (PEP_comm_type) sqlite3_column_int(session->get_identity, 2); + _lang = (const char *) sqlite3_column_text(session->get_identity, 3); if (_lang && _lang[0]) { assert(_lang[0] >= 'a' && _lang[0] <= 'z'); assert(_lang[1] >= 'a' && _lang[1] <= 'z'); @@ -724,6 +743,41 @@ DYNAMIC_API PEP_STATUS get_identity( return status; } +DYNAMIC_API PEP_STATUS get_best_user( + PEP_SESSION session, + const char *address, + char **user_id + ) +{ + PEP_STATUS status = PEP_STATUS_OK; + int result; + + assert(session); + assert(address); + assert(user_id); + + *user_id = NULL; + + if (!(session && address && user_id)) + return PEP_ILLEGAL_VALUE; + + sqlite3_reset(session->get_best_user); + sqlite3_bind_text(session->get_best_user, 1, address, -1, SQLITE_STATIC); + + result = sqlite3_step(session->get_best_user); + switch (result) { + case SQLITE_ROW: { + *user_id = strdup((const char *)sqlite3_column_text(session->get_best_user, 0)); + break; + } + default: + status = PEP_CANNOT_FIND_IDENTITY; + } + + sqlite3_reset(session->get_best_user); + return status; +} + DYNAMIC_API PEP_STATUS set_identity( PEP_SESSION session, const pEp_identity *identity ) @@ -868,16 +922,7 @@ DYNAMIC_API PEP_STATUS get_trust(PEP_SESSION session, pEp_identity *identity) result = sqlite3_step(session->get_trust); switch (result) { case SQLITE_ROW: { - const char * user_id = (const char *) sqlite3_column_text(session->get_trust, 0); - int comm_type = (PEP_comm_type) sqlite3_column_int(session->get_trust, 1); - - if (strcmp(user_id, identity->user_id) != 0) { - free(identity->user_id); - identity->user_id = strdup(user_id); - assert(identity->user_id); - if (identity->user_id == NULL) - return PEP_OUT_OF_MEMORY; - } + int comm_type = (PEP_comm_type) sqlite3_column_int(session->get_trust, 0); identity->comm_type = comm_type; break; } diff --git a/src/pEpEngine.h b/src/pEpEngine.h index 59393a91..3e5028bd 100644 --- a/src/pEpEngine.h +++ b/src/pEpEngine.h @@ -443,10 +443,32 @@ DYNAMIC_API void free_identity(pEp_identity *identity); // more DYNAMIC_API PEP_STATUS get_identity( - PEP_SESSION session, const char *address, + PEP_SESSION session, + const char *address, + const char *user_id, pEp_identity **identity ); +// get_best_user() - get best user_id candidate for a given address +// +// parameters: +// session (in) session handle +// address (in) C string with communication address, UTF-8 encoded +// user_id (out) pointer to user_id string +// +// caveat: +// the address string is being copied; the original string remains in the +// ownership of the caller +// the resulting user_id string goes to the ownership of the +// caller and has to be freed with free() when not in use any +// more + +DYNAMIC_API PEP_STATUS get_best_user( + PEP_SESSION session, + const char *address, + char **user_id + ); + // set_identity() - set identity information // diff --git a/src/pEp_internal.h b/src/pEp_internal.h index 1070c2be..8ef36352 100644 --- a/src/pEp_internal.h +++ b/src/pEp_internal.h @@ -88,6 +88,7 @@ typedef struct _pEpSession { sqlite3_stmt *log; sqlite3_stmt *trustword; sqlite3_stmt *get_identity; + sqlite3_stmt *get_best_user; sqlite3_stmt *set_person; sqlite3_stmt *set_pgp_keypair; sqlite3_stmt *set_identity;