Index: irc.c
===================================================================
--- irc.c	(revision 27db43361a3fdd3420b12aa5bf151dce4545273f)
+++ irc.c	(revision dc9797f7ad4177dc72373ce71d375257fb0271a1)
@@ -134,9 +134,5 @@
 	conf_loaddefaults( irc );
 
-	irc->otr_us = otrl_userstate_create();
-	irc->otr_keygen = 0;
-	irc->otr_to = NULL;
-	irc->otr_from = NULL;
-	irc->otr_ntodo = 0;
+	irc->otr = otr_new();
 	
 	return( irc );
@@ -290,10 +286,5 @@
 	}
 	
-	otrl_userstate_free(irc->otr_us);
-	if(irc->otr_keygen) {
-		kill(irc->otr_keygen, SIGTERM);
-		waitpid(irc->otr_keygen, NULL, 0);
-		/* TODO: remove stale keygen tempfiles */
-	}
+	otr_free(irc->otr);
 	
 	g_free(irc);
Index: irc.h
===================================================================
--- irc.h	(revision 27db43361a3fdd3420b12aa5bf151dce4545273f)
+++ irc.h	(revision dc9797f7ad4177dc72373ce71d375257fb0271a1)
@@ -98,9 +98,5 @@
 	gint ping_source_id;
 	
-	OtrlUserState otr_us;
-	pid_t otr_keygen;    /* pid of keygen slave */
-	FILE *otr_to;        /* pipe to keygen slave */
-	FILE *otr_from;      /* pipe from keygen slave */
-	int otr_ntodo;       /* number of keys left to generate */
+	otr_t *otr;            /* OTR state and book keeping */
 } irc_t;
 
Index: otr.c
===================================================================
--- otr.c	(revision 27db43361a3fdd3420b12aa5bf151dce4545273f)
+++ otr.c	(revision dc9797f7ad4177dc72373ce71d375257fb0271a1)
@@ -45,4 +45,5 @@
 #include <sys/wait.h>
 #include <unistd.h>
+#include <assert.h>
 
 
@@ -103,4 +104,7 @@
 
 /** misc. helpers/subroutines: **/
+
+/* check whether we are already generating a key for a given account */
+int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol);
 
 /* start background process to generate a (new) key for a given account */
@@ -191,4 +195,33 @@
 }
 
+otr_t *otr_new(void)
+{
+	otr_t *otr = g_new0(otr_t, 1);
+
+	otr->us = otrl_userstate_create();
+	
+	return otr;
+}
+
+void otr_free(otr_t *otr)
+{
+	otrl_userstate_free(otr->us);
+	if(otr->keygen) {
+		kill(otr->keygen, SIGTERM);
+		waitpid(otr->keygen, NULL, 0);
+		/* TODO: remove stale keygen tempfiles */
+	}
+	if(otr->to)
+		fclose(otr->to);
+	if(otr->from)
+		fclose(otr->from);
+	while(otr->todo) {
+		kg_t *p = otr->todo;
+		otr->todo = p->next;
+		g_free(p);
+	}
+	g_free(otr);
+}
+
 void otr_load(irc_t *irc)
 {
@@ -201,10 +234,10 @@
 
 	g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick);
-	e = otrl_privkey_read(irc->otr_us, s);
+	e = otrl_privkey_read(irc->otr->us, s);
 	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);
+	e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL);
 	if(e && e!=enoent) {
 		irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
@@ -225,5 +258,5 @@
 
 	g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
-	e = otrl_privkey_write_fingerprints(irc->otr_us, s);
+	e = otrl_privkey_write_fingerprints(irc->otr->us, s);
 	if(e) {
 		irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e));
@@ -263,8 +296,9 @@
 	OtrlPrivKey *k;
 	
