[pkg-otr-team] [irssi-plugin-otr] 80/167: 2012-04-30

Holger Levsen holger at moszumanska.debian.org
Mon Mar 3 21:55:34 UTC 2014


This is an automated email from the git hooks/post-receive script.

holger pushed a commit to tag 4.0.0
in repository irssi-plugin-otr.

commit c276bfa786bef8a4572a37d5633cf40f480d3ae0
Author: Rob Smits <rdfsmits at cs.uwaterloo.ca>
Date:   Wed May 2 17:28:16 2012 -0400

    2012-04-30
    
    	* gtk-dialog.c:
    	* gtk-ui.c:
    	* otr-plugin.c:
    	* otr-plugin.h:
    	* ui.c: More changes for instance tags. Some logging-
    	related changes too (output whether pidgin is logging,
    	change default not to log otr conversations). -- Rob
    	Smits
    
    2009-06-11
    
    	* gtk-dialog.c:
    	* otr-plugin.c:
    	* otr-plugin.h: Initial instance tags implementation
    	from Lisa Du
---
 AUTHORS                          |    2 +-
 ChangeLog                        |   18 +
 INSTALL                          |    2 +
 Makefile.mingw                   |   47 +-
 Makefile.mingw.notes             |   40 +
 README                           |    8 +-
 dialogs.c                        |    4 +-
 dialogs.h                        |    2 +-
 gtk-dialog.c                     | 2045 ++++++++++++++++++++++++++------------
 gtk-dialog.h                     |    2 +-
 gtk-ui.c                         |  247 +++--
 gtk-ui.h                         |    2 +-
 otr-icons.h                      |   18 +-
 otr-plugin.c                     |  752 ++++++++------
 otr-plugin.h                     |   26 +-
 packaging/fedora/pidgin-otr.spec |   21 +-
 packaging/windows/pidgin-otr.nsi |   35 +-
 tooltipmenu.c                    |  196 ++--
 tooltipmenu.h                    |   10 +-
 ui.c                             |   41 +-
 ui.h                             |    2 +-
 21 files changed, 2299 insertions(+), 1221 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index d9c4f0e..05f434a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,7 +2,7 @@ Off-the-Record Messaging plugin for pidgin
 
 Authors:
 
-    Ian Goldberg, Rob Smits, Chris Alexander, Willy Lew, Nikita Borisov
+    Ian Goldberg, Rob Smits, Chris Alexander, Willy Lew, Lisa Du, Nikita Borisov
     <otr at cypherpunks.ca>
 
 See the README file for mailing list information
diff --git a/ChangeLog b/ChangeLog
index 21fb8e3..12af88a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2012-04-30
+
+	* gtk-dialog.c:
+	* gtk-ui.c: 
+	* otr-plugin.c:
+	* otr-plugin.h:
+	* ui.c: More changes for instance tags. Some logging-
+	related changes too (output whether pidgin is logging,
+	change default not to log otr conversations). -- Rob
+	Smits
+
 2010-03-02
 
 	* po/vi.po: Vietnamese translation from Lyndon Johnson
@@ -9,6 +20,13 @@
 	the fingerprints in the manual authentication dialog
 	selectable (but not selected by default).
 
+2009-06-11
+
+	* gtk-dialog.c:
+	* otr-plugin.c:
+	* otr-plugin.h: Initial instance tags implementation
+	from Lisa Du
+
 2009-08-24
 
 	* po/fr.po: Fixed \n errors
diff --git a/INSTALL b/INSTALL
index 7c4aacc..ff3fb10 100644
--- a/INSTALL
+++ b/INSTALL
@@ -51,6 +51,8 @@ Use the provided Makefile.mingw:
 
     make -f Makefile.mingw
 
+See Makefile.mingw.notes for a few hints.
+
 INSTALLATION
 
 You should be able to simply do "make install".  If you want to install
diff --git a/Makefile.mingw b/Makefile.mingw
index e7626cd..3ed5c74 100644
--- a/Makefile.mingw
+++ b/Makefile.mingw
@@ -1,17 +1,35 @@
 WIN32=1
 
 # The version number to put in the plugin info
-PIDGIN_OTR_VERSION = 3.2.0
+PIDGIN_OTR_VERSION = 4.0.0
 
 # Name of the gettext domain
 GETTEXT_PACKAGE = pidgin-otr
 
 # Replace this with the path to the pidgin and purple headers
-PIDGIN_HEADERS ?= /usr/i586-mingw32msvc/include/pidgin
-PURPLE_HEADERS ?= /usr/i586-mingw32msvc/include/libpurple
+PIDGIN_HEADERS ?= -I/usr/i586-mingw32msvc/include/pidgin \
+ -I/usr/i586-mingw32msvc/include/pidgin/win32
+PURPLE_HEADERS ?= -I/usr/i586-mingw32msvc/include/libpurple \
+ -I/usr/i586-mingw32msvc/include/libpurple/win32
 
-# If you don't have pkg-config, put the appropriate -I entry on the next line
+# Replace this with the path to the extracted GTK+ "all-in-one" win32 bundle
+GTK_WIN32_BUNDLE ?= /usr/i586-mingw32msvc/misc/gtk_bundle
+
+# Replace this with the to path DLL files from a Win32 Pidgin distributable
+# (i.e. pidgin.dll and libpurple.dll)
+PIDGIN_WIN32_LIBS ?= /usr/i586-mingw32msvc/misc/pidgin_dlls
+
+# If you don't have pkg-config, uncomment the -I lines below
 GTK_HDRS ?= `pkg-config --cflags glib-2.0 gtk+-2.0`
+#GTK_HDRS ?= -I$(GTK_WIN32_BUNDLE)/include/gtk-2.0  \
+# -I$(GTK_WIN32_BUNDLE)/include/glib-2.0 \
+# -I$(GTK_WIN32_BUNDLE)/include/cairo \
+# -I$(GTK_WIN32_BUNDLE)/include/pango-1.0 \
+# -I$(GTK_WIN32_BUNDLE)/include/atk-1.0 \
+# -I$(GTK_WIN32_BUNDLE)/include/gdk-pixbuf-2.0 \
+# -I$(GTK_WIN32_BUNDLE)/lib/glib-2.0/include \
+# -I$(GTK_WIN32_BUNDLE)/lib/gtk-2.0/include
+
 
 # The location of the libotr include files.  Note that if, for example,
 # the full path of message.h is /usr/include/libotr/message.h, you
@@ -41,12 +59,13 @@ ZIPFILE = pidgin-otr-$(PIDGIN_OTR_VERSION).zip
 
 CC = i586-mingw32msvc-gcc
 LDFLAGS = -Wl,--enable-auto-image-base
-LDLIBS = $(LIBOTRLIBDIR)/libotr.a -lgtk-win32-2.0 -lglib-2.0 -lgdk_pixbuf-2.0 \
-	 -lgobject-2.0 -lpidgin -llibpurple -lgcrypt -lgpg-error \
-	 -L$(LIBINTLLIBDIR) -lintl
+LDLIBS = $(LIBOTRLIBDIR)/libotr.a -L$(GTK_WIN32_BUNDLE)/lib \
+	 -L$(PIDGIN_WIN32_LIBS) -lgtk-win32-2.0 -lglib-2.0 \
+	 -lgdk_pixbuf-2.0 -lgobject-2.0 -lpidgin -llibpurple \
+	 -lgcrypt -lgpg-error -L$(LIBINTLLIBDIR) -lintl
 
 CC ?= gcc
-override CFLAGS += -g -O2 -Wall -I$(PIDGIN_HEADERS) -I$(PURPLE_HEADERS) \
+override CFLAGS += -g -O2 -Wall $(PIDGIN_HEADERS) $(PURPLE_HEADERS) \
 	$(GTK_HDRS) -I$(LIBOTRINCDIR) $(FPIC) -DUSING_GTK -DPURPLE_PLUGINS \
 	-DPIDGIN_OTR_VERSION=\"$(PIDGIN_OTR_VERSION)\" \
 	-DPIDGIN_NAME=\"Pidgin\" -I$(LIBINTLINCDIR) -DENABLE_NLS \
@@ -66,8 +85,8 @@ clean:
 distclean: clean
 	$(MAKE) -C po -f Makefile.mingw distclean
 
-## Package up all the pieces needed to build the installer
-zip: all
+## Prepare the win32_export directory
+prepare_win32_export: all
 	mkdir win32_export
 	# Copy pieces over from the libotr source dir
 	for f in otr_mackey.exe otr_parse.exe otr_remac.exe otr_modify.exe \
@@ -90,8 +109,16 @@ zip: all
 	i586-mingw32msvc-strip *.exe *.dll; \
 	perl -pi -e 's/$$/\r/' README.Toolkit.txt Protocol-v2.html \
 		COPYING.txt COPYING.LIB.txt README.txt; \
+
+installer: prepare_win32_export
+	makensis packaging/windows/pidgin-otr-rob.nsi
+	rm -rf win32_export
+
+## Package up all the pieces needed to build the installer
+zip: prepare_win32_export
 	rm -f ../$(ZIPFILE); \
 	zip -r ../$(ZIPFILE) README.txt \
 		README.Toolkit.txt Protocol-v2.html COPYING.txt \
 		COPYING.LIB.txt *.exe *.dll *.nsi locale
 	rm -rf win32_export
+
diff --git a/Makefile.mingw.notes b/Makefile.mingw.notes
new file mode 100644
index 0000000..62b28f4
--- /dev/null
+++ b/Makefile.mingw.notes
@@ -0,0 +1,40 @@
+Here are some rough notes that might help you create a pidgin-otr Win32 build on
+a Linux system with mingw32. These have been tested on Ubuntu 11.04.
+
+I am listing packages and their build instructions in the order they should be
+built. Good luck!
+
+libgpg-error-1.0:
+  (before configure)
+  HOST_CC=gcc
+  DLLTOOL=i586-mingw32msvc-dlltool 
+  AS=i586-mingw32msvc-as
+  export HOST_CC DLLTOOL AS
+  ./configure --with-pic --build=`./config.guess` --host=i586-mingw32msvc --prefix=/usr/i586-mingw32msvc 
+  make
+  sudo make install
+
+gcrypt-1.2.1:
+  w32root=i586-mingw32msvc ./autogen.sh --build-w32
+  Then append #undef HAVE_GETTIMEOFDAY  to libgcrypt config.h
+  Apply windows slow random fix (patch on otr website, listed as "Note that if you're compiling from source on win32...")
+  make
+  sudo make install
+  
+libotr:
+  ./configure --with-pic --build=`./config.guess` --host=i586-mingw32msvc --prefix=/usr/i586-mingw32msvc --with-libgcrypt-prefix=/usr/i586-mingw32msvc
+  make
+  sudo make install
+
+pidgin-otr:
+  You will need: Pidgin source code distributable, Pidgin Win32 distributable, and an "all-in-one bundle" of the GTK+ stack 2.14.7 or greater (e.g., gtk+-bundle_2.24.10-20120208_win32.zip).
+  In Makefile.mingw, specify the location of PIDGIN_HEADERS, PURPLE_HEADERS, GTK_WIN32_BUNDLE, and PIDGIN_WIN32_LIBS
+  Ensure either pkg-config will correctly resolve all the dependencies for glib-2.0 and gtk+-2.0 (there is a README in the GTK+ bundle about this), or uncomment (and perhaps revise) the hardcoded list of includes for GTK_HDRS
+  make -f Makefile.mingw
+
+nsis:
+  sudo apt-get install nsis
+  Locate the "nsisunz" plugin (a google search for "nsisunz.zip" should be sufficient)
+  Extract the DLL to /usr/local/share/nsis/Plugins (yes it's a DLL extension, but it will still work for GNU/Linux nsis)
+  make -f Makefile.mingw installer     <-- This should now build the nsis installer
+
diff --git a/README b/README
index e208e8e..ddee61b 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 	       Off-the-Record Messaging plugin for pidgin
-			  v3.2.0, 15 Jun 2008
+			  v4.0.0, 2012
 
 This is a pidgin plugin which implements Off-the-Record (OTR) Messaging.
 It is known to work (at least) under the Linux and Windows versions of
@@ -300,9 +300,9 @@ The Off-the-Record Messaging plugin for pidgin is covered by the following
 (GPL) license:
 
     Off-the-Record Messaging plugin for pidgin
-    Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+    Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
                              Chris Alexander, Willy Lew,
-			     Nikita Borisov
+			     Lisa Du, Nikita Borisov
                              <otr at cypherpunks.ca>
 
 
@@ -325,7 +325,7 @@ CONTACT
 To report problems, comments, suggestions, patches, etc., you can email
 the authors:
 
-Ian Goldberg, Rob Smits, Chris Alexander, and Nikita Borisov
+Ian Goldberg, Rob Smits, Chris Alexander, Willy Lew, Lisa Du, Nikita Borisov
 <otr at cypherpunks.ca>
 
 For more information on Off-the-Record Messaging, visit
diff --git a/dialogs.c b/dialogs.c
index 4a4e99e..5bbc1b1 100644
--- a/dialogs.c
+++ b/dialogs.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -109,7 +109,7 @@ int otrg_dialog_display_otr_message( const char *accountname,
 	int force_create)
 {
     return ui_ops->display_otr_message(accountname, protocol, username, msg,
-					force_create);
+	    force_create);
 }
 
 /* Put up a Please Wait dialog.  This dialog can not be cancelled.
diff --git a/dialogs.h b/dialogs.h
index bf89368..a2a6456 100644
--- a/dialogs.h
+++ b/dialogs.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
diff --git a/gtk-dialog.c b/gtk-dialog.c
index 6cae9df..fc6bf75 100644
--- a/gtk-dialog.c
+++ b/gtk-dialog.c
@@ -1,8 +1,8 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
- *                           Nikita Borisov
+ *                           Lisa Du, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -56,6 +56,7 @@
 #include <libotr/proto.h>
 #include <libotr/message.h>
 #include <libotr/userstate.h>
+#include <libotr/instag.h>
 
 /* purple-otr headers */
 #include "otr-plugin.h"
@@ -75,11 +76,13 @@ static int img_id_finished = 0;
 
 typedef struct {
     ConnContext *context;       /* The context used to fire library code */
-    GtkEntry* question_entry;       /* The text entry field containing the user question */
+    GtkEntry* question_entry;   /* The text entry field containing the user
+				 * question */
     GtkEntry *entry;	        /* The text entry field containing the secret */
-    int smp_type;               /* Whether the SMP type is based on question challenge (0) or shared secret (1) */
+    int smp_type;               /* Whether the SMP type is based on question
+				 * challenge (0) or shared secret (1) */
     gboolean responder;	        /* Whether or not this is the first side to give
-			                       their secret */
+				 * their secret */
 } SmpResponsePair;
 
 /* Information used by the plugin that is specific to both the
@@ -90,6 +93,7 @@ typedef struct dialog_context_data {
     GtkWidget       *smp_progress_dialog;
     GtkWidget       *smp_progress_bar;
     GtkWidget       *smp_progress_label;
+    otrl_instag_t   their_instance;
 } SMPData;
 
 typedef struct {
@@ -99,6 +103,39 @@ typedef struct {
     GtkWidget       *notebook;
 } AuthSignalData;
 
+typedef struct {
+    enum {
+	convctx_none,
+	convctx_conv,
+	convctx_ctx
+    } convctx_type;
+    PurpleConversation *conv;
+    ConnContext *context;
+} ConvOrContext;
+
+gint get_new_instance_index(PurpleConversation *conv) {
+    gint max_index = (gint) purple_conversation_get_data(conv, "otr-max_idx");
+    max_index++;
+    purple_conversation_set_data(conv, "otr-max_idx", (gpointer) max_index);
+    return max_index;
+}
+
+gint get_context_instance_to_index(PurpleConversation *conv,
+	ConnContext *context) {
+    GHashTable * conv_to_idx_map =
+	    purple_conversation_get_data(conv, "otr-conv_to_idx");
+    gint index = 0;
+    gpointer key = NULL;
+
+    if (!g_hash_table_lookup_extended(conv_to_idx_map, context, &key,
+	    (void**)&index)) {
+	index = get_new_instance_index(conv);
+	g_hash_table_replace(conv_to_idx_map, context, (gpointer)index);
+    }
+
+    return index;
+}
+
 static void close_progress_window(SMPData *smp_data)
 {
     if (smp_data->smp_progress_dialog) {
@@ -137,6 +174,9 @@ static void otrg_gtk_dialog_add_smp_data(PurpleConversation *conv)
     smp_data->smp_progress_dialog = NULL;
     smp_data->smp_progress_bar = NULL;
     smp_data->smp_progress_label = NULL;
+    /* Chosen as initialized value since libotr should never allow
+     * this as a "their_instance" value */
+    smp_data->their_instance = OTRL_INSTAG_BEST;
 
     purple_conversation_set_data(conv, "otr-smpdata", smp_data);
 }
@@ -180,7 +220,8 @@ static void message_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget)
     gtk_widget_destroy(GTK_WIDGET(widget));
 }
 
-/* Forward declarations for the benefit of smp_message_response_cb/redraw authvbox */
+/* Forward declarations for the benefit of smp_message_response_cb/redraw
+ * authvbox */
 static void verify_fingerprint(GtkWindow *parent, Fingerprint *fprint);
 static void add_vrfy_fingerprint(GtkWidget *vbox, void *data);
 static struct vrfy_fingerprint_data* vrfy_fingerprint_data_new(
@@ -198,7 +239,7 @@ static void smp_progress_response_cb(GtkDialog *dialog, gint response,
 {
     PurpleConversation *conv = otrg_plugin_context_to_conv(context, 0);
     SMPData *smp_data = NULL;
-    
+
     if (conv) {
 	gdouble frac;
 
@@ -235,58 +276,58 @@ static void smp_secret_response_cb(GtkDialog *dialog, gint response,
     SmpResponsePair *smppair;
 
     if (!auth_opt_data) return;
-    
+
     smppair = auth_opt_data->smppair;
-    
+
     if (!smppair) return;
 
     context = smppair->context;
 
     if (response == GTK_RESPONSE_ACCEPT && smppair->entry) {
-        GtkEntry* entry = smppair->entry;
-        char *secret;
-        size_t secret_len;
-
-        GtkEntry* question_entry = smppair->question_entry;
-    
-        const char *user_question = NULL;
-
-
-        if (context == NULL || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
-            return;
-        }
-    
-        secret = g_strdup(gtk_entry_get_text(entry));
-        secret_len = strlen(secret);
-
-        if (smppair->responder) {
-            otrg_plugin_continue_smp(context, (const unsigned char *)secret,
-                secret_len);
-            
-        } else {
-            
-            if (smppair->smp_type == 0) {
-                if (!question_entry) {
-                    return;
-                }
-              
-                user_question = gtk_entry_get_text(question_entry);
-        
-                if (user_question == NULL || strlen(user_question) == 0) {
-                    return;
-                }
-            }
-
-            /* pass user question here */
-            otrg_plugin_start_smp(context, user_question,
-	           (const unsigned char *)secret, secret_len);
-
-        }
-    
-        g_free(secret);
-
-        /* launch progress bar window */
-        create_smp_progress_dialog(GTK_WINDOW(dialog), context);
+	GtkEntry* entry = smppair->entry;
+	char *secret;
+	size_t secret_len;
+
+	GtkEntry* question_entry = smppair->question_entry;
+
+	const char *user_question = NULL;
+
+
+	if (context == NULL || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
+	    return;
+	}
+
+	secret = g_strdup(gtk_entry_get_text(entry));
+	secret_len = strlen(secret);
+
+	if (smppair->responder) {
+	    otrg_plugin_continue_smp(context, (const unsigned char *)secret,
+		    secret_len);
+
+	} else {
+
+	    if (smppair->smp_type == 0) {
+		if (!question_entry) {
+		    return;
+		}
+
+		user_question = gtk_entry_get_text(question_entry);
+
+		if (user_question == NULL || strlen(user_question) == 0) {
+		    return;
+		}
+	    }
+
+	    /* pass user question here */
+	    otrg_plugin_start_smp(context, user_question,
+		    (const unsigned char *)secret, secret_len);
+
+	}
+
+	g_free(secret);
+
+	/* launch progress bar window */
+	create_smp_progress_dialog(GTK_WINDOW(dialog), context);
     } else if (response == GTK_RESPONSE_HELP) {
 	char *helpurl = g_strdup_printf("%s%s&context=%s",
 		AUTHENTICATE_HELPURL, _("?lang=en"),
@@ -307,19 +348,19 @@ static void smp_secret_response_cb(GtkDialog *dialog, gint response,
 	/* Don't destroy the window */
 	return;
     } else {
-        otrg_plugin_abort_smp(context);
+	otrg_plugin_abort_smp(context);
     }
-    
+
     /* In all cases except HELP, destroy the current window */
     gtk_widget_destroy(GTK_WIDGET(dialog));
-    
+
     /* Clean up references to this window */
     conv = otrg_plugin_context_to_conv(smppair->context, 0);
     smp_data = purple_conversation_get_data(conv, "otr-smpdata");
-    
+
     if (smp_data) {
-        smp_data->smp_secret_dialog = NULL;
-        smp_data->smp_secret_smppair = NULL;
+	smp_data->smp_secret_dialog = NULL;
+	smp_data->smp_secret_smppair = NULL;
     }
 
     /* Free memory */
@@ -382,7 +423,7 @@ static GtkWidget *create_dialog(GtkWindow *parent,
     gtk_window_set_role(GTK_WINDOW(dialog), "notify_dialog");
 
     g_signal_connect(G_OBJECT(dialog), "response",
-			 G_CALLBACK(message_response_cb), dialog);
+	    G_CALLBACK(message_response_cb), dialog);
     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT,
 	    sensitive);
 
@@ -401,10 +442,10 @@ static GtkWidget *create_dialog(GtkWindow *parent,
     }
 
     label_text = g_strdup_printf(
-		       "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
-		       (primary ? primary : ""),
-		       (primary ? "\n\n" : ""),
-		       (secondary ? secondary : ""));
+	    "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
+	    (primary ? primary : ""),
+	    (primary ? "\n\n" : ""),
+	    (secondary ? secondary : ""));
 
     label = gtk_label_new(NULL);
 
@@ -431,19 +472,19 @@ static void add_to_vbox_init_one_way_auth(GtkWidget *vbox,
     GtkWidget *entry;
     GtkWidget *label;
     GtkWidget *label2;
-    char *label_text;   
-    
+    char *label_text;
+
     SmpResponsePair* smppair = auth_opt_data->smppair;
-    
+
     if (smppair->responder) {
-        label_text = g_strdup_printf("<small><i>\n%s\n</i></small>",
+	label_text = g_strdup_printf("<small><i>\n%s\n</i></small>",
 	    _("Your buddy is attempting to determine if he or she is really "
 		"talking to you, or if it's someone pretending to be you.  "
 		"Your buddy has asked a question, indicated below.  "
 		"To authenticate to your buddy, enter the answer and "
 		"click OK."));
     } else {
-        label_text = g_strdup_printf("<small><i>\n%s\n</i></small>",
+	label_text = g_strdup_printf("<small><i>\n%s\n</i></small>",
 	    _("To authenticate using a question, pick a question whose "
 	    "answer is known only to you and your buddy.  Enter this "
 	    "question and this answer, then wait for your buddy to "
@@ -459,57 +500,58 @@ static void add_to_vbox_init_one_way_auth(GtkWidget *vbox,
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-       
-       
+
+
     if (smppair->responder) {
-        label_text = g_strdup_printf(_("This is the question asked by "
-		    "your buddy:"));
+	label_text = g_strdup_printf(_("This is the question asked by "
+		"your buddy:"));
     } else {
-        label_text = g_strdup_printf(_("Enter question here:"));
+	label_text = g_strdup_printf(_("Enter question here:"));
     }
-    
+
     label = gtk_label_new(label_text);
     gtk_label_set_selectable(GTK_LABEL(label), FALSE);
     g_free(label_text);
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    
 
-    
+
+
     if (smppair->responder && question) {
-        label_text = g_markup_printf_escaped("<span background=\"white\" foreground=\"black\" weight=\"bold\">%s</span>", question);
-        label = gtk_label_new(NULL);
-        gtk_label_set_markup (GTK_LABEL(label), label_text);
-        gtk_label_set_selectable(GTK_LABEL(label), FALSE);
-        g_free(label_text);
-        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-        gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-        gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-        smppair->question_entry = NULL;
+	label_text = g_markup_printf_escaped("<span background=\"white\" "
+		"foreground=\"black\" weight=\"bold\">%s</span>", question);
+	label = gtk_label_new(NULL);
+	gtk_label_set_markup (GTK_LABEL(label), label_text);
+	gtk_label_set_selectable(GTK_LABEL(label), FALSE);
+	g_free(label_text);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	smppair->question_entry = NULL;
     } else {
-        /* Create the text view where the user enters their question */
-        question_entry = gtk_entry_new ();
-        smppair->question_entry = GTK_ENTRY(question_entry);
-        gtk_box_pack_start(GTK_BOX(vbox), question_entry, FALSE, FALSE, 0);
+	/* Create the text view where the user enters their question */
+	question_entry = gtk_entry_new ();
+	smppair->question_entry = GTK_ENTRY(question_entry);
+	gtk_box_pack_start(GTK_BOX(vbox), question_entry, FALSE, FALSE, 0);
     }
-    
+
     if (context->active_fingerprint->trust &&
-        context->active_fingerprint->trust[0] && !(smppair->responder)) {
-        label2 = gtk_label_new(_("This buddy is already authenticated."));
+	context->active_fingerprint->trust[0] && !(smppair->responder)) {
+	label2 = gtk_label_new(_("This buddy is already authenticated."));
     } else {
-        label2 = NULL;
+	label2 = NULL;
     }
 
-    
+
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    
+
     /* Leave a blank line */
     gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
-        FALSE, 0);
+	    FALSE, 0);
 
     label_text = g_strdup_printf(_("Enter secret answer here "
-		"(case sensitive):"));
+	    "(case sensitive):"));
 
     label = gtk_label_new(NULL);
 
@@ -529,15 +571,15 @@ static void add_to_vbox_init_one_way_auth(GtkWidget *vbox,
 
     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    
+
     /* Leave a blank line */
     gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
-        FALSE, 0);
-        
+	    FALSE, 0);
+
     if (label2) {
-        gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 0);
-        gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
-            FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
+		FALSE, 0);
     }
 }
 
@@ -546,13 +588,13 @@ static void add_to_vbox_init_two_way_auth(GtkWidget *vbox,
     GtkWidget *entry;
     GtkWidget *label;
     GtkWidget *label2;
-    char *label_text;   
-    
+    char *label_text;
+
     label_text = g_strdup_printf("<small><i>\n%s\n</i></small>",
-        _("To authenticate, pick a secret known "
-            "only to you and your buddy.  Enter this secret, then "
-            "wait for your buddy to enter it too.  If the secrets "
-            "don't match, then you may be talking to an imposter."));
+	_("To authenticate, pick a secret known "
+	    "only to you and your buddy.  Enter this secret, then "
+	    "wait for your buddy to enter it too.  If the secrets "
+	    "don't match, then you may be talking to an imposter."));
 
     label = gtk_label_new(NULL);
 
@@ -562,7 +604,7 @@ static void add_to_vbox_init_two_way_auth(GtkWidget *vbox,
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-       
+
     label_text = g_strdup_printf(_("Enter secret here:"));
     label = gtk_label_new(label_text);
     gtk_label_set_selectable(GTK_LABEL(label), FALSE);
@@ -570,8 +612,8 @@ static void add_to_vbox_init_two_way_auth(GtkWidget *vbox,
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-        
-       
+
+
     /* Create the text view where the user enters their secret */
     entry = gtk_entry_new();
     gtk_entry_set_text(GTK_ENTRY(entry), "");
@@ -579,27 +621,28 @@ static void add_to_vbox_init_two_way_auth(GtkWidget *vbox,
     auth_opt_data->two_way_entry = GTK_ENTRY(entry);
 
     if (context->active_fingerprint->trust &&
-        context->active_fingerprint->trust[0]) {
-        label2 = gtk_label_new(_("This buddy is already authenticated."));
+	context->active_fingerprint->trust[0]) {
+	label2 = gtk_label_new(_("This buddy is already authenticated."));
     } else {
-        label2 = NULL;
+	label2 = NULL;
     }
 
     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    
+
     /* Leave a blank line */
     gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
-        FALSE, 0);
-        
+	FALSE, 0);
+
     if (label2) {
-        gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 0);
-        gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
-            FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE,
+		FALSE, 0);
     }
 }
 
-static void add_to_vbox_verify_fingerprint(GtkWidget *vbox, ConnContext *context, SmpResponsePair* smppair) {
+static void add_to_vbox_verify_fingerprint(GtkWidget *vbox,
+	ConnContext *context, SmpResponsePair* smppair) {
     char our_hash[45], their_hash[45];
     GtkWidget *label;
     char *label_text;
@@ -610,16 +653,14 @@ static void add_to_vbox_verify_fingerprint(GtkWidget *vbox, ConnContext *context
 
     if (fprint == NULL) return;
     if (fprint->fingerprint == NULL) return;
-    context = fprint->context;
-    if (context == NULL) return;
 
     label_text = g_strdup_printf("<small><i>\n%s %s\n</i></small>",
 	    _("To verify the fingerprint, contact your buddy via some "
 	    "<i>other</i> authenticated channel, such as the telephone "
 	    "or GPG-signed email.  Each of you should tell your fingerprint "
 	    "to the other."),
-	    _("If everything matches up, you should indicate in the above "
-	    "dialog that you <b>have</b> verified the fingerprint."));
+	    _("If everything matches up, you should chose <b>I have</b> "
+	    "in the menu below."));
     label = gtk_label_new(NULL);
     gtk_label_set_markup(GTK_LABEL(label), label_text);
     gtk_label_set_selectable(GTK_LABEL(label), FALSE);
@@ -631,18 +672,18 @@ static void add_to_vbox_verify_fingerprint(GtkWidget *vbox, ConnContext *context
 
     strcpy(our_hash, _("[none]"));
     otrl_privkey_fingerprint(otrg_plugin_userstate, our_hash,
-        context->accountname, context->protocol);
+	    context->accountname, context->protocol);
 
     otrl_privkey_hash_to_human(their_hash, fprint->fingerprint);
 
     p = purple_find_prpl(context->protocol);
     proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
     label_text = g_strdup_printf(_("Fingerprint for you, %s (%s):\n%s\n\n"
-        "Purported fingerprint for %s:\n%s\n"), context->accountname,
-        proto_name, our_hash, context->username, their_hash);
-        
+	    "Purported fingerprint for %s:\n%s\n"), context->accountname,
+	    proto_name, our_hash, context->username, their_hash);
+
     label = gtk_label_new(NULL);
-    
+
     gtk_label_set_markup(GTK_LABEL(label), label_text);
     /* Make the label containing the fingerprints selectable, but
      * not auto-selected. */
@@ -653,7 +694,7 @@ static void add_to_vbox_verify_fingerprint(GtkWidget *vbox, ConnContext *context
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-        
+
     add_vrfy_fingerprint(vbox, vfd);
     g_signal_connect(G_OBJECT(vbox), "destroy",
 	    G_CALLBACK(vrfy_fingerprint_destroyed), vfd);
@@ -665,25 +706,25 @@ static void redraw_auth_vbox(GtkComboBox *combo, void *data) {
     GtkWidget *notebook = auth_data ? auth_data->notebook : NULL;
 
     int selected;
-    
+
     if (auth_data == NULL) return;
 
     selected = gtk_combo_box_get_active(combo);
-    
+
     if (selected == 0) {
-        gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
-        auth_data->smppair->entry = auth_data->one_way_entry;
-        auth_data->smppair->smp_type = 0;
+	gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
+	auth_data->smppair->entry = auth_data->one_way_entry;
+	auth_data->smppair->smp_type = 0;
     } else if (selected == 1) {
-        gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 1);
-        auth_data->smppair->entry = auth_data->two_way_entry;
-        auth_data->smppair->smp_type = 1;
+	gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 1);
+	auth_data->smppair->entry = auth_data->two_way_entry;
+	auth_data->smppair->smp_type = 1;
     } else if (selected == 2) {
-        auth_data->smppair->entry = NULL;
-        gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 2);
-        auth_data->smppair->smp_type = -1;
+	auth_data->smppair->entry = NULL;
+	gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 2);
+	auth_data->smppair->smp_type = -1;
     }
