[pkg-otr-team] [irssi-plugin-otr] 26/167: Merged Version_3_Dev branch

Holger Levsen holger at moszumanska.debian.org
Mon Mar 3 21:55:29 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 f0c6b8e1391b7d1397b9c973477d75a31fbfea5c
Author: cypherpunk <cypherpunk>
Date:   Tue Jul 24 19:34:04 2007 +0000

    Merged Version_3_Dev branch
---
 AUTHORS                                            |    2 +-
 ChangeLog                                          |   81 ++
 INSTALL                                            |   19 +-
 Makefile.am                                        |   16 +-
 Makefile.mingw                                     |   20 +-
 Makefile.static                                    |   21 +
 NEWS                                               |   11 +
 README                                             |   19 +-
 configure.ac                                       |   22 +-
 dialogs.c                                          |   26 +-
 dialogs.h                                          |   23 +-
 gtk-dialog.c                                       | 1034 ++++++++++++++++----
 gtk-dialog.h                                       |    2 +-
 gtk-ui.c                                           |  234 +++--
 gtk-ui.h                                           |    2 +-
 makedist                                           |    1 +
 otr-plugin.c                                       |  364 +++++--
 otr-plugin.h                                       |   19 +-
 .../fedora/{gaim-otr.spec => pidgin-otr.spec}      |   18 +-
 packaging/windows/gaim-otr.nsi                     |  221 -----
 po/ChangeLog                                       |    0
 po/Makefile.mingw                                  |   57 ++
 po/POTFILES.in                                     |   12 +
 po/README                                          |    7 +
 ui.c                                               |   50 +-
 ui.h                                               |   26 +-
 26 files changed, 1693 insertions(+), 614 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index dc38a08..f9406d8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,6 +2,6 @@ Off-the-Record Messaging plugin for pidgin
 
 Authors:
 
-    Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+    Ian Goldberg, Chris Alexander, Nikita Borisov <otr at cypherpunks.ca>
 
 See the README file for mailing list information
diff --git a/ChangeLog b/ChangeLog
index 18bdd3e..b9fd875 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,84 @@
+2007-07-24
+
+	* INSTALL: Added information about i18n; added information about
+	linking libgcrypt statically
+
+	* otr-plugin.c: Added support for transparent fragmentation of
+	large messages
+
+	* Most files: Updated copyright information
+
+2007-07-22
+
+	* configure.ac: Check for libpurple and use a recent format for
+	AC_INIT/AM_INIT_AUTOMAKE, based on a patch from <synx13 at sf.net>.
+
+2007-07-18
+
+	* gtk-ui.c: Simplify account list option menu in Config UI,
+	thanks to Gabriel Schulhof <nix at go-nix.ca>.
+
+	* gtk-ui.c: Reorder the tabs in the Config UI so that Config is
+	first.
+
+	* README: Updated to reflect new tab ordering, and new
+	"Don't log OTR conversations" option.
+
+	* gtk-dialog.c: Fixed a bug in the verify fingerprint dialog in
+	the unlikely event that a fingerprint exists in the fingerprints
+	file with no corresponding key for the local account in the
+	private keys file.
+
+2007-07-17
+
+	* dialogs.c:
+	* dialogs.h:
+	* gtk-dialog.c: Removed Verify fingerprint and View secure
+	session id menu options.  Added "Advanced..." button to
+	Authenticate buddy dialog, which brings up the old Verify
+	fingerprint dialog.  The Authenticate buddy dialog now allows
+	users to authenticate their buddies without ever seeing a
+	fingerprint.
+
+2007-07-10
+
+	* dialogs.c:
+	* dialogs.h:
+	* gtk-dialog.c:
+	* gtk-ui.c:
+	* otr-plugin.c:
+	* ui.c:
+	* ui.h: Added init() and cleanup() callbacks to the ui and
+	dialog subsystems that are called when the plugin is loaded and
+	unloaded respectively.
+
+	* gtk-dialog.c: Fixed bug where multiple OTR buttons in one
+	window would cause pidgin to crash when the window is closed.
+
+2007-07-08
+
+	* otr-plugin.c:
+	* gtk-dialog.c:
+	* gtk-ui.c: Added an option to not log OTR conversations.
+
+	* gtk-ui.c:
+	* ui.c:
+	* ui.h: Generalized find_policy to get_prefs, allowing for other
+	preferences to be available.
+
+2007-07-07
+
+	* INSTALL: Update dependency information
+
+	* Makefile.am:
+	* Makefile.mingw:
+	* configure.ac:
+	* gtk-dialog.c:
+	* gtk-ui.c:
+	* makedist:
+	* otr-plugin.c:
+	* ui.c: i18n, thanks to Thomas B. <Tommy.B at gmx.net>.
+
 2007-05-06
 
 	* Makefile.am:
diff --git a/INSTALL b/INSTALL
index ea0437e..7c4aacc 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,12 +1,15 @@
 REQUIREMENTS
 