-	k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name);
+	k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name);
 	if(k) {
-		irc_usermsg(irc, "otr: %s/%s ready",
-			a->user, a->prpl->name);
+		irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name);
+	} if(keygen_in_progress(irc, a->user, a->prpl->name)) {
+		irc_usermsg(irc, "otr: keygen for %s/%s in progress", a->user, a->prpl->name);
 	} else {
 		otr_keygen(irc, a->user, a->prpl->name);
@@ -279,5 +313,5 @@
 	char *colormsg;
 	
-	ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic,
+	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);
@@ -293,5 +327,5 @@
 	} else {
 		/* OTR has processed this message */
-		ConnContext *context = otrl_context_find(ic->irc->otr_us, handle,
+		ConnContext *context = otrl_context_find(ic->irc->otr->us, handle,
 			ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
 		if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
@@ -319,5 +353,5 @@
 	ConnContext *ctx = NULL;
 	
-	st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
+	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);
@@ -326,5 +360,5 @@
 	}
 
-	ctx = otrl_context_find(ic->irc->otr_us,
+	ctx = otrl_context_find(ic->irc->otr->us,
 			handle, ic->acc->user, ic->acc->prpl->name,
 			1, NULL, NULL, NULL);
@@ -384,4 +418,12 @@
 	struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
 	const char *p;
+	
+	log_message(LOGLVL_DEBUG, "op_policy '%s' '%s'", context->accountname, context->protocol);
+	
+	/* policy override during keygen: if we're missing the key for context but are currently
+	   generating it, then that's as much as we can do. => temporarily return NEVER. */
+	if(keygen_in_progress(ic->irc, context->accountname, context->protocol) &&
+	   !otrl_privkey_find(ic->irc->otr->us, context->accountname, context->protocol))
+		return OTRL_POLICY_NEVER;
 
 	p = set_getstr(&ic->irc->set, "otr_policy");
@@ -402,11 +444,9 @@
 {
 	struct im_connection *ic = check_imc(opdata, accountname, protocol);
-	char *s;
 	
 	log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol);
 
-	s = g_strdup_printf("oops, no otr privkey for %s - generate one now?",
-		accountname);
-	query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc);
+	/* will fail silently if keygen already in progress */
+	otr_keygen(ic->irc, accountname, protocol);
 }
 
@@ -596,5 +636,5 @@
 	}
 	