-    
+
 }
 
 static void add_other_authentication_options(GtkWidget *vbox,
@@ -693,7 +734,7 @@ static void add_other_authentication_options(GtkWidget *vbox,
     char *labeltext;
 
     labeltext = g_strdup_printf("\n%s",
-	_("How would you like to authenticate your buddy?"));
+	    _("How would you like to authenticate your buddy?"));
     label = gtk_label_new(labeltext);
     g_free(labeltext);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
@@ -714,9 +755,9 @@ static void add_other_authentication_options(GtkWidget *vbox,
     gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
 
     data->notebook = notebook;
-   
+
     g_signal_connect (combo, "changed",
-                  G_CALLBACK (redraw_auth_vbox), data);
+	    G_CALLBACK (redraw_auth_vbox), data);
 }
 
 
@@ -729,124 +770,132 @@ static GtkWidget *create_smp_dialog(const char *title, const char *primary,
     SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
 
     close_progress_window(smp_data);
-    
+
+    if (smp_data->their_instance != context->their_instance) {
+	otrg_gtk_dialog_free_smp_data(conv);
+	otrg_gtk_dialog_add_smp_data(conv);
+    }
+
     if (!(smp_data->smp_secret_dialog)) {
-        GtkWidget *hbox;
-        GtkWidget *vbox;
-        GtkWidget *auth_vbox;
-        GtkWidget *label;
-        GtkWidget *img = NULL;
-        char *label_text;
-        const char *icon_name = NULL;
-        SmpResponsePair* smppair;
-        GtkWidget *notebook;
-        AuthSignalData *auth_opt_data;     
-    
-        icon_name = PIDGIN_STOCK_DIALOG_INFO;
-        img = gtk_image_new_from_stock(icon_name,
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *auth_vbox;
+	GtkWidget *label;
+	GtkWidget *img = NULL;
+	char *label_text;
+	const char *icon_name = NULL;
+	SmpResponsePair* smppair;
+	GtkWidget *notebook;
+	AuthSignalData *auth_opt_data;
+
+	smp_data->their_instance = context->their_instance;
+	icon_name = PIDGIN_STOCK_DIALOG_INFO;
+	img = gtk_image_new_from_stock(icon_name,
 		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
-        gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
-    
-        dialog = gtk_dialog_new_with_buttons(title ? title :
+	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+
+	dialog = gtk_dialog_new_with_buttons(title ? title :
 		PIDGIN_ALERT_TITLE, NULL, 0,
-                         GTK_STOCK_HELP, GTK_RESPONSE_HELP,
-                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
-                         _("_Authenticate"), GTK_RESPONSE_ACCEPT, NULL);
-        gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+		 GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+		 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+		 _("_Authenticate"), GTK_RESPONSE_ACCEPT, NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
 		GTK_RESPONSE_ACCEPT);
-    
-        auth_vbox = gtk_vbox_new(FALSE, 0);
-        hbox = gtk_hbox_new(FALSE, 15);
-        vbox = gtk_vbox_new(FALSE, 0);
-        
-        smppair = malloc(sizeof(SmpResponsePair));
-        smppair->responder = responder;
-        smppair->context = context;
-        
-        
-        notebook = gtk_notebook_new();
-        auth_opt_data = malloc(sizeof(AuthSignalData)); 
-        auth_opt_data->smppair = smppair;
-        
-        gtk_window_set_focus_on_map(GTK_WINDOW(dialog), !responder);
-        gtk_window_set_role(GTK_WINDOW(dialog), "notify_dialog");
-    
-        gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
-        gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
-        gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
-        gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 12);
-        gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 6);
-    
-        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
-    
-        gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-    
-        label_text = g_strdup_printf(
-               "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
-               (primary ? primary : ""),
+
+	auth_vbox = gtk_vbox_new(FALSE, 0);
+	hbox = gtk_hbox_new(FALSE, 15);
+	vbox = gtk_vbox_new(FALSE, 0);
+
+	smppair = malloc(sizeof(SmpResponsePair));
+	smppair->responder = responder;
+	smppair->context = context;
+
+
+	notebook = gtk_notebook_new();
+	auth_opt_data = malloc(sizeof(AuthSignalData));
+	auth_opt_data->smppair = smppair;
+
+	gtk_window_set_focus_on_map(GTK_WINDOW(dialog), !responder);
+	gtk_window_set_role(GTK_WINDOW(dialog), "notify_dialog");
+
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 12);
+	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+		6);
+
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+
+	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+
+	label_text = g_strdup_printf(
+		"<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+		(primary ? primary : ""),
 		_("Authenticating a buddy helps ensure that the person "
-		    "you are talking to is who he or she claims to be."));
-    
-        label = gtk_label_new(NULL);
-    
-        gtk_label_set_markup(GTK_LABEL(label), label_text);
-        gtk_label_set_selectable(GTK_LABEL(label), FALSE);
-        g_free(label_text);
-        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-        gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-        gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    
-        if (!responder) {
-            add_other_authentication_options(vbox, notebook, context, auth_opt_data);
-        }
-        
-        g_signal_connect(G_OBJECT(dialog), "response",
-                 G_CALLBACK(smp_secret_response_cb),
-                 auth_opt_data);
-    
-        if (!responder || (responder && question != NULL)) {
-            GtkWidget *one_way_vbox = gtk_vbox_new(FALSE, 0);
-            add_to_vbox_init_one_way_auth(one_way_vbox, context,
+		"you are talking to is who he or she claims to be."));
+
+	label = gtk_label_new(NULL);
+
+	gtk_label_set_markup(GTK_LABEL(label), label_text);
+	gtk_label_set_selectable(GTK_LABEL(label), FALSE);
+	g_free(label_text);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	if (!responder) {
+	    add_other_authentication_options(vbox, notebook, context,
+		    auth_opt_data);
+	}
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+		G_CALLBACK(smp_secret_response_cb),
+		auth_opt_data);
+
+	if (!responder || (responder && question != NULL)) {
+	    GtkWidget *one_way_vbox = gtk_vbox_new(FALSE, 0);
+	    add_to_vbox_init_one_way_auth(one_way_vbox, context,
 		    auth_opt_data, question);
-            gtk_notebook_append_page(GTK_NOTEBOOK(notebook), one_way_vbox,
-                gtk_label_new("0"));
-            smppair->entry = auth_opt_data->one_way_entry;
-            smppair->smp_type = 0;
-        }
-        
-        if (!responder || (responder && question == NULL)) {
-            GtkWidget *two_way_vbox = gtk_vbox_new(FALSE, 0);
-            add_to_vbox_init_two_way_auth(two_way_vbox, context, auth_opt_data);
-            gtk_notebook_append_page(GTK_NOTEBOOK(notebook), two_way_vbox,
-                gtk_label_new("1"));
-                    
-            if (responder && question == NULL) {
-                smppair->entry = auth_opt_data->two_way_entry;
-                smppair->smp_type = 1;
-            }
-        }
-        
-        if (!responder) {
-            GtkWidget *fingerprint_vbox = gtk_vbox_new(FALSE, 0);
-            add_to_vbox_verify_fingerprint(fingerprint_vbox, context, smppair);
-            gtk_notebook_append_page(GTK_NOTEBOOK(notebook), fingerprint_vbox,
-                gtk_label_new("2"));
-        }
-        
-        gtk_notebook_set_show_tabs (GTK_NOTEBOOK(notebook), FALSE);
-        
-        gtk_notebook_set_show_border (GTK_NOTEBOOK(notebook), FALSE);
-        gtk_box_pack_start(GTK_BOX(auth_vbox), notebook, FALSE, FALSE, 0);
-        gtk_widget_show(notebook);
-    
-    
-        gtk_box_pack_start(GTK_BOX(vbox), auth_vbox, FALSE, FALSE, 0);
-        
-        gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
-    
-        gtk_widget_show_all(dialog);
-        
-        gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
+	    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), one_way_vbox,
+		    gtk_label_new("0"));
+	    smppair->entry = auth_opt_data->one_way_entry;
+	    smppair->smp_type = 0;
+	}
+
+	if (!responder || (responder && question == NULL)) {
+	    GtkWidget *two_way_vbox = gtk_vbox_new(FALSE, 0);
+	    add_to_vbox_init_two_way_auth(two_way_vbox, context, auth_opt_data);
+	    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), two_way_vbox,
+		    gtk_label_new("1"));
+
+	    if (responder && question == NULL) {
+		smppair->entry = auth_opt_data->two_way_entry;
+		smppair->smp_type = 1;
+	    }
+	}
+
+	if (!responder) {
+	    GtkWidget *fingerprint_vbox = gtk_vbox_new(FALSE, 0);
+	    add_to_vbox_verify_fingerprint(fingerprint_vbox, context, smppair);
+	    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), fingerprint_vbox,
+		    gtk_label_new("2"));
+	}
+
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK(notebook), FALSE);
+
+	gtk_notebook_set_show_border (GTK_NOTEBOOK(notebook), FALSE);
+	gtk_box_pack_start(GTK_BOX(auth_vbox), notebook, FALSE, FALSE, 0);
+	gtk_widget_show(notebook);
+
+
+	gtk_box_pack_start(GTK_BOX(vbox), auth_vbox, FALSE, FALSE, 0);
+
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+
+	gtk_widget_show_all(dialog);
+
+	gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
 
 	if (!responder) {
 	    gtk_window_set_focus(GTK_WINDOW(dialog),
@@ -855,16 +904,16 @@ static GtkWidget *create_smp_dialog(const char *title, const char *primary,
 	    gtk_window_set_focus(GTK_WINDOW(dialog),
 		    GTK_WIDGET(smppair->entry));
 	}
-        
-        smp_data->smp_secret_dialog = dialog;
-        smp_data->smp_secret_smppair = smppair;
-    
+
+	smp_data->smp_secret_dialog = dialog;
+	smp_data->smp_secret_smppair = smppair;
+
     } else {
-        /* Set the responder field to TRUE if we were passed that value,
-         * even if the window was already up. */
-        if (responder) {
-            smp_data->smp_secret_smppair->responder = responder;
-        }
+	/* Set the responder field to TRUE if we were passed that value,
+	 * even if the window was already up. */
+	if (responder) {
+	    smp_data->smp_secret_smppair->responder = responder;
+	}
     }
 
     return smp_data->smp_secret_dialog;
@@ -892,9 +941,9 @@ static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
 
     dialog = gtk_dialog_new_with_buttons(
 	    context->smstate->received_question ?
-            /* Translators: you are asked to authenticate yourself */
+	    /* Translators: you are asked to authenticate yourself */
 	    _("Authenticating to Buddy") :
-            /* Translators: you asked your buddy to authenticate him/herself */
+	    /* Translators: you asked your buddy to authenticate him/herself */
 	    _("Authenticating Buddy"),
 	    parent, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
@@ -922,8 +971,8 @@ static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
 
     label_pat = g_strdup_printf("<span weight=\"bold\" size=\"larger\">"
 	    "%s</span>\n", context->smstate->received_question ?
-		   _("Authenticating to %s") :
-		   _("Authenticating %s"));
+	    _("Authenticating to %s") :
+	    _("Authenticating %s"));
     label_text = g_strdup_printf(label_pat, context->username);
     g_free(label_pat);
 
@@ -941,12 +990,12 @@ static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
     gtk_label_set_line_wrap(GTK_LABEL(proglabel), TRUE);
     gtk_misc_set_alignment(GTK_MISC(proglabel), 0, 0);
     gtk_box_pack_start(GTK_BOX(vbox), proglabel, FALSE, FALSE, 0);
-   
+
     /* Create the progress bar */
     bar = gtk_progress_bar_new();
     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar), 0.1);
     gtk_box_pack_start(GTK_BOX(vbox), bar, FALSE, FALSE, 0);
-    
+
     gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
 
     conv = otrg_plugin_context_to_conv(context, 0);
@@ -959,8 +1008,8 @@ static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
     gtk_label_set_text(GTK_LABEL(proglabel), _("Waiting for buddy..."));
 
     g_signal_connect(G_OBJECT(dialog), "response",
-		     G_CALLBACK(smp_progress_response_cb),
-		     context);
+	     G_CALLBACK(smp_progress_response_cb),
+	     context);
 
     gtk_widget_show_all(dialog);
 
@@ -999,11 +1048,11 @@ static OtrgDialogWaitHandle otrg_gtk_dialog_private_key_wait_start(
 
     p = purple_find_prpl(protocol);
     protocol_print = (p ? p->info->name : _("Unknown"));
-	
+
     /* Create the Please Wait... dialog */
     secondary = g_strdup_printf(_("Generating private key for %s (%s)..."),
 	    account, protocol_print);
-	
+
     dialog = create_dialog(NULL, PURPLE_NOTIFY_MSG_INFO, title, primary,
 	    secondary, 0, &label, NULL, NULL);
     handle = malloc(sizeof(struct s_OtrgDialogWait));
@@ -1015,7 +1064,7 @@ static OtrgDialogWaitHandle otrg_gtk_dialog_private_key_wait_start(
     while (gtk_events_pending ()) {
 	gtk_main_iteration ();
     }
-	
+
     g_free(secondary);
 
     return handle;
@@ -1026,13 +1075,15 @@ static int otrg_gtk_dialog_display_otr_message(const char *accountname,
 	int force_create)
 {
     /* See if there's a conversation window we can put this in. */
-    PurpleConversation *conv;
+    PurpleConversation *conv = otrg_plugin_userinfo_to_conv(accountname,
+	    protocol, username, force_create);
+
 
-    conv = otrg_plugin_userinfo_to_conv(accountname, protocol, username,
-		force_create);
     if (!conv) return -1;
 
-    purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+    purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
 
     return 0;
 }
@@ -1067,8 +1118,9 @@ static void otrg_gtk_dialog_unknown_fingerprint(OtrlUserState us,
 
     /* Figure out if this is the first fingerprint we've seen for this
      * user. */
-    context = otrl_context_find(us, who, accountname, protocol, FALSE,
-	    NULL, NULL, NULL);
+    context = otrl_context_find(us, who, accountname, protocol,
+	    OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
+
     if (context) {
 	Fingerprint *fp = context->fingerprint_root.next;
 	while(fp) {
@@ -1094,26 +1146,30 @@ static void otrg_gtk_dialog_unknown_fingerprint(OtrlUserState us,
 
     conv = otrg_plugin_userinfo_to_conv(accountname, protocol, who, TRUE);
 
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
+
     g_free(buf);
 }
 
 static void otrg_gtk_dialog_clicked_connect(GtkWidget *widget, gpointer data);
 
-static void build_otr_menu(PurpleConversation *conv, GtkWidget *menu,
+static void build_otr_menu(ConvOrContext *convctx, GtkWidget *menu,
 	TrustLevel level);
 static void otr_refresh_otr_buttons(PurpleConversation *conv);
 static void otr_destroy_top_menu_objects(PurpleConversation *conv);
 static void otr_add_top_otr_menu(PurpleConversation *conv);
 static void otr_add_buddy_top_menus(PurpleConversation *conv);
-static void otr_check_conv_status_change( PurpleConversation *conv);
+static void otr_check_conv_status_change(PurpleConversation *conv);
 
 static void destroy_menuitem(GtkWidget *widget, gpointer data)
 {
     gtk_widget_destroy(widget);
 }
 
+static void otr_build_status_submenu(PidginWindow *win,
+	ConvOrContext *convctx, GtkWidget *menu, TrustLevel level);
+
 static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
 {
     GtkWidget *label;
@@ -1130,6 +1186,9 @@ static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
     GtkWidget *menuview;
     GtkWidget *menuverf;
     GtkWidget *menusmp;
+#else
+    ConvOrContext *convctx;
+    GHashTable * conv_or_ctx_map;
 #endif
     PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
     label = purple_conversation_get_data(conv, "otr-label");
@@ -1208,7 +1267,19 @@ static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
 
 #ifdef OLD_OTR_BUTTON
 #else
-    build_otr_menu(conv, menu, level);
+    conv_or_ctx_map = purple_conversation_get_data(conv, "otr-convorctx");
+    convctx = g_hash_table_lookup(conv_or_ctx_map, conv);
+
+    if (!convctx) {
+	convctx = malloc(sizeof(ConvOrContext));
+	g_hash_table_insert(conv_or_ctx_map, conv, (gpointer)convctx);
+    }
+
+    convctx->convctx_type = convctx_conv;
+    convctx->conv = conv;
+    build_otr_menu(convctx, menu, level);
+    otr_build_status_submenu(pidgin_conv_get_window(gtkconv), convctx, menu,
+	    level);
 #endif
 
     conv = gtkconv->active_conv;
@@ -1216,7 +1287,7 @@ static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
 
     /* Update other widgets */
     if (gtkconv != pidgin_conv_window_get_active_gtkconv(gtkconv->win)) {
-        return;
+	return;
     }
 
     otr_destroy_top_menu_objects(conv);
@@ -1231,9 +1302,11 @@ static void dialog_update_label(ConnContext *context)
     PurpleConversation *conv;
     TrustLevel level = otrg_plugin_context_to_trust(context);
 
+
     account = purple_accounts_find(context->accountname, context->protocol);
     if (!account) return;
-    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, context->username, account);
+    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+	    context->username, account);
     if (!conv) return;
     dialog_update_label_conv(conv, level);
 }
@@ -1244,6 +1317,7 @@ struct vrfy_fingerprint_data {
 			      while.  Use the copied pieces below
 			      instead. */
     char *accountname, *username, *protocol;
+    otrl_instag_t their_instance;
     unsigned char fingerprint[20];
 };
 
@@ -1266,6 +1340,7 @@ static struct vrfy_fingerprint_data* vrfy_fingerprint_data_new(
     vfd->accountname = strdup(context->accountname);
     vfd->username = strdup(context->username);
     vfd->protocol = strdup(context->protocol);
+    vfd->their_instance = context->their_instance;
     memmove(vfd->fingerprint, fprint->fingerprint, 20);
 
     return vfd;
@@ -1281,15 +1356,14 @@ static void vrfy_fingerprint_changed(GtkComboBox *combo, void *data)
 {
     struct vrfy_fingerprint_data *vfd = data;
     ConnContext *context = otrl_context_find(otrg_plugin_userstate,
-	    vfd->username, vfd->accountname, vfd->protocol, 0, NULL,
-	    NULL, NULL);
+	    vfd->username, vfd->accountname, vfd->protocol, vfd->their_instance,
+	    0, NULL, NULL, NULL);
     Fingerprint *fprint;
     int oldtrust, trust;
 
     if (context == NULL) return;
 
-    fprint = otrl_context_find_fingerprint(context, vfd->fingerprint,
-	    0, NULL);
+    fprint = otrl_context_find_fingerprint(context, vfd->fingerprint, 0, NULL);
 
     if (fprint == NULL) return;
 
@@ -1304,7 +1378,7 @@ static void vrfy_fingerprint_changed(GtkComboBox *combo, void *data)
 	otrg_plugin_write_fingerprints();
 	otrg_ui_update_keylist();
 	otrg_dialog_resensitize_all();
-    
+
     }
 }
 
@@ -1323,10 +1397,10 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
 
     hbox = gtk_hbox_new(FALSE, 0);
     combo = gtk_combo_box_new_text();
-    /* Translators: the following four messages should give alternative sentences.
-       The user selects the first or second message in a combo box;
-      the third message, a new line, a fingerprint, a new line, and 
-      the fourth message will follow it. */
+    /* Translators: the following four messages should give alternative
+     * sentences. The user selects the first or second message in a combo box;
+     * the third message, a new line, a fingerprint, a new line, and
+     * the fourth message will follow it. */
     gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("I have not"));
     /* 2nd message */
     gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("I have"));
@@ -1348,7 +1422,7 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
     g_free(labelt);
     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-    
+
     /* Leave a blank line */
     gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE, FALSE, 0);
 }
