diff options
Diffstat (limited to 'otr.c')
-rw-r--r-- | otr.c | 190 |
1 files changed, 33 insertions, 157 deletions
@@ -102,26 +102,12 @@ const command_t otr_commands[] = { /** misc. helpers/subroutines: **/ -/* start background thread to generate a (new) key for a given account */ +/* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); -/* keygen thread main func */ -gpointer otr_keygen_thread_func(gpointer data); - -/* mainloop handler for when keygen thread finishes */ +/* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); -/* data to be passed to otr_keygen_thread_func */ -struct kgdata { - irc_t *irc; /* access to OTR userstate */ - char *keyfile; /* free me! */ - const char *handle; /* don't free! */ - const char *protocol; /* don't free! */ - GMutex *mutex; /* lock for the 'done' flag, free me! */ - int done; /* is the thread done? */ - gcry_error_t result; /* return value of otrl_privkey_generate */ -}; - /* some yes/no handlers */ void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); @@ -171,7 +157,6 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args); void otr_init(void) { - if(!g_thread_supported()) g_thread_init(NULL); OTRL_INIT; /* fill global OtrlMessageAppOps */ @@ -195,42 +180,24 @@ void otr_init(void) global.otr_ops.account_name_free = NULL; } -/* Notice on the otr_mutex: - - The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed, - this will prevent a concurrent keygen (possibly spawned by that very command) - from messing up the userstate. If the lock fails, that means there already is - a keygen in progress. Instead of blocking for an unknown time, they - will bail out gracefully, informing the user of this temporary "coma". - TODO: Hold back incoming/outgoing messages and process them when keygen completes? - - The other routines do not lock the otr_mutex themselves, it is done as a - catch-all in the root command handler. Rationale: - a) it's easy to code - b) it makes it obvious that no command can get its userstate corrupted - c) the "irc" struct is readily available there for feedback to the user - */ - void otr_load(irc_t *irc) { char s[512]; account_t *a; gcry_error_t e; - int eno; + gcry_error_t enoent = gcry_error_from_errno(ENOENT); log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } /* check for otr keys on all accounts */ @@ -249,7 +216,7 @@ void otr_save(irc_t *irc) g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_write_fingerprints(irc->otr_us, s); if(e) { - log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); + irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e)); } chmod(s, 0600); } @@ -301,19 +268,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* fallback for non-otr clients */ - if(u && !u->encrypted) { - return g_strdup(msg); - } - - irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return NULL; - } - ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -322,11 +276,9 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(ignore_msg) { /* this was an internal OTR protocol message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return NULL; } else if(!newmsg) { /* this was a non-OTR message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return g_strdup(msg); } else { /* OTR has processed this message */ @@ -346,7 +298,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha colormsg = g_strdup(newmsg); } otrl_message_free(newmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return colormsg; } } @@ -357,25 +308,10 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* Fallback for non-otr clients. - Yes, this better shouldn't send private stuff in the clear... */ - if(u && !u->encrypted) { - return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); - } - - irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return 1; - } - st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -386,7 +322,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m if(otrmsg) { if(!ctx) { otrl_message_free(otrmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return 1; } st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, @@ -398,7 +333,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -1482,101 +1416,43 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - GError *err; - GThread *thr; - struct kgdata *kg; - gint ev; irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); - - kg = g_new0(struct kgdata, 1); - if(!kg) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - return; - } + irc_usermsg(irc, "n/a: not implemented"); + return; - /* Assemble the job description to be passed to thread and handler */ - kg->irc = irc; - kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick); - if(!kg->keyfile) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - g_free(kg); - return; - } - kg->handle = handle; - kg->protocol = protocol; - kg->mutex = g_mutex_new(); - if(!kg->mutex) { - irc_usermsg(irc, "otr keygen failed: couldn't create mutex"); - g_free(kg->keyfile); - g_free(kg); - return; - } - kg->done = 0; - - /* Poll for completion of the thread periodically. I would have preferred - to just wait on a pipe but this way it's portable to Windows. *sigh* - */ - ev = b_timeout_add(1000, &keygen_finish_handler, kg); - if(!ev) { - irc_usermsg(irc, "otr keygen failed: couldn't register timeout"); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return; - } - - thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); - if(!thr) { - irc_usermsg(irc, "otr keygen failed: %s", err->message); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - b_event_remove(ev); - } -} - -gpointer otr_keygen_thread_func(gpointer data) -{ - struct kgdata *kg = (struct kgdata *)data; + /* see if we already have a keygen child running. if not, start one and put a + handler on its output. + + b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); - /* lock OTR subsystem and do the work */ - g_static_rec_mutex_lock(&kg->irc->otr_mutex); - kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, - kg->protocol); - chmod(kg->keyfile, 0600); - g_static_rec_mutex_unlock(&kg->irc->otr_mutex); - /* OTR enabled again */ + generate a fresh temp file name for our new key and save our current keys to it. + send the child filename and accountname/protocol for the new key + increment 'ntodo' */ - /* notify mainloop */ - g_mutex_lock(kg->mutex); - kg->done = 1; - g_mutex_unlock(kg->mutex); + /* in the child: + read filename, accountname, protocol from input, and start work: + + result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, + kg->protocol); + + when done, send filename, accountname, and protocol to output. */ - return NULL; } gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) { - struct kgdata *kg = (struct kgdata *)data; - int done; - - g_mutex_lock(kg->mutex); - done = kg->done; - g_mutex_unlock(kg->mutex); - if(kg->done) { - if(kg->result) { - irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result)); - } else { + /* in the handler: + read filename, accountname, and protocol from child output + print a message to the user irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); - } - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return FALSE; /* unregister timeout */ - } + load the file into userstate + call otr_save. + remove the tempfile. + decrement 'ntodo' + if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - return TRUE; /* still working, continue checking */ + return TRUE; /* still working, keep watching */ } void yes_keygen(gpointer w, void *data) |