-	otrl_message_disconnect(irc->otr_us, &global.otr_ops,
+	otrl_message_disconnect(irc->otr->us, &global.otr_ops,
 		u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
 	
@@ -602,5 +642,5 @@
 	if(u->encrypted) {
 		ConnContext *ctx;
-		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+		ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user,
 			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
 		if(ctx)
@@ -643,5 +683,5 @@
 	}
 	
-	ctx = otrl_context_find(irc->otr_us, u->handle,
+	ctx = otrl_context_find(irc->otr->us, u->handle,
 		u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL);
 	if(!ctx) {
@@ -654,5 +694,5 @@
 			"SMP already in phase %d, sending abort before reinitiating",
 			ctx->smstate->nextExpected+1);
-		otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx);
+		otrl_message_abort_smp(irc->otr->us, &global.otr_ops, u->ic, ctx);
 		otrl_sm_state_free(ctx->smstate);
 	}
@@ -662,5 +702,5 @@
 	if(ctx->smstate->secret == NULL) {
 		irc_usermsg(irc, "smp: initiating with %s...", u->nick);
-		otrl_message_initiate_smp(irc->otr_us, &global.otr_ops,
+		otrl_message_initiate_smp(irc->otr->us, &global.otr_ops,
 			u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
 		/* smp is now in EXPECT2 */
@@ -669,5 +709,5 @@
 		   received the SMP1, so let's issue a response */
 		irc_usermsg(irc, "smp: responding to %s...", u->nick);
-		otrl_message_respond_smp(irc->otr_us, &global.otr_ops,
+		otrl_message_respond_smp(irc->otr->us, &global.otr_ops,
 			u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
 		/* smp is now in EXPECT3 */
@@ -689,5 +729,5 @@
 	}
 	
-	ctx = otrl_context_find(irc->otr_us, u->handle,
+	ctx = otrl_context_find(irc->otr->us, u->handle,
 		u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
 	if(!ctx) {
@@ -753,5 +793,5 @@
 			*(myhandle++) = '\0';
 			handle = arg;
-			ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
+			ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
 			if(!ctx) {
 				irc_usermsg(irc, "no such context");
@@ -766,5 +806,5 @@
 				return;
 			}
-			ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+			ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user,
 				u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
 			if(!ctx) {
@@ -803,5 +843,10 @@
 	}
 	
-	if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) {
+	if(keygen_in_progress(irc, a->user, a->prpl->name)) {
+		irc_usermsg(irc, "keygen for account %d already in progress", n);
+		return;
+	}
+	
+	if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) {
 		char *s = g_strdup_printf("account %d already has a key, replace it?", n);
 		query_add(irc, NULL, s, yes_keygen, NULL, a);
@@ -871,5 +916,5 @@
 		}
 		
-		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+		ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user,
 			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
 		if(!ctx) {
@@ -907,5 +952,5 @@
 		}
 		
-		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+		ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user,
 			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
 		if(!ctx) {
@@ -958,5 +1003,5 @@
 {
 	irc_t *irc = ic->irc;
-	OtrlUserState us = irc->otr_us;
+	OtrlUserState us = irc->otr->us;
 	OtrlMessageAppOps *ops = &global.otr_ops;
 	OtrlTLV *tlv = NULL;
@@ -1307,6 +1352,6 @@
 	/* find first key which matches the given prefix */
 	n = strlen(prefix);
-	for(k=irc->otr_us->privkey_root; k; k=k->next) {
-		p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol);
+	for(k=irc->otr->us->privkey_root; k; k=k->next) {
+		p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
 		if(!p) /* gah! :-P */
 			continue;
@@ -1321,5 +1366,5 @@
 	/* make sure the match, if any, is unique */
 	for(k2=k->next; k2; k2=k2->next) {
-		p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol);
+		p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
 		if(!p) /* gah! :-P */
 			continue;
@@ -1343,5 +1388,5 @@
 	/* list all privkeys */
 	irc_usermsg(irc, "\x1fprivate keys:\x1f");
-	for(key=irc->otr_us->privkey_root; key; key=key->next) {
+	for(key=irc->otr->us->privkey_root; key; key=key->next) {
 		const char *hash;
 		
@@ -1358,5 +1403,5 @@
 		   account/protocol, but libotr currently doesn't provide a direct routine
 		   for hashing a given 'OtrlPrivKey'... */
-		hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol);
+		hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
 		if(hash) /* should always succeed */
 			irc_usermsg(irc, "    %s", human);
@@ -1366,5 +1411,5 @@
 	irc_usermsg(irc, "%s", "");
 	irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
-	for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\
+	for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\
 		user_t *u;
 		char *userstring;
@@ -1425,11 +1470,39 @@
 }
 
+int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
+{
+	kg_t *kg;
+	
+	log_message(LOGLVL_DEBUG, "keygen_in_progress '%s' '%s'", handle, protocol);
+	
+	if(!irc->otr->sent_accountname || !irc->otr->sent_protocol)
+		return 0;
+
+	/* are we currently working on this key? */
+	if(!strcmp(handle, irc->otr->sent_accountname) &&
+	   !strcmp(protocol, irc->otr->sent_protocol))
+		return 1;
+	
+	/* do we have it queued for later? */
+	for(kg=irc->otr->todo; kg; kg=kg->next) {
+		if(!strcmp(handle, kg->accountname) &&
+		   !strcmp(protocol, kg->protocol))
+			return 1;
+	}
+	
+	return 0;
+}
+
 void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
 {
+	/* do nothing if a key for the requested account is already being generated */
+	if(keygen_in_progress(irc, handle, protocol))
+		return;
+	
 	irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol);
 
 	/* see if we already have a keygen child running. if not, start one and put a
 	   handler on its output. */
-	if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) {
+	if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
 		pid_t p;
 		int to[2], from[2];
@@ -1457,19 +1530,35 @@
 			/* child process */
 			signal(SIGTERM, exit);
-			keygen_child_main(irc->otr_us, to[0], from[1]);
+			keygen_child_main(irc->otr->us, to[0], from[1]);
 			exit(0);
 		}
 		
-		irc->otr_keygen = p;
-		irc->otr_to = tof;
-		irc->otr_from = fromf;
-		irc->otr_ntodo = 0;
+		irc->otr->keygen = p;
+		irc->otr->to = tof;
+		irc->otr->from = fromf;
+		irc->otr->sent_accountname = NULL;
+		irc->otr->sent_protocol = NULL;
+		irc->otr->todo = NULL;
 		b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc);
 	}
 	
-	/* send accountname and protocol for the new key to our keygen slave */
-	fprintf(irc->otr_to, "%s\n%s\n", handle, protocol);
-	fflush(irc->otr_to);
-	irc->otr_ntodo++;
+	/* is the keygen slave currently working? */
+	if(irc->otr->sent_accountname) {
+		/* enqueue our job for later transmission */
+		log_message(LOGLVL_DEBUG, "enqueueing keygen for %s/%s", handle, protocol);
+		kg_t **kg = &irc->otr->todo;
+		while(*kg)
+			kg=&((*kg)->next);
+		*kg = g_new0(kg_t, 1);
+		(*kg)->accountname = handle;
+		(*kg)->protocol = protocol;
+	} else {
+		/* send our job over and remember it */
+		log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol);
+		fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
+		fflush(irc->otr->to);
+		irc->otr->sent_accountname = handle;
+		irc->otr->sent_protocol = protocol;
+	}
 }
 
@@ -1515,6 +1604,6 @@
 	log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond);
 
-	myfgets(filename, 512, irc->otr_from);
-	myfgets(msg, 512, irc->otr_from);
+	myfgets(filename, 512, irc->otr->from);
+	myfgets(msg, 512, irc->otr->from);
 	
 	log_message(LOGLVL_DEBUG, "filename='%s'", filename);
@@ -1528,21 +1617,34 @@
 		unlink(filename);
 		rename(tmp,kf);
-		otrl_privkey_read(irc->otr_us, kf);
+		otrl_privkey_read(irc->otr->us, kf);
 		g_free(kf);
 		g_free(tmp);
 	}
-		
-	irc->otr_ntodo--;
-	if(irc->otr_ntodo < 1) {
-		/* okay, we think the slave is idle now, so kill him */
+	
+	/* forget this job */
+	irc->otr->sent_accountname = NULL;
+	irc->otr->sent_protocol = NULL;
+	
+	/* see if there are any more in the queue */
+	if(irc->otr->todo) {
+		kg_t *p = irc->otr->todo;
+		/* send the next one over */
+		log_message(LOGLVL_DEBUG, "slave: keygen for %s/%s!", p->accountname, p->protocol);
+		fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
+		fflush(irc->otr->to);
+		irc->otr->sent_accountname = p->accountname;
+		irc->otr->sent_protocol = p->protocol;
+		irc->otr->todo = p->next;
+		g_free(p);
+		return TRUE;   /* keep watching */
+	} else {
+		/* okay, the slave is idle now, so kill him */
 		log_message(LOGLVL_DEBUG, "all keys done. die, slave!");
-		fclose(irc->otr_from);
-		fclose(irc->otr_to);
-		kill(irc->otr_keygen, SIGTERM);
-		waitpid(irc->otr_keygen, NULL, 0);
-		irc->otr_keygen = 0;
+		fclose(irc->otr->from);
+		fclose(irc->otr->to);
+		kill(irc->otr->keygen, SIGTERM);
+		waitpid(irc->otr->keygen, NULL, 0);
+		irc->otr->keygen = 0;
 		return FALSE;  /* unregister ourselves */
-	} else {
-		return TRUE;   /* slave still working, keep watching */
 	}
 }
@@ -1581,5 +1683,10 @@
 	account_t *acc = (account_t *)data;
 	
-	otr_keygen(acc->irc, acc->user, acc->prpl->name);
+	if(keygen_in_progress(acc->irc, acc->user, acc->prpl->name)) {
+		irc_usermsg(acc->irc, "keygen for %s/%s already in progress",
+			acc->user, acc->prpl->name);
+	} else {
+		otr_keygen(acc->irc, acc->user, acc->prpl->name);
+	}
 }
 
Index: otr.h
===================================================================
--- otr.h	(revision 27db43361a3fdd3420b12aa5bf151dce4545273f)
+++ otr.h	(revision dc9797f7ad4177dc72373ce71d375257fb0271a1)
@@ -49,6 +49,33 @@
 #include <libotr/privkey.h>
 
+/* representing a keygen job */
+typedef struct kg {
+	const char *accountname;
+	const char *protocol;
+	
+	struct kg *next;
+} kg_t;
+
+/* struct to encapsulate our book keeping stuff */
+typedef struct otr {
+	OtrlUserState us;
+	pid_t keygen;    /* pid of keygen slave (0 if none) */
+	FILE *to;        /* pipe to keygen slave */
+	FILE *from;      /* pipe from keygen slave */
+	
+	/* active keygen job (NULL if none) */
+	const char *sent_accountname;
+	const char *sent_protocol;
+	
+	/* keygen jobs waiting to be sent to slave */
+	kg_t *todo;
+} otr_t;
+
 /* called from main() */
 void otr_init(void);
+
+/* called from irc_new()/irc_free() */
+otr_t *otr_new();
+void otr_free(otr_t *otr);
 
 /* called by storage_* functions */
@@ -71,11 +98,10 @@
 #else
 
-typedef void *OtrlUserState;
+typedef void otr_t;
 typedef void *OtrlMessageAppOps;
 
-#define otrl_userstate_create() (NULL)
-#define otrl_userstate_free(us) {}
-
 #define otr_init() {}
+#define otr_new() (NULL)
+#define otr_free(otr) {}
 #define otr_load(irc) {}
 #define otr_save(irc) {}
