Index: irc.c
===================================================================
--- irc.c	(revision d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3)
+++ irc.c	(revision c59530844d25ae814141cf56f0fa810968e8df55)
@@ -131,5 +131,5 @@
 
 	irc->otr_us = otrl_userstate_create();
-	irc->otr_mutex = g_mutex_new();
+	g_static_rec_mutex_init(&irc->otr_mutex);
 	
 	return( irc );
@@ -284,5 +284,5 @@
 	
 	otrl_userstate_free(irc->otr_us);
-	g_mutex_free(irc->otr_mutex);
+	g_static_rec_mutex_free(&irc->otr_mutex);
 	
 	g_free(irc);
Index: irc.h
===================================================================
--- irc.h	(revision d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3)
+++ irc.h	(revision c59530844d25ae814141cf56f0fa810968e8df55)
@@ -99,5 +99,5 @@
 	
 	OtrlUserState otr_us;
-	GMutex *otr_mutex;      /* for locking otr during keygen */
+	GStaticRecMutex otr_mutex;      /* for locking otr during keygen */
 } irc_t;
 
Index: otr.c
===================================================================
--- otr.c	(revision d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3)
+++ otr.c	(revision c59530844d25ae814141cf56f0fa810968e8df55)
@@ -54,5 +54,5 @@
 void cmd_otr_info(irc_t *irc, char **args);
 void cmd_otr_keygen(irc_t *irc, char **args);
-/* void cmd_otr_forget(irc_t *irc, char **args); */
+void cmd_otr_forget(irc_t *irc, char **args);
 
 const command_t otr_commands[] = {
@@ -63,7 +63,5 @@
 	{ "info",        0, &cmd_otr_info,       0 },
 	{ "keygen",      1, &cmd_otr_keygen,     0 },
-	/*
-	{ "forget",      1, &cmd_otr_forget,     0 },
-	*/
+	{ "forget",      2, &cmd_otr_forget,     0 },
 	{ NULL }
 };
@@ -74,8 +72,11 @@
 /* start background thread 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 */
 gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
+
 /* data to be passed to otr_keygen_thread_func */
 struct kgdata {
@@ -89,7 +90,8 @@
 };
 
-/* yes/no handlers for "generate key now?" */
+/* some yes/no handlers */
 void yes_keygen(gpointer w, void *data);
-void no_keygen(gpointer w, void *data);
+void yes_forget_fingerprint(gpointer w, void *data);
+void yes_forget_context(gpointer w, void *data);
 
 /* helper to make sure accountname and protocol match the incoming "opdata" */
@@ -124,4 +126,7 @@
 /* show the list of fingerprints associated with a given context */
 void show_fingerprints(irc_t *irc, ConnContext *ctx);
+
+/* find a fingerprint by prefix (given as any number of hex strings) */
+Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
 
 /* to log out accounts during keygen */
@@ -259,5 +264,5 @@
 	char *colormsg;
 	
-    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
+    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
 		irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped",
 			peernick(ic->irc, handle, ic->acc->prpl->name));
@@ -273,9 +278,9 @@
 	if(ignore_msg) {
 		/* this was an internal OTR protocol message */
-		g_mutex_unlock(ic->irc->otr_mutex);
+		g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 		return NULL;
 	} else if(!newmsg) {
 		/* this was a non-OTR message */
-		g_mutex_unlock(ic->irc->otr_mutex);
+		g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 		return g_strdup(msg);
 	} else {
@@ -296,5 +301,5 @@
 		}
 		otrl_message_free(newmsg);
-		g_mutex_unlock(ic->irc->otr_mutex);
+		g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 		return colormsg;
 	}
@@ -307,5 +312,5 @@
 	ConnContext *ctx = NULL;
 	
-    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
+    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
 		irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent",
 			peernick(ic->irc, handle, ic->acc->prpl->name));
@@ -317,5 +322,5 @@
 		msg, NULL, &otrmsg, NULL, NULL);
 	if(st) {
-		g_mutex_unlock(ic->irc->otr_mutex);
+		g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 		return st;
 	}
@@ -328,5 +333,5 @@
 		if(!ctx) {
 			otrl_message_free(otrmsg);
-			g_mutex_unlock(ic->irc->otr_mutex);
+			g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 			return 1;
 		}
@@ -339,5 +344,5 @@
 	}
 	
-	g_mutex_unlock(ic->irc->otr_mutex);
+	g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
 	return st;
 }
@@ -359,5 +364,5 @@
 	
 	if(!cmd->command) {
-		irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02",
+		irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
 			args[0], args[1]);
 		return;
@@ -392,5 +397,5 @@
 	s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?",
 		accountname, protocol);
-	query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc);
+	query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc);
 }
 