-To compile the OTR plugin for gaim, you'll need at least:
+To compile the OTR plugin for pidgin, you'll need at least:
  - libgpg-error 1.0  [ftp://ftp.gnupg.org/gcrypt/libgpg-error/]
  - libgcrypt 1.2.0   [ftp://ftp.gnupg.org/gcrypt/libgcrypt/]
- - libotr 1.99.0     [http://www.cypherpunks.ca/otr/]
- - glib 2.4          [http://www.gtk.org/download/]
- - gtk+ 2.4          [http://www.gtk.org/download/]
- - gaim 1.x          [http://gaim.sourceforge.net/downloads.php]
+ - libotr 3.1.0      [http://otr.cypherpunks.ca/]
+ - glib 2.6          [http://www.gtk.org/download/]
+ - gtk+ 2.6          [http://www.gtk.org/download/]
+ - pidgin 2.x        [http://pidgin.im/]
+
+You'll also need the usual autotools, such as automake-1.9, autoreconf,
+libtool, intltool, etc.
 
 If you install these with a package manager, you'll probably need the
 -dev or -devel versions of the packages.
@@ -16,6 +19,7 @@ COMPILING (non-Win32)
 If you're got a CVS copy, you will need to regenerate the configure
 script using:
 
+    intltoolize --force --copy
     autoreconf -s -i
 
 [If you installed libotr.m4 somewhere that autoreconf can't find it,
@@ -36,6 +40,11 @@ NETBSD:
 Once the configure script writes a Makefile, you should be able to just
 run "make".
 
+If you want a plugin that has libgcrypt linked statically, use
+"make -f Makefile.static".  Makefile.static assumes libotr.a and libgcrypt.a
+are available in /usr/lib.  If they're somewhere else, use something like
+"LIBOTRDIR=/usr/local/lib make -f Makefile.static".
+
 COMPILING (Win32)
 
 Use the provided Makefile.mingw:
diff --git a/Makefile.am b/Makefile.am
index ac0235a..509a8c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,22 @@
 AM_CFLAGS=	@LIBGCRYPT_CFLAGS@ @LIBOTR_CFLAGS@ @EXTRA_CFLAGS@
 AM_CFLAGS+=	-DUSING_GTK -DPURPLE_PLUGINS \
-		-DPIDGIN_OTR_VERSION=\"@VERSION@\"
+		-DPIDGIN_OTR_VERSION=\"@VERSION@\" \
+		-DLOCALEDIR=\"$(datadir)/locale\"
+
+SUBDIRS=	po
 
 plugindir=		${libdir}/pidgin
 
 plugin_LTLIBRARIES=	pidgin-otr.la
 
-pidgin_otr_la_SOURCES=		otr-plugin.c ui.c dialogs.c gtk-ui.c gtk-dialog.c
-pidgin_otr_la_LDFLAGS=		-module -avoid-version
+pidgin_otr_la_SOURCES=	otr-plugin.c ui.c dialogs.c gtk-ui.c gtk-dialog.c
+pidgin_otr_la_LDFLAGS=	-module -avoid-version
 pidgin_otr_la_LDFLAGS+=	@LIBGCRYPT_LIBS@ @LIBOTR_LIBS@
 
 EXTRA_DIST=		dialogs.h gtk-dialog.h gtk-ui.h otr-plugin.h ui.h \
-			Makefile.mingw packaging
+			Makefile.mingw packaging/windows/pidgin-otr.nsi \
+			packaging/fedora/pidgin-otr.spec po/Makefile.mingw \
+			po/README intltool-extract.in intltool-merge.in \
+			intltool-update.in Makefile.static
+
+DISTCLEANFILES=		intltool-extract intltool-merge intltool-update
diff --git a/Makefile.mingw b/Makefile.mingw
index 3850847..45100f2 100644
--- a/Makefile.mingw
+++ b/Makefile.mingw
@@ -1,7 +1,10 @@
 WIN32=1
 
 # The version number to put in the plugin info
-PIDGIN_OTR_VERSION = 3.0.0
+PIDGIN_OTR_VERSION = 3.1.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
@@ -18,6 +21,12 @@ LIBOTRINCDIR = /usr/include
 # The locataion of libotr.a.
 LIBOTRLIBDIR = /usr/lib
 
+# Location of libintl.h
+LIBINTLINCDIR = /usr/i586-mingw32msvc/include
+
+# Location of libintl.a
+LIBINTLLIBDIR = /usr/i586-mingw32msvc/lib
+
 # The target
 TARGET = pidgin-otr.so
 
@@ -28,7 +37,8 @@ LIBOTRLIBDIR = /usr/i586-mingw32msvc/lib
 TARGET = pidgin-otr.dll
 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
+	 -lgobject-2.0 -lpidgin -llibpurple -lgcrypt -lgpg-error \
+	 -L$(LIBINTLLIBDIR) -lintl
 else
 FPIC = -fPIC
 LDFLAGS = -module -avoid-version
@@ -42,7 +52,9 @@ INSTALLDIR = $(DESTDIR)$(PIDGINDIR)
 CC ?= gcc
 override CFLAGS += -g -Wall -I$(PIDGIN_HEADERS) -I$(PURPLE_HEADERS) \
 	$(GTK_HDRS) -I$(LIBOTRINCDIR) $(FPIC) -DUSING_GTK -DPURPLE_PLUGINS \
-	-DPIDGIN_OTR_VERSION=\"$(PIDGIN_OTR_VERSION)\" -DPIDGIN_NAME=\"Pidgin\"
+	-DPIDGIN_OTR_VERSION=\"$(PIDGIN_OTR_VERSION)\" \
+	-DPIDGIN_NAME=\"Pidgin\" -I$(LIBINTLINCDIR) -DENABLE_NLS \
+	-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
  
 all: $(TARGET)
 
@@ -52,6 +64,8 @@ $(TARGET): otr-plugin.o ui.o dialogs.o gtk-ui.o gtk-dialog.o
 install: all
 	install -d $(INSTALLDIR)
 	install -m 0755 $(TARGET) $(INSTALLDIR)
+	$(MAKE) -C po -f Makefile.mingw install
+
 clean:
 	rm -f *.o
 	rm -f $(TARGET)
diff --git a/Makefile.static b/Makefile.static
new file mode 100644
index 0000000..2533bb4
--- /dev/null
+++ b/Makefile.static
@@ -0,0 +1,21 @@
+LIBOTRDIR?=/usr/lib
+LIBGCRYPTDIR?=/usr/lib
+
+.libs/pidgin-otr.so: FORCE
+	# Build everything from the standard Makefile
+	make
+	# Link everything, including libotr and libgcrypt, together into
+	# a single .o file
+	ld -r  .libs/otr-plugin.o .libs/ui.o .libs/dialogs.o .libs/gtk-ui.o \
+		.libs/gtk-dialog.o $(LIBOTRDIR)/libotr.a \
+		$(LIBGCRYPTDIR)/libgcrypt.a /usr/lib/libgpg-error.a \
+		-o .libs/pidgin-otr-shared.o
+	# Make all the libgcrypt references local to that .o file
+	objcopy -w -L '*gcry*' .libs/pidgin-otr-shared.o \
+		.libs/pidgin-otr-static.o
+	# Turn the .o into a .so
+	gcc -shared .libs/pidgin-otr-static.o -Wl,-soname -Wl,pidgin-otr.so \
+		-o .libs/pidgin-otr.so
+
+FORCE:
+
diff --git a/NEWS b/NEWS
index bb7cd07..ac96c6d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,14 @@
+24 Jul 2007:
+- Added option to not log OTR conversations
+- Large messages are now fragmented transparently instead of failing
+- Removed "view secure session id" and "verify fingerprint" options from
+  OTR button menu.  Added "authenticate buddy" option in its place.  This
+  new option allows you to authenticate your buddies by entering some
+  secret that only the two of you know, rather than by using a long
+  user-unfriendly sequence of hex characters.  [The old "verify
+  fingerprint" dialog is still available via an "Advanced..." button
+  from the new "authenticate buddy" dialog.]
+
 06 May 2007:
 - Ported to Pidgin 2.0.0
 
diff --git a/README b/README
index 55b8eac..ac4ed38 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 	       Off-the-Record Messaging plugin for pidgin
-			  v3.0.0,  6 May 2007
+			  v3.1.0, XX XXX 2007
 
 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
@@ -21,7 +21,7 @@ OTR allows you to have private conversations over IM by providing:
      is compromised.
 
 For more information on Off-the-Record Messaging, see
-http://www.cypherpunks.ca/otr/
+http://otr.cypherpunks.ca/
 
 USAGE
 
@@ -29,7 +29,7 @@ Run pidgin, and open the Plugins panel.  (If you had a copy of pidgin
 running before you installed pidgin-otr, you will need to restart it.)
 Find the Off-the-Record Messaging plugin, and enable it by selecting the
 checkbox next to it.  Click "Configure Plugin" to bring up the OTR UI.
-The UI has two "pages": "Known fingerprints" and "Config".
+The UI has two "pages": "Config" and "Known fingerprints".
 
 The "Config" page allows you generate private keys, and to set OTR
 options.
@@ -52,6 +52,7 @@ options.
     [X] Enable private messaging
       [X] Automatically initiate private messaging
         [ ] Require private messaging
+    [ ] Don't log OTR conversations
 
     If the "enable private messaging" box is unchecked, private messages
     will be disabled completely (and the other two boxes will be greyed
@@ -68,9 +69,13 @@ options.
     understand OTR private messages, and if so, automatically start a
     private conversation.
 
-    If all three boxes are checked, messages will not be sent to your
+    If the first three boxes are checked, messages will not be sent to your
     buddy unless you are in a private conversation.
 
+    If the fourth box is checked, OTR-protected conversations will not
+    be logged, even if logging of instant messages is turned on in
+    pidgin.
+
 The "Known fingerprints" page allows you to see the fingerprints of any
 buddies you have previously communicated with privately.
 
@@ -222,7 +227,7 @@ 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-2005  Nikita Borisov and Ian Goldberg
+    Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
 		             <otr at cypherpunks.ca>
 
     This program is free software; you can redistribute it and/or modify
@@ -244,7 +249,7 @@ CONTACT
 To report problems, comments, suggestions, patches, etc., you can email
 the authors:
 
-Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+Ian Goldberg, Chris Alexander, and Nikita Borisov <otr at cypherpunks.ca>
 
 For more information on Off-the-Record Messaging, visit
-http://www.cypherpunks.ca/otr/
+http://otr.cypherpunks.ca/
diff --git a/configure.ac b/configure.ac
index afb5e6d..c98eb23 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,13 +1,10 @@
 dnl Process this file with autoconf to produce configure.
 
-dnl XXX Check for headers, etc.
-
-dnl Not yet used.
-AC_INIT(otr-plugin.c)
+AC_INIT(pidgin-otr, 3.1.0)
 
 AM_CONFIG_HEADER(config.h)
 
-AM_INIT_AUTOMAKE(pidgin-otr, 3.0.0)
+AM_INIT_AUTOMAKE([-Wall -Werror])
 
 AC_PROG_CC
 
@@ -17,8 +14,17 @@ AM_PROG_LIBTOOL
 
 AM_PATH_LIBGCRYPT(1:1.2.0,,AC_MSG_ERROR(libgcrypt 1.2.0 or newer is required.))
 
-AM_PATH_LIBOTR(3.0.0,,AC_MSG_ERROR(libotr 3.0.0 or newer is required.))
+AM_PATH_LIBOTR(3.1.0,,AC_MSG_ERROR(libotr 3.1.0 or newer is required.))
+
+PKG_CHECK_MODULES(EXTRA, glib-2.0 >= 2.6 gtk+-2.0 >= 2.6 pidgin >= 2.0 purple >= 2.0, , AC_MSG_ERROR(glib, gtk, pidgin and purple required))
+
+AC_PROG_INTLTOOL
+
+GETTEXT_PACKAGE=pidgin-otr
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define the gettext package to be used])
 
-PKG_CHECK_MODULES(EXTRA, glib-2.0 >= 2.4 gtk+-2.0 >= 2.4 pidgin >= 2.0, , AC_MSG_ERROR(glib, gtk and pidgin required))
+ALL_LINGUAS=""
+AM_GLIB_GNU_GETTEXT
 
-AC_OUTPUT([Makefile])
+AC_OUTPUT([Makefile po/Makefile.in])
diff --git a/dialogs.c b/dialogs.c
index 0266278..997d8ba 100644
--- a/dialogs.c
+++ b/dialogs.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -49,6 +49,18 @@ const OtrgDialogUiOps *otrg_dialog_get_ui_ops(void)
     return ui_ops;
 }
 
+/* Initialize the OTR dialog subsystem */
+void otrg_dialog_init(void)
+{
+    ui_ops->init();
+}
+
+/* Deinitialize the OTR dialog subsystem */
+void otrg_dialog_cleanup(void)
+{
+    ui_ops->cleanup();
+}
+
 /* This is just like pidgin_notify_message, except: (a) it doesn't grab
  * keyboard focus, (b) the button is "OK" instead of "Close", and (c)
  * the labels aren't limited to 2K. */
@@ -125,6 +137,18 @@ void otrg_dialog_verify_fingerprint(Fingerprint *fprint)
     ui_ops->verify_fingerprint(fprint);
 }
 
+/* Show a dialog asking the user to give an SMP secret. */
+void otrg_dialog_socialist_millionaires(ConnContext *context)
+{
+    ui_ops->socialist_millionaires(context, TRUE);
+}
+
+/* Update the status of an ongoing socialist millionaires protocol. */
+void otrg_dialog_update_smp(ConnContext *context, double progress_level)
+{
+    ui_ops->update_smp(context, progress_level);
+}
+
 /* Call this when a context transitions to ENCRYPTED. */
 void otrg_dialog_connected(ConnContext *context)
 {
diff --git a/dialogs.h b/dialogs.h
index 733208e..f3eaf64 100644
--- a/dialogs.h
+++ b/dialogs.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
 
 /* The various help URLs */
 #define BASE_HELPURL "http://otr-help.cypherpunks.ca/"
+#define AUTHENTICATE_HELPURL  BASE_HELPURL "authenticate.php"
 #define FINGERPRINT_HELPURL   BASE_HELPURL "fingerprint.php"
 #define SESSIONID_HELPURL     BASE_HELPURL "sessionid.php"
 #define UNVERIFIED_HELPURL    BASE_HELPURL "unverified.php"
@@ -37,6 +38,10 @@
 typedef struct s_OtrgDialogWait *OtrgDialogWaitHandle;
 
 typedef struct {
+    void (*init)(void);
+
+    void (*cleanup)(void);
+
     void (*notify_message)(PurpleNotifyMsgType type,
 	const char *accountname, const char *protocol, const char *username,
 	const char *title, const char *primary, const char *secondary);
@@ -54,6 +59,10 @@ typedef struct {
 
     void (*verify_fingerprint)(Fingerprint *fprint);
 
+    void (*socialist_millionaires)(ConnContext *context, gboolean responder);
+
+    void (*update_smp)(ConnContext *context, double progress_level);
+
     void (*connected)(ConnContext *context);
 
     void (*disconnected)(ConnContext *context);
@@ -76,6 +85,12 @@ void otrg_dialog_set_ui_ops(const OtrgDialogUiOps *ops);
 /* Get the UI ops */
 const OtrgDialogUiOps *otrg_dialog_get_ui_ops(void);
 
+/* Initialize the OTR dialog subsystem */
+void otrg_dialog_init(void);
+
+/* Deinitialize the OTR dialog subsystem */
+void otrg_dialog_cleanup(void);
+
 /* This is just like pidgin_notify_message, except: (a) it doesn't grab
  * keyboard focus, (b) the button is "OK" instead of "Close", and (c)
  * the labels aren't limited to 2K. */
@@ -121,6 +136,12 @@ void otrg_dialog_unknown_fingerprint(OtrlUserState us, const char *accountname,
 /* Show a dialog asking the user to verify the given fingerprint. */
 void otrg_dialog_verify_fingerprint(Fingerprint *fprint);
 
+/* Show a dialog asking the user to give an SMP secret. */
+void otrg_dialog_socialist_millionaires(ConnContext *context);
+
+/* Update the status of an ongoing socialist millionaires protocol. */
+void otrg_dialog_update_smp(ConnContext *context, double progress_level);
+
 /* Call this when a context transitions to ENCRYPTED. */
 void otrg_dialog_connected(ConnContext *context);
 
diff --git a/gtk-dialog.c b/gtk-dialog.c
index df5a97d..8006ee6 100644
--- a/gtk-dialog.c
+++ b/gtk-dialog.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,11 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+/* config.h */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 /* system headers */
 #include <stdio.h>
 #include <stdlib.h>
@@ -35,6 +40,11 @@
 #include "gtkimhtml.h"
 #include "util.h"
 
+#ifdef ENABLE_NLS
+/* internationalisation headers */
+#include <glib/gi18n-lib.h>
+#endif
+
 /* libotr headers */
 #include <libotr/dh.h>
 #include <libotr/privkey.h>
@@ -45,6 +55,7 @@
 /* purple-otr headers */
 #include "otr-plugin.h"
 #include "dialogs.h"
+#include "gtk-dialog.h"
 #include "ui.h"
 
 /* The OTR icons */
@@ -593,6 +604,69 @@ static const char * finished_xpm[] = {
 "        + + &.+       + *.+ +           ",
 "              + + + + +                 "};
 
+typedef struct {
+    ConnContext *context;  /* The context used to fire library code */
+    GtkEntry *entry;	   /* The text entry field containing the secret */
+    gboolean responder;	   /* Whether or not this is the first side to give
+			      their secret */
+} SmpResponsePair;
+
+/* The response code returned by pushing the "Advanced..." button on the
+ * SMP dialog */
+#define OTRG_RESPONSE_ADVANCED 1
+
+/* Information used by the plugin that is specific to both the
+ * application and connection. */
+typedef struct dialog_context_data {
+    GtkWidget       *smp_secret_dialog;
+    SmpResponsePair *smp_secret_smppair;
+    GtkWidget       *smp_progress_dialog;
+    GtkWidget       *smp_progress_bar;
+    GtkWidget       *smp_progress_label;
+} SMPData;
+
+static void close_progress_window(SMPData *smp_data)
+{
+    if (smp_data->smp_progress_dialog) {
+	gtk_dialog_response(GTK_DIALOG(smp_data->smp_progress_dialog),
+		GTK_RESPONSE_REJECT);
+    }
+    smp_data->smp_progress_dialog = NULL;
+    smp_data->smp_progress_bar = NULL;
+    smp_data->smp_progress_label = NULL;
+}
+
+static void otrg_gtk_dialog_free_smp_data(PurpleConversation *conv)
+{
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+    if (!smp_data) return;
+
+    if (smp_data->smp_secret_dialog) {
+	gtk_dialog_response(GTK_DIALOG(smp_data->smp_secret_dialog),
+		GTK_RESPONSE_REJECT);
+    }
+    smp_data->smp_secret_dialog = NULL;
+    smp_data->smp_secret_smppair = NULL;
+
+    close_progress_window(smp_data);
+
+    free(smp_data);
+
+    g_hash_table_remove(conv->data, "otr-smpdata");
+}
+
+static void otrg_gtk_dialog_add_smp_data(PurpleConversation *conv)
+{
+    SMPData *smp_data = malloc(sizeof(SMPData));
+    smp_data->smp_secret_dialog = NULL;
+    smp_data->smp_secret_smppair = NULL;
+    smp_data->smp_progress_dialog = NULL;
+    smp_data->smp_progress_bar = NULL;
+    smp_data->smp_progress_label = NULL;
+
+    purple_conversation_set_data(conv, "otr-smpdata", smp_data);
+}
+
 static GtkWidget *otr_icon(GtkWidget *image, TrustLevel level)
 {
     GdkPixbuf *pixbuf = NULL;
@@ -629,7 +703,111 @@ static void message_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget)
     gtk_widget_destroy(GTK_WIDGET(widget));
 }
 
-static GtkWidget *create_dialog(PurpleNotifyMsgType type, const char *title,
+/* Forward declarations for the benefit of smp_message_response_cb */
+static void verify_fingerprint(GtkWindow *parent, Fingerprint *fprint);
+static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
+	ConnContext *context);
+
+/* Called when a button is pressed on the "progress bar" smp dialog */
+static void smp_progress_response_cb(GtkDialog *dialog, gint response,
+	ConnContext *context)
+{
+    PurpleConversation *conv = otrg_plugin_context_to_conv(context, 0);
+    SMPData *smp_data = NULL;
+    
+    if (conv) {
+	gdouble frac;
+
+	smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+	frac = gtk_progress_bar_get_fraction(
+		GTK_PROGRESS_BAR(smp_data->smp_progress_bar));
+
+	if (frac != 0.0 && frac != 1.0 && response == GTK_RESPONSE_REJECT) {
+	    otrg_plugin_abort_smp(context);
+	}
+    }
+    /* In all cases, destroy the current window */
+    gtk_widget_destroy(GTK_WIDGET(dialog));
+
+    /* Clean up variables pointing to the destroyed objects */
+
+    if (smp_data) {
+	smp_data->smp_progress_bar = NULL;
+	smp_data->smp_progress_label = NULL;
+	smp_data->smp_progress_dialog = NULL;
+    }
+}
+
+/* Called when a button is pressed on the "enter the secret" smp dialog
+ * The data passed contains a pointer to the text entry field containing
+ * the entered secret as well as the current context.
+ */
+static void smp_secret_response_cb(GtkDialog *dialog, gint response,
+	SmpResponsePair *smppair)
+{
+    if (!smppair) return;
+
+    ConnContext* context = smppair->context;
+    if (response == GTK_RESPONSE_ACCEPT) {
+	GtkEntry* entry = smppair->entry;
+	char *secret;
+	size_t secret_len;
+
+	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 {
+	    otrg_plugin_start_smp(context, (const unsigned char *)secret,
+		    secret_len);
+	}
+	g_free(secret);
+
+	/* launch progress bar window */
+	create_smp_progress_dialog(GTK_WINDOW(dialog), context);
+    } else if (response == OTRG_RESPONSE_ADVANCED) {
+	ConnContext* context = smppair->context;
+
+	if (context == NULL || context->msgstate != OTRL_MSGSTATE_ENCRYPTED)
+		return;
+
+	verify_fingerprint(GTK_WINDOW(dialog), context->active_fingerprint);
+    } else {
+        otrg_plugin_abort_smp(context);
+    }
+    /* In all cases, destroy the current window */
+    gtk_widget_destroy(GTK_WIDGET(dialog));
+    
+    /* Clean up references to this window */
+    PurpleConversation *conv =
+	otrg_plugin_context_to_conv(smppair->context, 0);
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+    if (smp_data) {
+	smp_data->smp_secret_dialog = NULL;
+	smp_data->smp_secret_smppair = NULL;
+    }
+
+    /* Free the smppair memory */
+    free(smppair);
+}
+
+static void close_smp_window(PurpleConversation *conv)
+{
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+    if (smp_data && smp_data->smp_secret_dialog) {
+	gtk_dialog_response(GTK_DIALOG(smp_data->smp_secret_dialog),
+		GTK_RESPONSE_REJECT);
+    }
+}
+
+static GtkWidget *create_dialog(GtkWindow *parent,
+	PurpleNotifyMsgType type, const char *title,
 	const char *primary, const char *secondary, int sensitive,
 	GtkWidget **labelp, void (*add_custom)(GtkWidget *vbox, void *data),
 	void *add_custom_data)
@@ -661,21 +839,22 @@ static GtkWidget *create_dialog(PurpleNotifyMsgType type, const char *title,
     }
 
     if (icon_name != NULL) {
-	img = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_DIALOG);
+	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 : PIDGIN_ALERT_TITLE,
-					 NULL, 0, GTK_STOCK_OK,
-					 GTK_RESPONSE_ACCEPT, NULL);
-    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT,
-	    sensitive);
+    dialog = gtk_dialog_new_with_buttons(
+	    title ? title : PIDGIN_ALERT_TITLE, parent, 0,
+	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
 
-    gtk_window_set_accept_focus(GTK_WINDOW(dialog), FALSE);
+    gtk_window_set_focus_on_map(GTK_WINDOW(dialog), FALSE);
     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);
 
     gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
     gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
@@ -716,6 +895,287 @@ static GtkWidget *create_dialog(PurpleNotifyMsgType type, const char *title,
     return dialog;
 }
 
+/* Adds a "What's this?" expander to a vbox, containing { some "whatsthis"
+ * markup (displayed in a GtkLabel) and a "More..." expander, containing
+ * { some "more" markup (displayed in a GtkIMHTML) } }. */
+static void add_whatsthis_more(GtkWidget *vbox, const char *whatsthismarkup,
+	const char *moremarkup)
+{
+    GtkWidget *expander;
+    GtkWidget *ebox;
+    GtkWidget *whatsthis;
+    GtkWidget *more;
+    GtkWidget *frame;
+    GtkWidget *scrl;
+    GtkWidget *imh;
+    GdkFont *font;
+
+    expander = gtk_expander_new_with_mnemonic(_("_What's this?"));
+    gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
+    frame = gtk_frame_new(NULL);
+    gtk_container_add(GTK_CONTAINER(expander), frame);
+    ebox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(frame), ebox);
+    whatsthis = gtk_label_new(NULL);
+    gtk_label_set_line_wrap(GTK_LABEL(whatsthis), TRUE);
+    gtk_label_set_markup(GTK_LABEL(whatsthis), whatsthismarkup);
+
+    gtk_box_pack_start(GTK_BOX(ebox), whatsthis, FALSE, FALSE, 0);
+    more = gtk_expander_new_with_mnemonic(_("_More..."));
+    gtk_box_pack_start(GTK_BOX(ebox), more, FALSE, FALSE, 0);
+    scrl = gtk_scrolled_window_new(NULL, NULL);
+    gtk_container_add(GTK_CONTAINER(more), scrl);
+
+    imh = gtk_imhtml_new(NULL, NULL);
+    pidgin_setup_imhtml(imh);
+    gtk_imhtml_append_text(GTK_IMHTML(imh), moremarkup, GTK_IMHTML_NO_SCROLL);
+
+    gtk_container_add(GTK_CONTAINER(scrl), imh);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrl),
+	    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+    /* This is a deprecated API, but mucking with PangoFontDescriptions
+     * is (a) complicated, and (b) not fully supported by older versions
+     * of libpango, which some people may have. */
+    font = gtk_style_get_font(imh->style);
+    gtk_widget_set_size_request(scrl, -1, 6 * (font->ascent + font->descent));
+}
+
+static GtkWidget *create_smp_dialog(const char *title,
+	const char *primary, const char *secondary, int sensitive,
+	GtkWidget **labelp, ConnContext *context, gboolean responder)
+{
+    GtkWidget *dialog;
+
+    PurpleConversation *conv = otrg_plugin_context_to_conv(context, 1);
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+
+    close_progress_window(smp_data);
+    if (!(smp_data->smp_secret_dialog)) {
+	GtkWidget *advbutton;
+	GtkWidget *buttonspacer;
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *entry;
+	GtkWidget *label;
+	GtkWidget *label2;
+	GtkWidget *img = NULL;
+	char *label_text;
+	const char *icon_name = NULL;
+	SmpResponsePair* smppair;
+
+	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 : PIDGIN_ALERT_TITLE, NULL, 0,
+				     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+				     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+		GTK_RESPONSE_ACCEPT);
+
+	/* Create the Advanced... button, and left-justify it.  This
+	 * involves adding the button, and a blank label as a spacer, and
+	 * reordering them so that they're at the beginning. */
+	advbutton = gtk_dialog_add_button(GTK_DIALOG(dialog), _("Advanced..."),
+		OTRG_RESPONSE_ADVANCED);
+	buttonspacer = gtk_label_new("");
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
+		buttonspacer, TRUE, TRUE, 0);
+	gtk_box_reorder_child(GTK_BOX(GTK_DIALOG(dialog)->action_area),
+		advbutton, 0);
+	gtk_box_reorder_child(GTK_BOX(GTK_DIALOG(dialog)->action_area),
+		buttonspacer, 1);
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+	        GTK_RESPONSE_ACCEPT, sensitive);
+
+	gtk_window_set_focus_on_map(GTK_WINDOW(dialog), FALSE);
+	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);
+
+	hbox = gtk_hbox_new(FALSE, 12);
+	vbox = gtk_vbox_new(FALSE, 0);
+	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>%s%s",
+	       (primary ? primary : ""),
+	       (primary ? "\n\n" : ""),
+	       (secondary ? secondary : ""));
+
+	label = gtk_label_new(NULL);
+
+	gtk_label_set_markup(GTK_LABEL(label), label_text);
+	gtk_label_set_selectable(GTK_LABEL(label), 1);
+	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);
+       
+	/* Create the text view where the user enters their secret */
+	entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(entry), _("Enter secret here"));
+	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+	if (context->active_fingerprint->trust &&
+		context->active_fingerprint->trust[0]) {
+	    label2 = gtk_label_new(_("This buddy is already authenticated."));
+	} else {
+	    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);
+	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);
+	}
+
+	char *moremarkup = g_strdup_printf(
+		"%s\n\n%s\n\n<a href=\"%s%s\">%s</a>",
+		_("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."),
+		_("If your buddy uses multiple IM accounts or multiple "
+		    "computers, you may have to authenticate multiple "
+		    "times.  However, as long as they use an account and "
+		    "computer that you've seen before, you don't need to "
+		    "authenticate each individual conversation."),
+		AUTHENTICATE_HELPURL, _("?lang=en"),
+		_("Click here for more information about authentication "
+		    "in OTR."));
+
+	add_whatsthis_more(vbox,
+		_("Authenticating a buddy helps ensure that the person "
+		    "you are talking to is who they claim to be."),
+		moremarkup);
+
+	g_free(moremarkup);
+	
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+
+	smppair = malloc(sizeof(SmpResponsePair));
+	smppair->context = context;
+	smppair->entry = GTK_ENTRY(entry);
+	smppair->responder = responder;
+	g_signal_connect(G_OBJECT(dialog), "response",
+			 G_CALLBACK(smp_secret_response_cb),
+			 smppair);
+
+	gtk_widget_show_all(dialog);
+	smp_data->smp_secret_dialog = dialog;
+	smp_data->smp_secret_smppair = smppair;
+
+	if (labelp) *labelp = label;
+    } 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;
+	}
+    }
+
+    return smp_data->smp_secret_dialog;
+}
+
+static GtkWidget *create_smp_progress_dialog(GtkWindow *parent,
+	ConnContext *context)
+{
+    GtkWidget *dialog;
+    GtkWidget *hbox;
+    GtkWidget *vbox;
+    GtkWidget *label;
+    GtkWidget *proglabel;
+    GtkWidget *bar;
+    GtkWidget *img = NULL;
+    char *label_text;
+    const char *icon_name = NULL;
+
+    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(_("Authenticating Buddy"),
+	    parent, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+	    GTK_RESPONSE_ACCEPT);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+	    GTK_RESPONSE_REJECT, 1);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+	    GTK_RESPONSE_ACCEPT, 0);
+
+    gtk_window_set_focus_on_map(GTK_WINDOW(dialog), FALSE);
+    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);
+
+    hbox = gtk_hbox_new(FALSE, 12);
+    vbox = gtk_vbox_new(FALSE, 0);
+    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 %s</span>\n",
+	       _("Authenticating"), context->username);
+
+    label = gtk_label_new(NULL);
+
+    gtk_label_set_markup(GTK_LABEL(label), label_text);
+    gtk_label_set_selectable(GTK_LABEL(label), 1);
+    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);
+
+    proglabel = gtk_label_new(NULL);
+    gtk_label_set_selectable(GTK_LABEL(proglabel), 1);
+    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);
+
+    PurpleConversation *conv = otrg_plugin_context_to_conv(context, 0);
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+    smp_data->smp_progress_dialog = dialog;
+    smp_data->smp_progress_bar = bar;
+    smp_data->smp_progress_label = proglabel;
+
+    g_signal_connect(G_OBJECT(dialog), "response",
+		     G_CALLBACK(smp_progress_response_cb),
+		     context);
+
+    gtk_widget_show_all(dialog);
+
+    return dialog;
+}
+
 /* This is just like purple_notify_message, except: (a) it doesn't grab
  * keyboard focus, (b) the button is "OK" instead of "Close", and (c)
  * the labels aren't limited to 2K. */
