aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--irc.c11
-rw-r--r--irc.h3
-rw-r--r--otr.c190
-rw-r--r--root_commands.c8
4 files changed, 44 insertions, 168 deletions
diff --git a/irc.c b/irc.c
index b7046e59..a2bcfc59 100644
--- a/irc.c
+++ b/irc.c
@@ -27,6 +27,8 @@
#include "bitlbee.h"
#include "crypting.h"
#include "ipc.h"
+#include <sys/types.h>
+#include <sys/wait.h>
static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
@@ -132,7 +134,8 @@ irc_t *irc_new( int fd )
conf_loaddefaults( irc );
irc->otr_us = otrl_userstate_create();
- g_static_rec_mutex_init(&irc->otr_mutex);
+ irc->otr_keygen = 0;
+ irc->otr_ntodo = 0;
return( irc );
}
@@ -285,7 +288,11 @@ void irc_free(irc_t * irc)
}
otrl_userstate_free(irc->otr_us);
- g_static_rec_mutex_free(&irc->otr_mutex);
+ if(irc->otr_keygen) {
+ kill(irc->otr_keygen, SIGTERM);
+ waitpid(irc->otr_keygen, NULL, 0);
+ /* TODO: remove stale keygen tempfiles */
+ }
g_free(irc);
diff --git a/irc.h b/irc.h
index 8c993e13..8e9c95c5 100644
--- a/irc.h
+++ b/irc.h
@@ -98,7 +98,8 @@ typedef struct irc
gint ping_source_id;
OtrlUserState otr_us;
- GStaticRecMutex otr_mutex; /* for locking otr during keygen */
+ pid_t otr_keygen;
+ int otr_ntodo;
} irc_t;
#include "user.h"
diff --git a/otr.c b/otr.c
index fcf653c5..c4e8a20a 100644
--- a/otr.c
+++ b/otr.c
@@ -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)
diff --git a/root_commands.c b/root_commands.c
index 04c62fea..9b492048 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -85,27 +85,19 @@ void root_command( irc_t *irc, char *cmd[] )
if( !cmd[0] )
return;
- if(!g_static_rec_mutex_trylock(&irc->otr_mutex)) {
- irc_usermsg(irc, "keygen in progress, bitlbee comatose - please wait");
- return;
- }
-
for( i = 0; commands[i].command; i++ )
if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
{
if( !cmd[commands[i].required_parameters] )
{
irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters );
- g_static_rec_mutex_unlock(&irc->otr_mutex);
return;
}
commands[i].execute( irc, cmd );
- g_static_rec_mutex_unlock(&irc->otr_mutex);
return;
}
irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] );
- g_static_rec_mutex_unlock(&irc->otr_mutex);
}
static void cmd_help( irc_t *irc, char **cmd )