@@ -1382,8 +1456,8 @@ static void verify_fingerprint(GtkWindow *parent, Fingerprint *fprint)
     p = purple_find_prpl(context->protocol);
     proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
     secondary = g_strdup_printf(_("<small><i>%s %s\n\n</i></small>"
-		"Fingerprint for you, %s (%s):\n%s\n\n"
-		"Purported fingerprint for %s:\n%s\n"),
+	    "Fingerprint for you, %s (%s):\n%s\n\n"
+	    "Purported fingerprint for %s:\n%s\n"),
 	    _("To verify the fingerprint, contact your buddy via some "
 	    "<i>other</i> authenticated channel, such as the telephone "
 	    "or GPG-signed email.  Each of you should tell your fingerprint "
@@ -1421,18 +1495,16 @@ static void otrg_gtk_dialog_socialist_millionaires(ConnContext *context,
     if (context == NULL) return;
 
     if (responder && question) {
-        primary = g_strdup_printf(_("Authentication from %s"),
-            context->username);
+	primary = g_strdup_printf(_("Authentication from %s"),
+	    context->username);
     } else {
-        primary = g_strdup_printf(_("Authenticate %s"),
-            context->username);
+	primary = g_strdup_printf(_("Authenticate %s"),
+	    context->username);
     }
-    
-    /* fprintf(stderr, "Question = ``%s''\n", question); */
 
     p = purple_find_prpl(context->protocol);
     proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
-    
+
 
     dialog = create_smp_dialog(_("Authenticate Buddy"),
 	    primary, context, responder, question);
@@ -1457,7 +1529,7 @@ static void otrg_gtk_dialog_update_smp(ConnContext *context,
 
     /* If the counter is reset to absolute zero, the protocol has aborted */
     if (progress_level == 0.0) {
-        GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog);
+	GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog);
 
 	gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT, 1);
 	gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_REJECT, 0);
@@ -1469,14 +1541,14 @@ static void otrg_gtk_dialog_update_smp(ConnContext *context,
 	return;
     } else if (progress_level == 1.0) {
 	/* If the counter reaches 1.0, the protocol is complete */
-        GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog);
+	GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog);
 
 	gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT, 1);
 	gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_REJECT, 0);
 	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
 		GTK_RESPONSE_ACCEPT);
 
-        if (smp_event == OTRL_SMPEVENT_SUCCESS) {
+	if (smp_event == OTRL_SMPEVENT_SUCCESS) {
 	    if (context->active_fingerprint->trust &&
 		    context->active_fingerprint->trust[0]) {
 		gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
@@ -1484,10 +1556,10 @@ static void otrg_gtk_dialog_update_smp(ConnContext *context,
 	    } else {
 		gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
 			_("Your buddy has successfully authenticated you.  "
-			    "You may want to authenticate your buddy as "
-			    "well by asking your own question."));
+			"You may want to authenticate your buddy as "
+			"well by asking your own question."));
 	    }
-        } else {
+	} else {
 	    gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
 		    _("Authentication failed."));
 	}
@@ -1505,6 +1577,7 @@ static void otrg_gtk_dialog_connected(ConnContext *context)
     char *format_buf;
     TrustLevel level;
     OtrgUiPrefs prefs;
+    gboolean is_multi_inst;
 
     conv = otrg_plugin_context_to_conv(context, TRUE);
     level = otrg_plugin_context_to_trust(context);
@@ -1517,33 +1590,55 @@ static void otrg_gtk_dialog_connected(ConnContext *context)
 
     switch(level) {
        case TRUST_PRIVATE:
-           format_buf = g_strdup(_("Private conversation with %s started.%s"));
-           break;
+	    format_buf = g_strdup(
+		    _("Private conversation with %s started.%s%s"));
+	    break;
 
        case TRUST_UNVERIFIED:
-           format_buf = g_strdup_printf(_("<a href=\"%s%s\">Unverified</a> "
-                       "conversation with %%s started.%%s"),
-                       UNVERIFIED_HELPURL, _("?lang=en"));
-           break;
+	    format_buf = g_strdup_printf(_("<a href=\"%s%s\">Unverified</a> "
+		    "conversation with %%s started.%%s%%s"),
+		    UNVERIFIED_HELPURL, _("?lang=en"));
+	    break;
 
        default:
-           /* This last case should never happen, since we know
-            * we're in ENCRYPTED. */
-           format_buf = g_strdup(_("Not private conversation with %s "
-                       "started.%s"));
-           break;
+	    /* This last case should never happen, since we know
+	     * we're in ENCRYPTED. */
+	    format_buf = g_strdup(_("Not private conversation with %s "
+		    "started.%s%s"));
+	    break;
     }
     buf = g_strdup_printf(format_buf,
 		purple_conversation_get_name(conv),
 		context->protocol_version == 1 ? _("  Warning: using old "
-		    "protocol version 1.") : "");
+		"protocol version 1.") : "", conv->logging ?
+		_("  Your client is logging this conversation.") :
+		_("   Your client is not logging this conversation."));
+
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
 
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
     g_free(buf);
     g_free(format_buf);
 
     dialog_update_label(context);
+
+    is_multi_inst = (gboolean) purple_conversation_get_data(conv,
+	    "otr-conv_multi_instances");
+
+    if (is_multi_inst) {
+	gboolean have_warned_instances = (gboolean)
+		purple_conversation_get_data(conv, "otr-warned_instances");
+
+	if (!have_warned_instances) {
+	    purple_conversation_set_data(conv, "otr-warned_instances",
+		    (gpointer)1);
+	    otrg_gtk_dialog_display_otr_message(context->accountname,
+		    context->protocol, context->username,
+		    _("Your buddy is logged in multiple times and OTR has "
+		    "established multiple sessions. Use the icon menu above if "
+		    "you wish to select the outgoing session."), 0);
+	}
+    }
 }
 
 /* Call this when a context transitions to PLAINTEXT. */
@@ -1557,16 +1652,16 @@ static void otrg_gtk_dialog_disconnected(ConnContext *context)
 
     buf = g_strdup_printf(_("Private conversation with %s lost."),
 	    purple_conversation_get_name(conv));
-    
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
+
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
+
     g_free(buf);
 
     otrg_ui_get_prefs(&prefs, purple_conversation_get_account(conv),
 	    context->username);
     if (prefs.avoid_logging_otr) {
-	if (purple_prefs_get_bool("/purple/logging/log_ims"))
-	{
+	if (purple_prefs_get_bool("/purple/logging/log_ims")) {
 	    purple_conversation_set_logging(conv, TRUE);
 	}
     }
@@ -1593,11 +1688,12 @@ static void otrg_gtk_dialog_finished(const char *accountname,
     if (!conv) return;
 
     buf = g_strdup_printf(_("%s has ended his/her private conversation with "
-		"you; you should do the same."),
+	    "you; you should do the same."),
 	    purple_conversation_get_name(conv));
-        
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-        
+
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
+
     g_free(buf);
 
     dialog_update_label_conv(conv, TRUST_FINISHED);
@@ -1617,33 +1713,34 @@ static void otrg_gtk_dialog_stillconnected(ConnContext *context)
     level = otrg_plugin_context_to_trust(context);
 
     switch(level) {
-       case TRUST_PRIVATE:
-           format_buf = g_strdup(_("Successfully refreshed the private "
-                       "conversation with %s.%s"));
-           break;
+	case TRUST_PRIVATE:
+	    format_buf = g_strdup(_("Successfully refreshed the private "
+		    "conversation with %s.%s"));
+	    break;
 
-       case TRUST_UNVERIFIED:
-           format_buf = g_strdup_printf(_("Successfully refreshed the "
-                       "<a href=\"%s%s\">unverified</a> conversation with "
-                       "%%s.%%s"),
-                       UNVERIFIED_HELPURL, _("?lang=en"));
-           break;
+	case TRUST_UNVERIFIED:
+	    format_buf = g_strdup_printf(_("Successfully refreshed the "
+		    "<a href=\"%s%s\">unverified</a> conversation with "
+		    "%%s.%%s"),
+		    UNVERIFIED_HELPURL, _("?lang=en"));
+	    break;
 
-       default:
-           /* This last case should never happen, since we know
-            * we're in ENCRYPTED. */
-           format_buf = g_strdup(_("Successfully refreshed the not private "
-                       "conversation with %s.%s"));
-           break;
+	default:
+	    /* This last case should never happen, since we know
+	     * we're in ENCRYPTED. */
+	    format_buf = g_strdup(_("Successfully refreshed the not private "
+		    "conversation with %s.%s"));
+	    break;
     }
 
     buf = g_strdup_printf(format_buf,
 		purple_conversation_get_name(conv),
 		context->protocol_version == 1 ? _("  Warning: using old "
-		    "protocol version 1.") : "");
+		"protocol version 1.") : "");
+
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
 
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
     g_free(buf);
     g_free(format_buf);
 
@@ -1660,7 +1757,7 @@ static void otrg_gtk_dialog_clicked_connect(GtkWidget *widget, gpointer data)
     PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
     if (gtkconv->active_conv != conv) {
-        pidgin_conv_switch_active_conversation(conv);
+	pidgin_conv_switch_active_conversation(conv);
     }
 
     if (purple_conversation_get_data(conv, "otr-private")) {
@@ -1669,19 +1766,28 @@ static void otrg_gtk_dialog_clicked_connect(GtkWidget *widget, gpointer data)
 	format = _("Attempting to start a private conversation with %s...");
     }
     buf = g_strdup_printf(format, purple_conversation_get_name(conv));
-    
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
+
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
+
     g_free(buf);
-	
+
     otrg_plugin_send_default_query_conv(conv);
 }
 
 /* Called when SMP verification option selected from menu */
 static void socialist_millionaires(GtkWidget *widget, gpointer data)
 {
-    PurpleConversation *conv = data;
-    ConnContext *context = otrg_plugin_conv_to_context(conv);
+    ConvOrContext *convctx = data;
+    PurpleConversation *conv;
+    ConnContext *context;
+
+    if (convctx->convctx_type == convctx_conv) {
+	conv = convctx->conv;
+	context = otrg_plugin_conv_to_selected_context(conv, 0);
+    } else if (convctx->convctx_type == convctx_ctx) {
+	context = convctx->context;
+    }
 
     if (context == NULL || context->msgstate != OTRL_MSGSTATE_ENCRYPTED)
 	return;
@@ -1698,8 +1804,17 @@ static void menu_whatsthis(GtkWidget *widget, gpointer data)
 
 static void menu_end_private_conversation(GtkWidget *widget, gpointer data)
 {
-    PurpleConversation *conv = data;
-    ConnContext *context = otrg_plugin_conv_to_context(conv);
+    PurpleConversation *conv;
+    ConnContext *context;
+    ConvOrContext *convctx = data;
+
+    if (convctx->convctx_type == convctx_conv) {
+	conv = convctx->conv;
+	context = otrg_plugin_conv_to_selected_context(conv, 0);
+    } else if (convctx->convctx_type == convctx_ctx) {
+	context = convctx->context;
+    }
+
 
     otrg_ui_disconnect_connection(context);
 }
@@ -1746,8 +1861,8 @@ static void otr_refresh_otr_buttons(PurpleConversation *conv) {
 
     for (;list_iter;list_iter = list_iter->next) {
 
-        current_conv = list_iter->data;
-        button = purple_conversation_get_data(current_conv, "otr-button");
+	current_conv = list_iter->data;
+	button = purple_conversation_get_data(current_conv, "otr-button");
 
 	if (button) {
 	    if (current_conv == gtkconv->active_conv) {
@@ -1759,24 +1874,41 @@ static void otr_refresh_otr_buttons(PurpleConversation *conv) {
     }
 }
 
-static void otr_destroy_top_menu_objects(PurpleConversation *conv) {
-    PidginConversation *gtkconv = PIDGIN_CONVERSATION ( conv );
-    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
+/* Menu has been destroyed -- let's remove it from the menu_list
+   so that it won't be destroyed again                           */
+static void otr_menu_destroy(GtkWidget *widget, gpointer pdata) {
+    PidginWindow *win = (PidginWindow *) pdata ;
+    GtkWidget *top_menu = widget;
+
     GList * menu_list = g_hash_table_lookup ( otr_win_menus, win );
-    GList * iter;
-    GList * next;
+    menu_list = g_list_remove ( menu_list, top_menu );
+    g_hash_table_replace ( otr_win_menus, win, menu_list );
+}
+
+static void otr_clear_win_menu_list(PidginWindow *win) {
+    GList * head = g_hash_table_lookup ( otr_win_menus, win ); /* menu_list */
+    GList * old_head = 0;
 
-    if ( menu_list != NULL ) {
-        iter = menu_list;
-        while ( iter ) {
-            if ( iter->data ) gtk_object_destroy ( GTK_OBJECT ( iter->data ) );
-            next = iter->next;
-            menu_list = g_list_remove ( menu_list, iter->data );
-            iter = next;
-        }
+    while(head) {
+	old_head = head;
+	gtk_object_destroy ( GTK_OBJECT ( head->data ) );
+	head = g_hash_table_lookup ( otr_win_menus, win );
+
+	if (head && head == old_head) {
+	    /* The head was not removed by the "destroyed" callback
+	       Something is wrong */
+	    break;
+	}
     }
-    g_hash_table_replace ( otr_win_menus, win, menu_list );
 
+    g_hash_table_replace ( otr_win_menus, win, head );
+}
+
+static void otr_destroy_top_menu_objects(PurpleConversation *conv) {
+    PidginConversation *gtkconv = PIDGIN_CONVERSATION ( conv );
+    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
+
+    otr_clear_win_menu_list(win);
 }
 
 static int otr_get_menu_insert_pos(PurpleConversation *conv) {
@@ -1784,13 +1916,13 @@ static int otr_get_menu_insert_pos(PurpleConversation *conv) {
     PidginWindow *win = pidgin_conv_get_window ( gtkconv );
     GtkWidget *menu_bar = win->menu.menubar;
 
-    GList * list_iter = gtk_container_get_children ( GTK_CONTAINER ( menu_bar ) );
+    GList * list_iter = gtk_container_get_children(GTK_CONTAINER(menu_bar));
     GList * head = list_iter;
 
     int pos = 0;
     while ( list_iter ) {
-        pos++;
-        list_iter = list_iter->next;
+	pos++;
+	list_iter = list_iter->next;
     }
 
     if (pos != 0) pos--;
@@ -1800,23 +1932,40 @@ static int otr_get_menu_insert_pos(PurpleConversation *conv) {
     return pos;
 }
 
-static void otr_set_menu_labels(PurpleConversation *conv, GtkWidget *query, GtkWidget *end, GtkWidget *smp) {
-    int insecure = purple_conversation_get_data(conv, "otr-private") ? 0 : 1;
-    int authen = purple_conversation_get_data(conv, "otr-authenticated") ?
-	1 : 0;
-    int finished = purple_conversation_get_data(conv, "otr-finished") ? 1 : 0;
+static void otr_set_menu_labels(ConvOrContext *convctx, GtkWidget *query,
+	GtkWidget *end, GtkWidget *smp) {
+    PurpleConversation *conv;
+    int insecure = 0;
+    int authen = 0;
+    int finished = 0;
+    TrustLevel level = TRUST_NOT_PRIVATE;
+
+
+    if (convctx->convctx_type == convctx_conv) {
+	conv = convctx->conv;
+	insecure = purple_conversation_get_data(conv, "otr-private") ? 0 : 1;
+	authen = purple_conversation_get_data(conv, "otr-authenticated") ? 1 :0;
+	finished = purple_conversation_get_data(conv, "otr-finished") ? 1 : 0;
+    } else if (convctx->convctx_type == convctx_ctx) {
+	level = otrg_plugin_context_to_trust(convctx->context);
+	insecure = level == TRUST_UNVERIFIED || level == TRUST_PRIVATE ? 0 : 1;
+	authen = level == TRUST_PRIVATE ? 1 : 0;
+	finished = level == TRUST_FINISHED ? 1 : 0;
+    } else {
+	return;
+    }
 
     GtkWidget * label = gtk_bin_get_child(GTK_BIN(query));
 
     gtk_label_set_markup_with_mnemonic(GTK_LABEL(label),
-        insecure ? _("Start _private conversation") :
-        _("Refresh _private conversation"));
+	    insecure ? _("Start _private conversation") :
+	    _("Refresh _private conversation"));
 
     label = gtk_bin_get_child(GTK_BIN(smp));
 
     gtk_label_set_markup_with_mnemonic(GTK_LABEL(label),
-        (!insecure && authen) ? _("Re_authenticate buddy") :
-        _("_Authenticate buddy"));
+	    (!insecure && authen) ? _("Re_authenticate buddy") :
+	    _("_Authenticate buddy"));
 
     gtk_widget_set_sensitive(GTK_WIDGET(end), !insecure || finished);
     gtk_widget_set_sensitive(GTK_WIDGET(smp), !insecure);
@@ -1828,7 +1977,7 @@ static void force_deselect(GtkItem *item, gpointer data)
 }
 
 static void otr_build_status_submenu(PidginWindow *win,
-	PurpleConversation *conv, GtkWidget *menu, TrustLevel level) {
+	ConvOrContext *convctx, GtkWidget *menu, TrustLevel level) {
     char *status = "";
     GtkWidget *image;
     GtkWidget *levelimage;
@@ -1838,8 +1987,21 @@ static void otr_build_status_submenu(PidginWindow *win,
     GdkPixbuf *pixbuf;
     GtkWidget *whatsthis;
 
-    gchar *text = g_strdup_printf("%s (%s)", conv->name,
+    gchar *text = NULL;
+
+    PurpleConversation *conv;
+
+    if (convctx->convctx_type == convctx_conv) {
+	conv = convctx->conv;
+    } else if (convctx->convctx_type == convctx_ctx) {
+	conv = otrg_plugin_context_to_conv(convctx->context, 0);
+    } else {
+	return;
+    }
+
+    text = g_strdup_printf("%s (%s)", conv->name,
 	    purple_account_get_username(conv->account));
+
     buddy_name = gtk_image_menu_item_new_with_label(text);
     g_free(text);
 
@@ -1856,24 +2018,24 @@ static void otr_build_status_submenu(PidginWindow *win,
     gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( buddy_name ), image);
 
     switch(level) {
-        case TRUST_NOT_PRIVATE:
-            status = _("Not Private");
-            break;
-        case TRUST_UNVERIFIED:
-            status = _("Unverified");
-            break;
-        case TRUST_PRIVATE:
-            status = _("Private");
-            break;
-        case TRUST_FINISHED:
-            status = _("Finished");
-            break;
-        }
+	case TRUST_NOT_PRIVATE:
+	    status = _("Not Private");
+	    break;
+	case TRUST_UNVERIFIED:
+	    status = _("Unverified");
+	    break;
+	case TRUST_PRIVATE:
+	    status = _("Private");
+	    break;
+	case TRUST_FINISHED:
+	    status = _("Finished");
+	    break;
+	}
 
     buddy_status = gtk_image_menu_item_new_with_label(status);
 
     levelimage = otr_icon(NULL, level, 1);
-    
+
     gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( buddy_status ),
 	    levelimage);
 
@@ -1882,8 +2044,8 @@ static void otr_build_status_submenu(PidginWindow *win,
     whatsthis = gtk_image_menu_item_new_with_mnemonic(_("_What's this?"));
     gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( whatsthis ),
 	    gtk_image_new_from_stock(GTK_STOCK_HELP,
-		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)));
-    
+	    gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)));
+
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menusep);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), buddy_name);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), buddy_status);
@@ -1897,24 +2059,39 @@ static void otr_build_status_submenu(PidginWindow *win,
     gtk_widget_show_all(whatsthis);
 
     gtk_signal_connect(GTK_OBJECT(buddy_name), "select",
-        GTK_SIGNAL_FUNC(force_deselect), NULL);
+	GTK_SIGNAL_FUNC(force_deselect), NULL);
     gtk_signal_connect(GTK_OBJECT(buddy_status), "select",
-        GTK_SIGNAL_FUNC(force_deselect), NULL);
+	GTK_SIGNAL_FUNC(force_deselect), NULL);
     gtk_signal_connect(GTK_OBJECT(whatsthis), "activate",
-        GTK_SIGNAL_FUNC(menu_whatsthis), conv);
+	GTK_SIGNAL_FUNC(menu_whatsthis), conv);
 }
 
-static void build_otr_menu(PurpleConversation *conv, GtkWidget *menu,
+static void build_otr_menu(ConvOrContext *convctx, GtkWidget *menu,
 	TrustLevel level)
 {
-    PidginConversation *gtkconv = PIDGIN_CONVERSATION ( conv );
-    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
+    PurpleConversation *conv;
+    PidginConversation *gtkconv;
+    PidginWindow *win;
 
-    GtkWidget *buddymenuquery = gtk_menu_item_new_with_mnemonic(_("Start _private conversation"));
-    GtkWidget *buddymenuend = gtk_menu_item_new_with_mnemonic(_("_End private conversation"));
-    GtkWidget *buddymenusmp = gtk_menu_item_new_with_mnemonic(_("_Authenticate buddy"));
+    if (convctx->convctx_type == convctx_conv) {
+	conv = convctx->conv;
+    } else if (convctx->convctx_type == convctx_ctx) {
+	conv = otrg_plugin_context_to_conv(convctx->context, 0);
+    } else {
+	return;
+    }
+
+    gtkconv = PIDGIN_CONVERSATION ( conv );
+    win = pidgin_conv_get_window ( gtkconv );
 
-    otr_set_menu_labels(conv, buddymenuquery, buddymenuend, buddymenusmp);
+    GtkWidget *buddymenuquery = gtk_menu_item_new_with_mnemonic(
+	    _("Start _private conversation"));
+    GtkWidget *buddymenuend = gtk_menu_item_new_with_mnemonic(
+	    _("_End private conversation"));
+    GtkWidget *buddymenusmp = gtk_menu_item_new_with_mnemonic(
+	    _("_Authenticate buddy"));
+
+    otr_set_menu_labels(convctx, buddymenuquery, buddymenuend, buddymenusmp);
 
     /* Empty out the menu */
     gtk_container_foreach(GTK_CONTAINER(menu), destroy_menuitem, NULL);
@@ -1928,13 +2105,12 @@ static void build_otr_menu(PurpleConversation *conv, GtkWidget *menu,
     gtk_widget_show(buddymenusmp);
 
     gtk_signal_connect(GTK_OBJECT(buddymenuquery), "activate",
-        GTK_SIGNAL_FUNC(otrg_gtk_dialog_clicked_connect), conv);
+	GTK_SIGNAL_FUNC(otrg_gtk_dialog_clicked_connect), conv);
     gtk_signal_connect(GTK_OBJECT(buddymenuend), "activate",
-        GTK_SIGNAL_FUNC(menu_end_private_conversation), conv);
+	GTK_SIGNAL_FUNC(menu_end_private_conversation), convctx);
     gtk_signal_connect(GTK_OBJECT(buddymenusmp), "activate",
-        GTK_SIGNAL_FUNC(socialist_millionaires), conv);
+	GTK_SIGNAL_FUNC(socialist_millionaires), convctx);
 