@@ -723,7 +1183,7 @@ static void otrg_gtk_dialog_notify_message(PurpleNotifyMsgType type,
 	const char *accountname, const char *protocol, const char *username,
 	const char *title, const char *primary, const char *secondary)
 {
-    create_dialog(type, title, primary, secondary, 1, NULL, NULL, NULL);
+    create_dialog(NULL, type, title, primary, secondary, 1, NULL, NULL, NULL);
 }
 
 struct s_OtrgDialogWait {
@@ -738,8 +1198,8 @@ static OtrgDialogWaitHandle otrg_gtk_dialog_private_key_wait_start(
 	const char *account, const char *protocol)
 {
     PurplePlugin *p;
-    const char *title = "Generating private key";
-    const char *primary = "Please wait";
+    const char *title = _("Generating private key");
+    const char *primary = _("Please wait");
     char *secondary;
     const char *protocol_print;
     GtkWidget *label;
@@ -747,14 +1207,14 @@ static OtrgDialogWaitHandle otrg_gtk_dialog_private_key_wait_start(
     OtrgDialogWaitHandle handle;
 
     p = purple_find_prpl(protocol);
-    protocol_print = (p ? p->info->name : "Unknown");
+    protocol_print = (p ? p->info->name : _("Unknown"));
 	
     /* Create the Please Wait... dialog */
-    secondary = g_strdup_printf("Generating private key for %s (%s)...",
+    secondary = g_strdup_printf(_("Generating private key for %s (%s)..."),
 	    account, protocol_print);
 	
-    dialog = create_dialog(PURPLE_NOTIFY_MSG_INFO, title, primary, secondary,
-	    0, &label, NULL, NULL);
+    dialog = create_dialog(NULL, PURPLE_NOTIFY_MSG_INFO, title, primary,
+	    secondary, 0, &label, NULL, NULL);
     handle = malloc(sizeof(struct s_OtrgDialogWait));
     handle->dialog = dialog;
     handle->label = label;
@@ -795,7 +1255,7 @@ static void otrg_gtk_dialog_private_key_wait_done(OtrgDialogWaitHandle handle)
     char *newmarkup;
 
     oldmarkup = gtk_label_get_label(GTK_LABEL(handle->label));
-    newmarkup = g_strdup_printf("%s Done.", oldmarkup);
+    newmarkup = g_strdup_printf(_("%s Done."), oldmarkup);
 
     gtk_label_set_markup(GTK_LABEL(handle->label), newmarkup);
     gtk_widget_show(handle->label);
@@ -806,94 +1266,71 @@ static void otrg_gtk_dialog_private_key_wait_done(OtrgDialogWaitHandle handle)
     free(handle);
 }
 
-/* Adds a "What's this?" expander to a vbox, containing { some "whatsthis"
- * markup (displayed in a GtkLabel) and a "More..." expander, containing
- * { some "more" markup (displayed in a GtkIMHTML) } }. */
-static void add_whatsthis_more(GtkWidget *vbox, const char *whatsthismarkup,
-	const char *moremarkup)
-{
-    GtkWidget *expander;
-    GtkWidget *ebox;
-    GtkWidget *whatsthis;
-    GtkWidget *more;
-    GtkWidget *frame;
-    GtkWidget *scrl;
-    GtkWidget *imh;
-    GdkFont *font;
-
-    expander = gtk_expander_new_with_mnemonic("_What's this?");
-    gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
-    frame = gtk_frame_new(NULL);
-    gtk_container_add(GTK_CONTAINER(expander), frame);
-    ebox = gtk_vbox_new(FALSE, 10);
-    gtk_container_add(GTK_CONTAINER(frame), ebox);
-    whatsthis = gtk_label_new(NULL);
-    gtk_label_set_line_wrap(GTK_LABEL(whatsthis), TRUE);
-    gtk_label_set_markup(GTK_LABEL(whatsthis), whatsthismarkup);
-
-    gtk_box_pack_start(GTK_BOX(ebox), whatsthis, FALSE, FALSE, 0);
-    more = gtk_expander_new_with_mnemonic("_More...");
-    gtk_box_pack_start(GTK_BOX(ebox), more, FALSE, FALSE, 0);
-    scrl = gtk_scrolled_window_new(NULL, NULL);
-    gtk_container_add(GTK_CONTAINER(more), scrl);
-
-    imh = gtk_imhtml_new(NULL, NULL);
-    pidgin_setup_imhtml(imh);
-    gtk_imhtml_append_text(GTK_IMHTML(imh), moremarkup, GTK_IMHTML_NO_SCROLL);
-
-    gtk_container_add(GTK_CONTAINER(scrl), imh);
-    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrl),
-	    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-
-    /* This is a deprecated API, but mucking with PangoFontDescriptions
-     * is (a) complicated, and (b) not fully supported by older versions
-     * of libpango, which some people may have. */
-    font = gtk_style_get_font(imh->style);
-    gtk_widget_set_size_request(scrl, -1, 6 * (font->ascent + font->descent));
-}
-
+#if 0
 static void add_unk_fingerprint_expander(GtkWidget *vbox, void *data)
 {
+    char *moremarkup = g_strdup_printf(
+	    "%s\n\n%s\n\n<a href=\"%s\">%s%s</a>",
+	    __("If your buddy has more than one IM account, or uses more than "
+	    "one computer, he may have multiple fingerprints."),
+	    __("However, the only way an imposter could duplicate one of your "
+	    "buddy's fingerprints is by stealing information from his "
+	    "computer."),
+	    FINGERPRINT_HELPURL, __("?lang=en"),
+	    __("Click here for more information about fingerprints."));
+
     add_whatsthis_more(vbox,
-	    "A <b>fingerprint</b> is a unique identifier that you should "
+	    __("A <b>fingerprint</b> is a unique identifier that you should "
 	    "use to authenticate your buddy.  Right-click on the OTR button "
 	    "in your buddy's conversation window, and choose \"Verify "
-	    "fingerprint\".",
+	    "fingerprint\"."), moremarkup);
 
-	    "If your buddy has more than one IM account, or uses more than "
-	    "one computer, he may have multiple fingerprints.\n\n"
-	    "However, the only way an imposter could duplicate one of your "
-	    "buddy's fingerprints is by stealing information from his "
-	    "computer.\n\n"
-	    "<a href=\"" FINGERPRINT_HELPURL "\">"
-	    "Click here for more information about fingerprints.</a>");
+    g_free(moremarkup);
 }
+#endif
 
-/* Show a dialog informing the user that a correspondent (who) has sent
- * us a Key Exchange Message (kem) that contains an unknown fingerprint.
- * Ask the user whether to accept the fingerprint or not.  If yes, call
- * response_cb(ops, opdata, response_data, resp) with resp = 1.  If no,
- * set resp = 0.  If the user destroys the dialog without answering, set
- * resp = -1. */
+/* Inform the user that an unknown fingerprint was received. */
 static void otrg_gtk_dialog_unknown_fingerprint(OtrlUserState us,
 	const char *accountname, const char *protocol, const char *who,
 	unsigned char fingerprint[20])
 {
-    char hash[45];
-    char *primary, *secondary;
-    PurplePlugin *p = purple_find_prpl(protocol);
-    
-    otrl_privkey_hash_to_human(hash, fingerprint);
-    primary = g_strdup_printf("%s (%s) has received an unknown fingerprint "
-	    "from %s:", accountname, 
-	    (p && p->info->name) ? p->info->name : "Unknown", who);
-    secondary = g_strdup_printf("%s\n", hash);
+    PurpleConversation *conv;
+    char *buf;
+    ConnContext *context;
+    int seenbefore = FALSE;
+
+    /* 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);
+    if (context) {
+	Fingerprint *fp = context->fingerprint_root.next;
+	while(fp) {
+	    if (memcmp(fingerprint, fp->fingerprint, 20)) {
+		/* This is a previously seen fingerprint for this user,
+		 * different from the one we were passed. */
+		seenbefore = TRUE;
+		break;
+	    }
+	    fp = fp->next;
+	}
+    }
 
-    create_dialog(PURPLE_NOTIFY_MSG_WARNING, "Unknown fingerprint",
-	    primary, secondary, 1, NULL, add_unk_fingerprint_expander, NULL);
+    if (seenbefore) {
+	buf = g_strdup_printf(_("%s is contacting you from an unrecognized "
+		    "computer.  You should <a href=\"%s%s\">authenticate</a> "
+		    "this buddy."), who, AUTHENTICATE_HELPURL, _("?lang=en"));
+    } else {
+	buf = g_strdup_printf(_("%s has not been authenticated yet.  You "
+		    "should <a href=\"%s%s\">authenticate</a> this buddy."),
+		who, AUTHENTICATE_HELPURL, _("?lang=en"));
+    }
 
-    g_free(primary);
-    g_free(secondary);
+    conv = otrg_plugin_userinfo_to_conv(accountname, protocol, who, TRUE);
+
+    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);
@@ -909,6 +1346,7 @@ static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
     GtkWidget *menuquerylabel;
     GtkWidget *menuview;
     GtkWidget *menuverf;
+    GtkWidget *menusmp;
     PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
     label = purple_conversation_get_data(conv, "otr-label");
     icon = purple_conversation_get_data(conv, "otr-icon");
@@ -919,27 +1357,29 @@ static void dialog_update_label_conv(PurpleConversation *conv, TrustLevel level)
     menuend = purple_conversation_get_data(conv, "otr-menuend");
     menuview = purple_conversation_get_data(conv, "otr-menuview");
     menuverf = purple_conversation_get_data(conv, "otr-menuverf");
+    menusmp = purple_conversation_get_data(conv, "otr-menusmp");
 
     /* Set the button's icon, label and tooltip. */
     otr_icon(icon, level);
     gtk_label_set_text(GTK_LABEL(label),
-	    level == TRUST_FINISHED ? "Finished" :
-	    level == TRUST_PRIVATE ? "Private" :
-	    level == TRUST_UNVERIFIED ? "Unverified" :
-	    "Not private");
+	    level == TRUST_FINISHED ? _("Finished") :
+	    level == TRUST_PRIVATE ? _("Private") :
+	    level == TRUST_UNVERIFIED ? _("Unverified") :
+	    _("Not private"));
     gtk_tooltips_set_tip(gtkconv->tooltips, button,
-	    level == TRUST_NOT_PRIVATE ? "Start a private conversation" :
-		    "Refresh the private conversation", NULL);
+	    level == TRUST_NOT_PRIVATE ? _("Start a private conversation") :
+		    _("Refresh the private conversation"), NULL);
 
     /* Set the menu item label for the OTR Query item. */
     gtk_label_set_markup_with_mnemonic(GTK_LABEL(menuquerylabel),
-	    level == TRUST_NOT_PRIVATE ? "Start _private conversation" :
-		    "Refresh _private conversation");
+	    level == TRUST_NOT_PRIVATE ? _("Start _private conversation") :
+		    _("Refresh _private conversation"));
 
     /* Sensitize the menu items as appropriate. */
     gtk_widget_set_sensitive(GTK_WIDGET(menuend), level != TRUST_NOT_PRIVATE);
     gtk_widget_set_sensitive(GTK_WIDGET(menuview), level != TRUST_NOT_PRIVATE);
     gtk_widget_set_sensitive(GTK_WIDGET(menuverf), level != TRUST_NOT_PRIVATE);
+    gtk_widget_set_sensitive(GTK_WIDGET(menusmp), level != TRUST_NOT_PRIVATE);
 
     /* Use any non-NULL value for "private", NULL for "not private" */
     purple_conversation_set_data(conv, "otr-private",
@@ -962,27 +1402,32 @@ static void dialog_update_label(ConnContext *context)
     dialog_update_label_conv(conv, level);
 }
 
+#if 0
 /* Add the help text for the "view session id" dialog. */
 static void add_sessid_expander(GtkWidget *vbox, void *data)
 {
-    add_whatsthis_more(vbox,
-	    "You can use this <b>secure session id</b> to double-check "
-	    "the privacy of <i>this one conversation</i>.",
-
-	    "To verify the session id, contact your buddy via some "
+    char *moremarkup = g_strdup_printf(
+	    "%s\n\n%s\n\n%s\n\n<a href=\"%s%s\">%s</a>",
+	    __("To verify the session id, contact your buddy via some "
 	    "<i>other</i> authenticated channel, such as the telephone "
 	    "or GPG-signed email.  Each of you should tell your bold "
 	    "half of the above session id to the other "
 	    "(your buddy will have the same session id as you, but with the "
-	    "other half bold).\n\nIf everything matches up, then <i>the "
+	    "other half bold)."),
+	    __("If everything matches up, then <i>the "
 	    "current conversation</i> between your computer and your buddy's "
-	    "computer is private.\n\n"
-	    "<b>Note:</b> You will probably never have to do this.  You "
+	    "computer is private."),
+	    __("<b>Note:</b> You will probably never have to do this.  You "
 	    "should normally use the \"Verify fingerprint\" functionality "
-	    "instead.\n\n"
-	    "<a href=\"" SESSIONID_HELPURL "\">"
-	    "Click here for more information about the secure "
-	    "session id.</a>");
+	    "instead."),
+	    SESSIONID_HELPURL, _("?lang=en"),
+	    __("Click here for more information about the secure session id."));
+
+    add_whatsthis_more(vbox,
+	    __("You can use this <b>secure session id</b> to double-check "
+	    "the privacy of <i>this one conversation</i>."), moremarkup);
+
+    g_free(moremarkup);
 }
 
 static GtkWidget* otrg_gtk_dialog_view_sessionid(ConnContext *context)
@@ -990,8 +1435,8 @@ static GtkWidget* otrg_gtk_dialog_view_sessionid(ConnContext *context)
     GtkWidget *dialog;
     unsigned char *sessionid;
     char sess1[21], sess2[21];
-    char *primary = g_strdup_printf("Private connection with %s "
-	    "established.", context->username);
+    char *primary = g_strdup_printf(__("Private connection with %s "
+	    "established."), context->username);
     char *secondary;
     int i;
     OtrlSessionIdHalf whichhalf = context->sessionid_half;
@@ -1003,15 +1448,16 @@ static GtkWidget* otrg_gtk_dialog_view_sessionid(ConnContext *context)
     for(i=0;i<idhalflen;++i) sprintf(sess2+(2*i), "%02x",
 	    sessionid[i+idhalflen]);
     
-    secondary = g_strdup_printf("Secure session id:\n"
+    secondary = g_strdup_printf("%s\n"
 	    "<span %s>%s</span> <span %s>%s</span>\n",
+	    __("Secure session id:"),
 	    whichhalf == OTRL_SESSIONID_FIRST_HALF_BOLD ?
 		    "weight=\"bold\"" : "", sess1,
 	    whichhalf == OTRL_SESSIONID_SECOND_HALF_BOLD ?
 		    "weight=\"bold\"" : "", sess2);
 
-    dialog = create_dialog(PURPLE_NOTIFY_MSG_INFO, "Private connection "
-	    "established", primary, secondary, 1, NULL,
+    dialog = create_dialog(PURPLE_NOTIFY_MSG_INFO,
+	    __("Private connection established"), primary, secondary, 1, NULL,
 	    add_sessid_expander, NULL);
 
     g_free(primary);
@@ -1019,6 +1465,7 @@ static GtkWidget* otrg_gtk_dialog_view_sessionid(ConnContext *context)
 
     return dialog;
 }
+#endif
 
 struct vrfy_fingerprint_data {
     Fingerprint *fprint;   /* You can use this pointer right away, but
@@ -1097,6 +1544,7 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
     struct vrfy_fingerprint_data *vfd = data;
     char *labelt;
     int verified = 0;
+    char *moremarkup;
 
     if (vfd->fprint->trust && vfd->fprint->trust[0]) {
 	verified = 1;
@@ -1104,10 +1552,10 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
 
     hbox = gtk_hbox_new(FALSE, 0);
     combo = gtk_combo_box_new_text();
-    gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "I have not");
-    gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "I have");
+    gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("I have not"));
+    gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("I have"));
     gtk_combo_box_set_active(GTK_COMBO_BOX(combo), verified);
-    label = gtk_label_new(" verified that this is in fact the correct");
+    label = gtk_label_new(_(" verified that this is in fact the correct"));
     gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
@@ -1116,7 +1564,7 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
 	    G_CALLBACK(vrfy_fingerprint_changed), vfd);
 
     hbox = gtk_hbox_new(FALSE, 0);
-    labelt = g_strdup_printf("fingerprint for %s.",
+    labelt = g_strdup_printf(_("fingerprint for %s."),
 	    vfd->username);
     label = gtk_label_new(labelt);
     g_free(labelt);
@@ -1126,26 +1574,30 @@ static void add_vrfy_fingerprint(GtkWidget *vbox, void *data)
     /* Leave a blank line */
     gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE, FALSE, 0);
 
-    add_whatsthis_more(vbox,
-	    "A <b>fingerprint</b> is a unique identifier that you should "
-	    "use to authenticate your buddy.",
-
-	    "To verify the fingerprint, contact your buddy via some "
+    moremarkup = g_strdup_printf(
+	    "%s\n\n%s\n\n%s\n\n%s\n\n<a href=\"%s%s\">%s</a>",
+	    _("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.\n\n"
-	    "If everything matches up, you should indicate in the above "
-	    "dialog that you <b>have</b> verified the fingerprint.\n\n"
-	    "If your buddy has more than one IM account, or uses more than "
-	    "one computer, he may have multiple fingerprints.\n\n"
-	    "However, the only way an imposter could duplicate one of your "
-	    "buddy's fingerprints is by stealing information from his "
-	    "computer.\n\n"
-	    "<a href=\"" FINGERPRINT_HELPURL "\">"
-	    "Click here for more information about fingerprints.</a>");
+	    "to the other."),
+	    _("If everything matches up, you should indicate in the above "
+	    "dialog that you <b>have</b> verified the fingerprint."),
+	    _("If your buddy has more than one IM account, or uses more than "
+	    "one computer, he may have multiple fingerprints."),
+	    _("However, the only way an imposter could duplicate one of your "
+	    "buddy's fingerprints is by stealing information from her/his "
+	    "computer."),
+	    FINGERPRINT_HELPURL, _("?lang=en"),
+	    _("Click here for more information about fingerprints."));
+
+    add_whatsthis_more(vbox,
+	    _("A <b>fingerprint</b> is a unique identifier that you should "
+	    "use to authenticate your buddy."), moremarkup);
+    g_free(moremarkup);
+
 }
 