@@ -476,4 +481,5 @@
 		check_imc(opdata, context->accountname, context->protocol);
 	user_t *u;
+	const char *trust;
 
 	log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
@@ -486,5 +492,7 @@
 		return;
 	}
-	if(context->active_fingerprint->trust[0])
+	
+	trust = context->active_fingerprint->trust;
+	if(trust && trust[0])
 		u->encrypted = 2;
 	else
@@ -579,4 +587,15 @@
 	otrl_message_disconnect(irc->otr_us, &global.otr_ops,
 		u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
+	
+	/* for some reason, libotr (3.1.0) doesn't do this itself: */
+	if(u->encrypted) {
+		ConnContext *ctx;
+		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
+		if(ctx)
+			op_gone_insecure(u->ic, ctx);
+		else /* huh? */
+			u->encrypted = 0;
+	}
 }
 
@@ -719,7 +738,4 @@
 			*(protocol++) = '\0';
 			myhandle = strchr(protocol, '/');
-			if(!myhandle) {
-				/* TODO: try to find a unique account for this context */
-			}
 		}
 		if(protocol && myhandle) {
@@ -728,5 +744,5 @@
 			ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
 			if(!ctx) {
-				irc_usermsg(irc, "no such context (%s %s %s)", handle, protocol, myhandle);
+				irc_usermsg(irc, "no such context");
 				g_free(arg);
 				return;
@@ -778,7 +794,120 @@
 	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, a->ic, s, yes_keygen, no_keygen, a);
+		query_add(irc, a->ic, s, yes_keygen, NULL, a);
 	} else {
 		otr_keygen(irc, a->user, a->prpl->name);
+	}
+}
+
+void yes_forget_fingerprint(gpointer w, void *data)
+{
+	struct im_connection *ic = (struct im_connection *)w;
+	Fingerprint *fp = (Fingerprint *)data;
+	
+	if(fp == fp->context->active_fingerprint) {
+		irc_usermsg(ic->irc, "that fingerprint is active, terminate otr connection first");
+		return;
+	}
+		
+	otrl_context_forget_fingerprint(fp, 0);
+}
+
+void yes_forget_context(gpointer w, void *data)
+{
+	struct im_connection *ic = (struct im_connection *)w;
+	ConnContext *ctx = (ConnContext *)data;
+	
+	if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+		irc_usermsg(ic->irc, "active otr connection with %s, terminate it first",
+			peernick(ic->irc, ctx->username, ctx->protocol));
+		return;
+	}
+		
+	if(ctx->msgstate == OTRL_MSGSTATE_FINISHED)
+		otrl_context_force_plaintext(ctx);
+	otrl_context_forget(ctx);
+}
+
+void cmd_otr_forget(irc_t *irc, char **args)
+{
+	if(!strcmp(args[1], "fingerprint"))
+	{
+		user_t *u;
+		ConnContext *ctx;
+		Fingerprint *fp;
+		char human[54];
+		char *s;
+		
+		if(!args[3]) {
+			irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
+			return;
+		}
+		
+		u = user_find(irc, args[2]);
+		if(!u || !u->ic) {
+			irc_usermsg(irc, "%s: unknown user", args[2]);
+			return;
+		}
+		
+		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
+		if(!ctx) {
+			irc_usermsg(irc, "no otr context with %s", args[2]);
+			return;
+		}
+		
+		fp = match_fingerprint(irc, ctx, ((const char **)args)+3);
+		if(!fp) {
+			/* match_fingerprint does error messages */
+			return;
+		}
+		
+		if(fp == ctx->active_fingerprint) {
+			irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
+			return;
+		}
+		
+		otrl_privkey_hash_to_human(human, fp->fingerprint);
+		s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
+		query_add(irc, u->ic, s, yes_forget_fingerprint, NULL, fp);
+	}
+	
+	else if(!strcmp(args[1], "context"))
+	{
+		user_t *u;
+		ConnContext *ctx;
+		char *s;
+		
+		u = user_find(irc, args[2]);
+		if(!u || !u->ic) {
+			irc_usermsg(irc, "%s: unknown user", args[2]);
+			return;
+		}
+		
+		ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
+			u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
+		if(!ctx) {
+			irc_usermsg(irc, "no otr context with %s", args[2]);
+			return;
+		}
+		
+		if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+			irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]);
+			return;
+		}
+		
+		s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
+		query_add(irc, u->ic, s, yes_forget_context, NULL, ctx);
+	}
+	
+	else if(!strcmp(args[1], "key"))
+	{
+		irc_usermsg(irc, "n/a: TODO");
+	}
+	
+	else
+	{
+		irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
+			args[0], args[1]);
 	}
 }