-    otr_build_status_submenu(win, conv, menu, level);
 }
 
 static void otr_add_top_otr_menu(PurpleConversation *conv) {
@@ -1948,7 +2124,12 @@ static void otr_add_top_otr_menu(PurpleConversation *conv) {
     GtkWidget *topmenuitem;
 
     TrustLevel level = TRUST_NOT_PRIVATE;
-    ConnContext *context = otrg_plugin_conv_to_context(conv);
+    ConnContext *context = otrg_plugin_conv_to_selected_context(conv, 1);
+
+    ConvOrContext *convctx;
+
+    GHashTable * conv_or_ctx_map = purple_conversation_get_data(conv,
+	    "otr-convorctx");
 
     int pos = otr_get_menu_insert_pos(conv);
 
@@ -1956,12 +2137,22 @@ static void otr_add_top_otr_menu(PurpleConversation *conv) {
 
     topmenuitem = gtk_menu_item_new_with_label ( "OTR" );
     topmenu = gtk_menu_new();
-        
+
     if (context != NULL) {
-        level = otrg_plugin_context_to_trust(context);
+	level = otrg_plugin_context_to_trust(context);
+    }
+
+    convctx = g_hash_table_lookup(conv_or_ctx_map, conv);
+
+    if (!convctx) {
+	convctx = malloc(sizeof(ConvOrContext));
+	g_hash_table_insert(conv_or_ctx_map, conv, (gpointer)convctx);
     }
-    
-    build_otr_menu(conv, topmenu, level);
+
+    convctx->convctx_type = convctx_conv;
+    convctx->conv = conv;
+    build_otr_menu(convctx, topmenu, level);
+    otr_build_status_submenu(win, convctx, topmenu, level);
 
     gtk_menu_item_set_submenu ( GTK_MENU_ITEM ( topmenuitem ), topmenu );
 
@@ -1970,6 +2161,8 @@ static void otr_add_top_otr_menu(PurpleConversation *conv) {
 
     gtk_menu_shell_insert ( GTK_MENU_SHELL ( menu_bar ), topmenuitem, pos++ );
 
+    g_signal_connect(G_OBJECT(topmenuitem), "destroy",
+	    G_CALLBACK(otr_menu_destroy), win);
 
     menu_list = g_list_append(menu_list, topmenuitem);
 
@@ -1981,175 +2174,642 @@ static GList* otr_get_full_buddy_list(PurpleConversation *conv) {
 
     GList *pres_list = NULL;
     GList *conv_list = NULL;
-    
+
     GSList *l, *buds;
 
     /* This code is derived from pidgin's 'generating sendto menu' stuff */
     if ( gtkconv->active_conv->type == PURPLE_CONV_TYPE_IM ) {
-        buds = purple_find_buddies ( gtkconv->active_conv->account, gtkconv->active_conv->name );
-        
-        if ( buds == NULL) {  /* buddy not on list */
-            conv_list = g_list_prepend ( conv_list, conv);
-        } else  {
-            for ( l = buds; l != NULL; l = l->next ) {
-                PurpleBlistNode *node = ( PurpleBlistNode * ) purple_buddy_get_contact ( ( PurpleBuddy * ) l->data );
-
-                for ( node = node->child; node != NULL; node = node->next ) {
-                    PurpleBuddy *buddy = ( PurpleBuddy * ) node;
-                    PurpleAccount *account;
-
-                    if ( !PURPLE_BLIST_NODE_IS_BUDDY ( node ) )
-                        continue;
-
-                    account = purple_buddy_get_account ( buddy );
-                    if ( purple_account_is_connected ( account ) ) {
-                        /* Use the PurplePresence to get unique buddies. */
-                        PurplePresence *presence = purple_buddy_get_presence ( buddy );
-                        if ( !g_list_find ( pres_list, presence ) ) {
-                            
-                            PurpleConversation * currentConv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, \
-                                purple_buddy_get_name ( buddy ), purple_buddy_get_account ( buddy ));
-                            
-                            pres_list = g_list_prepend ( pres_list, presence );
-                            
-                            if (currentConv != NULL) {
-                                conv_list = g_list_prepend ( conv_list, currentConv);    
-                            }
-                            
-                        }
-                    }
-                }
-            }
-            
-            g_slist_free ( buds );
-            g_list_free( pres_list );
-        }
-    }
-    
+	buds = purple_find_buddies ( gtkconv->active_conv->account,
+		gtkconv->active_conv->name );
+
+	if ( buds == NULL) {  /* buddy not on list */
+	    conv_list = g_list_prepend ( conv_list, conv);
+	} else  {
+	    for ( l = buds; l != NULL; l = l->next ) {
+		PurpleBlistNode *node = ( PurpleBlistNode * )
+			purple_buddy_get_contact ( ( PurpleBuddy * ) l->data );
+
+		for ( node = node->child; node != NULL; node = node->next ) {
+		    PurpleBuddy *buddy = ( PurpleBuddy * ) node;
+		    PurpleAccount *account;
+
+		    if ( !PURPLE_BLIST_NODE_IS_BUDDY ( node ) )
+			continue;
+
+		    account = purple_buddy_get_account ( buddy );
+		    if ( purple_account_is_connected ( account ) ) {
+			/* Use the PurplePresence to get unique buddies. */
+			PurplePresence *presence =
+				purple_buddy_get_presence( buddy );
+			if ( !g_list_find ( pres_list, presence ) ) {
+
+			    PurpleConversation * currentConv =
+				    purple_find_conversation_with_account(
+				    PURPLE_CONV_TYPE_IM, \
+				    purple_buddy_get_name ( buddy ),
+				    purple_buddy_get_account ( buddy ));
+
+			    pres_list = g_list_prepend ( pres_list, presence );
+
+			    if (currentConv != NULL) {
+				conv_list = g_list_prepend ( conv_list,
+					currentConv );
+			    }
+
+			}
+		    }
+		}
+	    }
+
+	    g_slist_free ( buds );
+	    g_list_free( pres_list );
+	}
+    }
+
     return conv_list;
 }
 
-static void otr_add_buddy_top_menus(PurpleConversation *conv) {
-    PidginConversation *gtkconv = PIDGIN_CONVERSATION ( conv );
-    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
-    
-    PurpleConversation * currentConv;
+static void unselect_meta_ctx(PurpleConversation *conv) {
+    GtkWidget *select_best = (GtkWidget *) purple_conversation_get_data(conv,
+	    "otr-select_best");
+    GtkWidget *select_recent = (GtkWidget *) purple_conversation_get_data(conv,
+	    "otr-select_recent");
+
+    GTK_CHECK_MENU_ITEM(select_recent)->active = 0;
+    GTK_CHECK_MENU_ITEM(select_best)->active = 0;
+}
+
+static void select_meta_ctx(GtkWidget *widget, gpointer data) {
+    PurpleConversation *conv = (PurpleConversation *) data;
+    GtkWidget *select_best = (GtkWidget *) purple_conversation_get_data(conv,
+	    "otr-select_best");
+    GtkWidget *select_recent = (GtkWidget *) purple_conversation_get_data(conv,
+	    "otr-select_recent");
+    gboolean value = gtk_check_menu_item_get_active(
+	    GTK_CHECK_MENU_ITEM(widget));
+    otrl_instag_t selected_instance = (otrl_instag_t)
+	    purple_conversation_get_data(conv, "otr-ui_selected_ctx");
+    ConnContext * context = NULL;
+    ConnContext * recent_context = NULL;
+
+    if (widget == select_best) {
+	GTK_CHECK_MENU_ITEM(select_recent)->active = !value;
+
+	if (value) {
+	    selected_instance = OTRL_INSTAG_BEST;
+	    purple_conversation_set_data(conv, "otr-ui_selected_ctx",
+		    (gpointer)selected_instance);
+	    context = (ConnContext *) otrg_plugin_conv_to_selected_context(conv,
+		    1);
+
+	    recent_context = (ConnContext *) otrg_plugin_conv_to_context(conv,
+		    OTRL_INSTAG_RECENT_RECEIVED, 0);
+	    if (context != recent_context) {
+		gchar *buf = g_strdup_printf(_("Warning: The selected outgoing "
+			"OTR session %u (%x) is not the most recently active "
+			"one %u (%x). Your buddy may not receive your messages."
+			" Use the icon menu above to select a different "
+			"outgoing session."), get_context_instance_to_index(
+			conv, context), context->their_instance,
+			get_context_instance_to_index(conv, recent_context),
+			recent_context->their_instance);
+		otrg_gtk_dialog_display_otr_message(context->accountname,
+			context->protocol, context->username, buf, 0);
+		g_free(buf);
+	    }
 
+	}
+
+    } else if (widget == select_recent) {
+	GTK_CHECK_MENU_ITEM(select_best)->active = !value;
+
+	if (value) {
+	    selected_instance = OTRL_INSTAG_RECENT_RECEIVED;
+	    purple_conversation_set_data(conv, "otr-ui_selected_ctx",
+		    (gpointer)selected_instance);
+	}
+    }
+
+    if (!context) context = (ConnContext *)
+	    otrg_plugin_conv_to_selected_context(conv, 1);
+    dialog_update_label(context);
+}
+
+static void select_menu_ctx(GtkWidget *widget, gpointer data) {
+    ConnContext *context = (ConnContext *) data;
+    PurpleConversation * conv = otrg_plugin_context_to_conv(context, 1);
+    ConnContext *recent_context = (ConnContext *) otrg_plugin_conv_to_context(
+	    conv, (otrl_instag_t)OTRL_INSTAG_RECENT_RECEIVED, 0);
+    otrl_instag_t selected_instance = (otrl_instag_t)
+	    purple_conversation_get_data(conv, "otr-ui_selected_ctx");
+
+    selected_instance = context->their_instance;
+    purple_conversation_set_data(conv, "otr-ui_selected_ctx",
+	    (gpointer)selected_instance);
+
+    unselect_meta_ctx(conv);
+
+    pidgin_conv_switch_active_conversation(conv);
+    dialog_update_label(context);
+
+    if (context != recent_context) {
+	gchar *buf = g_strdup_printf(_("Warning: The selected outgoing OTR "
+		"session %u (%x) is not the most recently active one %u (%x). "
+		"Your buddy may not receive your messages. Use the icon menu "
+		"above to select a different outgoing session."),
+		get_context_instance_to_index(conv, context),
+		context->their_instance,
+		get_context_instance_to_index(conv, recent_context),
+		recent_context->their_instance);
+	otrg_gtk_dialog_display_otr_message(context->accountname,
+		context->protocol, context->username, buf, 0);
+	g_free(buf);
+    }
+}
+
+static void build_meta_instance_submenu( PurpleConversation *conv,
+	GtkWidget *menu) {
+    GtkWidget *menusep = gtk_separator_menu_item_new();
+    GtkWidget *select_best = gtk_check_menu_item_new_with_label(
+	    _("Send to most secure"));
+    GtkWidget *select_recent = gtk_check_menu_item_new_with_label(
+	    _("Send to most recent"));
+    otrl_instag_t selected_instance;
+    gboolean selected_existed = g_hash_table_lookup_extended(conv->data,
+	    "otr-ui_selected_ctx", NULL, (void**)&selected_instance);
+
+    if (selected_existed) {
+	if (selected_instance == OTRL_INSTAG_BEST) {
+	    GTK_CHECK_MENU_ITEM(select_recent)->active = 0;
+	    GTK_CHECK_MENU_ITEM(select_best)->active = 1;
+	} else if (selected_instance == OTRL_INSTAG_RECENT_RECEIVED) {
+	    GTK_CHECK_MENU_ITEM(select_recent)->active = 1;
+	    GTK_CHECK_MENU_ITEM(select_best)->active = 0;
+	} else {
+	    GTK_CHECK_MENU_ITEM(select_recent)->active = 0;
+	    GTK_CHECK_MENU_ITEM(select_best)->active = 0;
+	}
+    } else {
+	GTK_CHECK_MENU_ITEM(select_recent)->active = 0;
+	GTK_CHECK_MENU_ITEM(select_best)->active = 1;
+    }
+
+    purple_conversation_set_data(conv, "otr-select_best", select_best);
+    purple_conversation_set_data(conv, "otr-select_recent", select_recent);
+
+    gtk_signal_connect(GTK_OBJECT(select_best), "toggled",
+	    GTK_SIGNAL_FUNC(select_meta_ctx), conv);
+    gtk_signal_connect(GTK_OBJECT(select_recent), "toggled",
+	    GTK_SIGNAL_FUNC(select_meta_ctx), conv);
+
+    gtk_widget_show(menusep);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menusep);
+    gtk_widget_show(select_best);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_best);
+    gtk_widget_show(select_recent);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_recent);
+}
+
+static void otr_add_buddy_instances_top_menu(PidginConversation *gtkconv, GList
+		*instances, gboolean active_conv, char *username, const char
+		*accountname, int *pos) {
+    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
     GtkWidget *menu_bar = win->menu.menubar;
+    GtkWidget *menu;
+    GtkWidget *menu_image;
+    GtkWidget * tooltip_menu;
+    gchar *tooltip_text;
+    PurpleAccount *account;
+    otrl_instag_t instance;
+    gboolean selection_exists = 0;
+    ConnContext * context = instances->data;
+    TrustLevel level = TRUST_NOT_PRIVATE;
+    GHashTable * conv_or_ctx_map;
+    PurpleConversation * conv = NULL;
+    ConvOrContext convctx;
+    GList * menu_list;
+    guint num_active_instances = g_list_length(instances);
+
+    menu = gtk_menu_new();
+
+    conv = otrg_plugin_context_to_conv(context, 0);
+    selection_exists = g_hash_table_lookup_extended(conv->data,
+	    "otr-ui_selected_ctx", NULL, (void**)&instance);
+
+    /* Find the selected or default instance */
+    if (selection_exists) {
+	context = otrl_context_find(otrg_plugin_userstate,
+		context->username, context->accountname, context->protocol,
+		instance, 0, NULL, NULL, NULL);
+    } else {
+	context = otrl_context_find(otrg_plugin_userstate,
+		context->username, context->accountname, context->protocol,
+		OTRL_INSTAG_BEST, 0, NULL, NULL, NULL);
+    }
+
+    menu = gtk_menu_new();
+
+    conv_or_ctx_map = purple_conversation_get_data(conv, "otr-convorctx");
+
+    for (; instances; instances = instances->next) {
+	GtkWidget *instance_menu_item;
+	GtkWidget *instance_submenu;
+	gchar text[35] ;
+	ConnContext *curr_context = instances->data;
+	ConvOrContext * curr_convctx = g_hash_table_lookup(conv_or_ctx_map,
+		curr_context);
+	gboolean selected = (curr_context->their_instance ==
+		context->their_instance);
+	gint instance_i = -1;
+
+	if (curr_context->their_instance == OTRL_INSTAG_MASTER &&
+		curr_context->msgstate == OTRL_MSGSTATE_PLAINTEXT) {
+	    continue;
+	}
+
+	if (!curr_convctx) {
+	    curr_convctx = malloc(sizeof(ConvOrContext));
+	    g_hash_table_insert(conv_or_ctx_map, curr_context,
+		    (gpointer)curr_convctx);
+	    curr_convctx->convctx_type = convctx_ctx;
+	    curr_convctx->context = curr_context;
+	}
+
+
+	instance_i = get_context_instance_to_index(conv, curr_context);
+
+	g_snprintf(text, 35, _("Session %u (%x)"), instance_i,
+		curr_context->their_instance);
+
+	instance_menu_item = gtk_image_menu_item_new_with_label(text);
+	instance_submenu = gtk_menu_new();
+
+	level = otrg_plugin_context_to_trust(curr_context);
+	menu_image = otr_icon(NULL, level, selected);
+
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(instance_menu_item),
+		menu_image);
+	gtk_image_menu_item_set_always_show_image(
+		GTK_IMAGE_MENU_ITEM(instance_menu_item), 1);
+
+	build_otr_menu(curr_convctx, instance_submenu, level);
+
+	if (!selection_exists || instance != curr_context->their_instance) {
+	    GtkWidget *select_ctx = gtk_menu_item_new_with_label(_("Select"));
+	    GtkWidget *menusep = gtk_separator_menu_item_new();
+
+	    gtk_signal_connect(GTK_OBJECT(select_ctx), "activate",
+		    GTK_SIGNAL_FUNC(select_menu_ctx), curr_context);
+
+	    gtk_menu_shell_prepend(GTK_MENU_SHELL(instance_submenu), menusep);
+	    gtk_widget_show(menusep);
+
+	    gtk_menu_shell_prepend(GTK_MENU_SHELL(instance_submenu),
+		    select_ctx);
+	    gtk_widget_show(select_ctx);
+	} else if (selection_exists && 
+		instance == curr_context->their_instance) {
+	    GtkWidget *selected_ctx =
+		    gtk_menu_item_new_with_label(_("Selected"));
+	    GtkWidget *menusep = gtk_separator_menu_item_new();
+
+	    gtk_signal_connect(GTK_OBJECT(selected_ctx), "select",
+		    GTK_SIGNAL_FUNC(force_deselect), NULL);
+
+	    gtk_menu_shell_prepend(GTK_MENU_SHELL(instance_submenu), menusep);
+	    gtk_widget_show(menusep);
+
+	    gtk_menu_shell_prepend(GTK_MENU_SHELL(instance_submenu),
+		    selected_ctx);
+	    gtk_widget_show(selected_ctx);
+	}
+
+	gtk_widget_show(menu_image);
+	gtk_widget_show(instance_menu_item);
+	gtk_widget_show(instance_submenu);
+	gtk_menu_item_set_submenu (GTK_MENU_ITEM (instance_menu_item),
+		instance_submenu);
+
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), instance_menu_item);
+
+    }
+
+
+    level = otrg_plugin_context_to_trust(context);
+    menu_image = otr_icon(NULL, level, active_conv);
+    convctx.convctx_type = convctx_ctx;
+    convctx.context = context;
+
+    build_meta_instance_submenu(conv, menu);
+
+    otr_build_status_submenu(win, &convctx, menu, level);
+
+    tooltip_menu = tooltip_menu_new();
+
+    gtk_widget_show ( menu_image );
+    gtk_widget_show(tooltip_menu);
+    gtk_menu_shell_insert ( GTK_MENU_SHELL(menu_bar), tooltip_menu, (*pos)++);
+    gtk_menu_item_set_submenu ( GTK_MENU_ITEM(tooltip_menu), menu);
+
+    tooltip_text = g_strdup_printf("%s (%s)", username, accountname);
+    tooltip_menu_prepend(TOOLTIP_MENU(tooltip_menu), menu_image, tooltip_text);
+    g_free(tooltip_text);
+
+    menu_list = g_hash_table_lookup ( otr_win_menus, win );
+
+    g_signal_connect(G_OBJECT(tooltip_menu), "destroy",
+	    G_CALLBACK(otr_menu_destroy), win);
+
+    menu_list = g_list_append(menu_list, tooltip_menu);
+
+    g_hash_table_replace ( otr_win_menus, win, menu_list );
+}
 
+static void otr_add_buddy_top_menu(PidginConversation *gtkconv, ConvOrContext
+	*convctx, gboolean active_conv, char *username, const char
+	*accountname, int *pos) {
+    PidginWindow *win = pidgin_conv_get_window ( gtkconv );
+    GtkWidget *menu_bar = win->menu.menubar;
     GtkWidget *menu;
     GtkWidget *menu_image;
+    TrustLevel level;
+    ConnContext *context;
+    GList * menu_list;
+    GtkWidget * tooltip_menu;
+    gchar *tooltip_text;
+    PurpleAccount *account;
+    GtkWidget *select_ctx = NULL;
+
+    if (convctx->convctx_type == convctx_ctx) {
+	context = convctx->context;
+    } else if (convctx->convctx_type == convctx_conv) {
+	context = otrg_plugin_conv_to_selected_context(convctx->conv, 0);
+    }
+
+    level = TRUST_NOT_PRIVATE;
+
+    if (context != NULL) {
+	level = otrg_plugin_context_to_trust(context);
+    }
+
+    menu_image = otr_icon(NULL, level, active_conv);
+
+    menu = gtk_menu_new();
+
+    build_otr_menu(convctx, menu, level);
+    otr_build_status_submenu(win, convctx, menu, level);
+
+    if (!active_conv) {
+	select_ctx = gtk_menu_item_new_with_label(_("Select"));
+
+	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), select_ctx);
+	gtk_widget_show(select_ctx);
+
+	gtk_signal_connect(GTK_OBJECT(select_ctx), "activate",
+	GTK_SIGNAL_FUNC(select_menu_ctx), context);
+    }
+
+    tooltip_menu = tooltip_menu_new();
+
+    gtk_widget_show ( menu_image );
+    gtk_widget_show(tooltip_menu);
+    gtk_menu_shell_insert ( GTK_MENU_SHELL(menu_bar), tooltip_menu, (*pos)++);
+    gtk_menu_item_set_submenu ( GTK_MENU_ITEM ( tooltip_menu ), menu );
+
+    tooltip_text = g_strdup_printf("%s (%s)", username, accountname);
+    tooltip_menu_prepend(TOOLTIP_MENU(tooltip_menu), menu_image, tooltip_text);
+    g_free(tooltip_text);
+
+    menu_list = g_hash_table_lookup ( otr_win_menus, win );
+
+    g_signal_connect(G_OBJECT(tooltip_menu), "destroy",
+	    G_CALLBACK(otr_menu_destroy), win);
+
+    menu_list = g_list_append(menu_list, tooltip_menu);
+
+    g_hash_table_replace ( otr_win_menus, win, menu_list );
+}
+
+static void otr_add_buddy_top_menus(PurpleConversation *conv) {
+    PidginConversation *gtkconv = PIDGIN_CONVERSATION ( conv );
+
+    PurpleConversation * currentConv = NULL; /* Auxiliary variables re-used */
+    ConnContext *currentContext = NULL;      /* within loops. */
 
     GList *full_buddy_list = NULL;
-    GList * list_iter;
-    
-    int pos;
-    
+    GList *list_iter;
+
+    int pos = otr_get_menu_insert_pos(conv);
+
+
+    GHashTable *conv_to_context_map = g_hash_table_new(g_direct_hash,
+	    g_direct_equal);
+
+    GHashTable * conv_or_ctx_map = purple_conversation_get_data(conv,
+	    "otr-convorctx");
+
     full_buddy_list = otr_get_full_buddy_list(conv);
 
     list_iter = full_buddy_list;
 
-    pos = otr_get_menu_insert_pos(conv);
-
-    for (list_iter = g_list_last ( full_buddy_list ); list_iter != NULL; list_iter = list_iter->prev) {
-        TrustLevel level;
-        ConnContext *context;
-        GList * menu_list;
-        GtkWidget * tooltip_menu;
-        gchar *tooltip_text;
-        
-        currentConv = list_iter->data;
-
-        if (currentConv == NULL) {
-            continue;       
-        }
-
-        if (purple_conversation_get_type(currentConv) != PURPLE_CONV_TYPE_IM) continue;
-
-        level = TRUST_NOT_PRIVATE;
-        context = otrg_plugin_conv_to_context(currentConv);
-        
-        if (context != NULL) {
-            level = otrg_plugin_context_to_trust(context);
-        }
-
-        menu_image = otr_icon(NULL, level, 1);
-
-        if (currentConv == gtkconv->active_conv) {
-            menu_image = otr_icon(menu_image, level, 1);
-        } else {
-            menu_image = otr_icon(menu_image, level, 0);
-        }
-        menu = gtk_menu_new();
-            
-	build_otr_menu(currentConv, menu, level);
-        
-        tooltip_menu = tooltip_menu_new();
-        
-        gtk_widget_show ( menu_image );
-        gtk_widget_show(tooltip_menu);
-        gtk_menu_shell_insert ( GTK_MENU_SHELL ( menu_bar ), tooltip_menu, pos++ );
-        gtk_menu_item_set_submenu ( GTK_MENU_ITEM ( tooltip_menu ), menu );
-        
-        tooltip_text = g_strdup_printf("%s (%s)", currentConv->name, purple_account_get_username(currentConv->account));
-        tooltip_menu_prepend(TOOLTIP_MENU(tooltip_menu), menu_image, tooltip_text);
-        g_free(tooltip_text);
-        
-        menu_list = g_hash_table_lookup ( otr_win_menus, win );
-        menu_list = g_list_append(menu_list, tooltip_menu);
-        
-        g_hash_table_replace ( otr_win_menus, win, menu_list );
-        
-
-    }
-    
+    /* First determine how many contexts are associated with each conv */
+    for (list_iter = g_list_last ( full_buddy_list ); list_iter != NULL;
+	    list_iter = list_iter->prev) {
+	int numContexts = 0;
+	PurpleAccount *account;
+	char *username;
+	const char *accountname, *proto;
+
+	currentConv = list_iter->data;
+
+	if (currentConv == NULL) {
+	    continue;
+	}
+
+	if (purple_conversation_get_type(currentConv) != PURPLE_CONV_TYPE_IM) {
+	    continue;
+	}
+
+	account = purple_conversation_get_account(currentConv);
+	accountname = purple_account_get_username(account);
+	proto = purple_account_get_protocol_id(account);
+	username = g_strdup(purple_normalize(account,
+		purple_conversation_get_name(currentConv)));
+	GList * contexts = NULL;
+
+	for (currentContext = otrg_plugin_userstate->context_root;
+		currentContext != NULL;
+		currentContext = currentContext->next) {
+
+	    if (!strcmp(currentContext->accountname, accountname) &&
+		    !strcmp(currentContext->protocol, proto) &&
+		    !strcmp(currentContext->username, username)) {
+		contexts = g_list_append(contexts, currentContext);
+	    }
+	}
+
+	g_hash_table_insert(conv_to_context_map,
+		currentConv, (gpointer) contexts);
+
+    }
+
+    list_iter = full_buddy_list;
+
+    for (list_iter = g_list_last ( full_buddy_list ); list_iter != NULL;
+	    list_iter = list_iter->prev) {
+	GList * contexts = NULL;
+	GList * contexts_iter = NULL;
+	gboolean active_conv = 0;
+	ConvOrContext * convctx = NULL;
+	ConnContext * m_context = NULL;
+	PurpleAccount * account = NULL;
+	char * username = NULL;
+	const char * accountname = NULL;
+	int num_contexts = 0;
+
+	currentConv = list_iter->data;
+
+	if (currentConv == NULL) {
+	    continue;
+	}
+
+	active_conv = (currentConv == gtkconv->active_conv);
+
+	contexts = (GList *) g_hash_table_lookup(conv_to_context_map,
+		currentConv);
+
+	if (purple_conversation_get_type(currentConv) != PURPLE_CONV_TYPE_IM) {
+	    continue;
+	}
+
+	num_contexts = g_list_length(contexts);
+
+	if (num_contexts > 1) {
+	    /* We will need the master context */
+	    currentContext = (ConnContext *) contexts->data;
+
+	    m_context = currentContext->m_context;
+	}
+
+	if (num_contexts <= 1) {
+	    /* Just add a menu for the possibly not yet created master inst */
+	    convctx = g_hash_table_lookup(conv_or_ctx_map, currentConv);
+
+	    if (!convctx) {
+		convctx = malloc(sizeof(ConvOrContext));
+		g_hash_table_insert(conv_or_ctx_map, currentConv,
+			(gpointer)convctx);
+		convctx->convctx_type = convctx_conv;
+		convctx->conv = currentConv;
+	    }
+
+	    account = purple_conversation_get_account(currentConv);
+	    accountname = purple_account_get_username(account);
+	    username = g_strdup(
+		    purple_normalize(account,
+			    purple_conversation_get_name(currentConv)));
+
+	    otr_add_buddy_top_menu(gtkconv, convctx, active_conv, username,
+		    accountname, &pos);
+	    g_free(username);
+
+	} else if (num_contexts == 2 &&
+		m_context->msgstate == OTRL_MSGSTATE_PLAINTEXT) {
+	    /* Just add a menu for the non_master instance */
+	    contexts_iter = contexts;
+	    currentContext = contexts_iter->data;
+
+	    while (currentContext->their_instance == OTRL_INSTAG_MASTER &&
+		    contexts_iter->next != NULL) {
+		contexts_iter = contexts_iter->next;
+		currentContext = contexts_iter->data;
+	    }
+
+	    convctx = g_hash_table_lookup(conv_or_ctx_map, currentContext);
+
+	    if (!convctx) {
+		convctx = malloc(sizeof(ConvOrContext));
+		g_hash_table_insert(conv_or_ctx_map, currentContext,
+			(gpointer)convctx);
+		convctx->convctx_type = convctx_ctx;
+		convctx->context = currentContext;
+	    }
+
+	    otr_add_buddy_top_menu(gtkconv, convctx, active_conv,
+		    currentContext->username, currentContext->accountname,
+		    &pos);
+
+	} else {
+	    /* Multi-instances */
+	    purple_conversation_set_data(currentConv,
+		    "otr-conv_multi_instances", (gpointer)1);
+	    otr_add_buddy_instances_top_menu(gtkconv, contexts, active_conv,
+		    currentContext->username, currentContext->accountname,
+		    &pos);
+	}
+
+	if (contexts) {
+	    g_list_free(contexts);
+	}
+    }
+
+    g_hash_table_destroy (conv_to_context_map);
     g_list_free ( full_buddy_list );
 
 }
 
