/* * Off-the-Record Messaging library * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew, * Nikita Borisov * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General * Public License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* system headers */ #include #include #include /* libgcrypt headers */ #include /* libotr headers */ #include "context.h" /* Create a new connection context. */ static ConnContext * new_context(const char * user, const char * accountname, const char * protocol) { ConnContext * context; OtrlSMState *smstate; context = malloc(sizeof(*context)); assert(context != NULL); context->username = strdup(user); context->accountname = strdup(accountname); context->protocol = strdup(protocol); context->msgstate = OTRL_MSGSTATE_PLAINTEXT; otrl_auth_new(&(context->auth)); smstate = malloc(sizeof(OtrlSMState)); assert(smstate != NULL); otrl_sm_state_new(smstate); context->smstate = smstate; context->fingerprint_root.fingerprint = NULL; context->fingerprint_root.context = context; context->fingerprint_root.next = NULL; context->fingerprint_root.tous = NULL; context->active_fingerprint = NULL; memset(context->sessionid, 0, 20); context->sessionid_len = 0; context->protocol_version = 0; context->otr_offer = OFFER_NOT; context->app_data = NULL; context->app_data_free = NULL; context->context_priv = context_priv_new(); assert(context->context_priv != NULL); context->next = NULL; return context; } /* Copy information from the master context. */ void copy_master_context(ConnContext *m_context, ConnContext *context) { context->username = strdup(m_context->username); context->accountname = strdup(m_context->accountname); context->protocol = strdup(m_context->protocol); // TODO anything else to copy? return; } /* Look up a connection context by name/account/protocol from the given * OtrlUserState. If add_if_missing is true, allocate and return a new * context if one does not currently exist. In that event, call * add_app_data(data, context) so that app_data and app_data_free can be * filled in by the application, and set *addedp to 1. */ ConnContext * otrl_context_find(OtrlUserState us, const char *user, const char *accountname, const char *protocol, instag_t their_instance, int add_if_missing, int *addedp, void (*add_app_data)(void *data, ConnContext *context), void *data) { //fprintf(stderr, "looking for %s usern, %s accn, %s proto\n", user, accountname, protocol); ConnContext ** curp; int usercmp = 1, acctcmp = 1, protocmp = 1; if (addedp) *addedp = 0; if (!user || !accountname || !protocol) return NULL; for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) { //fprintf(stderr, "checking for %s usern, %s accn, %s proto\n", (*curp)->username, (*curp)->accountname, (*curp)->protocol); if ((usercmp = strcmp((*curp)->username, user)) > 0 || (usercmp == 0 && (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) || (usercmp == 0 && acctcmp == 0 && (protocmp = strcmp((*curp)->protocol, protocol)) >= 0)) /* We're at the right place in the list. We've either found * it, or gone too far. */ break; } if (usercmp == 0 && acctcmp == 0 && protocmp == 0) { /* Found it! */ return *curp; } if (add_if_missing) { ConnContext *newctx; if (addedp) *addedp = 1; newctx = new_context(user, accountname, protocol); newctx->next = *curp; if (*curp) { (*curp)->tous = &(newctx->next); } *curp = newctx; newctx->tous = curp; if (add_app_data) { add_app_data(data, *curp); } return *curp; } return NULL; } /* Find the instance of this master context we last received a data message from. If there are no instances, then the master context is returned. */ ConnContext * otrl_context_find_recent_instance(ConnContext * m_context) { // FIXME: use --std=gnu99 to allow proper standard conformance ConnContext *curp; for (curp = m_context->next; curp; curp = curp->next) { if (strcmp(curp->username, m_context->username) || strcmp(curp->accountname, m_context->accountname)) break; if (curp->context_priv->lastrecv >= m_context->context_priv->lastrecv) return curp; } return m_context; } ConnContext * otrl_context_find_file_transfer(ConnContext * m_context, unsigned char id) { // FIXME: use --std=gnu99 to allow proper standard conformance ConnContext *curp; for (curp = m_context->next; curp; curp = curp->next) { if (strcmp(curp->username, m_context->username) || strcmp(curp->accountname, m_context->accountname)) break; if(context_priv_ft_recv_id(curp->context_priv, id)) return curp; } fprintf(stderr, "WARN: failed to find context for id %u\n", (unsigned int)id); return m_context; } // return 1 if instance should be ignored for that context // 0 - otherwise int otrl_check_instance(ConnContext *context, instag_t instance) { if(context->auth.ft_inst_len > 0) { // FIXME: modify autools helper files so --std=gnu99 is added for compilation on GNU/Linux // so there is no need in such retarted things: int i; for(i = 0; i < context->auth.ft_inst_len; i++) { if(context->auth.ft_instags[i] == instance) return 0; } } if(context->auth.our_instance != instance) return 1; return 0; } /* Find a fingerprint in a given context, perhaps adding it if not * present. */ Fingerprint *otrl_context_find_fingerprint(ConnContext *context, unsigned char fingerprint[20], int add_if_missing, int *addedp) { Fingerprint *f = context->fingerprint_root.next; if (addedp) *addedp = 0; while(f) { if (!memcmp(f->fingerprint, fingerprint, 20)) return f; f = f->next; } /* Didn't find it. */ if (add_if_missing) { if (addedp) *addedp = 1; f = malloc(sizeof(*f)); assert(f != NULL); f->fingerprint = malloc(20); assert(f->fingerprint != NULL); memmove(f->fingerprint, fingerprint, 20); f->context = context; f->trust = NULL; f->next = context->fingerprint_root.next; if (f->next) { f->next->tous = &(f->next); } context->fingerprint_root.next = f; f->tous = &(context->fingerprint_root.next); return f; } return NULL; } /* Set the trust level for a given fingerprint */ void otrl_context_set_trust(Fingerprint *fprint, const char *trust) { if (fprint == NULL) return; free(fprint->trust); fprint->trust = trust ? strdup(trust) : NULL; } /* Force a context into the OTRL_MSGSTATE_FINISHED state. */ void otrl_context_force_finished(ConnContext *context) { context->msgstate = OTRL_MSGSTATE_FINISHED; otrl_auth_clear(&(context->auth)); context->active_fingerprint = NULL; memset(context->sessionid, 0, 20); context->sessionid_len = 0; context->protocol_version = 0; otrl_sm_state_free(context->smstate); context_priv_force_finished(context->context_priv); } /* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */ void otrl_context_force_plaintext(ConnContext *context) { /* First clean up everything we'd need to do for the FINISHED state */ otrl_context_force_finished(context); /* And just set the state properly */ context->msgstate = OTRL_MSGSTATE_PLAINTEXT; } /* Forget a fingerprint (so long as it's not the active one. If it's a * fingerprint_root, forget the whole context (as long as * and_maybe_context is set, and it's PLAINTEXT). Also, if it's not * the fingerprint_root, but it's the only fingerprint, and we're * PLAINTEXT, forget the whole context if and_maybe_context is set. */ void otrl_context_forget_fingerprint(Fingerprint *fprint, int and_maybe_context) { ConnContext *context = fprint->context; if (fprint == &(context->fingerprint_root)) { if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT && and_maybe_context) { otrl_context_forget(context); } } else { if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT || context->active_fingerprint != fprint) { free(fprint->fingerprint); free(fprint->trust); *(fprint->tous) = fprint->next; if (fprint->next) { fprint->next->tous = fprint->tous; } free(fprint); if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT && context->fingerprint_root.next == NULL && and_maybe_context) { /* We just deleted the only fingerprint. Forget the * whole thing. */ otrl_context_forget(context); } } } } /* Forget a whole context, so long as it's PLAINTEXT. */ void otrl_context_forget(ConnContext *context) { if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return; /* Just to be safe, force to plaintext. This also frees any * extraneous data lying around. */ otrl_context_force_plaintext(context); /* First free all the Fingerprints */ while(context->fingerprint_root.next) { otrl_context_forget_fingerprint(context->fingerprint_root.next, 0); } /* Now free all the dynamic info here */ free(context->username); free(context->accountname); free(context->protocol); free(context->smstate); context->username = NULL; context->accountname = NULL; context->protocol = NULL; context->smstate = NULL; /* Free the application data, if it exists */ if (context->app_data && context->app_data_free) { (context->app_data_free)(context->app_data); context->app_data = NULL; } /* Fix the list linkages */ *(context->tous) = context->next; if (context->next) { context->next->tous = context->tous; } free(context); } /* Forget all the contexts in a given OtrlUserState. */ void otrl_context_forget_all(OtrlUserState us) { while (us->context_root) { otrl_context_force_plaintext(us->context_root); otrl_context_forget(us->context_root); } }