ENGINE-633: merged in second refactoring try.

doxygen_doc
commit 9ada278ffd

@ -643,7 +643,7 @@ DYNAMIC_API PEP_STATUS update_identity(
PEP_SESSION session, pEp_identity * identity
)
{
PEP_STATUS status;
PEP_STATUS status = PEP_STATUS_OK;
assert(session);
assert(identity);
@ -652,10 +652,22 @@ DYNAMIC_API PEP_STATUS update_identity(
if (!(session && identity && !EMPTYSTR(identity->address)))
return PEP_ILLEGAL_VALUE;
//
// Record some information about the input identity so that we don't keep
// evaluating it
bool is_own_user = identity->me;
bool input_has_user_id = !EMPTYSTR(identity->user_id);
bool input_has_username = !EMPTYSTR(identity->user_id);
bool input_has_real_id = input_has_user_id ? (strstr(identity->user_id, "TOFU_") != identity->user_id) : false;
char* default_own_id = NULL;
status = get_default_own_userid(session, &default_own_id);
pEp_identity* stored_ident = NULL;
bool is_own_user = identity->me;
status = get_default_own_userid(session, &default_own_id);
if (status == PEP_STATUS_OK || status == PEP_CANNOT_FIND_IDENTITY)
status = PEP_STATUS_OK;
else
goto pEp_free;
// To be clear, if an own identity comes in here, the only way we will accept
// it is if the caller did not KNOW this, as indicated by the lack of a known
@ -666,7 +678,7 @@ DYNAMIC_API PEP_STATUS update_identity(
// version of myself.
if (!is_own_user) {
if (default_own_id) {
if (!EMPTYSTR(identity->user_id)) {
if (input_has_user_id) {
if (strcmp(default_own_id, identity->user_id) == 0) {
is_own_user = true;
}
@ -697,29 +709,28 @@ DYNAMIC_API PEP_STATUS update_identity(
}
// Otherwise, we don't even HAVE an own user yet, so we're ok.
}
if (is_own_user)
{
if (is_own_user) {
free(default_own_id);
return PEP_ILLEGAL_VALUE;
}
// We have, at least, an address.
// Retrieve stored identity information!
pEp_identity* stored_ident = NULL;
// We don't want to have to keep reevaluating these
bool has_user_id = !EMPTYSTR(identity->user_id);
bool has_uname = !EMPTYSTR(identity->username);
bool addr_only = !(has_user_id || has_uname);
//////////////////////////////////////////////////////////////////////////////////////////////////////
// If we can get a starting identity from the database, do it. If we have a user_id (thank you, users),
// this is pretty simple.
//
// Otherwise, we double-check that someone didn't pass in an own address (hey, if you don't give us a
// user_id, we're have to guess somehow, and treating own identities like partner identities is dangerous).
//////////////////////////////////////////////////////////////////////////////////////////////////////
if (has_user_id) {
if (input_has_user_id) {
// (we're gonna update the trust/fpr anyway, so we use the no-fpr-from-trust-db variant)
// * do get_identity() to retrieve stored identity information
status = get_identity_without_trust_check(session, identity->address, identity->user_id, &stored_ident);
}
else {
// * See if there is an own identity that uses this address. If so, we'll
// prefer that
else { // see if we perhaps own this user
if (default_own_id) {
status = get_identity(session,
identity->address,
@ -728,148 +739,160 @@ DYNAMIC_API PEP_STATUS update_identity(
}
}
// Ok, trying to grab an identity with basic user_id defaults didn't work.
// Go deeper into the DB by address
//////////////////////////////////////////////////////////////////////////////////////////////////////
// If we're unable to get a starting stored ID, we now need to try to get IDs which match the address.
// Should we find them, we go through the list and try to find an acceptable one by evaluating the
// following properties (not in order of priority, and not for every case - the logic here is a mess):
//
// 1. Did the input have a user_id?
// 2. Did the input hava a username?
// 3. Is the input user_id a real id?
// 4. Is the stored user_id a real id?
// 5. Does the stored user_id have a username?
// 6. Do the names match?
//
// Based on this, if we find an acceptable candidate, we do one:
//
// 1. Replace the global DB user_id with the input user_id and patch the stored identity's user_id
// (this may be different than 1, though in practice it seems we always do both)
// 2. Patch the output identity's user_id from the stored identity
//
// If we find none, in the case if the has-username-but-no-user_id input case, we'll try a TOFU id
// fetch before giving up on stored identity candidates.
//
// Acceptable candidates are then passed to prepare_update_identity which will patch usernames and
// find any applicable keys.
//
// Unacceptable candidates will then have minimal record information entered depending on how much
// came in in the input, TOFU user_ids created when needed, and a new record placed in the DB
// accordingly.
//
if (!stored_ident) {
// Before we start - if there was no stored identity, we should check to make sure we don't
// have a stored identity with a temporary user_id that differs from the input user_id. This
// happens in multithreaded environments sometimes.
identity_list* id_list = NULL;
status = get_identities_by_address(session, identity->address, &id_list);
if (id_list) {
identity_list* stored_curr = id_list;
// Ok, here's where we search for stored identities and try to find a candidate.
while (stored_curr) {
// Ok, this is where the above code fun begins. Let's get some information about the identity.
pEp_identity* candidate = stored_curr->ident;
if (candidate) {
char* candidate_id = candidate->user_id;
// this_uid should never be NULL, as this is half of the ident
// DB primary key
assert(!EMPTYSTR(candidate_id));
// grab some information about the stored identity
bool candidate_has_real_id = strstr(candidate_id, "TOFU_") != candidate_id;
bool candidate_has_username = !EMPTYSTR(candidate->username);
bool names_match = (input_has_username && candidate_has_username) &&
(strcmp(identity->username, candidate->username) == 0);
// This is where the optimisation gets a little weird:
//
// Decide whether to accept and patch the database and stored id from the input,
// Accept and patch the input id from the database, or reject and go to the next
// one in the list
//
// This is unnecessary, but I think the terms need to be descriptive where possible
bool input_addr_only = !input_has_username && !input_has_user_id;
bool candidate_id_best = candidate_has_real_id && !input_has_real_id;
bool input_id_best = input_has_real_id && !candidate_has_real_id;
bool patch_input_id_conditions = input_has_user_id || names_match || !candidate_has_username;
if (input_addr_only || (candidate_id_best && patch_input_id_conditions)) {
identity->user_id = strdup(candidate_id);
assert(identity->user_id);
if (!identity->user_id)
goto enomem;
stored_ident = identity_dup(candidate);
break;
}
else if (input_id_best && (names_match || (input_has_username && !candidate_has_username))) {
// Replace the TOFU db in the database with the input ID globally
status = replace_userid(session,
candidate_id,
identity->user_id);
if (status != PEP_STATUS_OK) {
free_identity_list(id_list);
free(default_own_id);
return status;
}
if (id_list && status == PEP_STATUS_OK) {
// Results are ordered by timestamp descending, so this covers
// both the one-result and multi-result cases
if (addr_only) {
if (stored_ident) // unlikely
free_identity(stored_ident);
stored_ident = id_list->ident;
free(candidate_id);
candidate_id = NULL;
// Reflect the change we just made to the DB
free(candidate->user_id);
candidate->user_id = strdup(identity->user_id);
stored_ident = identity_dup(candidate);
break;
}
// Else, we reject this candidate and try the next one, if there is one.
stored_curr = stored_curr->next;
}
// Ok, we've checked all of the candidates, and if there's a stored identity, there's a duplicate.
// Freeeeeeee...
free_identity_list(id_list);
}
else {
bool input_is_TOFU =
has_user_id ? (strstr(identity->user_id, "TOFU_") == identity->user_id) : false;
bool stored_is_TOFU = false;
identity_list* id_curr = id_list;
while (id_curr) {
pEp_identity* db_list_id = id_curr->ident;
if (db_list_id) {
// Check to see if it's a stored TOFU id
char* db_uid = db_list_id->user_id; // should NEVER be null (DB key!)
assert(!EMPTYSTR(db_uid));
stored_is_TOFU = (strstr(db_uid, "TOFU_") == db_uid);
bool names_match = !EMPTYSTR(identity->username) && !EMPTYSTR(db_list_id->username) &&
(strcasecmp(identity->username, db_list_id->username) == 0);
// If we submitted a real user_id:
// If the stored one is TOFU, replace it in DB with the real one from input.
// Otherwise, if we have none:
// If the stored one is non-TOFU, we copy it TO the output
//
if (!stored_is_TOFU) {
if ((!input_is_TOFU && !has_user_id && names_match) || input_is_TOFU) {
free(identity->user_id);
identity->user_id = strdup(db_uid);
assert(identity->user_id);
if (!identity->user_id)
goto enomem;
stored_ident = identity_dup(db_list_id);
break;
}
}
else if (!input_is_TOFU) {
bool name_unset = false;
name_unset = EMPTYSTR(db_list_id->username) || strcasecmp(db_list_id->username, db_list_id->address) == 0;
if (name_unset || names_match) {
// Ok, we have a temp ID. We have to replace this
// with the real ID in the DB.
status = replace_userid(session,
db_uid,
identity->user_id);
if (status != PEP_STATUS_OK) {
free_identity_list(id_list);
free(default_own_id);
return status;
}
// Reflect the change we just made to the DB and make this the stored ident
stored_ident = identity_dup(db_list_id);
free(stored_ident->user_id);
stored_ident->user_id = strdup(identity->user_id);
break;
}
}
} // end if (db_list_id);
id_curr = id_curr->next;
} // end while(id_curr)
} // end else
} // end if (id_list)
} // end if (!stored_ident)
}
// If, by here, there is no user id on the identity, we put one on there.
// We've found any non-TOFU one we're going to find, so if this is empty,
// We don't have a stored ident.
if (EMPTYSTR(identity->user_id)) {
identity->user_id = calloc(1, strlen(identity->address) + 6);
if (!identity->user_id)
goto enomem;
snprintf(identity->user_id, strlen(identity->address) + 6,
"TOFU_%s", identity->address);
// Try one last time to see if there is an ident for us with a TOFU id, if there was no ID but there
// was a usernames
if (input_has_username) {
status = get_identity(session,
identity->address,
identity->user_id,
&stored_ident);
}
}
}
// A: There was a stored identity (and we had no error when trying to grab it)
//
// Either update the identity (and possibly DB to reflect stored ident information, or
// create a new identity and store it.
//
if (status == PEP_STATUS_OK && stored_ident) {
// We had a user_id, we've checked to see if it's TOFU corresponding to a stored ident.
// Now try to get key (and trust?) information.
// * if identity available
// * patch it with username
// (note: this will happen when
// setting automatically below...)
// * get valid key for identity
// * if valid key exists
// * set return value's fpr
// An identity was available.
// Call will patch the username where needed and
// get a valid default key (for ident or user)
status = prepare_updated_identity(session,
identity,
stored_ident, !addr_only);
identity,
stored_ident, true);
}
// B: There was either no stored identity, or we had an error grabbing it
// * else (identity unavailable in DB, we're updating a new identity - we might have a user key, but no
// other default is available and we do not elect keys!)
else if (has_user_id) {
status = PEP_STATUS_OK;
if (!has_uname) {
else { // No stored ident. We're done.
// If we needed TOFU, we've taken care of the ID above.
if (EMPTYSTR(identity->username)) { // currently, not after messing around
free(identity->username);
identity->username = strdup(identity->address);
if (!identity->username)
goto enomem;
}
identity->comm_type = PEP_ct_key_not_found;
free(identity->fpr);
identity->fpr = NULL;
identity->comm_type = PEP_ct_unknown;
adjust_pEp_trust_status(session, identity);
status = set_identity(session, identity);
}
else { // Make a TOFU id, query the DB for it
identity->user_id = calloc(1, strlen(identity->address) + 6);
if (!identity->user_id)
goto enomem;
snprintf(identity->user_id, strlen(identity->address) + 6,
"TOFU_%s", identity->address);
status = get_identity(session,
identity->address,
identity->user_id,
&stored_ident);
if (status == PEP_STATUS_OK && stored_ident) {
status = prepare_updated_identity(session,
identity,
stored_ident, true); // ?
}
else {
free(identity->fpr);
identity->fpr = NULL;
identity->comm_type = PEP_ct_key_not_found;
adjust_pEp_trust_status(session, identity);
status = set_identity(session, identity);
}
}
// FIXME: This is legacy. I presume it's a notification for the caller...
// Revisit once I can talk to Volker
// VB says, and I quote, "that is not implemented and no one is using it right now"
// about this bit. So, um, you're forewarned.
if (identity->comm_type != PEP_ct_compromised &&
identity->comm_type < PEP_ct_strong_but_unconfirmed)
if (session->examine_identity)

Loading…
Cancel
Save