@@ -956,5 +1085,5 @@
 	int trusted = u->encrypted > 1;
 	char flags[7];
-	int nflags;
+	int nflags=0;
 	char *p = flags;
 	char *from;
@@ -1036,4 +1165,70 @@
 }
 
+Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
+{
+	Fingerprint *fp, *fp2;
+	char human[45];
+	char prefix[45], *p;
+	int n;
+	int i,j;
+	
+	/* assemble the args into a prefix in standard "human" form */
+	n=0;
+	p=prefix;
+	for(i=0; args[i]; i++) {
+		for(j=0; args[i][j]; j++) {
+			char c = toupper(args[i][j]);
+			
+			if(n>=40) {
+				irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
+				return NULL;
+			}
+			
+			if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
+				*(p++) = c;
+			} else {
+				irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
+				return NULL;
+			}
+			
+			n++;
+			if(n%8 == 0)
+				*(p++) = ' ';
+		}
+	}
+	*p = '\0';
+	log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix);
+	log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix));
+	
+	/* find first fingerprint with the given prefix */
+	n = strlen(prefix);
+	for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
+		if(!fp->fingerprint)
+			continue;
+		otrl_privkey_hash_to_human(human, fp->fingerprint);
+		if(!strncmp(prefix, human, n))
+			break;
+	}
+	if(!fp) {
+		irc_usermsg(irc, "%s: no match", prefix);
+		return NULL;
+	}
+	
+	/* make sure the match, if any, is unique */
+	for(fp2=fp->next; fp2; fp2=fp2->next) {
+		if(!fp2->fingerprint)
+			continue;
+		otrl_privkey_hash_to_human(human, fp2->fingerprint);
+		if(!strncmp(prefix, human, n))
+			break;
+	}
+	if(fp2) {
+		irc_usermsg(irc, "%s: multiple matches", prefix);
+		return NULL;
+	}
+	
+	return fp;
+}
+
 void show_general_otr_info(irc_t *irc)
 {
@@ -1080,7 +1275,5 @@
 		
 		if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-			otrl_privkey_hash_to_human(human, ctx->active_fingerprint->fingerprint);
 			irc_usermsg(irc, "  \x02%s\x02", userstring);
-			irc_usermsg(irc, "    %s", human);
 		} else {
 			irc_usermsg(irc, "  %s", userstring);
@@ -1124,5 +1317,5 @@
 	}
 
-    irc_usermsg(irc, "  known fingerprints: (bold=active)");	
+    irc_usermsg(irc, "  fingerprints: (bold=active)");	
 	show_fingerprints(irc, ctx);
 }
@@ -1159,5 +1352,5 @@
 		return;
 	}
-	kg->done = FALSE;
+	kg->done = 0;
 
 	/* Poll for completion of the thread periodically. I would have preferred
@@ -1195,13 +1388,13 @@
 	
 	/* lock OTR subsystem and do the work */
-	g_mutex_lock(kg->irc->otr_mutex);
+	g_static_rec_mutex_lock(&kg->irc->otr_mutex);
 	kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
 		kg->protocol);
-	g_mutex_unlock(kg->irc->otr_mutex);
+	g_static_rec_mutex_unlock(&kg->irc->otr_mutex);
 	/* OTR enabled again */
 	
 	/* notify mainloop */
 	g_mutex_lock(kg->mutex);
-	kg->done = TRUE;
+	kg->done = 1;
 	g_mutex_unlock(kg->mutex);
 	
@@ -1239,12 +1432,4 @@
 }
 
-void no_keygen(gpointer w, void *data)
-{
-	account_t *acc = (account_t *)data;
-	
-	irc_usermsg(acc->irc, "keygen cancelled for %s/%s",
-		acc->user, acc->prpl->name);
-}
-
 
 #else /* WITH_OTR undefined */
Index: root_commands.c
===================================================================
--- root_commands.c	(revision d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3)
+++ root_commands.c	(revision c59530844d25ae814141cf56f0fa810968e8df55)
@@ -86,5 +86,5 @@
 		return;
 	
-	if(!g_mutex_trylock(irc->otr_mutex)) {
+	if(!g_static_rec_mutex_trylock(&irc->otr_mutex)) {
 		irc_usermsg(irc, "keygen in progress, bitlbee comatose - please wait");
 		return;
@@ -97,14 +97,14 @@
 			{
 				irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters );
-				g_mutex_unlock(irc->otr_mutex);
+				g_static_rec_mutex_unlock(&irc->otr_mutex);
 				return;
 			}
 			commands[i].execute( irc, cmd );
-			g_mutex_unlock(irc->otr_mutex);
+			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_mutex_unlock(irc->otr_mutex);
+	g_static_rec_mutex_unlock(&irc->otr_mutex);
 }
 