-static void otrg_gtk_dialog_verify_fingerprint(Fingerprint *fprint)
+static void verify_fingerprint(GtkWindow *parent, Fingerprint *fprint)
 {
     GtkWidget *dialog;
     char our_hash[45], their_hash[45];
@@ -1161,23 +1613,25 @@ static void otrg_gtk_dialog_verify_fingerprint(Fingerprint *fprint)
     context = fprint->context;
     if (context == NULL) return;
 
-    primary = g_strdup_printf("Verify fingerprint for %s",
+    primary = g_strdup_printf(_("Verify fingerprint for %s"),
 	    context->username);
     vfd = vrfy_fingerprint_data_new(fprint);
 
+    strcpy(our_hash, _("[none]"));
     otrl_privkey_fingerprint(otrg_plugin_userstate, our_hash,
 	    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";
-    secondary = g_strdup_printf("Fingerprint for you, %s (%s):\n%s\n\n"
-	    "Purported fingerprint for %s:\n%s\n", context->accountname,
+    proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
+    secondary = 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);
 
-    dialog = create_dialog(PURPLE_NOTIFY_MSG_INFO, "Verify fingerprint",
-	    primary, secondary, 1, NULL, add_vrfy_fingerprint, vfd);
+    dialog = create_dialog(parent, PURPLE_NOTIFY_MSG_INFO,
+	    _("Verify fingerprint"), primary, secondary, 1, NULL,
+	    add_vrfy_fingerprint, vfd);
     g_signal_connect(G_OBJECT(dialog), "destroy",
 	    G_CALLBACK(vrfy_fingerprint_destroyed), vfd);
 
@@ -1185,29 +1639,133 @@ static void otrg_gtk_dialog_verify_fingerprint(Fingerprint *fprint)
     g_free(secondary);
 }
 
+static void otrg_gtk_dialog_verify_fingerprint(Fingerprint *fprint)
+{
+    verify_fingerprint(NULL, fprint);
+}
+
+/* Create the SMP dialog.  responder is true if this is called in
+ * response to someone else's run of SMP. */
+static void otrg_gtk_dialog_socialist_millionaires(ConnContext *context,
+	gboolean responder)
+{
+    GtkWidget *dialog;
+    char *primary;
+    char *secondary;
+    PurplePlugin *p;
+    char *proto_name;
+
+    if (context == NULL) return;
+
+    primary = g_strdup_printf(_("Authenticate %s"),
+	    context->username);
+
+    p = purple_find_prpl(context->protocol);
+    proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
+    secondary = g_strdup_printf(_("Enter a secret known only to %s and "
+		"yourself.\n"), context->username);
+
+    dialog = create_smp_dialog(_("Authenticate buddy"),
+	    primary, secondary, 1, NULL, context, responder);
+
+    g_free(primary);
+    g_free(secondary);
+}
+
+/* Call this to update the status of an ongoing socialist millionaires
+ * protocol.  Progress_level is a percentage, from 0.0 (aborted) to
+ * 1.0 (complete).  Any other value represents an intermediate state. */
+static void otrg_gtk_dialog_update_smp(ConnContext *context,
+	double progress_level)
+{
+    PurpleConversation *conv = otrg_plugin_context_to_conv(context, 0);
+    GtkProgressBar *bar;
+    SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata");
+
+    if (!smp_data) return;
+
+    bar = GTK_PROGRESS_BAR(smp_data->smp_progress_bar);
+    gtk_progress_bar_set_fraction(bar, progress_level);
+
+    /* 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);
+
+	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);
+
+	gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
+		_("An error occurred during authentication."));
+	return;
+    }
+
+    /* If the counter reaches 1.0, the protocol is complete */
+    if (progress_level == 1.0) {
+        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 (context->active_fingerprint->trust &&
+		context->active_fingerprint->trust[0]) {
+	    gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
+		    _("Authentication successful."));
+        } else {
+	    gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label),
+		    _("Authentication failed."));
+	}
+    }
+}
+
 /* Call this when a context transitions to ENCRYPTED. */
 static void otrg_gtk_dialog_connected(ConnContext *context)
 {
     PurpleConversation *conv;
     char *buf;
+    char *format_buf;
     TrustLevel level;
+    OtrgUiPrefs prefs;
 
-    conv = otrg_plugin_context_to_conv(context, 1);
+    conv = otrg_plugin_context_to_conv(context, TRUE);
     level = otrg_plugin_context_to_trust(context);
 
-    buf = g_strdup_printf("%s conversation with %s started.%s",
-		level == TRUST_PRIVATE ? "Private" :
-		level == TRUST_UNVERIFIED ? "<a href=\"" UNVERIFIED_HELPURL
-			"\">Unverified</a>" :
-		    /* This last case should never happen, since we know
-		     * we're in ENCRYPTED. */
-		    "Not private",
+    otrg_ui_get_prefs(&prefs, purple_conversation_get_account(conv),
+	    context->username);
+    if (prefs.avoid_logging_otr) {
+	purple_conversation_set_logging(conv, FALSE);
+    }
+
+    switch(level) {
+       case TRUST_PRIVATE:
+           format_buf = g_strdup(_("Private conversation with %s started.%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;
+
+       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;
+    }
+    buf = g_strdup_printf(format_buf,
 		purple_conversation_get_name(conv),
-		context->protocol_version == 1 ? "  Warning: using old "
-		    "protocol version 1." : "");
+		context->protocol_version == 1 ? _("  Warning: using old "
+		    "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);
 
     dialog_update_label(context);
 }
@@ -1217,15 +1775,26 @@ static void otrg_gtk_dialog_disconnected(ConnContext *context)
 {
     PurpleConversation *conv;
     char *buf;
+    OtrgUiPrefs prefs;
 
     conv = otrg_plugin_context_to_conv(context, 1);
 
-    buf = g_strdup_printf("Private conversation with %s lost.",
+    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));
     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"))
+	{
+	    purple_conversation_set_logging(conv, TRUE);
+	}
+    }
+
     dialog_update_label(context);
+    close_smp_window(conv);
 }
 
 /* Call this if the remote user terminates his end of an ENCRYPTED
@@ -1241,15 +1810,19 @@ static void otrg_gtk_dialog_finished(const char *accountname,
     account = purple_accounts_find(accountname, protocol);
     if (!account) return;
 
-    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, account);
+    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+	    username, account);
     if (!conv) return;
 
-    buf = g_strdup_printf("%s has ended his private conversation with you; "
-	    "you should do the same.", purple_conversation_get_name(conv));
-    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+    buf = g_strdup_printf(_("%s has ended his/her private conversation with "
+		"you; you should do the same."),
+	    purple_conversation_get_name(conv));
+    purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM,
+	    time(NULL));
     g_free(buf);
 
     dialog_update_label_conv(conv, TRUST_FINISHED);
+    close_smp_window(conv);
 }
 
 /* Call this when we receive a Key Exchange message that doesn't cause
@@ -1258,25 +1831,42 @@ static void otrg_gtk_dialog_stillconnected(ConnContext *context)
 {
     PurpleConversation *conv;
     char *buf;
+    char *format_buf;
     TrustLevel level;
 
     conv = otrg_plugin_context_to_conv(context, 1);
     level = otrg_plugin_context_to_trust(context);
 
-    buf = g_strdup_printf("Successfully refreshed the %s conversation "
-		"with %s.%s",
-		level == TRUST_PRIVATE ? "private" :
-		level == TRUST_UNVERIFIED ? "<a href=\"" UNVERIFIED_HELPURL
-			"\">unverified</a>" :
-		    /* This last case should never happen, since we know
-		     * we're in ENCRYPTED. */
-		    "not private",
+    switch(level) {
+       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;
+
+       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." : "");
+		context->protocol_version == 1 ? _("  Warning: using old "
+		    "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);
 
     dialog_update_label(context);
 }
@@ -1290,17 +1880,19 @@ static void otrg_gtk_dialog_clicked_connect(GtkWidget *widget, gpointer data)
     PurpleConversation *conv = data;
 
     if (purple_conversation_get_data(conv, "otr-private")) {
-	format = "Attempting to refresh the private conversation with %s...";
+	format = _("Attempting to refresh the private conversation with %s...");
     } else {
-	format = "Attempting to start a private conversation with %s...";
+	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);
 }
 
+#if 0
 static void view_sessionid(GtkWidget *widget, gpointer data)
 {
     PurpleConversation *conv = data;
@@ -1311,7 +1903,21 @@ static void view_sessionid(GtkWidget *widget, gpointer data)
 
     otrg_gtk_dialog_view_sessionid(context);
 }
+#endif
+
+/* 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);
 
+    if (context == NULL || context->msgstate != OTRL_MSGSTATE_ENCRYPTED)
+	return;
+
+    otrg_gtk_dialog_socialist_millionaires(context, FALSE);
+}
+
+#if 0
 static void verify_fingerprint(GtkWidget *widget, gpointer data)
 {
     PurpleConversation *conv = data;
@@ -1322,10 +1928,13 @@ static void verify_fingerprint(GtkWidget *widget, gpointer data)
 
     otrg_gtk_dialog_verify_fingerprint(context->active_fingerprint);
 }
+#endif
 
 static void menu_whatsthis(GtkWidget *widget, gpointer data)
 {
-    purple_notify_uri(otrg_plugin_handle, BUTTON_HELPURL);
+    char *uri = g_strdup_printf("%s%s", BUTTON_HELPURL, _("?lang=en"));
+    purple_notify_uri(otrg_plugin_handle, uri);
+    g_free(uri);
 }
 
 static void menu_end_private_conversation(GtkWidget *widget, gpointer data)
@@ -1355,9 +1964,9 @@ static gboolean button_pressed(GtkWidget *w, GdkEventButton *event,
     return FALSE;
 }
 
-/* If the OTR button gets destroyed on us, clean up the data we stored
+/* If the conversation gets destroyed on us, clean up the data we stored
  * pointing to it. */
-static void button_destroyed(GtkWidget *w, PurpleConversation *conv)
+static void conversation_destroyed(PurpleConversation *conv, void *data)
 {
     GtkWidget *menu = purple_conversation_get_data(conv, "otr-menu");
     if (menu) gtk_object_destroy(GTK_OBJECT(menu));
@@ -1371,6 +1980,8 @@ static void button_destroyed(GtkWidget *w, PurpleConversation *conv)
     g_hash_table_remove(conv->data, "otr-menuend");
     g_hash_table_remove(conv->data, "otr-menuview");
     g_hash_table_remove(conv->data, "otr-menuverf");
+    g_hash_table_remove(conv->data, "otr-menusmp");
+    otrg_gtk_dialog_free_smp_data(conv);
 }
 
 /* Set up the per-conversation information display */
@@ -1390,8 +2001,11 @@ static void otrg_gtk_dialog_new_conv(PurpleConversation *conv)
     GtkWidget *menuquery;
     GtkWidget *menuend;
     GtkWidget *menusep;
+    /*
     GtkWidget *menuview;
     GtkWidget *menuverf;
+    */
+    GtkWidget *menusmp;
     GtkWidget *whatsthis;
 
     /* Do nothing if this isn't an IM conversation */
@@ -1428,7 +2042,7 @@ static void otrg_gtk_dialog_new_conv(PurpleConversation *conv)
     gtk_box_pack_start(GTK_BOX(bvbox), iconbox, FALSE, FALSE, 0);
     label = gtk_label_new(NULL);
     gtk_box_pack_start(GTK_BOX(bvbox), label, FALSE, FALSE, 0);
-    icontext = gtk_label_new("OTR:");
+    icontext = gtk_label_new(_("OTR:"));
     gtk_box_pack_start(GTK_BOX(iconbox), icontext, FALSE, FALSE, 0);
     icon = otr_icon(NULL, TRUST_NOT_PRIVATE);
     gtk_box_pack_start(GTK_BOX(iconbox), icon, TRUE, FALSE, 0);
@@ -1437,13 +2051,13 @@ static void otrg_gtk_dialog_new_conv(PurpleConversation *conv)
 
     /* Make the context menu */
     menu = gtk_menu_new();
-    gtk_menu_set_title(GTK_MENU(menu), "OTR Messaging");
+    gtk_menu_set_title(GTK_MENU(menu), _("OTR Messaging"));
 
     menuquery = gtk_menu_item_new_with_mnemonic("");
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuquery);
     gtk_widget_show(menuquery);
 
-    menuend = gtk_menu_item_new_with_mnemonic("_End private conversation");
+    menuend = gtk_menu_item_new_with_mnemonic(_("_End private conversation"));
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuend);
     gtk_widget_show(menuend);
 
@@ -1451,19 +2065,34 @@ static void otrg_gtk_dialog_new_conv(PurpleConversation *conv)
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menusep);
     gtk_widget_show(menusep);
 
-    menuverf = gtk_menu_item_new_with_mnemonic("_Verify fingerprint");
+    /*
+     * Don't show the Verify fingerprint menu option any more.  You can
+     * still get to the dialog through Authenticate connection ->
+     * Advanced...
+     *
+    menuverf = gtk_menu_item_new_with_mnemonic(_("_Verify fingerprint"));
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuverf);
     gtk_widget_show(menuverf);
+    */
+
+    menusmp = gtk_menu_item_new_with_mnemonic(_("_Authenticate buddy"));
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menusmp);
+    gtk_widget_show(menusmp);
 
-    menuview = gtk_menu_item_new_with_mnemonic("View _secure session id");
+    /*
+     * Don't show the View secure session id menu option any more.  It's
+     * not really useful at all.
+     *
+    menuview = gtk_menu_item_new_with_mnemonic(_("View _secure session id"));
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuview);
     gtk_widget_show(menuview);
+    */
 
     menusep = gtk_separator_menu_item_new();
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menusep);
     gtk_widget_show(menusep);
 
-    whatsthis = gtk_menu_item_new_with_mnemonic("_What's this?");
+    whatsthis = gtk_menu_item_new_with_mnemonic(_("_What's this?"));
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), whatsthis);
     gtk_widget_show(whatsthis);
 
@@ -1474,27 +2103,37 @@ static void otrg_gtk_dialog_new_conv(PurpleConversation *conv)
     purple_conversation_set_data(conv, "otr-menu", menu);
     purple_conversation_set_data(conv, "otr-menuquery", menuquery);
     purple_conversation_set_data(conv, "otr-menuend", menuend);
+    /*
     purple_conversation_set_data(conv, "otr-menuview", menuview);
     purple_conversation_set_data(conv, "otr-menuverf", menuverf);
+    */
+    purple_conversation_set_data(conv, "otr-menusmp", menusmp);
     gtk_signal_connect(GTK_OBJECT(menuquery), "activate",
 	    GTK_SIGNAL_FUNC(otrg_gtk_dialog_clicked_connect), conv);
     gtk_signal_connect(GTK_OBJECT(menuend), "activate",
 	    GTK_SIGNAL_FUNC(menu_end_private_conversation), conv);
+    /*
     gtk_signal_connect(GTK_OBJECT(menuverf), "activate",
 	    GTK_SIGNAL_FUNC(verify_fingerprint), conv);
+    */
+    gtk_signal_connect(GTK_OBJECT(menusmp), "activate",
+	    GTK_SIGNAL_FUNC(socialist_millionaires), conv);
+    /*
     gtk_signal_connect(GTK_OBJECT(menuview), "activate",
 	    GTK_SIGNAL_FUNC(view_sessionid), conv);
+    */
     gtk_signal_connect(GTK_OBJECT(whatsthis), "activate",
 	    GTK_SIGNAL_FUNC(menu_whatsthis), conv);
     gtk_signal_connect(GTK_OBJECT(button), "clicked",
 	    GTK_SIGNAL_FUNC(otrg_gtk_dialog_clicked_connect), conv);
-    g_signal_connect(G_OBJECT(button), "destroy",
-	    G_CALLBACK(button_destroyed), conv);
     g_signal_connect(G_OBJECT(button), "button-press-event",
 	    G_CALLBACK(button_pressed), conv);
 
     dialog_update_label_conv(conv, otrg_plugin_context_to_trust(context));
     dialog_resensitize(conv);