+
 static void otr_check_conv_status_change( PurpleConversation *conv) {
     PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
     TrustLevel current_level = TRUST_NOT_PRIVATE;
-    ConnContext *context = otrg_plugin_conv_to_context(conv);
-    
+    ConnContext *context = otrg_plugin_conv_to_context(conv,
+	    OTRL_INSTAG_RECENT, 0);
+    otrl_instag_t last_received_instance;
+    gboolean have_received = 0;
+
     int *previous_level;
     char *buf;
     char *status = "";
-    
+
     if (context != NULL) {
-        current_level = otrg_plugin_context_to_trust(context);
+	current_level = otrg_plugin_context_to_trust(context);
     }
-    
-    previous_level = g_hash_table_lookup ( otr_win_status, gtkconv );    
-    
-    if (!previous_level || (previous_level && *previous_level == current_level)) {
-        return;
+
+    previous_level = g_hash_table_lookup ( otr_win_status, gtkconv );
+
+    if (!previous_level ||
+	    (previous_level && *previous_level == current_level)) {
+	return;
     }
-    
-    buf = _("The privacy status of the current conversation is now: <a href=\"%s%s\">%s</a>");
-    
+
+    buf = _("The privacy status of the current conversation is now: "
+	    "<a href=\"%s%s\">%s</a>");
+
     switch(current_level) {
-        case TRUST_NOT_PRIVATE:
-            status = _("Not Private");
-            break;
-        case TRUST_UNVERIFIED:
-            status = _("Unverified");
-            break;
-        case TRUST_PRIVATE:
-            status = _("Private");
-            break;
-        case TRUST_FINISHED:
-            status = _("Finished");
-            break;
-    }
-    
+	case TRUST_NOT_PRIVATE:
+	    status = _("Not Private");
+	    break;
+	case TRUST_UNVERIFIED:
+	    status = _("Unverified");
+	    break;
+	case TRUST_PRIVATE:
+	    status = _("Private");
+	    break;
+	case TRUST_FINISHED:
+	    status = _("Finished");
+	    break;
+    }
+
     buf = g_strdup_printf(buf, LEVELS_HELPURL, _("?lang=en"), status);
-    
-    /* Write a new message indicating the level change. The timestamp image will be appended as the message
-       timestamp signal is caught, which will also update the privacy level for this gtkconv */
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
-    
+
+    /* Write a new message indicating the level change. The timestamp image will
+     * be appended as the message timestamp signal is caught, which will also
+     * update the privacy level for this gtkconv */
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
+
     g_free(buf);
+
 }
 
 /* If the conversation switches on us */
@@ -2164,13 +2824,23 @@ static void conversation_switched ( PurpleConversation *conv, void * data ) {
  * pointing to it. */
 static void conversation_destroyed(PurpleConversation *conv, void *data)
 {
-    GtkWidget *menu = purple_conversation_get_data(conv, "otr-menu");
+    GtkWidget *menu = (GtkWidget *) purple_conversation_get_data(conv,
+	    "otr-menu");
     PidginConversation *gtkconv;
     PidginWindow *win;
-    GList * menu_list;
     GList * iter;
-    
+    GHashTable * conv_or_ctx_map;
+    GHashTable * conv_to_idx_map;
+
+
     if (menu) gtk_object_destroy(GTK_OBJECT(menu));
+
+    conv_or_ctx_map = purple_conversation_get_data(conv, "otr-convorctx");
+    g_hash_table_destroy(conv_or_ctx_map);
+
+    conv_to_idx_map = purple_conversation_get_data(conv, "otr-conv_to_idx");
+    g_hash_table_destroy(conv_to_idx_map);
+
     g_hash_table_remove(conv->data, "otr-label");
     g_hash_table_remove(conv->data, "otr-button");
     g_hash_table_remove(conv->data, "otr-icon");
@@ -2178,6 +2848,16 @@ static void conversation_destroyed(PurpleConversation *conv, void *data)
     g_hash_table_remove(conv->data, "otr-private");
     g_hash_table_remove(conv->data, "otr-authenticated");
     g_hash_table_remove(conv->data, "otr-finished");
+    g_hash_table_remove(conv->data, "otr-select_best");
+    g_hash_table_remove(conv->data, "otr-select_recent");
+    g_hash_table_remove(conv->data, "otr-convorctx");
+    g_hash_table_remove(conv->data, "otr-conv_to_idx");
+    g_hash_table_remove(conv->data, "otr-max_idx");
+    g_hash_table_remove(conv->data, "otr-conv_multi_instances");
+    g_hash_table_remove(conv->data, "otr-warned_instances");
+    g_hash_table_remove(conv->data, "otr-last_msg_event");
+    g_hash_table_remove(conv->data, "otr-last_received_ctx");
+
 #ifdef OLD_OTR_BUTTON
     g_hash_table_remove(conv->data, "otr-icontext");
     g_hash_table_remove(conv->data, "otr-menuquery");
@@ -2197,19 +2877,11 @@ static void conversation_destroyed(PurpleConversation *conv, void *data)
     }
 
     win = pidgin_conv_get_window ( gtkconv );
-    menu_list = g_hash_table_lookup ( otr_win_menus, win );
-    iter = menu_list;
 
-    while ( iter ) {
-        GList * next;
-        if ( iter->data ) gtk_object_destroy ( GTK_OBJECT ( iter->data ) );
-        next = iter->next;
-        menu_list = g_list_remove ( menu_list, iter->data );
-        iter = next;
-    }
+    otr_clear_win_menu_list(win);
 
     g_hash_table_remove(otr_win_menus, win);
-    g_list_free(menu_list);
+
 }
 
 /* Set up the per-conversation information display */
@@ -2234,6 +2906,8 @@ static void otrg_gtk_dialog_new_purple_conv(PurpleConversation *conv)
     */
     GtkWidget *menusmp;
     GtkWidget *whatsthis;
+#else
+    ConvOrContext *convctx;
 #endif
     GtkWidget *icon;
     GtkWidget *menu;
@@ -2242,6 +2916,9 @@ static void otrg_gtk_dialog_new_purple_conv(PurpleConversation *conv)
     const char *name;
     OtrgUiPrefs prefs;
 
+    GHashTable * conv_or_ctx_map;
+    GHashTable * ctx_to_idx_map;
+
     /* Do nothing if this isn't an IM conversation */
     if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM) return;
 
@@ -2256,7 +2933,7 @@ static void otrg_gtk_dialog_new_purple_conv(PurpleConversation *conv)
     bbox = gtkconv->toolbar;
 #endif
 
-    context = otrg_plugin_conv_to_context(conv);
+    context = otrg_plugin_conv_to_selected_context(conv, 0);
 
     /* See if we're already set up */
     button = purple_conversation_get_data(conv, "otr-button");
@@ -2279,6 +2956,15 @@ static void otrg_gtk_dialog_new_purple_conv(PurpleConversation *conv)
 	return;
     }
 
+    conv_or_ctx_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+	    free);
+    purple_conversation_set_data(conv, "otr-convorctx", conv_or_ctx_map);
+
+    ctx_to_idx_map = g_hash_table_new(g_direct_hash, g_direct_equal);
+    purple_conversation_set_data(conv, "otr-conv_to_idx", ctx_to_idx_map);
+
+    purple_conversation_set_data(conv, "otr-max_idx", (gpointer) 0);
+
     /* Make the button */
     button = gtk_button_new();
 
@@ -2362,7 +3048,13 @@ static void otrg_gtk_dialog_new_purple_conv(PurpleConversation *conv)
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), whatsthis);
     gtk_widget_show(whatsthis);
 #else
-    build_otr_menu(conv, menu, TRUST_NOT_PRIVATE);
+    convctx = malloc(sizeof(ConvOrContext));
+    convctx->convctx_type = convctx_conv;
+    convctx->conv = conv;
+    g_hash_table_replace ( conv_or_ctx_map, conv, convctx );
+    build_otr_menu(convctx, menu, TRUST_NOT_PRIVATE);
+    otr_build_status_submenu(pidgin_conv_get_window(gtkconv), convctx, menu,
+	    TRUST_NOT_PRIVATE);
 #endif
 
     purple_conversation_set_data(conv, "otr-label", label);
@@ -2449,7 +3141,7 @@ static void dialog_resensitize(PurpleConversation *conv)
     if (prefs.policy == OTRL_POLICY_NEVER) {
 	otrg_gtk_dialog_remove_conv(conv);
     } else {
-        otrg_gtk_dialog_new_conv(conv);
+	otrg_gtk_dialog_new_conv(conv);
     }
     button = purple_conversation_get_data(conv, "otr-button");
     if (!button) return;
@@ -2457,7 +3149,7 @@ static void dialog_resensitize(PurpleConversation *conv)
 	connection = purple_account_get_connection(account);
 	if (connection) {
 	    /* Set the button to "sensitive" */
-	    gtk_widget_set_sensitive(button, 1);   
+	    gtk_widget_set_sensitive(button, 1);
 	    return;
 	}
     }
@@ -2473,86 +3165,124 @@ static void otrg_gtk_dialog_resensitize_all(void)
 }
 
 static void foreach_free_lists(void * key, void * value, void* data) {
-    GList * menu_list = (GList *) value;
-    GList * iter = menu_list;
-
-    while ( iter ) {
-        GList * next;
-        if ( iter->data ) gtk_object_destroy ( GTK_OBJECT ( iter->data ) );
-        next = iter->next;
-        menu_list = g_list_remove ( menu_list, iter->data );
-        iter = next;
-    }
+    PidginWindow *win = (PidginWindow *) key;
 
-    g_list_free(menu_list);
+    otr_clear_win_menu_list(win);
 }
 
+
+
 static char* conversation_timestamp(PurpleConversation *conv, time_t mtime,
-                                gboolean show_date) {
+	gboolean show_date) {
 
     PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
     TrustLevel current_level = TRUST_NOT_PRIVATE;
-    ConnContext *context = otrg_plugin_conv_to_context(conv);
-    
+    ConnContext *context = (ConnContext *) otrg_plugin_conv_to_context(conv,
+	    OTRL_INSTAG_RECENT, 0);
     int *previous_level;
     int id;
-    
-    
+
+
     if (context != NULL) {
-        current_level = otrg_plugin_context_to_trust(context);
+	current_level = otrg_plugin_context_to_trust(context);
     }
-    
-    previous_level = g_hash_table_lookup ( otr_win_status, gtkconv );    
-    
-    
+
+    previous_level = g_hash_table_lookup ( otr_win_status, gtkconv );
+
+
     if (previous_level && *previous_level == current_level) {
-        return NULL;
+	return NULL;
     }
-    
-    /* We want to update this gtkconv's privacy level only if the new privacy level we 
-       received corresponds to the active conversation.  */
+
+    /* We want to update this gtkconv's privacy level only if the new privacy
+     * level we received corresponds to the active conversation.  */
     if (conv == gtkconv->active_conv) {
-        int * current_level_ptr = malloc(sizeof(int));  /* 'free' is handled by the hashtable */
-        *current_level_ptr = current_level;
-        g_hash_table_replace ( otr_win_status, gtkconv, current_level_ptr );
+	/* 'free' is handled by the hashtable */
+	int * current_level_ptr = malloc(sizeof(int));
+	*current_level_ptr = current_level;
+	g_hash_table_replace ( otr_win_status, gtkconv, current_level_ptr );
     }
-    
+
     if (!previous_level) {
-        return NULL;
+	return NULL;
     }
-    
+
     id = -1;
 
     switch(current_level) {
-        case TRUST_NOT_PRIVATE:
-            id = img_id_not_private;
-            break;
-        case TRUST_UNVERIFIED:
-            id = img_id_unverified;
-            break;
-        case TRUST_PRIVATE:
-            id = img_id_private;
-            break;
-        case TRUST_FINISHED:
-            id = img_id_finished;
-            break;
-    }
-    
+	case TRUST_NOT_PRIVATE:
+	    id = img_id_not_private;
+	    break;
+	case TRUST_UNVERIFIED:
+	    id = img_id_unverified;
+	    break;
+	case TRUST_PRIVATE:
+	    id = img_id_private;
+	    break;
+	case TRUST_FINISHED:
+	    id = img_id_finished;
+	    break;
+    }
+
 
     if (id > 0 ) {
-        char * msg = g_strdup_printf("<IMG ID=\"%d\"> ", id);
-        gtk_imhtml_append_text_with_images((GtkIMHtml*)gtkconv->imhtml, msg, 0, NULL);  
-        g_free(msg);  
+	char * msg = g_strdup_printf("<IMG ID=\"%d\"> ", id);
+	gtk_imhtml_append_text_with_images((GtkIMHtml*)gtkconv->imhtml, msg, 0,
+		NULL);
+	g_free(msg);
     }
-    
-    
+
+
     return NULL;
 }
 
+/* If the user has selected a meta instance, an incoming message may trigger an
+ * instance change... we need to update the GUI appropriately */
+static gboolean check_incoming_instance_change(PurpleAccount *account,
+	char *sender, char *message, PurpleConversation *conv,
+	PurpleMessageFlags flags) {
+    otrl_instag_t last_received_instance;
+    otrl_instag_t selected_instance;
+    gboolean have_received = 0;
+    ConnContext *received_context = NULL;
+    ConnContext *current_out = NULL;
+
+
+    if (!conv) {
+	return 0;
+    }
+
+    selected_instance = otrg_plugin_conv_to_selected_instag(conv, 0);
+    current_out = otrg_plugin_conv_to_selected_context(conv, 0);
+
+    have_received = g_hash_table_lookup_extended(conv->data,
+	    "otr-last_received_ctx", NULL, (void**)&last_received_instance);
+    received_context = (ConnContext *) otrg_plugin_conv_to_context(conv,
+	    (otrl_instag_t)OTRL_INSTAG_RECENT_RECEIVED, 0);
+
+    if (!received_context) {
+	return 0;
+    }
+
+    if (have_received &&
+	    last_received_instance != received_context->their_instance &&
+	    selected_instance != OTRL_INSTAG_MASTER &&
+	    selected_instance <= 0xFF) {
+	dialog_update_label_conv(conv,
+		otrg_plugin_context_to_trust(current_out));
+    }
+
+    last_received_instance = received_context->their_instance;
+    purple_conversation_set_data(conv, "otr-last_received_ctx",
+	    (gpointer)last_received_instance);
+
+    return 0;
+}
+
 static void unref_img_by_id(int *id)
 {
     if (id && *id > 0) {
-        purple_imgstore_unref_by_id(*id);
+	purple_imgstore_unref_by_id(*id);
 	*id = -1;
     }
 }
@@ -2573,26 +3303,27 @@ static void dialog_quitting(void)
 static void otrg_gtk_dialog_init(void)
 {
     otr_win_menus = g_hash_table_new(g_direct_hash, g_direct_equal);
-    otr_win_status = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free);
-    
-    
+    otr_win_status = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+	    free);
+
+
     img_id_not_private = purple_imgstore_add_with_id(
 	    g_memdup(not_private_png, sizeof(not_private_png)),
 	    sizeof(not_private_png), "");
-    
+
     img_id_unverified = purple_imgstore_add_with_id(
 	    g_memdup(unverified_png, sizeof(unverified_png)),
 	    sizeof(unverified_png), "");
-    
+
     img_id_private = purple_imgstore_add_with_id(
 	    g_memdup(private_png, sizeof(private_png)),
 	    sizeof(private_png), "");
-    
+
     img_id_finished = purple_imgstore_add_with_id(
 	    g_memdup(finished_png, sizeof(finished_png)),
 	    sizeof(finished_png), "");
 
-    
+
     purple_signal_connect(pidgin_conversations_get_handle(),
 	    "conversation-switched", otrg_plugin_handle,
 	    PURPLE_CALLBACK(conversation_switched), NULL);
@@ -2600,14 +3331,18 @@ static void otrg_gtk_dialog_init(void)
     purple_signal_connect(purple_conversations_get_handle(),
 	    "deleting-conversation", otrg_plugin_handle,
 	    PURPLE_CALLBACK(conversation_destroyed), NULL);
-        
+
     purple_signal_connect(pidgin_conversations_get_handle(),
-        "conversation-timestamp", otrg_plugin_handle,
-        PURPLE_CALLBACK(conversation_timestamp), NULL);
+	    "conversation-timestamp", otrg_plugin_handle,
+	    PURPLE_CALLBACK(conversation_timestamp), NULL);
+
+    purple_signal_connect(purple_conversations_get_handle(),
+	    "received-im-msg", otrg_plugin_handle,
+	    PURPLE_CALLBACK(check_incoming_instance_change), NULL);
 
     purple_signal_connect(purple_get_core(),
-	"quitting", otrg_plugin_handle, PURPLE_CALLBACK(dialog_quitting),
-	NULL);
+	    "quitting", otrg_plugin_handle,
+	    PURPLE_CALLBACK(dialog_quitting), NULL);
 }
 
 /* Deinitialize the OTR dialog subsystem */
@@ -2621,13 +3356,17 @@ static void otrg_gtk_dialog_cleanup(void)
 	    PURPLE_CALLBACK(conversation_switched));
 
     purple_signal_disconnect(pidgin_conversations_get_handle(),
-        "conversation-timestamp", otrg_plugin_handle,
-        PURPLE_CALLBACK(conversation_timestamp));
+	"conversation-timestamp", otrg_plugin_handle,
+	PURPLE_CALLBACK(conversation_timestamp));
 
     purple_signal_disconnect(purple_conversations_get_handle(),
 	    "deleting-conversation", otrg_plugin_handle,
 	    PURPLE_CALLBACK(conversation_destroyed));
 
+    purple_signal_disconnect(pidgin_conversations_get_handle(),
+	    "received-im-msg", otrg_plugin_handle,
+	    PURPLE_CALLBACK(check_incoming_instance_change));
+
     /* If we're quitting, the imgstore will already have been destroyed
      * by purple, but we should have already called dialog_quitting(),
      * so the img_id_* should be -1, and all should be OK. */
@@ -2635,11 +3374,11 @@ static void otrg_gtk_dialog_cleanup(void)
     unref_img_by_id(&img_id_unverified);
     unref_img_by_id(&img_id_private);
     unref_img_by_id(&img_id_finished);
-    
+
     g_hash_table_foreach(otr_win_menus, foreach_free_lists, NULL);
 
     g_hash_table_destroy(otr_win_menus);
-    
+
     g_hash_table_destroy(otr_win_status);
 }
 