+
+    /* Finally, add the state for the socialist millionaires dialogs */
+    otrg_gtk_dialog_add_smp_data(conv);
 }
 
 /* Remove the per-conversation information display */
@@ -1507,6 +2146,7 @@ static void otrg_gtk_dialog_remove_conv(PurpleConversation *conv)
 
     button = purple_conversation_get_data(conv, "otr-button");
     if (button) gtk_object_destroy(GTK_OBJECT(button));
+    conversation_destroyed(conv, NULL);
 }
 
 /* Set the OTR button to "sensitive" or "insensitive" as appropriate. */
@@ -1516,16 +2156,16 @@ static void dialog_resensitize(PurpleConversation *conv)
     PurpleConnection *connection;
     GtkWidget *button;
     const char *name;
-    OtrlPolicy policy;
+    OtrgUiPrefs prefs;
 
     /* Do nothing if this isn't an IM conversation */
     if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM) return;
 
     account = purple_conversation_get_account(conv);
     name = purple_conversation_get_name(conv);
-    policy = otrg_ui_find_policy(account, name);
+    otrg_ui_get_prefs(&prefs, account, name);
 
-    if (policy == OTRL_POLICY_NEVER) {
+    if (prefs.policy == OTRL_POLICY_NEVER) {
 	otrg_gtk_dialog_remove_conv(conv);
     } else {
 	otrg_gtk_dialog_new_conv(conv);
@@ -1551,13 +2191,33 @@ static void otrg_gtk_dialog_resensitize_all(void)
     purple_conversation_foreach(dialog_resensitize);
 }
 
+/* Initialize the OTR dialog subsystem */
+static void otrg_gtk_dialog_init(void)
+{
+    purple_signal_connect(purple_conversations_get_handle(),
+	    "deleting-conversation", otrg_plugin_handle,
+	    PURPLE_CALLBACK(conversation_destroyed), NULL);
+}
+
+/* Deinitialize the OTR dialog subsystem */
+static void otrg_gtk_dialog_cleanup(void)
+{
+    purple_signal_disconnect(purple_conversations_get_handle(),
+	    "deleting-conversation", otrg_plugin_handle,
+	    PURPLE_CALLBACK(conversation_destroyed));
+}
+
 static const OtrgDialogUiOps gtk_dialog_ui_ops = {
+    otrg_gtk_dialog_init,
+    otrg_gtk_dialog_cleanup,
     otrg_gtk_dialog_notify_message,
     otrg_gtk_dialog_display_otr_message,
     otrg_gtk_dialog_private_key_wait_start,
     otrg_gtk_dialog_private_key_wait_done,
     otrg_gtk_dialog_unknown_fingerprint,
     otrg_gtk_dialog_verify_fingerprint,
+    otrg_gtk_dialog_socialist_millionaires,
+    otrg_gtk_dialog_update_smp,
     otrg_gtk_dialog_connected,
     otrg_gtk_dialog_disconnected,
     otrg_gtk_dialog_stillconnected,
diff --git a/gtk-dialog.h b/gtk-dialog.h
index a9951a5..2a1098c 100644
--- a/gtk-dialog.h
+++ b/gtk-dialog.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/gtk-ui.c b/gtk-ui.c
index efc01dd..37f1b8e 100644
--- a/gtk-ui.c
+++ b/gtk-ui.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,11 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+/* config.h */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 /* system headers */
 #include <gtk/gtk.h>
 
@@ -32,6 +37,11 @@
 #include "notify.h"
 #include "gtkutils.h"
 
+#ifdef ENABLE_NLS
+/* internationalisation header */
+#include <glib/gi18n-lib.h>
+#endif
+
 /* purple-otr headers */
 #include "dialogs.h"
 #include "ui.h"
@@ -41,6 +51,7 @@ struct otroptionsdata {
     GtkWidget *enablebox;
     GtkWidget *automaticbox;
     GtkWidget *onlyprivatebox;
+    GtkWidget *avoidloggingotrbox;
 };
 
 static struct {
@@ -59,10 +70,10 @@ static struct {
 } ui_layout;
 
 static const gchar *trust_states[] = {
-    "Not private",
-    "Unverified",
-    "Private",
-    "Finished"
+    N_("Not private"),
+    N_("Unverified"),
+    N_("Private"),
+    N_("Finished")
 };
 
 static void account_menu_changed_cb(GtkWidget *item, PurpleAccount *account,
@@ -82,16 +93,16 @@ static void account_menu_changed_cb(GtkWidget *item, PurpleAccount *account,
 		fingerprint_buf, accountname, protocol);
 
 	if (fingerprint) {
-	    sprintf(s, "Fingerprint: %.80s", fingerprint);
+	    sprintf(s, _("Fingerprint: %.80s"), fingerprint);
 	    if (ui_layout.generate_button)
 		gtk_widget_set_sensitive(ui_layout.generate_button, 0);
 	} else {
-	    sprintf(s, "No key present");
+	    sprintf(s, _("No key present"));
 	    if (ui_layout.generate_button)
 		gtk_widget_set_sensitive(ui_layout.generate_button, 1);
 	}
     } else {
-	sprintf(s, "No account available");
+	sprintf(s, _("No account available"));
 	if (ui_layout.generate_button)
 	    gtk_widget_set_sensitive(ui_layout.generate_button, 0);
     }
@@ -101,39 +112,11 @@ static void account_menu_changed_cb(GtkWidget *item, PurpleAccount *account,
     }
 }
 
-static GtkWidget *accountmenu_get_selected_item(void)
-{
-    GtkWidget *menu;
-
-    if (ui_layout.accountmenu == NULL) return NULL;
-
-    menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(ui_layout.accountmenu));
-    return gtk_menu_get_active(GTK_MENU(menu));
-}
-
-static PurpleAccount *item_get_account(GtkWidget *item)
-{
-    if (!item) return NULL;
-    return g_object_get_data(G_OBJECT(item), "account");
-}
-
 /* Call this function when the DSA key is updated; it will redraw the
  * UI, if visible. */
 static void otrg_gtk_ui_update_fingerprint(void)
 {
-    GtkWidget *item;
-    PurpleAccount *account;
-    gpointer user_data;
-
-    item = accountmenu_get_selected_item();
-
-    if (!item) return;
-
-    account   = item_get_account(item);
-    user_data = g_object_get_data(G_OBJECT(ui_layout.accountmenu),
-	    "user_data");
-
-    account_menu_changed_cb(item, account, user_data);
+    g_signal_emit_by_name(G_OBJECT(ui_layout.accountmenu), "changed");
 }
 
 static void account_menu_added_removed_cb(PurpleAccount *account, void *data)
@@ -179,17 +162,17 @@ static void otrg_gtk_ui_update_keylist(void)
 	    titles[0] = context->username;
 	    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
 		    context->active_fingerprint != fingerprint) {
-		titles[1] = "Unused";
+		titles[1] = _("Unused");
 	    } else {
 		titles[1] = (gchar *)
-		    trust_states[otrg_plugin_context_to_trust(context)];
+		    _(trust_states[otrg_plugin_context_to_trust(context)]);
 	    }
 	    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";
+	    proto_name = (p && p->info->name) ? p->info->name : _("Unknown");
 	    titles[4] = g_strdup_printf("%s (%s)", context->accountname,
 		proto_name);
 	    i = gtk_clist_append(GTK_CLIST(keylist), titles);
@@ -217,7 +200,7 @@ static void otrg_gtk_ui_update_keylist(void)
 static void generate(GtkWidget *widget, gpointer data)
 {
     PurpleAccount *account;
-    account = item_get_account(accountmenu_get_selected_item());
+    account = pidgin_account_option_menu_get_selected(ui_layout.accountmenu);
 	
     if (account == NULL) return;
 	
@@ -397,9 +380,11 @@ static void otroptions_clicked_cb(GtkButton *button, struct otroptionsdata *oo)
 	} else {
 	    gtk_widget_set_sensitive(oo->onlyprivatebox, FALSE);
 	}
+	gtk_widget_set_sensitive(oo->avoidloggingotrbox, TRUE);
     } else {
 	gtk_widget_set_sensitive(oo->automaticbox, FALSE);
 	gtk_widget_set_sensitive(oo->onlyprivatebox, FALSE);
+	gtk_widget_set_sensitive(oo->avoidloggingotrbox, FALSE);
     }
 }
 
@@ -408,12 +393,14 @@ static void create_otroption_buttons(struct otroptionsdata *oo,
 {
     GtkWidget *tempbox1, *tempbox2;
 
-    oo->enablebox = gtk_check_button_new_with_label("Enable private "
-	    "messaging");
-    oo->automaticbox = gtk_check_button_new_with_label("Automatically "
-	    "initiate private messaging");
-    oo->onlyprivatebox = gtk_check_button_new_with_label("Require private "
-	    "messaging");
+    oo->enablebox = gtk_check_button_new_with_label(_("Enable private "
+	    "messaging"));
+    oo->automaticbox = gtk_check_button_new_with_label(_("Automatically "
+	    "initiate private messaging"));
+    oo->onlyprivatebox = gtk_check_button_new_with_label(_("Require private "
+	    "messaging"));
+    oo->avoidloggingotrbox = gtk_check_button_new_with_label(
+	    _("Don't log OTR conversations"));
 
     gtk_box_pack_start(GTK_BOX(vbox), oo->enablebox,
 	    FALSE, FALSE, 0);
@@ -433,32 +420,39 @@ static void create_otroption_buttons(struct otroptionsdata *oo,
     gtk_box_pack_start(GTK_BOX(tempbox2), oo->onlyprivatebox,
 	    FALSE, FALSE, 0);
 
+    gtk_box_pack_start(GTK_BOX(vbox), oo->avoidloggingotrbox, FALSE, FALSE, 5);
+
     g_signal_connect(G_OBJECT(oo->enablebox), "clicked",
 		     G_CALLBACK(otroptions_clicked_cb), oo);
     g_signal_connect(G_OBJECT(oo->automaticbox), "clicked",
 		     G_CALLBACK(otroptions_clicked_cb), oo);
     g_signal_connect(G_OBJECT(oo->onlyprivatebox), "clicked",
 		     G_CALLBACK(otroptions_clicked_cb), oo);
+    g_signal_connect(G_OBJECT(oo->avoidloggingotrbox), "clicked",
+		     G_CALLBACK(otroptions_clicked_cb), oo);
 }
 
 /* Load the global OTR prefs */
 static void otrg_gtk_ui_global_prefs_load(gboolean *enabledp,
-	gboolean *automaticp, gboolean *onlyprivatep)
+	gboolean *automaticp, gboolean *onlyprivatep,
+	gboolean *avoidloggingotrp)
 {
     if (purple_prefs_exists("/OTR/enabled")) {
 	*enabledp = purple_prefs_get_bool("/OTR/enabled");
 	*automaticp = purple_prefs_get_bool("/OTR/automatic");
 	*onlyprivatep = purple_prefs_get_bool("/OTR/onlyprivate");
+	*avoidloggingotrp = purple_prefs_get_bool("/OTR/avoidloggingotr");
     } else {
 	*enabledp = TRUE;
 	*automaticp = TRUE;
 	*onlyprivatep = FALSE;
+	*avoidloggingotrp = FALSE;
     }
 }
 
 /* Save the global OTR prefs */
 static void otrg_gtk_ui_global_prefs_save(gboolean enabled,
-	gboolean automatic, gboolean onlyprivate)
+	gboolean automatic, gboolean onlyprivate, gboolean avoidloggingotr)
 {
     if (! purple_prefs_exists("/OTR")) {
 	purple_prefs_add_none("/OTR");
@@ -466,30 +460,34 @@ static void otrg_gtk_ui_global_prefs_save(gboolean enabled,
     purple_prefs_set_bool("/OTR/enabled", enabled);
     purple_prefs_set_bool("/OTR/automatic", automatic);
     purple_prefs_set_bool("/OTR/onlyprivate", onlyprivate);
+    purple_prefs_set_bool("/OTR/avoidloggingotr", avoidloggingotr);
 }
 
 /* Load the OTR prefs for a particular buddy */
 static void otrg_gtk_ui_buddy_prefs_load(PurpleBuddy *buddy,
 	gboolean *usedefaultp, gboolean *enabledp, gboolean *automaticp,
-	gboolean *onlyprivatep)
+	gboolean *onlyprivatep, gboolean *avoidloggingotrp)
 {
     PurpleBlistNode *node = &(buddy->node);
 
     *usedefaultp = ! purple_blist_node_get_bool(node, "OTR/overridedefault");
 
     if (*usedefaultp) {
-	otrg_gtk_ui_global_prefs_load(enabledp, automaticp, onlyprivatep);
+	otrg_gtk_ui_global_prefs_load(enabledp, automaticp, onlyprivatep,
+		avoidloggingotrp);
     } else {
 	*enabledp = purple_blist_node_get_bool(node, "OTR/enabled");
 	*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");
     }
 }
 
 /* Save the OTR prefs for a particular buddy */
 static void otrg_gtk_ui_buddy_prefs_save(PurpleBuddy *buddy,
 	gboolean usedefault, gboolean enabled, gboolean automatic,
-	gboolean onlyprivate)
+	gboolean onlyprivate, gboolean avoidloggingotr)
 {
     PurpleBlistNode *node = &(buddy->node);
 
@@ -497,6 +495,7 @@ static void otrg_gtk_ui_buddy_prefs_save(PurpleBuddy *buddy,
     purple_blist_node_set_bool(node, "OTR/enabled", enabled);
     purple_blist_node_set_bool(node, "OTR/automatic", automatic);
     purple_blist_node_set_bool(node, "OTR/onlyprivate", onlyprivate);
+    purple_blist_node_set_bool(node, "OTR/avoidloggingotr", avoidloggingotr);
 }
 
 static void load_otroptions(struct otroptionsdata *oo)
@@ -504,8 +503,10 @@ static void load_otroptions(struct otroptionsdata *oo)
     gboolean otrenabled;
     gboolean otrautomatic;
     gboolean otronlyprivate;
+    gboolean otravoidloggingotr;
 
-    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate);
+    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate,
+	    &otravoidloggingotr);
 
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->enablebox),
 	    otrenabled);
@@ -513,6 +514,8 @@ static void load_otroptions(struct otroptionsdata *oo)
 	    otrautomatic);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->onlyprivatebox),
 	    otronlyprivate);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->avoidloggingotrbox),
+	    otravoidloggingotr);
 
     otroptions_clicked_cb(GTK_BUTTON(oo->enablebox), oo);
 }
@@ -525,7 +528,7 @@ static void make_privkeys_ui(GtkWidget *vbox)
     GtkWidget *label;
     GtkWidget *frame;
 
-    frame = gtk_frame_new("My private keys");
+    frame = gtk_frame_new(_("My private keys"));
     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
 
     fbox = gtk_vbox_new(FALSE, 5);
@@ -534,7 +537,7 @@ static void make_privkeys_ui(GtkWidget *vbox)
 
     hbox = gtk_hbox_new(FALSE, 5);
     gtk_box_pack_start(GTK_BOX(fbox), hbox, FALSE, FALSE, 0);
-    label = gtk_label_new("Key for account:");
+    label = gtk_label_new(_("Key for account:"));
     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
     ui_layout.accountmenu = pidgin_account_option_menu_new(NULL, 1,
@@ -559,7 +562,7 @@ static void make_privkeys_ui(GtkWidget *vbox)
     gtk_signal_connect(GTK_OBJECT(ui_layout.generate_button), "clicked",
 	    GTK_SIGNAL_FUNC(generate), NULL);
 
-    label = gtk_label_new("Generate");
+    label = gtk_label_new(_("Generate"));
     gtk_container_add(GTK_CONTAINER(ui_layout.generate_button), label);
 
     otrg_gtk_ui_update_fingerprint();
@@ -577,7 +580,9 @@ static void otroptions_save_cb(GtkButton *button, struct otroptionsdata *oo)
 	    gtk_toggle_button_get_active(
 		GTK_TOGGLE_BUTTON(oo->automaticbox)),
 	    gtk_toggle_button_get_active(
-		GTK_TOGGLE_BUTTON(oo->onlyprivatebox)));
+		GTK_TOGGLE_BUTTON(oo->onlyprivatebox)),
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(oo->avoidloggingotrbox)));
 
     otrg_dialog_resensitize_all();
 }
@@ -588,7 +593,7 @@ static void make_options_ui(GtkWidget *vbox)
     GtkWidget *fbox;
     GtkWidget *frame;
 
-    frame = gtk_frame_new("Default OTR Settings");
+    frame = gtk_frame_new(_("Default OTR Settings"));
     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
 
     fbox = gtk_vbox_new(FALSE, 0);
@@ -605,6 +610,8 @@ static void make_options_ui(GtkWidget *vbox)
 		     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
     g_signal_connect(G_OBJECT(ui_layout.oo.onlyprivatebox), "clicked",
 		     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
+    g_signal_connect(G_OBJECT(ui_layout.oo.avoidloggingotrbox), "clicked",
+		     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
 }
 
 /* Create the fingerprint UI, and pack it into the vbox */
@@ -613,8 +620,13 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     GtkWidget *hbox;
     GtkWidget *table;
     GtkWidget *label;
-    char *titles[5] = { "Screenname", "Status", "Verified",
-	"Fingerprint", "Account" };
+    char *titles[5];
+
+    titles[0] = _("Screenname");
+    titles[1] = _("Status");
+    titles[2] = _("Verified");
+    titles[3] = _("Fingerprint");
+    titles[4] = _("Account");
 
     ui_layout.scrollwin = gtk_scrolled_window_new(0, 0);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui_layout.scrollwin), 
@@ -650,7 +662,7 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     ui_layout.connect_button = gtk_button_new();
     gtk_signal_connect(GTK_OBJECT(ui_layout.connect_button), "clicked",
 	    GTK_SIGNAL_FUNC(connect_connection), NULL);
-    label = gtk_label_new("Start private connection");
+    label = gtk_label_new(_("Start private connection"));
     gtk_container_add(GTK_CONTAINER(ui_layout.connect_button), label);
     gtk_table_attach_defaults(GTK_TABLE(table), ui_layout.connect_button,
 	    0, 1, 0, 1);
@@ -658,7 +670,7 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     ui_layout.disconnect_button = gtk_button_new();
     gtk_signal_connect(GTK_OBJECT(ui_layout.disconnect_button), "clicked",
 	    GTK_SIGNAL_FUNC(disconnect_connection), NULL);
-    label = gtk_label_new("End private connection");
+    label = gtk_label_new(_("End private connection"));
     gtk_container_add(GTK_CONTAINER(ui_layout.disconnect_button), label);
     gtk_table_attach_defaults(GTK_TABLE(table), ui_layout.disconnect_button,
 	    0, 1, 1, 2);
@@ -666,7 +678,7 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     ui_layout.verify_button = gtk_button_new();
     gtk_signal_connect(GTK_OBJECT(ui_layout.verify_button), "clicked",
 	    GTK_SIGNAL_FUNC(verify_fingerprint), NULL);
-    label = gtk_label_new("Verify fingerprint");
+    label = gtk_label_new(_("Verify fingerprint"));
     gtk_container_add(GTK_CONTAINER(ui_layout.verify_button), label);
     gtk_table_attach_defaults(GTK_TABLE(table), ui_layout.verify_button,
 	    1, 2, 0, 1);
@@ -674,7 +686,7 @@ static void make_fingerprints_ui(GtkWidget *vbox)
     ui_layout.forget_button = gtk_button_new();
     gtk_signal_connect(GTK_OBJECT(ui_layout.forget_button), "clicked",
 	    GTK_SIGNAL_FUNC(forget_fingerprint), NULL);
-    label = gtk_label_new("Forget fingerprint");
+    label = gtk_label_new(_("Forget fingerprint"));
     gtk_container_add(GTK_CONTAINER(ui_layout.forget_button), label);
     gtk_table_attach_defaults(GTK_TABLE(table), ui_layout.forget_button,
 	    1, 2, 1, 2);
@@ -722,10 +734,10 @@ GtkWidget* otrg_gtk_ui_make_widget(PurplePlugin *plugin)
 
     make_fingerprints_ui(fingerprintbox);
 
-    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), fingerprintbox,
-	    gtk_label_new("Known fingerprints"));
     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), configbox,
-	    gtk_label_new("Config"));
+	    gtk_label_new(_("Config")));
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), fingerprintbox,
+	    gtk_label_new(_("Known fingerprints")));
 
     gtk_widget_show_all(vbox);
 
@@ -747,6 +759,7 @@ static void default_clicked_cb(GtkButton *button, struct cbdata *data)
 	gtk_widget_set_sensitive(data->oo.enablebox, FALSE);
 	gtk_widget_set_sensitive(data->oo.automaticbox, FALSE);
 	gtk_widget_set_sensitive(data->oo.onlyprivatebox, FALSE);
+	gtk_widget_set_sensitive(data->oo.avoidloggingotrbox, FALSE);
     } else {
 	otroptions_clicked_cb(button, &(data->oo));
     }
@@ -754,10 +767,10 @@ static void default_clicked_cb(GtkButton *button, struct cbdata *data)
 
 static void load_buddyprefs(struct cbdata *data)
 {
-    gboolean usedefault, enabled, automatic, onlyprivate;
+    gboolean usedefault, enabled, automatic, onlyprivate, avoidloggingotr;
 
     otrg_gtk_ui_buddy_prefs_load(data->buddy, &usedefault, &enabled,
-	    &automatic, &onlyprivate);
+	    &automatic, &onlyprivate, &avoidloggingotr);
 
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->defaultbox),
 	    usedefault);
@@ -773,6 +786,9 @@ static void load_buddyprefs(struct cbdata *data)
 		GTK_TOGGLE_BUTTON(data->oo.automaticbox), automatic);
 	gtk_toggle_button_set_active(
 		GTK_TOGGLE_BUTTON(data->oo.onlyprivatebox), onlyprivate);
+	gtk_toggle_button_set_active(
+		GTK_TOGGLE_BUTTON(data->oo.avoidloggingotrbox),
+		avoidloggingotr);
     }
 
     default_clicked_cb(GTK_BUTTON(data->defaultbox), data);
@@ -796,7 +812,9 @@ static void config_buddy_clicked_cb(GtkButton *button, struct cbdata *data)
 	 gtk_toggle_button_get_active(
 	     GTK_TOGGLE_BUTTON(data->oo.automaticbox)),
 	 gtk_toggle_button_get_active(
-	     GTK_TOGGLE_BUTTON(data->oo.onlyprivatebox)));
+	     GTK_TOGGLE_BUTTON(data->oo.onlyprivatebox)),
+	 gtk_toggle_button_get_active(
+	     GTK_TOGGLE_BUTTON(data->oo.avoidloggingotrbox)));
 
     otrg_dialog_resensitize_all();
 }
@@ -812,11 +830,12 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
     GtkWidget *dialog;
     GtkWidget *label;
     char *label_text;
+    char *label_markup;
     struct cbdata *data = malloc(sizeof(struct cbdata));
 
     if (!data) return;
 
-    dialog = gtk_dialog_new_with_buttons("OTR Settings",
+    dialog = gtk_dialog_new_with_buttons(_("OTR Settings"),
 					 NULL, 0,
 					 GTK_STOCK_OK, GTK_RESPONSE_OK,
 					 NULL);
@@ -834,12 +853,15 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
 
     /* Set the title */
 
-    label_text = g_strdup_printf("<span weight=\"bold\" size=\"larger\">"
-	    "OTR Settings for %s</span>", purple_buddy_get_contact_alias(buddy));
+    label_text = g_strdup_printf(_("OTR Settings for %s"),
+	    purple_buddy_get_contact_alias(buddy));
+    label_markup = g_strdup_printf("<span weight=\"bold\" size=\"larger\">"
+	    "%s</span>", label_text);
 
     label = gtk_label_new(NULL);
 
-    gtk_label_set_markup(GTK_LABEL(label), label_text);
+    gtk_label_set_markup(GTK_LABEL(label), label_markup);
+    g_free(label_markup);
     g_free(label_text);
     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
@@ -848,8 +870,8 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
 
     /* Make the cascaded checkboxes */
 
-    data->defaultbox = gtk_check_button_new_with_label("Use default "
-	    "OTR settings for this buddy");
+    data->defaultbox = gtk_check_button_new_with_label(_("Use default "
+	    "OTR settings for this buddy"));
 
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), data->defaultbox,
 	    FALSE, FALSE, 0);
@@ -869,6 +891,8 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
 		     G_CALLBACK(config_buddy_clicked_cb), data);
     g_signal_connect(G_OBJECT(data->oo.onlyprivatebox), "clicked",
 		     G_CALLBACK(config_buddy_clicked_cb), data);
+    g_signal_connect(G_OBJECT(data->oo.avoidloggingotrbox), "clicked",
+		     G_CALLBACK(config_buddy_clicked_cb), data);
 
     /* Set the inital states of the buttons */
     load_buddyprefs(data);
@@ -881,63 +905,81 @@ static void otrg_gtk_ui_config_buddy(PurpleBuddy *buddy)
     gtk_widget_show_all(dialog);
 }
 
-/* Calculate the policy for a particular account / username */
-static OtrlPolicy otrg_gtk_ui_find_policy(PurpleAccount *account,
+/* Load the preferences for a particular account / username */
+static void otrg_gtk_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
 	const char *name)
 {
     PurpleBuddy *buddy;
-    gboolean otrenabled, otrautomatic, otronlyprivate;
-    gboolean buddyusedefault, buddyenabled, buddyautomatic, buddyonlyprivate;
-    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+    gboolean otrenabled, otrautomatic, otronlyprivate, otravoidloggingotr;
+    gboolean buddyusedefault, buddyenabled, buddyautomatic, buddyonlyprivate,
+	     buddyavoidloggingotr;
+
+    prefsp->policy = OTRL_POLICY_DEFAULT;
+    prefsp->avoid_logging_otr = FALSE;
     
     /* Get the default policy */
-    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate);
+    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate,
+	    &otravoidloggingotr);
 
     if (otrenabled) {
 	if (otrautomatic) {
 	    if (otronlyprivate) {
-		policy = OTRL_POLICY_ALWAYS;
+		prefsp->policy = OTRL_POLICY_ALWAYS;
 	    } else {
-		policy = OTRL_POLICY_OPPORTUNISTIC;
+		prefsp->policy = OTRL_POLICY_OPPORTUNISTIC;
 	    }
 	} else {
-	    policy = OTRL_POLICY_MANUAL;
+	    prefsp->policy = OTRL_POLICY_MANUAL;
 	}
+	prefsp->avoid_logging_otr = otravoidloggingotr;
     } else {
-	policy = OTRL_POLICY_NEVER;
+	prefsp->policy = OTRL_POLICY_NEVER;
     }
 
     buddy = purple_find_buddy(account, name);
-    if (!buddy) return policy;
+    if (!buddy) return;
 
     /* Get the buddy-specific policy, if present */
     otrg_gtk_ui_buddy_prefs_load(buddy, &buddyusedefault, &buddyenabled,
-	    &buddyautomatic, &buddyonlyprivate);
+	    &buddyautomatic, &buddyonlyprivate, &buddyavoidloggingotr);
 
-    if (buddyusedefault) return policy;
+    if (buddyusedefault) return;
 
     if (buddyenabled) {
 	if (buddyautomatic) {
 	    if (buddyonlyprivate) {
-		policy = OTRL_POLICY_ALWAYS;
+		prefsp->policy = OTRL_POLICY_ALWAYS;
 	    } else {
-		policy = OTRL_POLICY_OPPORTUNISTIC;
+		prefsp->policy = OTRL_POLICY_OPPORTUNISTIC;
 	    }
 	} else {
-	    policy = OTRL_POLICY_MANUAL;
+	    prefsp->policy = OTRL_POLICY_MANUAL;
 	}
+	prefsp->avoid_logging_otr = buddyavoidloggingotr;
     } else {
-	policy = OTRL_POLICY_NEVER;
+	prefsp->policy = OTRL_POLICY_NEVER;
     }
+}
 
-    return policy;
+/* Initialize the OTR UI subsystem */
+static void otrg_gtk_ui_init(void)
+{
+    /* Nothing to do */
+}
+
+/* Deinitialize the OTR UI subsystem */
+static void otrg_gtk_ui_cleanup(void)
+{
+    /* Nothing to do */
 }
 
 static const OtrgUiUiOps gtk_ui_ui_ops = {
+    otrg_gtk_ui_init,
+    otrg_gtk_ui_cleanup,
     otrg_gtk_ui_update_fingerprint,
     otrg_gtk_ui_update_keylist,
     otrg_gtk_ui_config_buddy,
-    otrg_gtk_ui_find_policy
+    otrg_gtk_ui_get_prefs
 };
 
 /* Get the GTK UI ops */
diff --git a/gtk-ui.h b/gtk-ui.h
index 3e82cb6..64d4452 100644
--- a/gtk-ui.h
+++ b/gtk-ui.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
diff --git a/makedist b/makedist
index c10610b..0dade7f 100644
--- a/makedist
+++ b/makedist
@@ -2,6 +2,7 @@
 
 # Make the distribution tar.gz file from the CVS exported version
 
+intltoolize --force --copy
 autoreconf -s -i
 ./configure --mandir=/usr/share/man --prefix=/usr
 fakeroot make dist
diff --git a/otr-plugin.c b/otr-plugin.c
index 18f3845..8e2bab3 100644
--- a/otr-plugin.c
+++ b/otr-plugin.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -43,9 +43,23 @@
 #include "gtkplugin.h"
 #endif
 
+#ifdef ENABLE_NLS
+
+#ifdef WIN32
+/* On Win32, include win32dep.h from pidgin for correct definition
+ * of LOCALEDIR */
+#include "win32dep.h"
+#endif /* WIN32 */
+
+/* internationalisation header */
+#include <glib/gi18n-lib.h>
+
+#endif /* ENABLE_NLS */
+
 /* libotr headers */
 #include <libotr/privkey.h>
 #include <libotr/proto.h>
+#include <libotr/tlv.h>
 #include <libotr/message.h>
 #include <libotr/userstate.h>
 
@@ -56,6 +70,7 @@
 
 #ifdef USING_GTK
 /* purple-otr GTK headers */
+#include <glib.h>
 #include "gtk-ui.h"
 #include "gtk-dialog.h"
 #endif
@@ -79,6 +94,10 @@ PurplePlugin *otrg_plugin_handle;
 /* We'll only use the one OtrlUserState. */
 OtrlUserState otrg_plugin_userstate = NULL;
 
+/* GLib HashTable for storing the maximum message size for various
+ * 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,
@@ -91,11 +110,11 @@ void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
 	const char *protocol = purple_account_get_protocol_id(account);
 	const char *accountname = purple_account_get_username(account);
 	PurplePlugin *p = purple_find_prpl(protocol);
-	char *msg = g_strdup_printf("You are not currently connected to "
-		"account %s (%s).", accountname,
-		(p && p->info->name) ? p->info->name : "Unknown");
+	char *msg = g_strdup_printf(_("You are not currently connected to "
+		"account %s (%s)."), accountname,
+		(p && p->info->name) ? p->info->name : _("Unknown"));
 	otrg_dialog_notify_error(accountname, protocol, recipient,
-		"Not connected", msg, NULL);
+		_("Not connected"), msg, NULL);
 	g_free(msg);
 	return;
     }
@@ -106,13 +125,15 @@ static OtrlPolicy policy_cb(void *opdata, ConnContext *context)
 {
     PurpleAccount *account;
     OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+    OtrgUiPrefs prefs;
 
     if (!context) return policy;
 
     account = purple_accounts_find(context->accountname, context->protocol);
     if (!account) return policy;
 
-    return otrg_ui_find_policy(account, context->username);
+    otrg_ui_get_prefs(&prefs, account, context->username);
+    return prefs.policy;
 }
 
 static const char *protocol_name_cb(void *opdata, const char *protocol)
@@ -137,13 +158,13 @@ void otrg_plugin_create_privkey(const char *accountname,
 
     gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME, NULL);
     if (!privkeyfile) {
-	fprintf(stderr, "Out of memory building filenames!\n");
+	fprintf(stderr, _("Out of memory building filenames!\n"));
 	return;
     }
     privf = g_fopen(privkeyfile, "w+b");
     g_free(privkeyfile);
     if (!privf) {
-	fprintf(stderr, "Could not write private key file\n");
+	fprintf(stderr, _("Could not write private key file\n"));
 	return;
     }
 
@@ -186,10 +207,11 @@ static void inject_message_cb(void *opdata, const char *accountname,
     PurpleAccount *account = purple_accounts_find(accountname, protocol);
     if (!account) {
 	PurplePlugin *p = purple_find_prpl(protocol);
-	char *msg = g_strdup_printf("Unknown account %s (%s).", accountname,
-		(p && p->info->name) ? p->info->name : "Unknown");
+	char *msg = g_strdup_printf(_("Unknown account %s (%s)."),
+		accountname,
+		(p && p->info->name) ? p->info->name : _("Unknown"));
 	otrg_dialog_notify_error(accountname, protocol, recipient,
-		"Unknown account", msg, NULL);
+		_("Unknown account"), msg, NULL);
 	g_free(msg);
 	return;
     }
@@ -241,6 +263,8 @@ static void confirm_fingerprint_cb(void *opdata, OtrlUserState us,
 static void write_fingerprints_cb(void *opdata)
 {
     otrg_plugin_write_fingerprints();
+    otrg_ui_update_keylist();
+    otrg_dialog_resensitize_all();
 }
 
 static void gone_secure_cb(void *opdata, ConnContext *context)
@@ -265,6 +289,15 @@ static void log_message_cb(void *opdata, const char *message)
     purple_debug_info("otr", message);
 }
 
+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;
+    else
+        return *((int*)lookup_result);
+}
+
 static OtrlMessageAppOps ui_ops = {
     policy_cb,
     create_privkey_cb,
@@ -280,11 +313,14 @@ static OtrlMessageAppOps ui_ops = {
     gone_secure_cb,
     gone_insecure_cb,
     still_secure_cb,
-    log_message_cb
+    log_message_cb,
+    max_message_size_cb,
+    NULL,                   /* account_name */
+    NULL                    /* account_name_free */
 };
 
-static void process_sending_im(PurpleAccount *account, char *who, char **message,
-	void *m)
+static void process_sending_im(PurpleAccount *account, char *who,
+	char **message, void *m)
 {
     char *newmessage = NULL;
     const char *accountname = purple_account_get_username(account);
@@ -307,17 +343,45 @@ static void process_sending_im(PurpleAccount *account, char *who, char **message
 	free(*message);
 	*message = ourm;
     } else if (newmessage) {
-	char *ourm = malloc(strlen(newmessage) + 1);
-	if (ourm) {
-	    strcpy(ourm, newmessage);
-	}
-	otrl_message_free(newmessage);
+	/* Fragment the message if necessary, and send all but the last
+	 * fragment over the network.  Pidgin will send the last
+	 * fragment for us. */
+	ConnContext *context = otrl_context_find(otrg_plugin_userstate,
+		username, accountname, protocol, 0, NULL, NULL, NULL);
 	free(*message);
-	*message = ourm;
+	*message = NULL;
+	err = otrl_message_fragment_and_send(&ui_ops, NULL, context,
+		newmessage, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, message);
+	otrl_message_free(newmessage);
     }
     free(username);
 }
 