diff --git a/gtk-dialog.h b/gtk-dialog.h
index 07152b1..6eafbaa 100644
--- a/gtk-dialog.h
+++ b/gtk-dialog.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
diff --git a/gtk-ui.c b/gtk-ui.c
index 29cc065..459097f 100644
--- a/gtk-ui.c
+++ b/gtk-ui.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -91,7 +91,7 @@ static void account_menu_changed_cb(GtkWidget *item, PurpleAccount *account,
     GtkWidget *fprint = ui_layout.fprint_label;
     char s[100];
     char *fingerprint;
-    
+
     if (account) {
 	char fingerprint_buf[45];
 	accountname = purple_account_get_username(account);
@@ -123,7 +123,9 @@ static void account_menu_changed_cb(GtkWidget *item, PurpleAccount *account,
  * UI, if visible. */
 static void otrg_gtk_ui_update_fingerprint(void)
 {
-    g_signal_emit_by_name(G_OBJECT(ui_layout.accountmenu), "changed");
+    if (ui_layout.accountmenu) {
+	g_signal_emit_by_name(G_OBJECT(ui_layout.accountmenu), "changed");
+    }
 }
 
 static void account_menu_added_removed_cb(PurpleAccount *account, void *data)
@@ -162,26 +164,54 @@ static void otrg_gtk_ui_update_keylist(void)
 	int i;
 	PurplePlugin *p;
 	char *proto_name;
+
+	if (context->their_instance != OTRL_INSTAG_MASTER) continue;
+
 	fingerprint = context->fingerprint_root.next;
 	/* If there's no fingerprint, don't add it to the known
 	 * fingerprints list */
 	while(fingerprint) {
+	    ConnContext *context_iter;
+	    TrustLevel best_level = TRUST_NOT_PRIVATE;
+	    int used = 0;
+
 	    titles[0] = context->username;
-	    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-		    context->active_fingerprint != fingerprint) {
-		titles[1] = _("Unused");
-	    } else {
+	    titles[1] = _("Unused");
+
+	    for (context_iter = context->m_context;
+		    context_iter && context_iter->m_context == context->m_context;
+		    context_iter = context_iter->next) {
+
+		TrustLevel this_level = TRUST_NOT_PRIVATE;
+
+		if (context_iter->active_fingerprint == fingerprint) {
+		    this_level = otrg_plugin_context_to_trust(context_iter);
+		    used = 1;
+
+		    if (this_level == TRUST_PRIVATE) {
+			best_level = TRUST_PRIVATE;
+		    } else if (this_level == TRUST_UNVERIFIED
+			    && best_level != TRUST_PRIVATE) {
+			best_level = TRUST_UNVERIFIED;
+		    } else if (this_level == TRUST_FINISHED
+			    && best_level == TRUST_NOT_PRIVATE) {
+			best_level = TRUST_FINISHED;
+		    }
+		}
+	    }
+
+	    if (used) {
 		titles[1] = (gchar *)
-		    _(trust_states[otrg_plugin_context_to_trust(context)]);
+		    _(trust_states[best_level]);
 	    }
 	    titles[2] = (fingerprint->trust && fingerprint->trust[0]) ?
-		_("Yes") : _("No");
+		    _("Yes") : _("No");
 	    otrl_privkey_hash_to_human(hash, fingerprint->fingerprint);
 	    titles[3] = hash;
 	    p = purple_find_prpl(context->protocol);
 	    proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
 	    titles[4] = g_strdup_printf("%s (%s)", context->accountname,
-		proto_name);
+		    proto_name);
 	    i = gtk_clist_append(GTK_CLIST(keylist), titles);
 	    g_free(titles[4]);
 	    gtk_clist_set_row_data(GTK_CLIST(keylist), i, fingerprint);
@@ -208,9 +238,9 @@ static void generate(GtkWidget *widget, gpointer data)
 {
     PurpleAccount *account;
     account = pidgin_account_option_menu_get_selected(ui_layout.accountmenu);
-	
+
     if (account == NULL) return;
-	
+
     otrg_plugin_create_privkey(purple_account_get_username(account),
 	    purple_account_get_protocol_id(account));
 }
@@ -243,26 +273,33 @@ static void clist_selected(GtkWidget *widget, gint row, gint column,
     int verify_sensitive = 0;
     Fingerprint *f = gtk_clist_get_row_data(GTK_CLIST(ui_layout.keylist),
 	    row);
-    if (f && f->context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-	    f->context->active_fingerprint == f) {
-	disconnect_sensitive = 1;
-    }
-    if (f && f->context->msgstate == OTRL_MSGSTATE_FINISHED) {
-	disconnect_sensitive = 1;
-    }
-    if (f && (f->context->msgstate != OTRL_MSGSTATE_ENCRYPTED ||
-	    f->context->active_fingerprint != f)) {
-	forget_sensitive = 1;
-    }
-    if (f && f->context->msgstate == OTRL_MSGSTATE_PLAINTEXT) {
-	connect_sensitive = 1;
-    }
-    if (f && f->context->msgstate == OTRL_MSGSTATE_FINISHED) {
-	connect_sensitive = 1;
-    }
+    ConnContext *context_iter;
+
     if (f) {
 	verify_sensitive = 1;
+	forget_sensitive = 1;
+
+	if (f->context && f->context->m_context) {
+	    for (context_iter = f->context;
+		    context_iter && context_iter->m_context ==
+		    f->context->m_context;
+		    context_iter = context_iter->next) {
+
+		if (context_iter->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+		    context_iter->active_fingerprint == f) {
+		    disconnect_sensitive = 1;
+		    forget_sensitive = 0;
+		}
+		else if (context_iter->msgstate == OTRL_MSGSTATE_FINISHED) {
+		    connect_sensitive = 1;
+		}
+		else if (context_iter->msgstate == OTRL_MSGSTATE_PLAINTEXT) {
+		    connect_sensitive = 1;
+		}
+	    }
+	}
     }
+
     gtk_widget_set_sensitive(ui_layout.connect_button,
 	    connect_sensitive);
     gtk_widget_set_sensitive(ui_layout.disconnect_button,
@@ -280,23 +317,44 @@ static void clist_unselected(GtkWidget *widget, gint row, gint column,
 
 static int fngsortval(Fingerprint *f)
 {
-    int is_active = (f->context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-	    f->context->active_fingerprint == f);
-    TrustLevel level = otrg_plugin_context_to_trust(f->context);
-
-    switch(level) {
-	case TRUST_PRIVATE:
-	    return is_active ? 0 : 100;
-	case TRUST_UNVERIFIED:
-	    return is_active ? 1 : 100;
-	case TRUST_FINISHED:
-	    return 2;
-	case TRUST_NOT_PRIVATE:
-	    return 3;
+    int result = 200;
+    ConnContext *context_iter;
+
+    for (context_iter = f->context->m_context;
+	context_iter && context_iter->m_context == f->context->m_context;
+	context_iter = context_iter->next) {
+
+	int is_active = 0;
+	TrustLevel level;
+
+	if  (context_iter->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+	    context_iter->active_fingerprint == f) {
+	    is_active = 1;
+	}
+
+	level = otrg_plugin_context_to_trust(context_iter);
+
+	if (level == TRUST_PRIVATE) {
+	    if (is_active) {
+		result = 0;
+		break;
+	    } else {
+		result = result > 100 ? 100 : result;
+	    }
+	} else if (level == TRUST_UNVERIFIED) {
+	    if (is_active) {
+		result = 1;
+	    } else {
+		result = result > 100 ? 100 : result;
+	    }
+	} else if (level == TRUST_FINISHED) {
+	    result = result > 2 ? 2 : result;
+	} else if (level == TRUST_NOT_PRIVATE) {
+	    result = result > 3 ? 3 : result;
+	}
     }
 
-    /* Shouldn't get here, but anyway. */
-    return 200;
+    return result;
 }
 
 static gint statuscmp(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
@@ -345,20 +403,25 @@ static void disconnect_connection(GtkWidget *widget, gpointer data)
 {
     /* Forget whatever state we've got with this context */
     ConnContext *context;
+    ConnContext *context_iter;
 
     if (ui_layout.selected_fprint == NULL) return;
 
     context = ui_layout.selected_fprint->context;
     if (context == NULL) return;
-	
-    /* Don't do anything with fingerprints other than the active one
-     * if we're in the ENCRYPTED state */
-    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-	    context->active_fingerprint != ui_layout.selected_fprint) {
-	return;
+
+    for (context_iter = context->m_context;
+	    context_iter && context_iter->m_context == context->m_context;
+	    context_iter = context_iter->next) {
+
+	/* Don't do anything with fingerprints other than the active one
+	 * if we're in the ENCRYPTED state */
+	if (context_iter->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+	    context_iter->active_fingerprint == ui_layout.selected_fprint) {
+	    otrg_ui_disconnect_connection(context_iter);
+	}
     }
-	
-    otrg_ui_disconnect_connection(context);
+
 }
 
 static void forget_fingerprint(GtkWidget *widget, gpointer data)
@@ -383,7 +446,7 @@ static void otrsettings_clicked_cb(GtkButton *button,
 		GTK_TOGGLE_BUTTON(os->enablebox))) {
 	gtk_widget_set_sensitive(os->automaticbox, TRUE);
 	if (gtk_toggle_button_get_active(
-		    GTK_TOGGLE_BUTTON(os->automaticbox))) {
+		GTK_TOGGLE_BUTTON(os->automaticbox))) {
 	    gtk_widget_set_sensitive(os->onlyprivatebox, TRUE);
 	} else {
 	    gtk_widget_set_sensitive(os->onlyprivatebox, FALSE);
@@ -431,13 +494,13 @@ static void create_otrsettings_buttons(struct otrsettingsdata *os,
     gtk_box_pack_start(GTK_BOX(vbox), os->avoidloggingotrbox, FALSE, FALSE, 5);
 
     g_signal_connect(G_OBJECT(os->enablebox), "clicked",
-		     G_CALLBACK(otrsettings_clicked_cb), os);
+	    G_CALLBACK(otrsettings_clicked_cb), os);
     g_signal_connect(G_OBJECT(os->automaticbox), "clicked",
-		     G_CALLBACK(otrsettings_clicked_cb), os);
+	    G_CALLBACK(otrsettings_clicked_cb), os);
     g_signal_connect(G_OBJECT(os->onlyprivatebox), "clicked",
-		     G_CALLBACK(otrsettings_clicked_cb), os);
+	    G_CALLBACK(otrsettings_clicked_cb), os);
     g_signal_connect(G_OBJECT(os->avoidloggingotrbox), "clicked",
-		     G_CALLBACK(otrsettings_clicked_cb), os);
+	    G_CALLBACK(otrsettings_clicked_cb), os);
 }
 
 static void otroptions_clicked_cb(GtkButton *button,
@@ -461,7 +524,7 @@ static void create_otroptions_buttons(struct otroptionsdata *oo,
     gtk_box_pack_start(GTK_BOX(vbox), oo->showotrbutton, FALSE, FALSE, 0);
 
     g_signal_connect(G_OBJECT(oo->showotrbutton), "clicked",
-		     G_CALLBACK(otroptions_clicked_cb), oo);
+	    G_CALLBACK(otroptions_clicked_cb), oo);
 }
 
 /* Load the global OTR prefs */
@@ -478,8 +541,8 @@ static void otrg_gtk_ui_global_prefs_load(gboolean *enabledp,
 	*enabledp = TRUE;
 	*automaticp = TRUE;
 	*onlyprivatep = FALSE;
-	*avoidloggingotrp = FALSE;
-    }
+	*avoidloggingotrp = TRUE;
+  }
 }
 
 /* Save the global OTR prefs */
@@ -512,7 +575,7 @@ static void otrg_gtk_ui_buddy_prefs_load(PurpleBuddy *buddy,
 	*automaticp = purple_blist_node_get_bool(node, "OTR/automatic");
 	*onlyprivatep = purple_blist_node_get_bool(node, "OTR/onlyprivate");
 	*avoidloggingotrp =
-	    purple_blist_node_get_bool(node, "OTR/avoidloggingotr");
+		purple_blist_node_get_bool(node, "OTR/avoidloggingotr");
     }
 }
 
@@ -686,13 +749,13 @@ static void make_settings_ui(GtkWidget *vbox)
     load_otrsettings(&(ui_layout.os));
 
     g_signal_connect(G_OBJECT(ui_layout.os.enablebox), "clicked",
-		     G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
+	    G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
     g_signal_connect(G_OBJECT(ui_layout.os.automaticbox), "clicked",
-		     G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
+	    G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
     g_signal_connect(G_OBJECT(ui_layout.os.onlyprivatebox), "clicked",
-		     G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
+	    G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
     g_signal_connect(G_OBJECT(ui_layout.os.avoidloggingotrbox), "clicked",
-		     G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
+	    G_CALLBACK(otrsettings_save_cb), &(ui_layout.os));
 }
 
 /* Make the options UI, and pack it into the vbox */
@@ -713,7 +776,7 @@ static void make_options_ui(GtkWidget *vbox)
     load_otroptions(&(ui_layout.oo));
 
     g_signal_connect(G_OBJECT(ui_layout.oo.showotrbutton), "clicked",
-		     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
+	     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
 }
 
 /* Create the fingerprint UI, and pack it into the vbox */
@@ -731,8 +794,8 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     titles[4] = _("Account");
 
     ui_layout.scrollwin = gtk_scrolled_window_new(0, 0);
-    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui_layout.scrollwin), 
-            GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui_layout.scrollwin),
+	    GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
 
     ui_layout.keylist = gtk_clist_new_with_titles(5, titles);
     gtk_clist_set_column_width(GTK_CLIST(ui_layout.keylist), 0, 90);
@@ -906,19 +969,19 @@ static void config_buddy_destroy_cb(GtkWidget *w, struct cbdata *data)
 static void config_buddy_clicked_cb(GtkButton *button, struct cbdata *data)
 {
     gboolean enabled = gtk_toggle_button_get_active(
-			     GTK_TOGGLE_BUTTON(data->os.enablebox));
-    
+	    GTK_TOGGLE_BUTTON(data->os.enablebox));
+
     /* Apply the changes */
     otrg_gtk_ui_buddy_prefs_save(data->buddy,
-	 gtk_toggle_button_get_active(
-	     GTK_TOGGLE_BUTTON(data->defaultbox)),
-	 enabled,
-	 gtk_toggle_button_get_active(
-	     GTK_TOGGLE_BUTTON(data->os.automaticbox)),
-	 gtk_toggle_button_get_active(
-	     GTK_TOGGLE_BUTTON(data->os.onlyprivatebox)),
-	 gtk_toggle_button_get_active(
-	     GTK_TOGGLE_BUTTON(data->os.avoidloggingotrbox)));
+	    gtk_toggle_button_get_active(
+	    GTK_TOGGLE_BUTTON(data->defaultbox)),
+		enabled,
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(data->os.automaticbox)),
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(data->os.onlyprivatebox)),
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(data->os.avoidloggingotrbox)));
 
     otrg_dialog_resensitize_all();
 }
@@ -940,9 +1003,9 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
     if (!data) return;
 
     dialog = gtk_dialog_new_with_buttons(_("OTR Settings"),
-					 NULL, 0,
-					 GTK_STOCK_OK, GTK_RESPONSE_OK,
-					 NULL);
+	    NULL, 0,
+	    GTK_STOCK_OK, GTK_RESPONSE_OK,
+	    NULL);
     gtk_window_set_accept_focus(GTK_WINDOW(dialog), FALSE);
     gtk_window_set_role(GTK_WINDOW(dialog), "otr_settings");
 
@@ -986,25 +1049,25 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
     create_otrsettings_buttons(&(data->os), GTK_DIALOG(dialog)->vbox);
 
     g_signal_connect(G_OBJECT(data->defaultbox), "clicked",
-		     G_CALLBACK(default_clicked_cb), data);
+	    G_CALLBACK(default_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->defaultbox), "clicked",
-		     G_CALLBACK(config_buddy_clicked_cb), data);
+	    G_CALLBACK(config_buddy_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->os.enablebox), "clicked",
-		     G_CALLBACK(config_buddy_clicked_cb), data);
+	    G_CALLBACK(config_buddy_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->os.automaticbox), "clicked",
-		     G_CALLBACK(config_buddy_clicked_cb), data);
+	    G_CALLBACK(config_buddy_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->os.onlyprivatebox), "clicked",
-		     G_CALLBACK(config_buddy_clicked_cb), data);
+	    G_CALLBACK(config_buddy_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->os.avoidloggingotrbox), "clicked",
-		     G_CALLBACK(config_buddy_clicked_cb), data);
+	    G_CALLBACK(config_buddy_clicked_cb), data);
 
     /* Set the inital states of the buttons */
     load_buddyprefs(data);
 
     g_signal_connect(G_OBJECT(dialog), "destroy",
-		     G_CALLBACK(config_buddy_destroy_cb), data);
+	    G_CALLBACK(config_buddy_destroy_cb), data);
     g_signal_connect(G_OBJECT(dialog), "response",
-		     G_CALLBACK(config_buddy_response_cb), data);
+	    G_CALLBACK(config_buddy_response_cb), data);
 
     gtk_widget_show_all(dialog);
 }
@@ -1016,12 +1079,12 @@ static void otrg_gtk_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
     PurpleBuddy *buddy;
     gboolean otrenabled, otrautomatic, otronlyprivate, otravoidloggingotr;
     gboolean buddyusedefault, buddyenabled, buddyautomatic, buddyonlyprivate,
-	     buddyavoidloggingotr;
+	    buddyavoidloggingotr;
 
     prefsp->policy = OTRL_POLICY_DEFAULT;
     prefsp->avoid_logging_otr = FALSE;
     prefsp->show_otr_button = FALSE;
-    
+
     /* Get the default policy */
     otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate,
 	    &otravoidloggingotr);
diff --git a/gtk-ui.h b/gtk-ui.h
index 8797850..c20dd3d 100644
--- a/gtk-ui.h
+++ b/gtk-ui.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
diff --git a/otr-icons.h b/otr-icons.h
index f9ff68a..4abaeb3 100644
--- a/otr-icons.h
+++ b/otr-icons.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -32,9 +32,9 @@
 #pragma align 4 (not_private_pixbuf)
 #endif
 #ifdef __GNUC__
-static const guint8 not_private_pixbuf[] __attribute__ ((__aligned__ (4))) = 
+static const guint8 not_private_pixbuf[] __attribute__ ((__aligned__ (4))) =
 #else
-static const guint8 not_private_pixbuf[] = 
+static const guint8 not_private_pixbuf[] =
 #endif
 { ""
   /* Pixbuf magic (0x47646b50) */
@@ -153,9 +153,9 @@ static const char not_private_png[] =
 #pragma align 4 (unverified_pixbuf)
 #endif
 #ifdef __GNUC__
-static const guint8 unverified_pixbuf[] __attribute__ ((__aligned__ (4))) = 
+static const guint8 unverified_pixbuf[] __attribute__ ((__aligned__ (4))) =
 #else
-static const guint8 unverified_pixbuf[] = 
+static const guint8 unverified_pixbuf[] =
 #endif
 { ""
   /* Pixbuf magic (0x47646b50) */
@@ -270,9 +270,9 @@ static const char unverified_png[] =
 #pragma align 4 (private_pixbuf)
 #endif
 #ifdef __GNUC__
-static const guint8 private_pixbuf[] __attribute__ ((__aligned__ (4))) = 
+static const guint8 private_pixbuf[] __attribute__ ((__aligned__ (4))) =
 #else
-static const guint8 private_pixbuf[] = 
+static const guint8 private_pixbuf[] =
 #endif
 { ""
   /* Pixbuf magic (0x47646b50) */
@@ -387,9 +387,9 @@ static const char private_png[] =
 #pragma align 4 (finished_pixbuf)
 #endif
 #ifdef __GNUC__
-static const guint8 finished_pixbuf[] __attribute__ ((__aligned__ (4))) = 
+static const guint8 finished_pixbuf[] __attribute__ ((__aligned__ (4))) =
 #else
-static const guint8 finished_pixbuf[] = 
+static const guint8 finished_pixbuf[] =
 #endif
 { ""
   /* Pixbuf magic (0x47646b50) */
diff --git a/otr-plugin.c b/otr-plugin.c
index 26c7a17..1e213f4 100644
--- a/otr-plugin.c
+++ b/otr-plugin.c
@@ -1,8 +1,8 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
- *                           Nikita Borisov
+ *                           Lisa Du, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -64,6 +64,7 @@
 #include <libotr/tlv.h>
 #include <libotr/message.h>
 #include <libotr/userstate.h>
+#include <libotr/instag.h>
 
 /* purple-otr headers */
 #include "ui.h"
@@ -100,6 +101,7 @@ OtrlUserState otrg_plugin_userstate = NULL;
  * protocols. */
 GHashTable* mms_table;
 
+
 /* Send an IM from the given account to the given recipient.  Display an
  * error dialog if that account isn't currently logged in. */
 void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
@@ -126,25 +128,25 @@ void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
 /* Display a notification message for a particular accountname /
  * protocol / username conversation. */
 static void notify(void *opdata, OtrlNotifyLevel level,
-        const char *accountname, const char *protocol, const char *username,
-        const char *title, const char *primary, const char *secondary)
+	const char *accountname, const char *protocol, const char *username,
+	const char *title, const char *primary, const char *secondary)
 {
     PurpleNotifyMsgType purplelevel = PURPLE_NOTIFY_MSG_ERROR;
 
     switch (level) {
-        case OTRL_NOTIFY_ERROR:
-            purplelevel = PURPLE_NOTIFY_MSG_ERROR;
-            break;
-        case OTRL_NOTIFY_WARNING:
-            purplelevel = PURPLE_NOTIFY_MSG_WARNING;
-            break;
-        case OTRL_NOTIFY_INFO:
-            purplelevel = PURPLE_NOTIFY_MSG_INFO;
-            break;
+	case OTRL_NOTIFY_ERROR:
+	    purplelevel = PURPLE_NOTIFY_MSG_ERROR;
+	    break;
+	case OTRL_NOTIFY_WARNING:
+	    purplelevel = PURPLE_NOTIFY_MSG_WARNING;
+	    break;
+	case OTRL_NOTIFY_INFO:
+	    purplelevel = PURPLE_NOTIFY_MSG_INFO;
+	    break;
     }
 
     otrg_dialog_notify_message(purplelevel, accountname, protocol,
-            username, title, primary, secondary);
+	    username, title, primary, secondary);
 }
 
 /* Display an OTR control message for a particular accountname /
@@ -153,16 +155,16 @@ static void notify(void *opdata, OtrlNotifyLevel level,
  * conversation window will be created and the message will be displayed
  * there. Returns 0 if message is successfully displayed. */
 static int display_otr_message(void *opdata, const char *accountname,
-        const char *protocol, const char *username, const char *msg,
-        int force_create)
+	const char *protocol, const char *username, const char *msg,
+	int force_create)
 {
     return otrg_dialog_display_otr_message(accountname, protocol,
-            username, msg, force_create);
+	    username, msg, force_create);
 }
 
 static void log_message(void *opdata, const char *message)
 {
-    purple_debug_info("otr", message);
+    purple_debug_info("otr", "%s", message);
 }
 
 static OtrlPolicy policy_cb(void *opdata, ConnContext *context)