+/* Abort the SMP protocol.  Used when malformed or unexpected messages
+ * are received. */
+void otrg_plugin_abort_smp(ConnContext *context)
+{
+    otrl_message_abort_smp(otrg_plugin_userstate, &ui_ops, NULL, context);
+}
+
+/* Start the Socialist Millionaires' Protocol over the current connection,
+ * using the given initial secret. */
+void otrg_plugin_start_smp(ConnContext *context,
+	const unsigned char *secret, size_t secretlen)
+{
+    otrl_message_initiate_smp(otrg_plugin_userstate, &ui_ops, NULL,
+	    context, secret, secretlen);
+}
+
+/* Continue the Socialist Millionaires' Protocol over the current connection,
+ * using the given initial secret (ie finish step 2). */
+void otrg_plugin_continue_smp(ConnContext *context,
+	const unsigned char *secret, size_t secretlen)
+{
+    otrl_message_respond_smp(otrg_plugin_userstate, &ui_ops, NULL,
+	    context, secret, secretlen);
+}
+
 /* Send the default OTR Query message to the correspondent of the given
  * context, from the given account.  [account is actually a
  * PurpleAccount*, but it's declared here as void* so this can be passed
@@ -325,8 +389,10 @@ static void process_sending_im(PurpleAccount *account, char *who, char **message
 void otrg_plugin_send_default_query(ConnContext *context, void *vaccount)
 {
     PurpleAccount *account = vaccount;
+    OtrgUiPrefs prefs;
+    otrg_ui_get_prefs(&prefs, account, context->username);
     char *msg = otrl_proto_default_query_msg(context->accountname,
-	    otrg_ui_find_policy(account, context->username));
+	    prefs.policy);
     otrg_plugin_inject_message(account, context->username,
 	    msg ? msg : "?OTRv2?");
     free(msg);
@@ -339,13 +405,14 @@ void otrg_plugin_send_default_query_conv(PurpleConversation *conv)
     PurpleAccount *account;
     const char *username, *accountname;
     char *msg;
+    OtrgUiPrefs prefs;
     
     account = purple_conversation_get_account(conv);
     accountname = purple_account_get_username(account);
     username = purple_conversation_get_name(conv);
     
-    msg = otrl_proto_default_query_msg(accountname,
-	    otrg_ui_find_policy(account, username));
+    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?");
     free(msg);
 }
@@ -388,6 +455,53 @@ static gboolean process_receiving_im(PurpleAccount *account, char **who,
 	otrg_dialog_finished(accountname, protocol, username);
 	otrg_ui_update_keylist();
     }
+
+    /* Keep track of our current progress in the Socialist Millionaires'
+     * Protocol. */
+    ConnContext *context = otrl_context_find(otrg_plugin_userstate, username,
+	    accountname, protocol, 0, NULL, NULL, NULL);
+    NextExpectedSMP nextMsg = context->smstate->nextExpected;
+
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT1)
+	    otrg_plugin_abort_smp(context);
+        else {
+	    otrg_dialog_socialist_millionaires(context);
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT2)
+	    otrg_plugin_abort_smp(context);
+        else {
+	    otrg_dialog_update_smp(context, 0.6);
+            context->smstate->nextExpected = OTRL_SMP_EXPECT4;
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT3)
+	    otrg_plugin_abort_smp(context);
+        else {
+	    otrg_dialog_update_smp(context, 1.0);
+            context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+    if (tlv) {
+        if (nextMsg != OTRL_SMP_EXPECT4)
+	    otrg_plugin_abort_smp(context);
+        else {
+	    otrg_dialog_update_smp(context, 1.0);
+            context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+        }
+    }
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+    if (tlv) {
+	otrg_dialog_update_smp(context, 0.0);
+	context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+    }
     
     otrl_tlv_free(tlvs);
 
@@ -409,6 +523,25 @@ static void process_conv_create(PurpleConversation *conv, void *data)
     if (conv) otrg_dialog_new_conv(conv);
 }
 
+static void process_conv_updated(PurpleConversation *conv,
+	PurpleConvUpdateType type, void *data)
+{
+    /* See if someone's trying to turn logging on for this conversation,
+     * and we don't want them to. */
+    if (type == PURPLE_CONV_UPDATE_LOGGING) {
+	OtrgUiPrefs prefs;
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	otrg_ui_get_prefs(&prefs, account, purple_conversation_get_name(conv));
+
+	ConnContext *context = otrg_plugin_conv_to_context(conv);
+	if (context && prefs.avoid_logging_otr &&
+		context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
+		conv->logging == TRUE) {
+	    purple_conversation_set_logging(conv, FALSE);
+	}
+    }
+}
+
 static void process_connection_change(PurpleConnection *conn, void *data)
 {
     /* If we log in or out of a connection, make sure all of the OTR
@@ -441,8 +574,8 @@ static void supply_extended_menu(PurpleBlistNode *node, GList **menu)
     proto = purple_account_get_protocol_id(acct);
     if (!otrg_plugin_proto_supports_otr(proto)) return;
 
-    act = purple_menu_action_new("OTR Settings", (PurpleCallback)otr_options_cb,
-	    NULL, NULL);
+    act = purple_menu_action_new(_("OTR Settings"),
+	    (PurpleCallback)otr_options_cb, NULL, NULL);
     *menu = g_list_append(*menu, act);
 }
 
@@ -487,25 +620,35 @@ ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv)
     return context;
 }
 
-/* Find the PurpleConversation appropriate to the given ConnContext.  If
+/* Find the PurpleConversation appropriate to the given userinfo.  If
  * one doesn't yet exist, create it if force_create is true. */
-PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
-	int force_create)
+PurpleConversation *otrg_plugin_userinfo_to_conv(const char *accountname,
+	const char *protocol, const char *username, int force_create)
 {
     PurpleAccount *account;
     PurpleConversation *conv;
 
-    account = purple_accounts_find(context->accountname, context->protocol);
+    account = purple_accounts_find(accountname, protocol);
     if (account == NULL) return NULL;
 
-    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, context->username, account);
+    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+	    username, account);
     if (conv == NULL && force_create) {
-	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, context->username);
+	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
     }
 
     return conv;
 }
 
+/* Find the PurpleConversation appropriate to the given ConnContext.  If
+ * one doesn't yet exist, create it if force_create is true. */
+PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
+	int force_create)
+{
+    return otrg_plugin_userinfo_to_conv(context->accountname,
+	    context->protocol, context->username, force_create);
+}
+
 /* What level of trust do we have in the privacy of this ConnContext? */
 TrustLevel otrg_plugin_context_to_trust(ConnContext *context)
 {
@@ -539,6 +682,98 @@ static void process_quitting(void)
     }
 }
 
+/* Read the maxmsgsizes from a FILE* into the given GHashTable.
+ * The FILE* must be open for reading. */
+static void mms_read_FILEp(FILE *mmsf, GHashTable *ght)
+{
+    char storeline[50];
+    size_t maxsize = sizeof(storeline);
+
+    if (!mmsf) return;
+
+    while(fgets(storeline, maxsize, mmsf)) {
+	char *protocol;
+	char *prot_in_table;
+	char *mms;
+	int *mms_in_table;
+	char *tab;
+	char *eol;
+	/* Parse the line, which should be of the form:
+	 *    protocol\tmaxmsgsize\n          */
+	protocol = storeline;
+	tab = strchr(protocol, '\t');
+	if (!tab) continue;
+	*tab = '\0';
+
+	mms = tab + 1;
+	tab = strchr(mms, '\t');
+	if (tab) continue;
+        eol = strchr(mms, '\r');
+	if (!eol) eol = strchr(mms, '\n');
+	if (!eol) continue;
+	*eol = '\0';
+	
+	prot_in_table = strdup(protocol);
+	mms_in_table = malloc(sizeof(int));
+	*mms_in_table = atoi(mms);
+	g_hash_table_insert(ght, prot_in_table, mms_in_table);
+    }
+}
+
+static void otrg_str_free(gpointer data)
+{
+    g_free((char*)data);
+}
+
+static void otrg_int_free(gpointer data)
+{
+    g_free((int*)data);
+}
+
+static void otrg_init_mms_table()
+{
+    mms_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+	    otrg_str_free, otrg_int_free);
+
+    /* Hardcoded defaults for maximum message sizes for various
+     * protocols.  These can be overridden in the user's MAXMSGSIZEFNAME
+     * file. */
+    static const struct s_OtrgIdProtPair {
+        char *protid;
+	int maxmsgsize;
+    } mmsPairs[8] = {{"prpl-msn", 1409}, {"prpl-icq", 2346},
+	{"prpl-aim", 2343}, {"prpl-yahoo", 832}, {"prpl-gg", 1999},
+	{"prpl-irc", 417}, {"prpl-oscar", 2343}, {NULL, 0}};
+
+    int i = 0;
+    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);
+    }
+
+    gchar *maxmsgsizefile = g_build_filename(purple_user_dir(),
+	    MAXMSGSIZEFNAME, NULL);
+    FILE *mmsf;
+
+    if (maxmsgsizefile) {
+	mmsf = g_fopen(maxmsgsizefile, "rt");
+	/* Actually read the file here */
+	if (mmsf) {
+	    mms_read_FILEp(mmsf, mms_table);
+	    fclose(mmsf);
+	}
+    }
+    
+    g_free(maxmsgsizefile);
+}
+
+static void otrg_free_mms_table()
+{
+    g_hash_table_destroy(mms_table);
+}
+
 static gboolean otr_plugin_load(PurplePlugin *handle)
 {
     gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME,
@@ -562,6 +797,8 @@ static gboolean otr_plugin_load(PurplePlugin *handle)
     g_free(privkeyfile);
     g_free(storefile);
 
+    otrg_init_mms_table();
+
     otrg_plugin_handle = handle;
 
     /* Make our OtrlUserState; we'll only use the one. */
@@ -581,6 +818,8 @@ static gboolean otr_plugin_load(PurplePlugin *handle)
             PURPLE_CALLBACK(process_sending_im), NULL);
     purple_signal_connect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
             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(conn_handle, "signed-on", otrg_plugin_handle,
@@ -590,6 +829,9 @@ static gboolean otr_plugin_load(PurplePlugin *handle)
     purple_signal_connect(blist_handle, "blist-node-extended-menu",
 	    otrg_plugin_handle, PURPLE_CALLBACK(supply_extended_menu), NULL);
 
+    otrg_ui_init();
+    otrg_dialog_init();
+
     purple_conversation_foreach(otrg_dialog_new_conv);
 
     return 1;
@@ -606,12 +848,16 @@ static gboolean otr_plugin_unload(PurplePlugin *handle)
     otrl_userstate_free(otrg_plugin_userstate);
     otrg_plugin_userstate = NULL;
 
+    otrg_free_mms_table();
+
     purple_signal_disconnect(core_handle, "quitting", otrg_plugin_handle,
 	    PURPLE_CALLBACK(process_quitting));
-    purple_signal_disconnect(conv_handle, "sending-im-msg", otrg_plugin_handle,
-            PURPLE_CALLBACK(process_sending_im));
-    purple_signal_disconnect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
-            PURPLE_CALLBACK(process_receiving_im));
+    purple_signal_disconnect(conv_handle, "sending-im-msg",
+	    otrg_plugin_handle, PURPLE_CALLBACK(process_sending_im));
+    purple_signal_disconnect(conv_handle, "receiving-im-msg",
+	    otrg_plugin_handle, PURPLE_CALLBACK(process_receiving_im));
+    purple_signal_disconnect(conv_handle, "conversation-updated",
+	    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(conn_handle, "signed-on", otrg_plugin_handle,
@@ -623,23 +869,23 @@ static gboolean otr_plugin_unload(PurplePlugin *handle)
 
     purple_conversation_foreach(otrg_dialog_remove_conv);
 
+    otrg_dialog_cleanup();
+    otrg_ui_cleanup();
+
     return 1;
 }
 
 /* Return 1 if the given protocol supports OTR, 0 otherwise. */
 int otrg_plugin_proto_supports_otr(const char *proto)
 {
-    /* IRC is the only protocol we know of that OTR doesn't work on (its
-     * maximum message size is too small to fit a Key Exchange Message). */
-    if (proto && !strcmp(proto, "prpl-irc")) {
-	return 0;
-    }
+    /* Right now, OTR should work on all protocols, possibly
+     * with the help of fragmentation. */
     return 1;
 }
 
 #ifdef USING_GTK
 
-static PurplePluginUiInfo ui_info =
+static PidginPluginUiInfo ui_info =
 {
 	otrg_gtk_ui_make_widget
 };
@@ -662,26 +908,23 @@ static PurplePluginInfo info =
         2,                                                /* major version  */
 	0,                                                /* minor version  */
 
-	PURPLE_PLUGIN_STANDARD,                             /* type           */
+	PURPLE_PLUGIN_STANDARD,                           /* type           */
 	PLUGIN_TYPE,                                      /* ui_requirement */
 	0,                                                /* flags          */
 	NULL,                                             /* dependencies   */
-	PURPLE_PRIORITY_DEFAULT,                            /* priority       */
+	PURPLE_PRIORITY_DEFAULT,                          /* priority       */
 	"otr",                                            /* id             */
-	"Off-the-Record Messaging",                       /* name           */
-	PIDGIN_OTR_VERSION,                                 /* version        */
-	                                                  /* summary        */
-	"Provides private and secure conversations",
-	                                                  /* description    */
-	"Preserves the privacy of IM communications by providing "
-	    "encryption, authentication, deniability, and perfect "
-	    "forward secrecy.",
+	NULL,                                             /* name           */
+	PIDGIN_OTR_VERSION,                               /* version        */
+	NULL,                                             /* summary        */
+	NULL,                                             /* description    */
 	                                                  /* author         */
-	"Nikita Borisov and Ian Goldberg\n\t\t\t<otr at cypherpunks.ca>",
-	"http://www.cypherpunks.ca/otr/",                 /* homepage       */
+	"Ian Goldberg, Chris Alexander, Nikita Borisov\n"
+	    "\t\t\t<otr at cypherpunks.ca>",
+	"http://otr.cypherpunks.ca/",                     /* homepage       */
 
-	otr_plugin_load,                                 /* load           */
-	otr_plugin_unload,                               /* unload         */
+	otr_plugin_load,                                  /* load           */
+	otr_plugin_unload,                                /* unload         */
 	NULL,                                             /* destroy        */
 
 	UI_INFO,                                          /* ui_info        */
@@ -701,6 +944,17 @@ __init_plugin(PurplePlugin *plugin)
 
     /* Initialize the OTR library */
     OTRL_INIT;
+
+#ifdef ENABLE_NLS
+    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+    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, "
+			 "deniability, and perfect forward secrecy.");
 }
 
 PURPLE_INIT_PLUGIN(otr, __init_plugin, info)
diff --git a/otr-plugin.h b/otr-plugin.h
index cbdf322..db10632 100644
--- a/otr-plugin.h
+++ b/otr-plugin.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
 
 #define PRIVKEYFNAME "otr.private_key"
 #define STOREFNAME "otr.fingerprints"
+#define MAXMSGSIZEFNAME "otr.max_message_size"
 
 extern PurplePlugin *otrg_plugin_handle;
 
@@ -44,6 +45,17 @@ void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
 void otrg_plugin_create_privkey(const char *accountname,
 	const char *protocol);
 
+/* Start or continue the Socialist Millionaires' Protocol over the current
+ * connection, using the given initial secret. */
+void otrg_plugin_start_smp(ConnContext *context,
+	const unsigned char *secret, size_t secretlen);
+void otrg_plugin_continue_smp(ConnContext *context,
+	const unsigned char *secret, size_t secretlen);
+
+/* Abort the SMP protocol.  Used when malformed or unexpected messages
+ * are received. */
+void otrg_plugin_abort_smp(ConnContext *context);
+
 /* Send the default OTR Query message to the correspondent of the given
  * context, from the given account.  [account is actually a
  * PurpleAccount*, but it's declared here as void* so this can be passed
@@ -64,6 +76,11 @@ void otrg_plugin_write_fingerprints(void);
 /* Find the ConnContext appropriate to a given PurpleConversation. */
 ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv);
 
+/* 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,
+	const char *protocol, const char *username, int force_create);
+
 /* Find the PurpleConversation appropriate to the given ConnContext.  If
  * one doesn't yet exist, create it if force_create is true. */
 PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
diff --git a/packaging/fedora/gaim-otr.spec b/packaging/fedora/pidgin-otr.spec
similarity index 86%
rename from packaging/fedora/gaim-otr.spec
rename to packaging/fedora/pidgin-otr.spec
index d5edbd6..c88c2d1 100644
--- a/packaging/fedora/gaim-otr.spec
+++ b/packaging/fedora/pidgin-otr.spec
@@ -1,6 +1,6 @@
-Summary: Off-The-Record Messaging plugin for GAIM
-Name: gaim-otr
-Version: 3.0.0
+Summary: Off-The-Record Messaging plugin for pidgin
+Name: pidgin-otr
+Version: 3.1.0
 Release: 1%{?dist}
 Source: http://www.cypherpunks.ca/otr/%{name}-%{version}.tar.gz
 Url: http://www.cypherpunks.ca/otr/
@@ -9,14 +9,14 @@ Group: Applications/Internet
 Provides: otr-plugin = %{version}
 Obsoletes: otr-plugin
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: gaim >= 1.0.0, libotr >= 3.0.0
-BuildRequires: glib2-devel, gtk2-devel, libgcrypt-devel >= 1.2.0, libgpg-error-devel, gaim >= 1.0.0, libotr-devel >= 3.0.0
+Requires: pidgin >= 2.0.0, libotr >= 3.1.0
+BuildRequires: glib2-devel, gtk2-devel, libgcrypt-devel >= 1.2.0, libgpg-error-devel, pidgin >= 2.0.0, libotr-devel >= 3.1.0
 
 %description 
 
-This is a gaim plugin which implements Off-the-Record (OTR) Messaging.
+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
-gaim (1.x).
+pidgin (2.x).
 
 %prep
 %setup -q
@@ -30,7 +30,7 @@ make %{?_smp_mflags} all
 rm -rf $RPM_BUILD_ROOT
 make DESTDIR=$RPM_BUILD_ROOT install
 # libtool insists on creating this
-rm $RPM_BUILD_ROOT/%{_libdir}/gaim/gaim-otr.la
+rm $RPM_BUILD_ROOT/%{_libdir}/pidgin/pidgin-otr.la
 
 %clean
 rm -rf $RPM_BUILD_ROOT
@@ -38,7 +38,7 @@ rm -rf $RPM_BUILD_ROOT
 %files
 %defattr(-, root, root, 0755)
 %doc README COPYING
-%{_libdir}/gaim/gaim-otr.so
+%{_libdir}/pidgin/pidgin-otr.so
 
 %changelog
 * Mon Oct 17 2005 Paul Wouters <paul at cypherpunks.ca> 3.0.0
diff --git a/packaging/windows/gaim-otr.nsi b/packaging/windows/gaim-otr.nsi
deleted file mode 100644
index a7ff749..0000000
--- a/packaging/windows/gaim-otr.nsi
+++ /dev/null
@@ -1,221 +0,0 @@
-; Script based on generated HM NIS Edit Script Wizard.
-; Forgive me, i am new at this. -- paul at cypherpunks.ca
-;
-; known issue. installer induced uninstaller abortion causes overwrite by installer without
-; uninstall.
-; v3.0.1    - Version for gaim-2.0.0 beta5
-; v3.0.0   - Bump version number.
-; v2.0.2   - Bump version number.
-; v2.0.1   - Bump version number.
-; v2.0.0-2 - linking to libotr-2.0.1
-; v2.0.0   - Bump version number. Fixed upgrading gaim2-otr (it didn't overwrite the dll)
-;            bug reported by Aldert Hazenberg <aldert at xelerance.com>
-;          - Added many safeguards and fixed conditions of failures when gaim is running
-;             during install, or failed to (un)install previously.
-;           - Removed popup signifying gaim is found
-; v1.99.0-1 - Bump version number, install Protocol.txt file
-; v1.0.3-2  - Fix for detecting gaim if not installed by Administrator
-;             bug report by Joanna Rutkowska <joanna at mailsnare.net>
-;           - Fix for uninstalling the dll when not installed as Administrator
-; v1.0.3    - Initial version
-
-
-; todo: SetBrandingImage
-; HM NIS Edit Wizard helper defines
-!define PRODUCT_NAME "gaim-otr"
-!define PRODUCT_VERSION "3.0.1"
-!define PRODUCT_PUBLISHER "Cypherpunks CA"
-!define PRODUCT_WEB_SITE "http://www.cypherpunks.ca/otr/"
-!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
-!define PRODUCT_UNINST_ROOT_KEY "HKLM"
-
-; MUI 1.67 compatible ------
-!include "MUI.nsh"
-
-; MUI Settings
-!define MUI_ABORTWARNING
-!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
-!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
-
-; Welcome page
-!insertmacro MUI_PAGE_WELCOME
-; License page
-!insertmacro MUI_PAGE_LICENSE "c:\otr\COPYING.txt"
-; Directory page
-!insertmacro MUI_PAGE_DIRECTORY
-; Instfiles page
-!insertmacro MUI_PAGE_INSTFILES
-; Finish page
-!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt"
-!insertmacro MUI_PAGE_FINISH
-
-; Uninstaller pages
-!insertmacro MUI_UNPAGE_INSTFILES
-
-; Language files
-!insertmacro MUI_LANGUAGE "English"
-
-; MUI end ------
-
-Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
-OutFile "${PRODUCT_NAME}-${PRODUCT_VERSION}.exe"
-InstallDir "$PROGRAMFILES\gaim2-otr"
-InstallDirRegKey HKEY_LOCAL_MACHINE SOFTWARE\gaim-otr "Install_Dir"
-;WriteRegStr HKLM "SOFTWARE\gaim2-otr" "gaimdir" ""
-
-Var "GaimDir"
-
-ShowInstDetails show
-ShowUnInstDetails show
-
-Section "MainSection" SEC01
-;InstallDir "$PROGRAMFILES\Gaim\plugins"
-
-; uninstall previous gaim2-otr install if found.
-Call UnInstOld
- ;Check for gaim installation
-Call GetGaimInstPath
-WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "SOFTWARE\gaim2-otr" "gaimdir" "$GaimDir"
-
-	SetOutPath "$INSTDIR"
-  SetOverwrite on
-  File "c:\otr\gaim2-otr.dll"
-  ; move to gaim plugin directory, check if not busy (gaim 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\gaim-otr.nsi"
-SectionEnd
-
-Section -AdditionalIcons
-  CreateDirectory "$SMPROGRAMS\gaim2-otr"
-  CreateShortCut "$SMPROGRAMS\gaim2-otr\Uninstall.lnk" "$INSTDIR\gaim2-otr-uninst.exe"
-SectionEnd
-
-Section -Post
-  WriteUninstaller "$INSTDIR\gaim2-otr-uninst.exe"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gaim2-otr-uninst.exe"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
- 
-SectionEnd
-
-Function un.onUninstSuccess
-  HideWindow
-  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
-FunctionEnd
-
-Function un.onInit
-  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
-  Abort
-FunctionEnd
-
-Section Uninstall
-  Delete "$INSTDIR\gaim2-otr-uninst.exe"
-	Delete "$INSTDIR\README.Toolkit.txt"
-	Delete "$INSTDIR\README.txt"
-	Delete "$INSTDIR\Protocol-v2.txt"
-	Delete "$INSTDIR\COPYING.txt"
-	Delete "$INSTDIR\COPYING.LIB.txt"
-	Delete "$INSTDIR\otr_mackey.exe"
-	Delete "$INSTDIR\otr_modify.exe"
-	Delete "$INSTDIR\otr_parse.exe"
-	Delete "$INSTDIR\otr_readforge.exe"
-	Delete "$INSTDIR\otr_remac.exe"
-	Delete "$INSTDIR\otr_sesskeys.exe"
-	Delete "$INSTDIR\gaim2-otr.nsi"
-  Delete "$SMPROGRAMS\gaim2-otr\Uninstall.lnk"
-  RMDir "$SMPROGRAMS\gaim2-otr"
-  RMDir "$INSTDIR"
-  
-	ReadRegStr $GaimDir HKLM Software\gaim-otr "gaimdir"
-	IfFileExists "$GaimDir\plugins\gaim-otr.dll" dodelete
-  ReadRegStr $GaimDir HKCU Software\gaim-otr "gaimdir"
-	IfFileExists "$GaimDir\plugins\gaim-otr.dll" dodelete
-	
-  ReadRegStr $GaimDir HKLM Software\gaim2-otr "gaimdir"
-	IfFileExists "$GaimDir\plugins\gaim2-otr.dll" dodelete
-  ReadRegStr $GaimDir HKCU Software\gaim2-otr "gaimdir"
-	IfFileExists "$GaimDir\plugins\gaim2-otr.dll" dodelete
-  MessageBox MB_OK|MB_ICONINFORMATION "Could not find gaim plugin directory, gaim2-otr.dll not uninstalled!" IDOK ok
-dodelete:
-	Delete "$GaimDir\plugins\gaim-otr.dll"
-	Delete "$GaimDir\plugins\gaim2-otr.dll"
-	
-	IfFileExists "$GaimDir\plugins\gaim2-otr.dll" 0 +2
-		MessageBox MB_OK|MB_ICONINFORMATION "gaim2-otr.dll is busy. Probably Gaim is still running. Please delete $GaimDir\plugins\gaim2-otr.dll manually."
-  DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
-  DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "SOFTWARE\gaim2-otr\gaimdir"
-ok:
-SetAutoClose true
-SectionEnd
-Function GetGaimInstPath
-  Push $0
-  ReadRegStr $0 HKLM "Software\gaim" ""
-	IfFileExists "$0\gaim.exe" cont
-	ReadRegStr $0 HKCU "Software\gaim" ""
-	IfFileExists "$0\gaim.exe" cont
-  MessageBox MB_OK|MB_ICONINFORMATION "Failed to find GAIM installation."
-		Abort "Failed to find GAIM installation. Please install GAIM first."
-cont:
-	StrCpy $GaimDir $0
-	;MessageBox MB_OK|MB_ICONINFORMATION "Gaim plugin directory found at $GaimDir\plugins ."
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "SOFTWARE\gaim2-otr" "gaimdir" "$GaimDir"
-FunctionEnd
-
-Function UnInstOld
-	  Push $0
-	  ReadRegStr $0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
-		IfFileExists "$0" deinst cont
-	deinst:
-		MessageBox MB_OK|MB_ICONEXCLAMATION  "gaim2-otr was already found on your system and will first be uninstalled"
-		; the uninstaller copies itself to temp and execs itself there, so it can delete 
-		; everything including its own original file location. To prevent the installer and
-		; uninstaller racing you can't simply ExecWait.
-		; We hide the uninstall because otherwise it gets really confusing window-wise
-		;HideWindow
-		  ClearErrors
-			ExecWait '"$0" _?=$INSTDIR'
-			IfErrors 0 cont
-				MessageBox MB_OK|MB_ICONEXCLAMATION  "Uninstall failed or aborted"
-				Abort "Uninstalling of the previous version gave an error. Install aborted."
-			
-		;BringToFront
-	cont:
-		;MessageBox MB_OK|MB_ICONINFORMATION "No old gaim2-otr found, continuing."
-		
-FunctionEnd
-
-Function CopyDLL
-SetOverwrite try
-ClearErrors
-; 3 hours wasted so you guys don't need a reboot!
-; Rename /REBOOTOK "$INSTDIR\gaim2-otr.dll" "$GaimDir\plugins\gaim2-otr.dll"
-IfFileExists "$GaimDir\plugins\gaim2-otr.dll" 0 copy ; remnant or uninstall prev version failed
-Delete "$GaimDir\plugins\gaim2-otr.dll"
-copy:
-ClearErrors
-Rename "$INSTDIR\gaim2-otr.dll" "$GaimDir\plugins\gaim2-otr.dll"
-IfErrors dllbusy
-	Return
-dllbusy:
-	MessageBox MB_RETRYCANCEL "gaim2-otr.dll is busy. Please close Gaim (including tray icon) and try again" IDCANCEL cancel
-	Delete "$GaimDir\plugins\gaim2-otr.dll"
-	Goto copy
-	Return
-cancel:
-	Abort "Installation of gaim2-otr aborted"
-FunctionEnd
diff --git a/po/ChangeLog b/po/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/po/Makefile.mingw b/po/Makefile.mingw
new file mode 100644
index 0000000..c1fbfe8
--- /dev/null
+++ b/po/Makefile.mingw
@@ -0,0 +1,57 @@
+# Makefile.mingw
+#
+# Description: Makefile to generate mo files
+#
+# based on Gaim's po/Makefile.mingw
+#
+
+# Name of the gettext domain
+GETTEXT_PACKAGE = pidgin-otr
+
+# msgfmt command
+GMSGFMT ?= /usr/bin/msgfmt
+
+# Path where the mo files should be installed
+PIDGIN_PO_DIR ?= /usr/share/locale
+INSTALL_PO_DIR = $(DESTDIR)$(PIDGIN_PO_DIR)
+
+.SUFFIXES:
+.SUFFIXES: .po .gmo
+
+##
+## SOURCES, OBJECTS
+##
+
+CATALOGS = $(patsubst %.po,%.gmo,$(wildcard *.po))
+
+##
+## RULES
+##
+
+.po.gmo:
+	rm -f $@ && $(GMSGFMT) --statistics -o $@ $<
+
+##
+## TARGETS
+##
+
+.PHONY: all install clean
+
+all: $(CATALOGS)
+
+install: all
+	mkdir -p $(INSTALL_PO_DIR)
+	@catalogs='$(CATALOGS)'; \
+	for cat in $$catalogs; do \
+	  cat=`basename $$cat`; \
+	  lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+	  dir=$(INSTALL_PO_DIR)/$$lang/LC_MESSAGES; \
+	  mkdir -p $$dir; \
+	  if test -r $$cat; then \
+	    cp $$cat $$dir/$(PACKAGE).mo; \
+	    echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE).mo"; \
+	  fi; \
+	done
+
+clean:
+	rm -f *.gmo
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..e10e88e
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,12 @@
+# List of files that (could) contain strings for translation
+
+dialogs.c
+dialogs.h
+gtk-dialog.c
+gtk-dialog.h
+gtk-ui.c
+gtk-ui.h
+otr-plugin.c
+otr-plugin.h
+ui.c
+ui.h
diff --git a/po/README b/po/README
new file mode 100644
index 0000000..44982cf
--- /dev/null
+++ b/po/README
@@ -0,0 +1,7 @@
+To get the latest list of strings to translate:
+
+intltool-update --pot
+
+To merge the latest list into an existing translation file fr.po:
+
+intltool-update fr
diff --git a/ui.c b/ui.c
index 11dff02..886b2c7 100644
--- a/ui.c
+++ b/ui.c
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,11 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+/* config.h */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 /* system headers */
 #include <stdlib.h>
 
@@ -24,6 +29,11 @@
 #include "util.h"
 #include "account.h"
 
+#ifdef ENABLE_NLS
+/* internationalisation header */
+#include <glib/gi18n-lib.h>
+#endif
+
 /* libotr headers */
 #include <libotr/privkey.h>
 #include <libotr/proto.h>
@@ -48,6 +58,22 @@ const OtrgUiUiOps *otrg_ui_get_ui_ops(void)
     return ui_ops;
 }
 
+/* Initialize the OTR UI subsystem */
+void otrg_ui_init(void)
+{
+    if (ui_ops != NULL) {
+	ui_ops->init();
+    }
+}
+
+/* Deinitialize the OTR UI subsystem */
+void otrg_ui_cleanup(void)
+{
+    if (ui_ops != NULL) {
+	ui_ops->cleanup();
+    }
+}
+
 /* Call this function when the DSA key is updated; it will redraw the
  * UI, if visible. */
 void otrg_ui_update_fingerprint(void)
@@ -79,11 +105,11 @@ void otrg_ui_connect_connection(ConnContext *context)
     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",
+	msg = g_strdup_printf(_("Account %s (%s) could not be found"),
 		  context->accountname,
-		  (p && p->info->name) ? p->info->name : "Unknown");
+		  (p && p->info->name) ? p->info->name : _("Unknown"));
 	otrg_dialog_notify_error(context->accountname, context->protocol,
-		context->username, "Account not found", msg, NULL);
+		context->username, _("Account not found"), msg, NULL);
 	g_free(msg);
 	return;
     }
@@ -128,17 +154,23 @@ void otrg_ui_config_buddy(PurpleBuddy *buddy)
     }
 }
 
-/* Calculate the policy for a particular account / username */
-OtrlPolicy otrg_ui_find_policy(PurpleAccount *account, const char *name)
+/* Load the preferences for a particular account / username */
+void otrg_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
+	const char *name)
 {
     /* Check to see if the protocol for this account supports OTR at all. */
     const char *proto = purple_account_get_protocol_id(account);
     if (!otrg_plugin_proto_supports_otr(proto)) {
-	return OTRL_POLICY_NEVER;
+	prefsp->policy = OTRL_POLICY_NEVER;
+	prefsp->avoid_logging_otr = FALSE;
+	return;
     }
 
     if (ui_ops != NULL) {
-	return ui_ops->find_policy(account, name);
+	ui_ops->get_prefs(prefsp, account, name);
+	return;
     }
-    return OTRL_POLICY_DEFAULT;
+    /* If we've got no other way to get the prefs, use sensible defaults */
+    prefsp->policy = OTRL_POLICY_DEFAULT;
+    prefsp->avoid_logging_otr = FALSE;
 }
diff --git a/ui.h b/ui.h
index c510928..3da58af 100644
--- a/ui.h
+++ b/ui.h
@@ -1,6 +1,6 @@
 /*
  *  Off-the-Record Messaging plugin for pidgin
- *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *  Copyright (C) 2004-2007  Ian Goldberg, Chris Alexander, Nikita Borisov
  *                           <otr at cypherpunks.ca>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,14 +22,25 @@
 
 #include <libotr/context.h>
 
+/* Global and per-buddy preferences */
 typedef struct {
+    OtrlPolicy policy;
+    gboolean avoid_logging_otr;
+} OtrgUiPrefs;
+
+typedef struct {
+    void (*init)(void);
+
+    void (*cleanup)(void);
+
     void (*update_fingerprint)(void);
 
     void (*update_keylist)(void);
 
     void (*config_buddy)(PurpleBuddy *buddy);
 
-    OtrlPolicy (*find_policy)(PurpleAccount *account, const char *name);
+    void (*get_prefs)(OtrgUiPrefs *prefsp, PurpleAccount *account,
+	    const char *name);
 } OtrgUiUiOps;
 
 /* Set the UI ops */
@@ -38,6 +49,12 @@ void otrg_ui_set_ui_ops(const OtrgUiUiOps *ops);
 /* Get the UI ops */
 const OtrgUiUiOps *otrg_ui_get_ui_ops(void);
 
+/* Initialize the UI subsystem */
+void otrg_ui_init(void);
+
+/* Deinitialize the UI subsystem */
+void otrg_ui_cleanup(void);
+
 /* Call this function when the DSA key is updated; it will redraw the
  * UI. */
 void otrg_ui_update_fingerprint(void);
@@ -57,7 +74,8 @@ void otrg_ui_forget_fingerprint(Fingerprint *fingerprint);
 /* Configure OTR for a particular buddy */
 void otrg_ui_config_buddy(PurpleBuddy *buddy);
 
-/* Calculate the policy for a particular account / username */
-OtrlPolicy otrg_ui_find_policy(PurpleAccount *account, const char *name);
+/* Load the preferences for a particular account / username */
+void otrg_ui_get_prefs(OtrgUiPrefs *prefsp, PurpleAccount *account,
+	const char *name);
 
 #endif

-- 
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