@@ -190,7 +192,8 @@ void otrg_plugin_create_privkey(const char *accountname,
 #endif  /* WIN32 */
     FILE *privf;
 
-    gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME, NULL);
+    gchar *privkeyfile = g_build_filename(purple_user_dir(),
+	    PRIVKEYFNAME, NULL);
     if (!privkeyfile) {
 	fprintf(stderr, _("Out of memory building filenames!\n"));
 	return;
@@ -226,6 +229,38 @@ static void create_privkey_cb(void *opdata, const char *accountname,
     otrg_plugin_create_privkey(accountname, protocol);
 }
 
+/* Generate a instance tag for the given accountname/protocol */
+void otrg_plugin_create_instag(const char *accountname,
+	const char *protocol)
+{
+    OtrgDialogWaitHandle waithandle;
+    FILE *instagf;
+
+    gchar *instagfile = g_build_filename(purple_user_dir(), INSTAGFNAME, NULL);
+    if (!instagfile) {
+	fprintf(stderr, _("Out of memory building filenames!\n"));
+	return;
+    }
+    instagf = g_fopen(instagfile, "w+b");
+    g_free(instagfile);
+    if (!instagf) {
+	fprintf(stderr, _("Could not write private key file\n"));
+	return;
+    }
+
+    /* Generate the key */
+    otrl_instag_generate_FILEp(otrg_plugin_userstate, instagf,
+	    accountname, protocol);
+    fclose(instagf);
+
+}
+
+static void create_instag_cb(void *opdata, const char *accountname,
+	const char *protocol)
+{
+    otrg_plugin_create_instag(accountname, protocol);
+}
+
 static int is_logged_in_cb(void *opdata, const char *accountname,
 	const char *protocol, const char *recipient)
 {
@@ -299,13 +334,13 @@ static int max_message_size_cb(void *opdata, ConnContext *context)
 {
     void* lookup_result = g_hash_table_lookup(mms_table, context->protocol);
     if (!lookup_result)
-        return 0;
+	return 0;
     else
-        return *((int*)lookup_result);
+	return *((int*)lookup_result);
 }
 
 static const char* otr_error_message_cb(void *opdata, ConnContext *context,
-        OtrlErrorCode err_code)
+	OtrlErrorCode err_code)
 {
     char *err_msg = NULL;
     switch (err_code)
@@ -313,20 +348,21 @@ static const char* otr_error_message_cb(void *opdata, ConnContext *context,
     case OTRL_ERRCODE_NONE :
 	break;
     case OTRL_ERRCODE_ENCRYPTION_ERROR :
-        err_msg = g_strdup(_("Error occurred encrypting message."));
-        break;
+	err_msg = g_strdup(_("Error occurred encrypting message."));
+	break;
     case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE :
-        if (context) {
-            err_msg = g_strdup_printf(_("You sent encrypted data to %s, who"
-                " wasn't expecting it."), context->accountname);
-        }
-        break;
+	if (context) {
+	    err_msg = g_strdup_printf(_("You sent encrypted data to %s, who"
+		    " wasn't expecting it."), context->accountname);
+	}
+	break;
     case OTRL_ERRCODE_MSG_UNREADABLE :
-        err_msg = g_strdup(_("You transmitted an unreadable encrypted message."));
-        break;
+	err_msg =
+		g_strdup(_("You transmitted an unreadable encrypted message."));
+	break;
     case OTRL_ERRCODE_MSG_MALFORMED :
-        err_msg = g_strdup(_("You transmitted a malformed data message."));
-        break;
+	err_msg = g_strdup(_("You transmitted a malformed data message."));
+	break;
     }
     return err_msg;
 }
@@ -347,242 +383,283 @@ static void resent_msg_prefix_free_cb(void *opdata, const char *prefix)
 }
 
 static void handle_smp_event_cb(void *opdata, OtrlSMPEvent smp_event,
-        ConnContext *context, unsigned short progress_percent,
+	ConnContext *context, unsigned short progress_percent,
 	char *question)
 {
     if (!context) return;
     switch (smp_event)
     {
-        case OTRL_SMPEVENT_NONE :
+	case OTRL_SMPEVENT_NONE :
+	    break;
+	case OTRL_SMPEVENT_ASK_FOR_SECRET :
+	    otrg_dialog_socialist_millionaires(context);
 	    break;
-        case OTRL_SMPEVENT_ASK_FOR_SECRET :
-            otrg_dialog_socialist_millionaires(context);
-            break;
-        case OTRL_SMPEVENT_ASK_FOR_ANSWER :
-            otrg_dialog_socialist_millionaires_q(context, question);
-            break;
-        case OTRL_SMPEVENT_CHEATED :
-            otrg_plugin_abort_smp(context);
+	case OTRL_SMPEVENT_ASK_FOR_ANSWER :
+	    otrg_dialog_socialist_millionaires_q(context, question);
+	    break;
+	case OTRL_SMPEVENT_CHEATED :
+	    otrg_plugin_abort_smp(context);
 	    /* FALLTHROUGH */
-        case OTRL_SMPEVENT_IN_PROGRESS :
-        case OTRL_SMPEVENT_SUCCESS :
-        case OTRL_SMPEVENT_FAILURE :
-        case OTRL_SMPEVENT_ABORT :
-            otrg_dialog_update_smp(context,
+	case OTRL_SMPEVENT_IN_PROGRESS :
+	case OTRL_SMPEVENT_SUCCESS :
+	case OTRL_SMPEVENT_FAILURE :
+	case OTRL_SMPEVENT_ABORT :
+	    otrg_dialog_update_smp(context,
 		    smp_event, ((gdouble)progress_percent)/100.0);
-            break;
-        case OTRL_SMPEVENT_ERROR :
-            otrg_plugin_abort_smp(context);
-            break;
+	    break;
+	case OTRL_SMPEVENT_ERROR :
+	    otrg_plugin_abort_smp(context);
+	    break;
     }
 }
 
+void otrg_emit_msg_received(ConnContext *context, const char* message) {
+    PurpleConversation *conv = otrg_plugin_userinfo_to_conv(
+	    context->accountname, context->protocol, context->username, 1);
+    PurpleMessageFlags flags = PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM
+	    | PURPLE_MESSAGE_NOTIFY;
+    PurpleAccount * account = purple_conversation_get_account(conv);
+
+    purple_signal_emit(purple_conversations_get_handle(), "received-im-msg",
+	    account, context->username, message, conv, flags);
+}
+
 static void handle_msg_event_cb(void *opdata, OtrlMessageEvent msg_event,
-        ConnContext *context, const char* message, gcry_error_t err)
+	ConnContext *context, const char* message, gcry_error_t err)
 {
+    PurpleConversation *conv = NULL;
     if (!context) return;
     char *buf;
     const char *format;
+    OtrlMessageEvent last_msg_event;
+    gboolean value_existed;
+
+    conv = otrg_plugin_context_to_conv(context, 1);
+    value_existed = g_hash_table_lookup_extended(conv->data,
+	    "otr-last_msg_event", NULL, (void**)&last_msg_event);
+
+    purple_conversation_set_data(conv, "otr-last_msg_event",
+	    (gpointer)msg_event);
 
     switch (msg_event)
     {
-        case OTRL_MSGEVENT_NONE:
+	case OTRL_MSGEVENT_NONE:
+	    break;
+	case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
+	    if (display_otr_message(opdata, context->accountname,
+		    context->protocol, context->username, _("Attempting to"
+		    " start a private conversation..."), 1)) {
+		format = _("You attempted to send an "
+		"unencrypted message to %s");
+		buf = malloc(strlen(format) +
+			strlen(context->username) - 1);
+		if (buf) {
+		    sprintf(buf, format, context->username);
+		    notify(opdata, OTRL_NOTIFY_WARNING, context->accountname,
+			    context->protocol, context->username, _("OTR Policy"
+			    " Violation"), buf,
+			    _("Unencrypted messages to this recipient are "
+			    "not allowed.  Attempting to start a private "
+			    "conversation.\n\nYour message will be "
+			    "retransmitted when the private conversation "
+			    "starts."));
+		    free(buf);
+		}
+	    }
+	    break;
+	case OTRL_MSGEVENT_ENCRYPTION_ERROR:
+	    if (display_otr_message(opdata, context->accountname,
+		    context->protocol, context->username, _("An error occurred "
+		    "when encrypting your message.  The message was not sent."
+		    ), 1)) {
+		notify(opdata, OTRL_NOTIFY_ERROR,
+			context->accountname, context->protocol,
+			context->username,
+			_("Error encrypting message"),
+			_("An error occurred when encrypting your message"),
+			_("The message was not sent."));
+	    }
+	    break;
+	case OTRL_MSGEVENT_CONNECTION_ENDED:
+	    if (display_otr_message(opdata, context->accountname,
+		    context->protocol, context->username, _("Your message"
+		    "was not sent.  Either end your private conversation, "
+		    "or restart it."), 1)) {
+		format = _("%s has already closed his/her private "
+			"connection to you");
+		buf = malloc(strlen(format) + strlen(context->username) - 1);
+		if (buf) {
+		    sprintf(buf, format, context->username);
+		    notify(opdata, OTRL_NOTIFY_ERROR,
+			    context->accountname, context->protocol,
+			    context->username,
+			    _("Private connection closed"), buf,
+			    _("Your message was not sent.  Either close your "
+			    "private connection to him, or refresh it."));
+		    free(buf);
+		}
+	    }
+	    break;
+	case OTRL_MSGEVENT_SETUP_ERROR:
+	    if (!err) {
+		err = GPG_ERR_INV_VALUE;
+	    }
+	    format = _("Error setting up private conversation: %s");
+	    const char *strerr;
+
+	    switch(gcry_err_code(err)) {
+		case GPG_ERR_INV_VALUE:
+		    strerr = _("Malformed message received");
+		    break;
+		default:
+		    strerr = gcry_strerror(err);
+		    break;
+	    }
+	    buf = malloc(strlen(format) + strlen(strerr) - 1);
+	    if (buf) {
+		sprintf(buf, format, strerr);
+	    }
+	    if (display_otr_message(opdata, context->accountname,
+		    context->protocol, context->username, buf, 1)) {
+		notify(opdata, OTRL_NOTIFY_ERROR, context->accountname,
+			context->protocol, context->username, "OTR error",
+			buf, NULL);
+	    }
+	    free(buf);
+	    break;
+	case OTRL_MSGEVENT_MSG_REFLECTED:
+	    if (display_otr_message(opdata,
+		    context->accountname, context->protocol,
+		    context->username,
+		    _("We are receiving our own OTR messages.  "
+		    "You are either trying to talk to yourself, "
+		    "or someone is reflecting your messages back "
+		    "at you."), 1))  {
+		notify(opdata, OTRL_NOTIFY_ERROR,
+			context->accountname, context->protocol,
+			context->username, "OTR Error",
+			_("We are receiving our own OTR messages."),
+			_("You are either trying to talk to yourself, "
+			"or someone is reflecting your messages back "
+			"at you."));
+	    }
+	    break;
+	case OTRL_MSGEVENT_MSG_RESENT:
+	    format = _("<b>The last message to %s was resent.</b>");
+	    buf = malloc(strlen(format) +
+		    strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		display_otr_message(opdata, context->accountname,
+			context->protocol, context->username, buf, 1);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
+	    format = _("<b>The encrypted message received from %s is "
+		    "unreadable, as you are not currently communicating "
+		    "privately.</b>");
+	    buf = malloc(strlen(format) +
+		    strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		display_otr_message(opdata, context->accountname,
+			context->protocol, context->username, buf, 1);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
+	    format = _("We received an unreadable "
+		    "encrypted message from %s.");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		if (display_otr_message(opdata,
+			context->accountname, context->protocol,
+			context->username, buf, 1)) {
+		    notify(opdata, OTRL_NOTIFY_ERROR,
+			    context->accountname, context->protocol,
+			    context->username, "OTR Error", buf, NULL);
+		}
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
+	    format =  _("We received a malformed data "
+		    "message from %s.");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		if (display_otr_message(opdata,
+			context->accountname, context->protocol,
+			context->username, buf, 1)) {
+		    notify(opdata, OTRL_NOTIFY_ERROR,
+			    context->accountname, context->protocol,
+			    context->username, "OTR Error", buf, NULL);
+		}
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
+	    format = _("Heartbeat received from %s.\n");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		log_message(opdata, buf);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
+	    format = _("Heartbeat sent to %s.\n");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		log_message(opdata, buf);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
+	    display_otr_message(opdata, context->accountname,
+		    context->protocol, context->username, message, 1);
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
+	    format = _("<b>The following message received "
+		    "from %s was <i>not</i> encrypted: [</b>%s<b>]</b>");
+	    buf = malloc(strlen(format) + strlen(context->username)
+		    + strlen(message) - 3);
+		/* Remove "%s%s", add username + message + '\0' */
+	    if (buf) {
+		sprintf(buf, format, context->username, message);
+		display_otr_message(opdata, context->accountname,
+			context->protocol, context->username, buf, 1);
+		otrg_emit_msg_received(context, buf);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
+	    format = _("Unrecognized OTR message received from %s.\n");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		log_message(opdata, buf);
+		free(buf);
+	    }
+	    break;
+	case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
+	    if (value_existed && last_msg_event == msg_event) {
+		break;
+	    }
+	    format = _("%s has sent a message intended for a different session."
+		    "If you are logged in multiple times, another session may "
+		    "have received the message.");
+	    buf = malloc(strlen(format) + strlen(context->username) - 1);
+	    if (buf) {
+		sprintf(buf, format, context->username);
+		display_otr_message(opdata, context->accountname,
+			context->protocol, context->username, buf, 1);
+		free(buf);
+	    }
 	    break;
-        case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
-            if (display_otr_message(opdata, context->accountname,
-                    context->protocol, context->username, _("Attempting to start a "
-                    "private conversation..."), 1)) {
-                format = _("You attempted to send an "
-                "unencrypted message to %s");
-                buf = malloc(strlen(format) +
-                    strlen(context->username) - 1);
-                if (buf) {
-                    sprintf(buf, format, context->username);
-                    notify(opdata, OTRL_NOTIFY_WARNING, context->accountname,
-                        context->protocol, context->username, _("OTR Policy Violation"),
-                        buf,
-                        _("Unencrypted messages to this recipient are "
-                        "not allowed.  Attempting to start a private "
-                        "conversation.\n\nYour message will be "
-                        "retransmitted when the private conversation "
-                        "starts."));
-                    free(buf);
-                }
-            }
-            break;
-        case OTRL_MSGEVENT_ENCRYPTION_ERROR:
-            if (display_otr_message(opdata, context->accountname,
-                context->protocol, context->username, _("An error occurred when "
-                "encrypting your message.  The message was not sent."), 1)) {
-                notify(opdata, OTRL_NOTIFY_ERROR,
-                    context->accountname, context->protocol, context->username,
-                    _("Error encrypting message"),
-                    _("An error occurred when encrypting your message"),
-                    _("The message was not sent."));
-            }
-            break;
-        case OTRL_MSGEVENT_CONNECTION_ENDED:
-            if (display_otr_message(opdata, context->accountname,
-                context->protocol, context->username, _("Your message was not sent.  "
-                "Either end your private conversation, or restart it."), 1)) {
-                format = _("%s has already closed his/her private "
-                    "connection to you");
-                buf = malloc(strlen(format) + strlen(context->username) - 1);
-                if (buf) {
-                    sprintf(buf, format, context->username);
-                    notify(opdata, OTRL_NOTIFY_ERROR,
-                        context->accountname, context->protocol, context->username,
-                        _("Private connection closed"), buf,
-                        _("Your message was not sent.  Either close your "
-                        "private connection to him, or refresh it."));
-                    free(buf);
-                }
-            }
-            break;
-        case OTRL_MSGEVENT_SETUP_ERROR:
-            if (!err) {
-                err = GPG_ERR_INV_VALUE;
-            }
-            format = _("Error setting up private conversation: %s");
-            const char *strerr;
-
-            switch(gcry_err_code(err)) {
-                case GPG_ERR_INV_VALUE:
-                    strerr = _("Malformed message received");
-                    break;
-                default:
-                    strerr = gcry_strerror(err);
-                    break;
-            }
-            buf = malloc(strlen(format) + strlen(strerr) - 1);
-            if (buf) {
-                sprintf(buf, format, strerr);
-            }
-            if (display_otr_message(opdata, context->accountname,
-                    context->protocol, context->username, buf, 1)) {
-                notify(opdata, OTRL_NOTIFY_ERROR, context->accountname,
-                    context->protocol, context->username, "OTR error",
-                    buf, NULL);
-            }
-            free(buf);
-            break;
-        case OTRL_MSGEVENT_MSG_REFLECTED:
-            if (display_otr_message(opdata,
-                context->accountname, context->protocol,
-                context->username,
-                _("We are receiving our own OTR messages.  "
-                "You are either trying to talk to yourself, "
-                "or someone is reflecting your messages back "
-                "at you."), 1))  {
-                notify(opdata, OTRL_NOTIFY_ERROR,
-                    context->accountname, context->protocol,
-                    context->username, "OTR Error",
-                    _("We are receiving our own OTR messages."),
-                    _("You are either trying to talk to yourself, "
-                    "or someone is reflecting your messages back "
-                    "at you."));
-            }
-            break;
-        case OTRL_MSGEVENT_MSG_RESENT:
-            format = _("<b>The last message to %s was resent.</b>");
-            buf = malloc(strlen(format) +
-                strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                display_otr_message(opdata, context->accountname,
-                        context->protocol, context->username, buf, 1);
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
-            format = _("<b>The encrypted message received from %s is "
-                "unreadable, as you are not currently communicating "
-                "privately.</b>");
-            buf = malloc(strlen(format) +
-                strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                display_otr_message(opdata, context->accountname,
-                        context->protocol, context->username, buf, 1);
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
-            format = _("We received an unreadable "
-                "encrypted message from %s.");
-            buf = malloc(strlen(format) + strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                if (display_otr_message(opdata,
-                    context->accountname, context->protocol,
-                    context->username, buf, 1)) {
-                    notify(opdata, OTRL_NOTIFY_ERROR,
-                        context->accountname, context->protocol,
-                        context->username, "OTR Error", buf, NULL);
-                }
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
-            format =  _("We received a malformed data "
-                "message from %s.");
-            buf = malloc(strlen(format) + strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                if (display_otr_message(opdata,
-                    context->accountname, context->protocol,
-                    context->username, buf, 1)) {
-                    notify(opdata, OTRL_NOTIFY_ERROR,
-                        context->accountname, context->protocol,
-                        context->username, "OTR Error", buf, NULL);
-                }
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
-            format = _("Heartbeat received from %s.\n");
-            buf = malloc(strlen(format) + strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                log_message(opdata, buf);
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
-            format = _("Heartbeat sent to %s.\n");
-            buf = malloc(strlen(format) + strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                log_message(opdata, buf);
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
-            display_otr_message(opdata, context->accountname,
-                    context->protocol, context->username, message, 1);
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
-            format = _("<b>The following message received "
-                "from %s was <i>not</i> encrypted: [</b>%s<b>]</b>");
-            buf = malloc(strlen(format) + strlen(context->username)
-                + strlen(message) - 3);
-                /* Remove "%s%s", add username + message + '\0' */
-            if (buf) {
-                sprintf(buf, format, context->username, message);
-                display_otr_message(opdata, context->accountname,
-                    context->protocol, context->username, buf, 1);
-                free(buf);
-            }
-            break;
-        case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
-            format = _("Unrecognized OTR message received from %s.\n");
-            buf = malloc(strlen(format) + strlen(context->username) - 1);
-            if (buf) {
-                sprintf(buf, format, context->username);
-                log_message(opdata, buf);
-                free(buf);
-            }
-            break;
     }
 }
 
@@ -629,9 +706,15 @@ static OtrlMessageAppOps ui_ops = {
     resent_msg_prefix_cb,
     resent_msg_prefix_free_cb,
     handle_smp_event_cb,
-    handle_msg_event_cb
+    handle_msg_event_cb,
+    create_instag_cb,
+    NULL, /* convert_data */
+    NULL /* convert_data_free */
 };
 
+otrl_instag_t otrg_plugin_conv_to_selected_instag(PurpleConversation *conv,
+	otrl_instag_t default_value);
+
 static void process_sending_im(PurpleAccount *account, char *who,
 	char **message, void *m)
 {
@@ -640,24 +723,32 @@ static void process_sending_im(PurpleAccount *account, char *who,
     const char *protocol = purple_account_get_protocol_id(account);
     char *username;
     gcry_error_t err;
+    PurpleConversation * conv = NULL;
+    otrl_instag_t instance;
 
     if (!who || !message || !*message)
 	return;
 
     username = strdup(purple_normalize(account, who));
 
+    conv = otrg_plugin_userinfo_to_conv(accountname, protocol, username, 1);
+
+    instance = otrg_plugin_conv_to_selected_instag(conv, OTRL_INSTAG_BEST);
+
     err = otrl_message_sending(otrg_plugin_userstate, &ui_ops, NULL,
-	    accountname, protocol, username, message, NULL, &newmessage,
-	    OTRL_FRAGMENT_SEND_ALL_BUT_LAST, NULL, NULL, NULL, NULL);
+	    accountname, protocol, username, instance, *message, NULL,
+	    &newmessage, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, NULL, NULL, NULL);
 
     if (err) {
-    	/* Do not send out plain text */
-    	char *ourm = strdup("");
-    	free(*message);
-    	*message = ourm;
+	/* Do not send out plain text */
+	char *ourm = strdup("");
+	free(*message);
+	*message = ourm;
+    } else if (newmessage) {
+	*message = strdup(newmessage);
     }
 
-	otrl_message_free(newmessage);
+    otrl_message_free(newmessage);
     free(username);
 }
 
@@ -701,7 +792,7 @@ void otrg_plugin_send_default_query(ConnContext *context, void *vaccount)
     msg = otrl_proto_default_query_msg(context->accountname,
 	    prefs.policy);
     otrg_plugin_inject_message(account, context->username,
-	    msg ? msg : "?OTRv2?");
+	    msg ? msg : "?OTRv23?");
     free(msg);
 }
 
@@ -720,12 +811,12 @@ void otrg_plugin_send_default_query_conv(PurpleConversation *conv)
 
     otrg_ui_get_prefs(&prefs, account, username);
     msg = otrl_proto_default_query_msg(accountname, prefs.policy);
-    otrg_plugin_inject_message(account, username, msg ? msg : "?OTRv2?");
+    otrg_plugin_inject_message(account, username, msg ? msg : "?OTRv23?");
     free(msg);
 }
 
 static gboolean process_receiving_im(PurpleAccount *account, char **who,
-        char **message, int *flags, void *m)
+	char **message, int *flags, void *m)
 {
     char *newmessage = NULL;
     OtrlTLV *tlvs = NULL;
@@ -736,7 +827,7 @@ static gboolean process_receiving_im(PurpleAccount *account, char **who,
     const char *protocol;
 
     if (!who || !*who || !message || !*message)
-        return 0;
+	return 0;
 
     username = strdup(purple_normalize(account, *who));
     accountname = purple_account_get_username(account);
@@ -744,7 +835,7 @@ static gboolean process_receiving_im(PurpleAccount *account, char **who,
 
     res = otrl_message_receiving(otrg_plugin_userstate, &ui_ops, NULL,
 	    accountname, protocol, username, *message,
-	    &newmessage, &tlvs, NULL, NULL, NULL, NULL);
+	    &newmessage, &tlvs, NULL, NULL, NULL);
 
     if (newmessage) {
 	char *ourm = malloc(strlen(newmessage) + 1);
@@ -778,9 +869,63 @@ static gboolean process_receiving_im(PurpleAccount *account, char **who,
     return res;
 }
 
+/* Find the ConnContext appropriate to a given PurpleConversation. */
+ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv,
+	otrl_instag_t their_instance, int force_create)
+{
+    PurpleAccount *account;
+    char *username;
+    const char *accountname, *proto;
+    ConnContext *context;
+
+    account = purple_conversation_get_account(conv);
+    accountname = purple_account_get_username(account);
+    proto = purple_account_get_protocol_id(account);
+    username = g_strdup(
+	    purple_normalize(account, purple_conversation_get_name(conv)));
+
+    context = otrl_context_find(otrg_plugin_userstate, username, accountname,
+	    proto, their_instance, force_create, NULL, NULL, NULL);
+
+    g_free(username);
+
+    return context;
+}
+
+/* Given a PurpleConversation, return the selected instag */
+otrl_instag_t otrg_plugin_conv_to_selected_instag(PurpleConversation *conv,
+	otrl_instag_t default_val)
+{
+    otrl_instag_t selected_instance;
+
+    if (!g_hash_table_lookup_extended(conv->data, "otr-ui_selected_ctx", NULL,
+	    (void**)&selected_instance)) {
+	selected_instance = default_val;
+    }
+
+    return selected_instance;
+}
+
+/* Given a PurpleConversation, return the selected ConnContext */
+ConnContext* otrg_plugin_conv_to_selected_context(PurpleConversation *conv,
+	int force_create)
+{
+    otrl_instag_t selected_instance;
+    ConnContext *result = NULL;
+
+    selected_instance = otrg_plugin_conv_to_selected_instag(conv,
+	    OTRL_INSTAG_BEST);
+
+    return otrg_plugin_conv_to_context(conv, selected_instance, force_create);
+}
+
 static void process_conv_create(PurpleConversation *conv, void *data)
 {
-    if (conv) otrg_dialog_new_conv(conv);
+    if (!conv) return;
+
+    purple_conversation_set_data(conv, "otr-ui_selected_ctx",
+	    (gpointer)OTRL_INSTAG_BEST);
+    otrg_dialog_new_conv(conv);
 }
 
 static void process_conv_updated(PurpleConversation *conv,
@@ -794,7 +939,7 @@ static void process_conv_updated(PurpleConversation *conv,
 	PurpleAccount *account = purple_conversation_get_account(conv);
 	otrg_ui_get_prefs(&prefs, account, purple_conversation_get_name(conv));
 
-	context = otrg_plugin_conv_to_context(conv);
+	context = otrg_plugin_conv_to_selected_context(conv, 0);
 	if (context && prefs.avoid_logging_otr &&
 		context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
 		conv->logging == TRUE) {
@@ -803,6 +948,11 @@ static void process_conv_updated(PurpleConversation *conv,
     }
 }
 
+static void process_conv_destroyed(PurpleConversation *conv)
+{
+    g_hash_table_remove(conv->data, "otr-ui_selected_ctx");
+}
+
 static void process_connection_change(PurpleConnection *conn, void *data)
 {
     /* If we log in or out of a connection, make sure all of the OTR
@@ -840,12 +990,21 @@ static void supply_extended_menu(PurpleBlistNode *node, GList **menu)
     *menu = g_list_append(*menu, act);
 }
 
+/* Disconnect all context instances, sending a notice to the other side, if
+ * appropriate. */
+void otrg_plugin_disconnect_all_instances(ConnContext *context)
+{
+    otrl_message_disconnect_all_instances(otrg_plugin_userstate, &ui_ops, NULL,
+	    context->accountname, context->protocol, context->username);
+}
+
 /* Disconnect a context, sending a notice to the other side, if
  * appropriate. */
 void otrg_plugin_disconnect(ConnContext *context)
 {
     otrl_message_disconnect(otrg_plugin_userstate, &ui_ops, NULL,
-	    context->accountname, context->protocol, context->username);
+	    context->accountname, context->protocol, context->username,
+	    context->their_instance);
 }
 
 /* Write the fingerprints to disk. */
@@ -869,27 +1028,6 @@ void otrg_plugin_write_fingerprints(void)
     fclose(storef);
 }
 
-/* Find the ConnContext appropriate to a given PurpleConversation. */
-ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv)
-{
-    PurpleAccount *account;
-    char *username;
-    const char *accountname, *proto;
-    ConnContext *context;
-
-    account = purple_conversation_get_account(conv);
-    accountname = purple_account_get_username(account);
-    proto = purple_account_get_protocol_id(account);
-    username = g_strdup(
-	    purple_normalize(account, purple_conversation_get_name(conv)));
-
-    context = otrl_context_find(otrg_plugin_userstate, username, accountname,
-	    proto, 0, NULL, NULL, NULL);
-    g_free(username);
-
-    return context;
-}
-
 /* Find the PurpleConversation appropriate to the given userinfo.  If
  * one doesn't yet exist, create it if force_create is true. */
 PurpleConversation *otrg_plugin_userinfo_to_conv(const char *accountname,
@@ -925,7 +1063,8 @@ TrustLevel otrg_plugin_context_to_trust(ConnContext *context)
     TrustLevel level = TRUST_NOT_PRIVATE;
 
     if (context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-	if (context->active_fingerprint->trust &&
+	if (context->active_fingerprint &&
+		context->active_fingerprint->trust &&
 		context->active_fingerprint->trust[0] != '\0') {
 	    level = TRUST_PRIVATE;
 	} else {
@@ -978,7 +1117,7 @@ static void mms_read_FILEp(FILE *mmsf, GHashTable *ght)
 	mms = tab + 1;
 	tab = strchr(mms, '\t');
 	if (tab) continue;
-        eol = strchr(mms, '\r');
+	eol = strchr(mms, '\r');
 	if (!eol) eol = strchr(mms, '\n');
 	if (!eol) continue;
 	*eol = '\0';
@@ -1006,10 +1145,10 @@ static void otrg_init_mms_table()
      * protocols.  These can be overridden in the user's MAXMSGSIZEFNAME
      * file. */
     static const struct s_OtrgIdProtPair {
-        char *protid;
+	char *protid;
 	int maxmsgsize;
     } mmsPairs[8] = {{"prpl-msn", 1409}, {"prpl-icq", 2346},
-	{"prpl-aim", 2343}, {"prpl-yahoo", 832}, {"prpl-gg", 1999},
+	{"prpl-aim", 2343}, {"prpl-yahoo", 799}, {"prpl-gg", 1999},
 	{"prpl-irc", 417}, {"prpl-oscar", 2343}, {NULL, 0}};
     int i = 0;
     gchar *maxmsgsizefile;
@@ -1019,10 +1158,10 @@ static void otrg_init_mms_table()
 	    otrg_str_free, otrg_int_free);
 
     for (i=0; mmsPairs[i].protid != NULL; i++) {
-    	char* nextprot = g_strdup(mmsPairs[i].protid);
-    	int* nextsize = g_malloc(sizeof(int));
-    	*nextsize = mmsPairs[i].maxmsgsize;
-    	g_hash_table_insert(mms_table, nextprot, nextsize);
+	char* nextprot = g_strdup(mmsPairs[i].protid);
+	int* nextsize = g_malloc(sizeof(int));
+	*nextsize = mmsPairs[i].maxmsgsize;
+	g_hash_table_insert(mms_table, nextprot, nextsize);
     }
 
     maxmsgsizefile = g_build_filename(purple_user_dir(),
@@ -1049,23 +1188,28 @@ static gboolean otr_plugin_load(PurplePlugin *handle)
     gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME,
 	    NULL);
     gchar *storefile = g_build_filename(purple_user_dir(), STOREFNAME, NULL);
+    gchar *instagfile = g_build_filename(purple_user_dir(), INSTAGFNAME, NULL);
     void *conv_handle = purple_conversations_get_handle();
     void *conn_handle = purple_connections_get_handle();
     void *blist_handle = purple_blist_get_handle();
     void *core_handle = purple_get_core();
     FILE *privf;
     FILE *storef;
+    FILE *instagf;
 
-    if (!privkeyfile || !storefile) {
+    if (!privkeyfile || !storefile || !instagfile) {
 	g_free(privkeyfile);
 	g_free(storefile);
+	g_free(instagfile);
 	return 0;
     }
 
     privf = g_fopen(privkeyfile, "rb");
     storef = g_fopen(storefile, "rb");
+    instagf = g_fopen(instagfile, "rb");
     g_free(privkeyfile);
     g_free(storefile);
+    g_free(instagfile);
 
     otrg_init_mms_table();
 
@@ -1077,21 +1221,25 @@ static gboolean otr_plugin_load(PurplePlugin *handle)
     otrl_privkey_read_FILEp(otrg_plugin_userstate, privf);
     otrl_privkey_read_fingerprints_FILEp(otrg_plugin_userstate, storef,
 	    NULL, NULL);
+    otrl_instag_read_FILEp(otrg_plugin_userstate, instagf);
     if (privf) fclose(privf);
     if (storef) fclose(storef);
+    if (instagf) fclose(instagf);
 
     otrg_ui_update_fingerprint();
 
     purple_signal_connect(core_handle, "quitting", otrg_plugin_handle,
 	    PURPLE_CALLBACK(process_quitting), NULL);
     purple_signal_connect(conv_handle, "sending-im-msg", otrg_plugin_handle,
-            PURPLE_CALLBACK(process_sending_im), NULL);
+	    PURPLE_CALLBACK(process_sending_im), NULL);
     purple_signal_connect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
-            PURPLE_CALLBACK(process_receiving_im), NULL);
+	    PURPLE_CALLBACK(process_receiving_im), NULL);
     purple_signal_connect(conv_handle, "conversation-updated",
 	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_updated), NULL);
     purple_signal_connect(conv_handle, "conversation-created",
 	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create), NULL);
+    purple_signal_connect(conv_handle, "deleting-conversation",
+	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_destroyed), NULL);
     purple_signal_connect(conn_handle, "signed-on", otrg_plugin_handle,
 	    PURPLE_CALLBACK(process_connection_change), NULL);
     purple_signal_connect(conn_handle, "signed-off", otrg_plugin_handle,
@@ -1130,6 +1278,8 @@ static gboolean otr_plugin_unload(PurplePlugin *handle)
 	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_updated));
     purple_signal_disconnect(conv_handle, "conversation-created",
 	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create));
+    purple_signal_disconnect(conv_handle, "deleting-conversation",
+	    otrg_plugin_handle, PURPLE_CALLBACK(process_conv_destroyed));
     purple_signal_disconnect(conn_handle, "signed-on", otrg_plugin_handle,
 	    PURPLE_CALLBACK(process_connection_change));
     purple_signal_disconnect(conn_handle, "signed-off", otrg_plugin_handle,
@@ -1174,8 +1324,8 @@ static PurplePluginInfo info =
 {
 	PURPLE_PLUGIN_MAGIC,
 
-        /* Use the 2.0.x API */
-        2,                                                /* major version  */
+	/* Use the 2.0.x API */
+	2,                                                /* major version  */
 	0,                                                /* minor version  */
 
 	PURPLE_PLUGIN_STANDARD,                           /* type           */
@@ -1188,7 +1338,7 @@ static PurplePluginInfo info =
 	PIDGIN_OTR_VERSION,                               /* version        */
 	NULL,                                             /* summary        */
 	NULL,                                             /* description    */
-	                                                  /* author         */
+							  /* author         */
 	"Ian Goldberg, Rob Smits,\n"
 	    "\t\t\tChris Alexander, Willy Lew, Nikita Borisov\n"
 	    "\t\t\t<otr at cypherpunks.ca>",
@@ -1229,7 +1379,7 @@ __init_plugin(PurplePlugin *plugin)
     info.name        = _("Off-the-Record Messaging");
     info.summary     = _("Provides private and secure conversations");
     info.description = _("Preserves the privacy of IM communications "
-                         "by providing encryption, authentication, "
+			 "by providing encryption, authentication, "
 			 "deniability, and perfect forward secrecy.");
 }
 
diff --git a/otr-plugin.h b/otr-plugin.h
index 97afced..f3c7b7e 100644
--- a/otr-plugin.h
+++ b/otr-plugin.h
@@ -1,8 +1,8 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
- *                           Nikita Borisov
+ *                           Lisa Du, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -29,15 +29,25 @@
 /* libotr headers */
 #include <libotr/context.h>
 #include <libotr/userstate.h>
+#include <libotr/instag.h>
 
 #define PRIVKEYFNAME "otr.private_key"
 #define STOREFNAME "otr.fingerprints"
+#define INSTAGFNAME "otr.instance_tags"
 #define MAXMSGSIZEFNAME "otr.max_message_size"
 
 extern PurplePlugin *otrg_plugin_handle;
 
 extern OtrlUserState otrg_plugin_userstate;
 
+/* Given a PurpleConversation, return the selected ConnContext */
+ConnContext* otrg_plugin_conv_to_selected_context(PurpleConversation *conv,
+	int force_create);
+
+/* Given a PurpleConversation, return the selected instag */
+otrl_instag_t otrg_plugin_conv_to_selected_instag(PurpleConversation *conv,
+	otrl_instag_t default_value);
+
 /* Send an IM from the given account to the given recipient.  Display an
  * error dialog if that account isn't currently logged in. */
 void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
@@ -47,6 +57,10 @@ void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
 void otrg_plugin_create_privkey(const char *accountname,
 	const char *protocol);
 
+/* Generate a instance tag for the given accountname/protocol */
+void otrg_plugin_create_instag(const char *accountname,
+	const char *protocol);
+
 /* Start the Socialist Millionaires' Protocol over the current connection,
  * using the given initial secret, and optionally a question to pass to
  * the buddy. */
@@ -77,7 +91,8 @@ void otrg_plugin_disconnect(ConnContext *context);
 void otrg_plugin_write_fingerprints(void);
 
 /* Find the ConnContext appropriate to a given PurpleConversation. */
-ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv);
+ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv,
+	otrl_instag_t their_instance, int force_create);
 
 /* Find the PurpleConversation appropriate to the given userinfo.  If
  * one doesn't yet exist, create it if force_create is true. */
@@ -89,6 +104,11 @@ PurpleConversation *otrg_plugin_userinfo_to_conv(const char *accountname,
 PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
 	int force_create);
 
+/* Cause the "msg-received" signal to be emitted with the given message
+ * payload. This causes the message to pop-up in the notification area when the
+ * user has the libnotify plugin enabled. */
+void otrg_emit_msg_received(ConnContext *context, const char* message);
+
 typedef enum {
     TRUST_NOT_PRIVATE,
     TRUST_UNVERIFIED,
diff --git a/packaging/fedora/pidgin-otr.spec b/packaging/fedora/pidgin-otr.spec
index 705360f..15fa31f 100644
--- a/packaging/fedora/pidgin-otr.spec
+++ b/packaging/fedora/pidgin-otr.spec
@@ -1,19 +1,20 @@
 Summary: Off-The-Record Messaging plugin for pidgin
 Name: pidgin-otr
-Version: 3.1.0
+Version: 4.0.0
 Release: 1%{?dist}
 Source: http://otr.cypherpunks.ca/%{name}-%{version}.tar.gz
 Url: http://otr.cypherpunks.ca/
-License: GPL
+License: GPLv2
 Group: Applications/Internet
 Provides: gaim-otr = %{version}
 Obsoletes: gaim-otr
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: pidgin >= 2.0.0, libotr >= 3.1.0
-BuildRequires: glib2-devel, gtk2-devel, libgcrypt-devel >= 1.2.0, libgpg-error-devel, pidgin-devel >= 2.0.0, libotr-devel >= 3.1.0, libpurple-devel 
+Requires: pidgin >= 2.0.0, libotr >= 4.0.0
+BuildRequires: glib2-devel, gtk2-devel, libgcrypt-devel >= 1.2.0
+BuildRequires: libgpg-error-devel, pidgin-devel >= 2.0.0
+BuildRequires: libotr-devel >= 4.0.0, perl(XML::Parser), gettext
 
 %description 
-
 This is a pidgin plugin which implements Off-the-Record (OTR) Messaging.
 It is known to work (at least) under the Linux and Windows versions of
 pidgin (2.x).
@@ -22,6 +23,11 @@ pidgin (2.x).
 %setup -q
 
 %build
+if [ \! -f configure ]; then
+	echo "Building from pre-release"
+	intltoolize --force --copy
+	autoreconf -s -i
+fi
 
 %configure 
 make %{?_smp_mflags} all
@@ -44,6 +50,11 @@ rm -rf $RPM_BUILD_ROOT
 %{_libdir}/pidgin/pidgin-otr.so
 
 %changelog
+* Thu Jun 11 2009 Paul Wouters <paul at xelerance.com> - 4.0.0-1
+- Upgraded to 4.0.0.
+- Updated buildrequires
+- Fix license tag
+
 * Thu Jul 26 2007 Paul Wouters <paul at cypherpunks.ca> 3.1.0-preview2
 - Added locale support to spec file
 - Upgraded to current version
diff --git a/packaging/windows/pidgin-otr.nsi b/packaging/windows/pidgin-otr.nsi
index 54beb3d..b667a37 100644
--- a/packaging/windows/pidgin-otr.nsi
+++ b/packaging/windows/pidgin-otr.nsi
@@ -3,6 +3,7 @@
 ;
 ; known issue. installer induced uninstaller abortion causes overwrite
 ; by installer without uninstall.
+; v4.0.0   - New source version.
 ; v3.2.0   - New source version.
 ; v3.1.0   - New source version.  Install and uninstall i18n files.
 ; v3.0.0   - Version for pidgin-2.0.0
@@ -25,7 +26,7 @@
 ; todo: SetBrandingImage
 ; HM NIS Edit Wizard helper defines
 !define PRODUCT_NAME "pidgin-otr"
-!define PRODUCT_VERSION "3.2.0-1"
+!define PRODUCT_VERSION "4.0.0-0"
 !define PRODUCT_PUBLISHER "Cypherpunks CA"
 !define PRODUCT_WEB_SITE "http://otr.cypherpunks.ca/"
 !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
@@ -42,7 +43,7 @@
 ; Welcome page
 !insertmacro MUI_PAGE_WELCOME
 ; License page
-!insertmacro MUI_PAGE_LICENSE "c:\otr\COPYING.txt"
+!insertmacro MUI_PAGE_LICENSE "../../../libotr/COPYING"
 ; Directory page
 !insertmacro MUI_PAGE_DIRECTORY
 ; Instfiles page
@@ -81,29 +82,29 @@ Section "MainSection" SEC01
 
     SetOutPath "$PidginDir\locale"
     SetOverwrite on
-    ; What the next line means is to recursively search c:\otr\locale
+    ; What the next line means is to recursively search /usr/share/locale
     ; and install all files under there named pidgin-otr.mo
-    File /r "c:\otr\locale\pidgin-otr.mo"
+    File /r "/usr/share/locale/pidgin-otr.mo"
 
     SetOutPath "$INSTDIR"
     SetOverwrite on
-    File "c:\otr\pidgin-otr.dll"
+    File "../../win32_export/pidgin-otr.dll"
     ; move to pidgin plugin directory, check if not busy (pidgin is running)
     call CopyDLL
     ; hard part is done, do the rest now.
     SetOverwrite on	  
-    File "c:\otr\README.Toolkit.txt"
-    File "c:\otr\README.txt"
-    File "c:\otr\Protocol-v2.html"
-    File "c:\otr\COPYING.txt"
-    File "c:\otr\COPYING.LIB.txt"
-    File "c:\otr\otr_mackey.exe"
-    File "c:\otr\otr_modify.exe"
-    File "c:\otr\otr_parse.exe"
-    File "c:\otr\otr_readforge.exe"
-    File "c:\otr\otr_remac.exe"
-    File "c:\otr\otr_sesskeys.exe"
-    File "c:\otr\pidgin-otr.nsi"
+    File "../../win32_export/README.Toolkit.txt"
+    File "../../win32_export/README.txt"
+    File "../../win32_export/COPYING.txt"
+    File "../../win32_export/COPYING.LIB.txt"
+    File "../../win32_export/Protocol-v2.html"
+    File "../../win32_export/otr_mackey.exe"
+    File "../../win32_export/otr_modify.exe"
+    File "../../win32_export/otr_parse.exe"
+    File "../../win32_export/otr_readforge.exe"
+    File "../../win32_export/otr_remac.exe"
+    File "../../win32_export/otr_sesskeys.exe"
+    File "pidgin-otr.nsi"
 SectionEnd
 
 Section -AdditionalIcons
diff --git a/tooltipmenu.c b/tooltipmenu.c
index 880cebb..1478d27 100644
--- a/tooltipmenu.c
+++ b/tooltipmenu.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -53,8 +53,8 @@
  * Enums
  *****************************************************************************/
 enum {
-        PROP_ZERO = 0,
-        PROP_BOX
+	PROP_ZERO = 0,
+	PROP_BOX
 };
 
 /******************************************************************************
@@ -88,69 +88,69 @@ tooltip_menu_deselect(GtkItem *item) {
  *****************************************************************************/
 static void
 tooltip_menu_get_property(GObject *obj, guint param_id, GValue *value,
-                                                                GParamSpec *pspec)
+								GParamSpec *pspec)
 {
-        TooltipMenu *tooltip_menu = TOOLTIP_MENU(obj);
-
-        switch(param_id) {
-                case PROP_BOX:
-                        g_value_set_object(value, tooltip_menu_get_box(tooltip_menu));
-                        break;
-                default:
-                        G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-                        break;
-        }
+	TooltipMenu *tooltip_menu = TOOLTIP_MENU(obj);
+
+	switch(param_id) {
+		case PROP_BOX:
+			g_value_set_object(value, tooltip_menu_get_box(tooltip_menu));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
 }
 
 static void
 tooltip_menu_finalize(GObject *obj) {
 #if 0
-        /* This _might_ be leaking, but I have a sneaking suspicion that the widget is
-         * getting destroyed in GtkContainer's finalize function.  But if were are
-         * leaking here, be sure to figure out why this causes a crash.
-         *      -- Gary
-         */
-        TooltipMenu *tray = TOOLTIP_MENU(obj);
-
-        if(GTK_IS_WIDGET(tray->tray))
-                gtk_widget_destroy(GTK_WIDGET(tray->tray));
+	/* This _might_ be leaking, but I have a sneaking suspicion that the widget is
+	 * getting destroyed in GtkContainer's finalize function.  But if were are
+	 * leaking here, be sure to figure out why this causes a crash.
+	 *      -- Gary
+	 */
+	TooltipMenu *tray = TOOLTIP_MENU(obj);
+
+	if(GTK_IS_WIDGET(tray->tray))
+		gtk_widget_destroy(GTK_WIDGET(tray->tray));
 #endif
 
-        G_OBJECT_CLASS(parent_class)->finalize(obj);
+	G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
 static void
 tooltip_menu_class_init(TooltipMenuClass *klass) {
-        GObjectClass *object_class = G_OBJECT_CLASS(klass);
-        GParamSpec *pspec;
+	GObjectClass *object_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
 
-        parent_class = g_type_class_peek_parent(klass);
+	parent_class = g_type_class_peek_parent(klass);
 
-        object_class->finalize = tooltip_menu_finalize;
-        object_class->get_property = tooltip_menu_get_property;
+	object_class->finalize = tooltip_menu_finalize;
+	object_class->get_property = tooltip_menu_get_property;
 
-        pspec = g_param_spec_object("box", "The box",
+	pspec = g_param_spec_object("box", "The box",
 		"The box",
 		GTK_TYPE_BOX,
 		G_PARAM_READABLE);
-        g_object_class_install_property(object_class, PROP_BOX, pspec);
+	g_object_class_install_property(object_class, PROP_BOX, pspec);
 }
 
 static void
 tooltip_menu_init(TooltipMenu *tooltip_menu) {
-        GtkWidget *widget = GTK_WIDGET(tooltip_menu);
-        gtk_menu_item_set_right_justified(GTK_MENU_ITEM(tooltip_menu), TRUE);
+	GtkWidget *widget = GTK_WIDGET(tooltip_menu);
+	gtk_menu_item_set_right_justified(GTK_MENU_ITEM(tooltip_menu), TRUE);
 
-        if(!GTK_IS_WIDGET(tooltip_menu->tray))
-                tooltip_menu->tray = gtk_hbox_new(FALSE, 0);
+	if(!GTK_IS_WIDGET(tooltip_menu->tray))
+		tooltip_menu->tray = gtk_hbox_new(FALSE, 0);
 
-        tooltip_menu->tooltips = gtk_tooltips_new();
+	tooltip_menu->tooltips = gtk_tooltips_new();
 
-        gtk_widget_set_size_request(widget, -1, -1);
+	gtk_widget_set_size_request(widget, -1, -1);
 
-        gtk_container_add(GTK_CONTAINER(tooltip_menu), tooltip_menu->tray);
+	gtk_container_add(GTK_CONTAINER(tooltip_menu), tooltip_menu->tray);
 
-        gtk_widget_show(tooltip_menu->tray);
+	gtk_widget_show(tooltip_menu->tray);
 }
 
 /******************************************************************************
@@ -158,96 +158,96 @@ tooltip_menu_init(TooltipMenu *tooltip_menu) {
  *****************************************************************************/
 GType
 tooltip_menu_get_gtype(void) {
-        static GType type = 0;
-
-        if(type == 0) {
-                static const GTypeInfo info = {
-                        sizeof(TooltipMenuClass),
-                        NULL,
-                        NULL,
-                        (GClassInitFunc)tooltip_menu_class_init,
-                        NULL,
-                        NULL,
-                        sizeof(TooltipMenu),
-                        0,
-                        (GInstanceInitFunc)tooltip_menu_init,
-                        NULL
-                };
-
-                type = g_type_register_static(GTK_TYPE_MENU_ITEM,
-                                                                          "TooltipMenu",
-                                                                          &info, 0);
-        }
-
-        return type;
+	static GType type = 0;
+
+	if(type == 0) {
+		static const GTypeInfo info = {
+			sizeof(TooltipMenuClass),
+			NULL,
+			NULL,
+			(GClassInitFunc)tooltip_menu_class_init,
+			NULL,
+			NULL,
+			sizeof(TooltipMenu),
+			0,
+			(GInstanceInitFunc)tooltip_menu_init,
+			NULL
+		};
+
+		type = g_type_register_static(GTK_TYPE_MENU_ITEM,
+									  "TooltipMenu",
+									  &info, 0);
+	}
+
+	return type;
 }
 
 GtkWidget *
 tooltip_menu_new() {
-        return g_object_new(TYPE_TOOLTIP_MENU, NULL);
+	return g_object_new(TYPE_TOOLTIP_MENU, NULL);
 }
 
 GtkWidget *
 tooltip_menu_get_box(TooltipMenu *tooltip_menu) {
-        g_return_val_if_fail(IS_TOOLTIP_MENU(tooltip_menu), NULL);
-        return tooltip_menu->tray;
+	g_return_val_if_fail(IS_TOOLTIP_MENU(tooltip_menu), NULL);
+	return tooltip_menu->tray;
 }
 
 static void
 tooltip_menu_add(TooltipMenu *tooltip_menu, GtkWidget *widget,
-                                           const char *tooltip, gboolean prepend)
+					   const char *tooltip, gboolean prepend)
 {
-        g_return_if_fail(IS_TOOLTIP_MENU(tooltip_menu));
-        g_return_if_fail(GTK_IS_WIDGET(widget));
+	g_return_if_fail(IS_TOOLTIP_MENU(tooltip_menu));
+	g_return_if_fail(GTK_IS_WIDGET(widget));
 
-        if (GTK_WIDGET_NO_WINDOW(widget))
-        {
-                GtkWidget *event;
+	if (GTK_WIDGET_NO_WINDOW(widget))
+	{
+		GtkWidget *event;
 
-                event = gtk_event_box_new();
-                gtk_container_add(GTK_CONTAINER(event), widget);
-                gtk_widget_show(event);
-                widget = event;
-        }
+		event = gtk_event_box_new();
+		gtk_container_add(GTK_CONTAINER(event), widget);
+		gtk_widget_show(event);
+		widget = event;
+	}
 
-        tooltip_menu_set_tooltip(tooltip_menu, widget, tooltip);
+	tooltip_menu_set_tooltip(tooltip_menu, widget, tooltip);
 
-        if (prepend)
-                gtk_box_pack_start(GTK_BOX(tooltip_menu->tray), widget, FALSE, FALSE, 0);
-        else
-                gtk_box_pack_end(GTK_BOX(tooltip_menu->tray), widget, FALSE, FALSE, 0);
+	if (prepend)
+		gtk_box_pack_start(GTK_BOX(tooltip_menu->tray), widget, FALSE, FALSE, 0);
+	else
+		gtk_box_pack_end(GTK_BOX(tooltip_menu->tray), widget, FALSE, FALSE, 0);
 }
 
 void
 tooltip_menu_append(TooltipMenu *tooltip_menu, GtkWidget *widget, const char *tooltip)
 {
-        tooltip_menu_add(tooltip_menu, widget, tooltip, FALSE);
+	tooltip_menu_add(tooltip_menu, widget, tooltip, FALSE);
 }
 
 void
 tooltip_menu_prepend(TooltipMenu *tooltip_menu, GtkWidget *widget, const char *tooltip)
 {
-        tooltip_menu_add(tooltip_menu, widget, tooltip, TRUE);
+	tooltip_menu_add(tooltip_menu, widget, tooltip, TRUE);
 }
 
 void
 tooltip_menu_set_tooltip(TooltipMenu *tooltip_menu, GtkWidget *widget, const char *tooltip)
 {
-        if (!tooltip_menu->tooltips)
-                return;
-
-        /* Should we check whether widget is a child of tooltip_menu? */
-
-        /*
-         * If the widget does not have it's own window, then it
-         * must have automatically been added to an event box
-         * when it was added to the menu tray.  If this is the
-         * case, we want to set the tooltip on the widget's parent,
-         * not on the widget itself.
-         */
-        if (GTK_WIDGET_NO_WINDOW(widget))
-                widget = widget->parent;
-
-        gtk_tooltips_set_tip(tooltip_menu->tooltips, widget, tooltip, NULL);
+	if (!tooltip_menu->tooltips)
+		return;
+
+	/* Should we check whether widget is a child of tooltip_menu? */
+
+	/*
+	 * If the widget does not have it's own window, then it
+	 * must have automatically been added to an event box
+	 * when it was added to the menu tray.  If this is the
+	 * case, we want to set the tooltip on the widget's parent,
+	 * not on the widget itself.
+	 */
+	if (GTK_WIDGET_NO_WINDOW(widget))
+		widget = widget->parent;
+
+	gtk_tooltips_set_tip(tooltip_menu->tooltips, widget, tooltip, NULL);
 }
 
diff --git a/tooltipmenu.h b/tooltipmenu.h
index b5395d0..24a6ee5 100644
--- a/tooltipmenu.h
+++ b/tooltipmenu.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -57,13 +57,13 @@ typedef struct _TooltipMenu                          TooltipMenu;
 typedef struct _TooltipMenuClass             TooltipMenuClass;
 
 struct _TooltipMenu {
-        GtkMenuItem gparent;                                    /**< The parent instance */
-        GtkWidget *tray;                                                /**< The tray */
-        GtkTooltips *tooltips;                                  /**< Tooltips */
+	GtkMenuItem gparent;                                    /**< The parent instance */
+	GtkWidget *tray;                                                /**< The tray */
+	GtkTooltips *tooltips;                                  /**< Tooltips */
 };
 
 struct _TooltipMenuClass {
-        GtkMenuItemClass gparent;                               /**< The parent class */
+	GtkMenuItemClass gparent;                               /**< The parent class */
 };
 
 G_BEGIN_DECLS
diff --git a/ui.c b/ui.c
index 95a32ea..47477f0 100644
--- a/ui.c
+++ b/ui.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>
@@ -99,52 +99,59 @@ void otrg_ui_connect_connection(ConnContext *context)
     /* Send an OTR Query to the other side. */
     PurpleAccount *account;
     char *msg;
-	
+
     /* Don't do this if we're already ENCRYPTED */
     if (context == NULL || context->msgstate == OTRL_MSGSTATE_ENCRYPTED)
 	return;
-	
+
     account = purple_accounts_find(context->accountname, context->protocol);
     if (!account) {
 	PurplePlugin *p = purple_find_prpl(context->protocol);
 	msg = g_strdup_printf(_("Account %s (%s) could not be found"),
-		  context->accountname,
-		  (p && p->info->name) ? p->info->name : _("Unknown"));
+		context->accountname,
+		(p && p->info->name) ? p->info->name : _("Unknown"));
 	otrg_dialog_notify_error(context->accountname, context->protocol,
 		context->username, _("Account not found"), msg, NULL);
 	g_free(msg);
 	return;
     }
-    otrg_plugin_send_default_query(context, account);	
+    otrg_plugin_send_default_query(context, account);
 }
 
 /* Drop a context to PLAINTEXT state */
 void otrg_ui_disconnect_connection(ConnContext *context)
 {
-    /* Don't do anything with PLAINTEXT fingerprints */
-    if (context == NULL || context->msgstate == OTRL_MSGSTATE_PLAINTEXT)
+
+    if (context == NULL)
 	return;
-		
+
     otrg_plugin_disconnect(context);
-    otrg_dialog_disconnected(context);	
+    otrg_dialog_disconnected(context);
 }
 
 /* Forget a fingerprint */
 void otrg_ui_forget_fingerprint(Fingerprint *fingerprint)
 {
     ConnContext *context;
-	
+    ConnContext *context_iter;
+
     if (fingerprint == NULL) return;
 
     /* Don't do anything with the active fingerprint if we're in the
      * ENCRYPTED state. */
     context = fingerprint->context;
-    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-	    context->active_fingerprint == fingerprint) return;
-	
+
+    for (context_iter = context->m_context;
+	    context_iter && context_iter->m_context == context->m_context;
+	    context_iter = context_iter->next) {
+
+	if (context_iter->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+		context_iter->active_fingerprint == fingerprint) return;
+    }
+
     otrl_context_forget_fingerprint(fingerprint, 1);
     otrg_plugin_write_fingerprints();
-	
+
     otrg_ui_update_keylist();
 }
 
@@ -164,7 +171,7 @@ void otrg_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
     const char *proto = purple_account_get_protocol_id(account);
     if (!otrg_plugin_proto_supports_otr(proto)) {
 	prefsp->policy = OTRL_POLICY_NEVER;
-	prefsp->avoid_logging_otr = FALSE;
+	prefsp->avoid_logging_otr = TRUE;
 	prefsp->show_otr_button = FALSE;
 	return;
     }
@@ -175,6 +182,6 @@ void otrg_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
     }
     /* If we've got no other way to get the prefs, use sensible defaults */
     prefsp->policy = OTRL_POLICY_DEFAULT;
-    prefsp->avoid_logging_otr = FALSE;
+    prefsp->avoid_logging_otr = TRUE;
     prefsp->show_otr_button = FALSE;
 }
diff --git a/ui.h b/ui.h
index 9ef64e6..c1c6481 100644
--- a/ui.h
+++ b/ui.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2009  Ian Goldberg, Rob Smits,
+ *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits,
  *                           Chris Alexander, Willy Lew,
  *                           Nikita Borisov
  *                           <otr at cypherpunks.ca>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-otr/packages/irssi-plugin-otr.git



More information about the Pkg-otr-team mailing list