[pkg-otr-team] [irssi-plugin-otr] 01/167: Initial revision

Holger Levsen holger at moszumanska.debian.org
Mon Mar 3 21:55:27 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 d426db7dd8e5b06eb3e4e9550404895d0e273505
Author: cypherpunk <cypherpunk>
Date:   Wed May 4 17:49:46 2005 +0000

    Initial revision
---
 AUTHORS                        |   7 +
 COPYING                        | 340 ++++++++++++++++
 ChangeLog                      |  33 ++
 INSTALL                        |  45 ++
 Makefile.am                    |  14 +
 Makefile.mingw                 |  59 +++
 NEWS                           | 201 +++++++++
 README                         | 210 ++++++++++
 configure.ac                   |  24 ++
 dialogs.c                      | 169 ++++++++
 dialogs.h                      | 143 +++++++
 gtk-dialog.c                   | 562 +++++++++++++++++++++++++
 gtk-dialog.h                   |  26 ++
 gtk-ui.c                       | 904 +++++++++++++++++++++++++++++++++++++++++
 gtk-ui.h                       |  29 ++
 makedist                       |   7 +
 otr-plugin.c                   | 581 ++++++++++++++++++++++++++
 otr-plugin.h                   |  61 +++
 packaging/fedora/gaim-otr.spec | 104 +++++
 packaging/windows/gaim-otr.nsi | 212 ++++++++++
 ui.c                           | 139 +++++++
 ui.h                           |  63 +++
 22 files changed, 3933 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..6381be9
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Off-the-Record Messaging plugin for gaim
+
+Authors:
+
+    Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+
+See the README file for mailing list information
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..ad0fca6
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,33 @@
+2005-05-03
+
+	* README:
+	* packaging/fedora/gaim-otr.spec:
+	* packaging/windows/gaim-otr.nsi:
+	* configure.ac: Change version to 2.0.2
+
+	* packaging/debian: Remove this directory, as Thibaut VARENE
+	<varenet at debian.org> is now responsible for the debian packages.
+
+2005-03-23
+
+	* otr-plugin.c (process_receiving_im): If libotr tells us to
+	ignore this received message (because it's an internal protocol
+	message), it's not enough just to return 1 from this function,
+	since other plugins that catch receiving-im-msg may later return
+	0, and gaim only checks the return value from the last such
+	plugin.  So we additionally set the message to NULL.
+
+2005-03-21
+
+	* gtk-ui.c (otrg_gtk_ui_config_buddy):
+	* otr-plugin.c (supply_extended_menu): For consistency, use "OTR
+	Settings" instead of "OTR Options" everywhere.
+
+2005-03-08
+
+	* gtk-dialog.c (otrg_gtk_dialog_private_key_wait_start): Move a
+	variable declaration to the right place.
+
+2005-03-01
+
+	Initial autoconfiscation, thanks to Greg Troxel <gdt at ir.bbn.com>.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..aa30ba8
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,45 @@
+REQUIREMENTS
+
+To compile the OTR plugin for gaim, 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]
+
+If you install these with a package manager, you'll probably need the
+-dev or -devel versions of the packages.
+
+COMPILING (non-Win32)
+
+If you're got a CVS copy, you will need to regenerate the configure
+script using:
+
+    autoreconf -s -i
+
+Once you have the configure script (which comes with the source
+deistribution), run it with any options that may be necessary for your
+system.  Some examples:
+
+Linux:
+    ./configure --prefix=/usr --mandir=/usr/share/man
+
+NETBSD:
+    CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-R/usr/pkg/lib -L/usr/pkg/lib" \
+	./configure --prefix=/usr/pkg
+
+Once the configure script writes a Makefile, you should be able to just
+run "make".
+
+COMPILING (Win32)
+
+Use the provided Makefile.mingw:
+
+    make -f Makefile.mingw
+
+INSTALLATION
+
+You should be able to simply do "make install".  If you want to install
+somewhere other than / (this is useful for package creators), use
+something like "make DESTDIR=/path/to/install/to install".
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..0a071b6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
+AM_CFLAGS=	@LIBGCRYPT_CFLAGS@ @LIBOTR_CFLAGS@ @EXTRA_CFLAGS@
+AM_CFLAGS+=	-DUSING_GTK -DGAIM_PLUGINS \
+		-DGAIM_OTR_VERSION=\"@VERSION@\"
+
+plugindir=		${libdir}/gaim
+
+plugin_LTLIBRARIES=	gaim-otr.la
+
+gaim_otr_la_SOURCES=		otr-plugin.c ui.c dialogs.c gtk-ui.c gtk-dialog.c
+gaim_otr_la_LDFLAGS=		-module -avoid-version
+gaim_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
diff --git a/Makefile.mingw b/Makefile.mingw
new file mode 100644
index 0000000..bd113f1
--- /dev/null
+++ b/Makefile.mingw
@@ -0,0 +1,59 @@
+WIN32=1
+
+# The version number to put in the plugin info
+GAIM_OTR_VERSION = 2.0.2
+
+# Replace this with the path to the GAIM headers
+GAIM_SOURCE ?= /usr/include/gaim
+
+# If you don't have pkg-config, put the appropriate -I entry on the next line
+GTK_HDRS ?= `pkg-config --cflags glib-2.0 gtk+-2.0`
+
+# The location of the libotr include files.  Note that if, for example,
+# the full path of message.h is /usr/include/libotr/message.h, you
+# should put /usr/include on the next line, not /usr/include/libotr
+LIBOTRINCDIR = /usr/include
+
+# The locataion of libotr.a.
+LIBOTRLIBDIR = /usr/lib
+
+# The target
+TARGET = gaim-otr.so
+
+ifdef WIN32
+CC = i586-mingw32msvc-gcc
+LIBOTRINCDIR = /usr/i586-mingw32msvc/include
+LIBOTRLIBDIR = /usr/i586-mingw32msvc/lib
+TARGET = gaim-otr.dll
+LDFLAGS = -Wl,--enable-auto-image-base
+LDLIBS = $(LIBOTRLIBDIR)/libotr.a -lgtk-win32-2.0-0 -latk-1.0-0 -lpango-1.0-0 \
+         -lglib-2.0-0 -lgdk-win32-2.0-0 -lgobject-2.0-0 -lgaim \
+         -lgcrypt -lgpg-error
+else
+FPIC = -fPIC
+LDFLAGS = -module -avoid-version
+LDLIBS = -lotr -lgcrypt
+endif
+
+# Install directory
+GAIMDIR = /usr/lib/gaim
+INSTALLDIR = $(DESTDIR)$(GAIMDIR)
+
+CC ?= gcc
+override CFLAGS += -g -Wall -I$(GAIM_SOURCE) $(GTK_HDRS) \
+	-I$(LIBOTRINCDIR) $(FPIC) -DUSING_GTK -DGAIM_PLUGINS \
+	-DGAIM_OTR_VERSION=\"$(GAIM_OTR_VERSION)\"
+ 
+all: $(TARGET)
+
+$(TARGET): otr-plugin.o ui.o dialogs.o gtk-ui.o gtk-dialog.o
+	$(CC) -g -shared $(LDFLAGS) $^ -o $@ $(LDLIBS)
+
+install: all
+	install -d $(INSTALLDIR)
+	install -m 0755 $(TARGET) $(INSTALLDIR)
+clean:
+	rm -f *.o
+	rm -f $(TARGET)
+
+distclean: clean
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..1916915
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,201 @@
+3 May 2005:
+- Released 2.0.2
+- Fix to co-exist more nicely with other encrypting gaim plugins.
+
+1 Mar 2005:
+- Initial autoconfiscation, thanks to Greg Troxel <gdt at ir.bbn.com>. 
+
+23 Feb 2005:
+- Released 2.0.1
+
+22 Feb 2005:
+- Removed people without fingerprints from the Known Fingerprints list
+- The column heads in the Known Fingerprints list cause sorting to
+  happen in the expected way.
+
+8 Feb 2005:
+- Released 2.0.0
+- Clicking the OTR button produces a notice in the conversation window
+  that it's doing something.
+
+30 Jan 2005:
+- Added default and per-buddy policy selection: never use OTR, OTR only
+  if manually requested, automatically start OTR if possible, refuse to
+  *not* use OTR.
+- The OTR: button disappears if a particular buddy is set to never use
+  OTR.
+- Resend the last message if it caused a re-keying.
+- OTR control messages are no longer displayed as if they were received
+  as IM messages.
+- New multi-page UI
+- Send a control message to your buddy if you terminate a private
+  conversation with him.
+
+27 Jan 2005:
+- Updated gaim-otr to match libotr 2.0.0 API.
+
+23 Jan 2005:
+- Separated gtk-specific code from general gaim code, with help from
+  Evan Schoenberg <evan.s at dreskin.net>.
+
+18 Jan 2005:
+- Released 1.0.3
+- Split gaim-otr and libotr into separate packages.
+
+13 Jan 2005:
+- Generate private keys automatically, if needed.  Show a Please Wait
+  dialog while this is happening.
+- We may as well try to use the "tag" method of checking for OTR, even
+  when we don't already know a fingerprint for the correspondent.
+- Add version checking to the otrl_init() call.
+
+12 Jan 2005:
+- Refactored the logic parts of gaim-otr into libotr, so they can be
+  shared by other libotr-enabled apps.
+
+21 Dec 2004:
+- Released 1.0.2
+- If a Man-in-the-Middle steals both Alice's and Bob's DSA private keys,
+  he can perform a birthday attack to try to get his session id with
+  each end to match.  Since the session id was only 64 bits long, his
+  work was only 2^32, which is not enough.  We now make the session id
+  the whole SHA-1 hash, instead of truncating it.
+- Made otr_sesskeys output the calculated public key as well, for added
+  ease of forging messages when you don't know any plaintext.
+
+14 Dec 2004:
+- Released 1.0.1
+- Added a more sensible error message in the event that we receive our
+  own OTR Key Exchange messages.
+- If we're about to send a plaintext message to a correspondent for whom
+  we've got a fingerprint, append a special (whitespace) OTR tag
+  sequence.  The other side (if in fact running OTR) will recognize it
+  and start a Key Exchange.
+
+12 Dec 2004:
+- Released 1.0.0
+
+11 Dec 2004:
+- OTR button now gets sensitized and desensitized along with the other
+  buttons in the conversation window when you log in and out of
+  accounts.
+
+10 Dec 2004:
+- Released 0.9.9rc2
+- Heartbeats now only get sent if (1) we have just received a message,
+  and (2) we haven't sent one to that user in over a minute.
+
+9 Dec 2004:
+- Back out of the sending of heartbeats.  They were causing too many
+  problems.  It seems some networks don't let buddies know when you
+  log out, and then you get a dialog box "unable to send message" each
+  minute.  :-(
+
+8 Dec 2004:
+- Released 0.9.9rc1
+- Removed the 100 private connection limit, by not using a fixed amount
+  of secure memory.  Unfortuantely, this means that *no* memory is
+  pinned any more, but pinning only ever happened before in the unlikely
+  event you ran gaim as root.
+- Changed the "Private connection with (username) refreshed" dialog at
+  Paul's request so that it's no longer in "scary" "evil" bold, and
+  rephrased it so it's less likely to be misread as "refused" instead of
+  "refreshed".  ;-)
+- We now send heartbeats (OTR Data Messages with an empty message part)
+  once a minute, to anyone we're confident is still online.  If both
+  sides are doing this, then keys get rotated regularly, even if one
+  or both sides aren't actively typing.  This aids perfect forward
+  secrecy.
+
+4 Dec 2004:
+- Fixed a bug wherein multi-person chat windows would get the OTR button
+  in their button bar if the OTR plugin was enabled when one of them was
+  active.
+
+3 Dec 2004:
+- Released 0.9.1
+
+2 Dec 2004:
+- Clicking "OTR: Private" when you're already private will display an
+  info dialog letting you know the connection was refreshed (assuming it
+  actually is; if the other side isn't running OTR at all, the dialog
+  doesn't show, and if the other side had lost its private connection, a
+  new one will be established, with the "new private connection" dialog
+  displayed to each side (as before)).
+- The toolip for "OTR: Private" is now "Refresh the private connection".
+- "make install" now depends on "make all".
+- Added man page for OTR toolkit programs
+- Log a debug message when we receive and discard a heartbeat
+
+1 Dec 2004:
+- Fixed the Makefiles so that "make clean" also removes the binaries
+- Fixed the Makefiles so that they install into DESTDIR
+- Added packaging/debian
+
+30 Nov 2004:
+- Released 0.9.0
+- Included the OTR Messaging Toolkit.  See the README for details.
+
+28 Nov 2004:
+- Finished the Protocol document
+- Changed the name of the plugin binary from "otr-plugin.so" to
+  "gaim-otr.so".  *** NOTE: this means you'll have to (1) remove the
+  old otr-plugin.so file from your plugins directory, and (2) re-enable
+  the Off-the-Record Messaging plugin in the Preferences panel.
+- Included MAC keys used to create messages in the revealed MAC section
+  of the Data message, in addition to MAC keys used to verify messages.
+- Set all exported symbols to start with otrl_ (for the library) or
+  otrg_ (for the gaim plugin), in preparation for moving the pieces
+  into their own directories.
+- If we receive a Data message with no actual message in it, don't
+  display it to the user.  This may eventually be useful for doing
+  "heartbeat" key rotations.
+- Separated libotr and gaim-otr into their own directories.
+
+27 Nov 2004:
+- Switched from using gaim_notify_* to a slightly modified version that
+  doesn't grab the focus
+
+26 Nov 2004:
+- Put all the cipher operations in secure memory.  This makes each
+  private connection take 9472 bytes of secure memory, so we up the
+  available amount of secure memory to 100 times that.  Eventually,
+  we'd like to make this dynamically grow.
+
+25 Nov 2004:
+- Released 0.8.3
+- Don't put the DSA keys in libgcrypt secure memory, since (a) we read
+  them off disk anyway, and (b) we want to avoid running out of secure
+  memory.
+- Removed the "Do you want to start a private conversation" dialogs when
+  one side in encrypted and the other side isn't, and instead just try
+  to start one if we know for sure the other side supports it.
+- Sped up the DH computations by using a 320-bit exponent.
+
+23 Nov 2004:
+- Released 0.8.2
+- There was a crash if you received an OTR Query before setting up a
+  private key.  Fixed.
+- The fingerprint in the UI is now selectable, for cut/paste.
+- *** Protocol change.  We're no longer backward compatible.
+  - The "revealed MAC keys" moved out of the MAC'd region of the data
+    packet.  It's not wrong where it is, but it's more obviously
+    correct in the new place.
+
+22 Nov 2004:
+- Released 0.8.1
+- Jabber wasn't working, for two reasons:
+  - it sticks <tags>...</tags> around the message
+  - it refers to the same user by multiple names; e.g. "user at jabber.org"
+    vs. "user at jabber.org/Gaim"
+  Both are now fixed: we look for the OTR message anywhere in the packet
+  now, not just at the beginning, and we normalize all usernames.
+- Each account now has its own private key / fingerprint
+  - This is so you don't automatically leak the information that the
+    accounts are owned by the same person
+- There's a better indicator of private / not private status in the
+  conversation window, which you can click to start the private
+  communication.
+
+21 Nov 2004:
+- Initial 0.8.0 release
diff --git a/README b/README
new file mode 100644
index 0000000..2938642
--- /dev/null
+++ b/README
@@ -0,0 +1,210 @@
+		Off-the-Record Messaging plugin for GAIM
+			  v2.0.2,  3 May 2005
+
+This is a gaim 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).
+
+OTR allows you to have private conversations over IM by providing:
+ - Encryption
+   - No one else can read your instant messages.
+ - Authentication
+   - You are assured the correspondent is who you think it is.
+ - Deniability
+   - The messages you send do _not_ have digital signatures that are
+     checkable by a third party.  Anyone can forge messages after a
+     conversation to make them look like they came from you.  However,
+     _during_ a conversation, your correspondent is assured the messages
+     he sees are authentic and unmodified.
+ - Perfect forward secrecy
+   - If you lose control of your private keys, no previous conversation
+     is compromised.
+
+For more information on Off-the-Record Messaging, see
+http://www.cypherpunks.ca/otr/
+
+USAGE
+
+Run gaim, and open the Preferences panel.  (If you had a copy of gaim
+running before you installed gaim-otr, you will need to restart it.)
+Choose "Plugins".  Find the Off-the-Record Messaging plugin, and enable
+it by selecting the "Load" box next to it.  This will cause
+"Off-the-Record Messaging" to appear under "Plugins" in the list at the
+left.  Click "Off-the-Record Messaging" to bring up the OTR UI.  The UI
+has two "pages": "Known fingerprints" and "Config".
+
+The "Config" page allows you generate private keys, and to set OTR
+options.
+
+    Private keys are used to authenticate you to your buddies.  Choose
+    one of your accounts from the menu, click "Generate" and wait until
+    it's finished.  You'll see a sequence of letters and number appear
+    above the "Generate" button.  This is the "fingerprint" for that
+    account; it is unique to that account.  If you have multiple IM
+    accounts, you can generate private keys for each one separately.
+    Note that if you don't generate keys in this way, they will be
+    generated automatically, when they are needed.
+
+    The OTR options determine when private messaging is enabled.  The
+    checkboxes on this page control the default settings; you can edit
+    the per-buddy settings by right-clicking on your buddy in the buddy
+    list, and choosing "OTR Options" from the menu.
+
+    The options are:
+    [X] Enable private messaging
+      [X] Automatically initiate private messaging
+        [ ] Require private messaging
+
+    If the "enable private messaging" box is unchecked, private messages
+    will be disabled completely (and the other two boxes will be greyed
+    out, as they're irrelevant).
+
+    If the first box is checked, but "automatically initiate private
+    messaging" is unchecked, private messaging will be enabled, but only
+    if either you or your buddy explicitly requests to start a private
+    conversation (and the third box will be greyed out, as it's
+    irrelevant).
+
+    If the first two boxes are checked, but "require private messaging"
+    is unchecked, OTR will attempt to detect whether your buddy can
+    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
+    buddy unless you are in a private conversation.
+
+The "Known fingerprints" page allows you to see the fingerprints of any
+buddies you have previously communicated with privately.
+
+You can close the Preferences panel (but make sure not to disable
+(un-"Load") the OTR plugin).
+
+IM as normal with your buddies.  If you want to start a private
+conversation with one of them, click the "OTR: Not Private" button in
+the conversation window.
+
+If your buddy does not have the OTR plugin, a private conversation will
+(of course) not be started.  [But he'll get some information about OTR
+instead.]
+
+If your buddy does have the OTR plugin (and it's enabled), a private
+conversation will be initiated.
+
+If both you and your buddy have OTR software, and your OTR options set
+to automatically initiate private messaging, your clients may recognize
+each other and automatically start a private conversation.
+
+The first time you have a private conversation with one of your buddies,
+his fingerprint will appear, and you will be asked to verify that it is
+valid.  It's usually a good idea to make sure it's correct, perhaps via
+the phone, or some other authenticated communication.
+
+If it's wrong, it means someone's intercepting your communication.
+While unlikely, this is one of the things this plugin detects.
+
+Once you've verified your buddy's fingerprint, it will be stored, and
+future private conversations with him won't bother you with this dialog.
+[Unless, of course, he uses a different fingerprint, perhaps from a
+different IM account, or on a different computer.  It's OK to have
+multiple fingerprints for the same IM account, on different machines.]
+
+When private communication has been established, you each will see an
+information popup containing:
+ - Your buddy's screen name  (he'll see yours, of course)
+ - His fingerprint  (similarly, he'll see yours)
+ - A "secure id" for the session.  Half of this id will be in bold.
+   Your buddy sees the same id, but the other half is in bold for him.
+
+The "secure id" is another way to verify that you're actually chatting
+with your buddy, and not some eavesdropper ("man-in-the-middle" is the
+technical term).  Phone him up, and ask him to read his bold part, and
+read yours back to him.  If they're both correct, you're assured that
+there's no one intercepting your private conversation.  This is secure,
+even if you know that one or both of your private keys have been
+compromised.
+
+Then just use IM with him normally; all your instant messages will be
+encrypted and authenticated.  You should see an "OTR: Private" label in
+the conversation window.
+
+If you open the Preferences panel back up, and go to the OTR UI, you'll
+see your buddy, and his fingerprint, listed there.  The "Status" should
+currently be "Private", which means you're having a private
+conversation.  Other possibilities are "Not private", which means you're
+just chatting in IM the usual (non-OTR) way, and "Setting up", which
+means the private conversation is in the process of being set up.
+
+By selecting one of your buddies from the list, you'll be able to do one
+or more of the following things by clicking the buttons below the list:
+ - "Start private conversation": if the status is "Not private", this
+   will attempt to start a private conversation.  It's the same as
+   typing "?OTR?" to your buddy.
+ - "End private conversation": if the status is "Private" (or "Setting
+   up"), you can force an end to your private conversation by clicking
+   this button.  There's not usually a good reason to do this, though.
+   Note that your buddy will have to click the button at his end, as
+   well.  [This is so he doesn't inadvertently type a message he thinks
+   is private, when suddenly the privacy is removed from him.]  When you
+   end a private conversation, you'll see a warning box to that effect.
+ - "Forget fingerprint": this will remove your buddy's fingerprint from
+   the list.  You'll have to re-verify it the next time you start a
+   private conversation with him.  Note that you can't forget a
+   fingerprint that's currently in use in a private conversation.
+
+NOTES
+
+Please send your bug reports, comments, suggestions, patches, etc. to us
+at the contact address below.
+
+This plugin only attempts to protect instant messages, not multi-party
+chats, file transfers, etc.
+
+MAILING LISTS
+
+There are three mailing lists pertaining to Off-the-Record Messaging:
+
+otr-announce:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-announce/
+    *** All users of OTR software should join this. ***  It is used to
+    announce new versions of OTR software, and other important information.
+
+otr-users:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-users/
+    Discussion of usage issues related to OTR Messaging software.
+
+otr-dev:
+    http://lists.cypherpunks.ca/mailman/listinfo/otr-dev/
+    Discussion of OTR Messaging software development.
+
+LICENSE
+
+The Off-the-Record Messaging plugin for gaim is covered by the following
+(GPL) license:
+
+    Off-the-Record Messaging plugin for gaim
+    Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+		             <otr at cypherpunks.ca>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of version 2 of the GNU General Public License as
+    published by the Free Software Foundation.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    There is a copy of the GNU General Public License in the COPYING file
+    packaged with this plugin; if you cannot find it, write to the Free
+    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307  USA
+
+CONTACT
+
+To report problems, comments, suggestions, patches, etc., you can email
+the authors:
+
+Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+
+For more information on Off-the-Record Messaging, visit
+http://www.cypherpunks.ca/otr/
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..2f4d650
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,24 @@
+dnl Process this file with autoconf to produce configure.
+
+dnl XXX Check for headers, etc.
+
+dnl Not yet used.
+AC_INIT(otr-plugin.c)
+
+AM_CONFIG_HEADER(config.h)
+
+AM_INIT_AUTOMAKE(gaim-otr, 2.0.2)
+
+AC_PROG_CC
+
+dnl We do not want to create a .a for the plugin, so disable by default.
+AM_DISABLE_STATIC
+AM_PROG_LIBTOOL
+
+AM_PATH_LIBGCRYPT(1:1.2.0,,AC_MSG_ERROR(libgcrypt 1.2.0 or newer is required.))
+
+AM_PATH_LIBOTR(2.0.2,,AC_MSG_ERROR(libotr 2.0.2 or newer is required.))
+
+PKG_CHECK_MODULES(EXTRA, glib-2.0 >= 2.4 gtk+-2.0 >= 2.4 gaim >= 1.0, , AC_MSG_ERROR(glib, gtk and gaim required))
+
+AC_OUTPUT([Makefile])
diff --git a/dialogs.c b/dialogs.c
new file mode 100644
index 0000000..95920a1
--- /dev/null
+++ b/dialogs.c
@@ -0,0 +1,169 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* system headers */
+#include <stdlib.h>
+
+/* gaim headers */
+#include "notify.h"
+
+/* libotr headers */
+#include <libotr/proto.h>
+#include <libotr/message.h>
+
+/* gaim-otr headers */
+#include "dialogs.h"
+#include "otr-plugin.h"
+
+static const OtrgDialogUiOps *ui_ops = NULL;
+
+/* Set the UI ops */
+void otrg_dialog_set_ui_ops(const OtrgDialogUiOps *ops)
+{
+    ui_ops = ops;
+}
+
+/* Get the UI ops */
+const OtrgDialogUiOps *otrg_dialog_get_ui_ops(void)
+{
+    return ui_ops;
+}
+
+/* This is just like gaim_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. */
+void otrg_dialog_notify_message(GaimNotifyMsgType type,
+	const char *accountname, const char *protocol, const char *username,
+	const char *title, const char *primary, const char *secondary)
+{
+    ui_ops->notify_message(type, accountname, protocol, username,
+	    title, primary, secondary);
+}
+
+/* Put up the error version of otrg_dialog_notify_message */
+void otrg_dialog_notify_error(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary)
+{
+    otrg_dialog_notify_message(GAIM_NOTIFY_MSG_ERROR, accountname,
+	    protocol, username, title, primary, secondary);
+}
+
+/* Put up the warning version of otrg_dialog_notify_message */
+void otrg_dialog_notify_warning(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary)
+{
+    otrg_dialog_notify_message(GAIM_NOTIFY_MSG_WARNING, accountname,
+	    protocol, username, title, primary, secondary);
+}
+
+/* Put up the info version of otrg_dialog_notify_message */
+void otrg_dialog_notify_info(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary)
+{
+    otrg_dialog_notify_message(GAIM_NOTIFY_MSG_INFO, accountname,
+	    protocol, username, title, primary, secondary);
+}
+
+/* Display an OTR control message for the given accountname / protocol /
+ * username conversation.  Return 0 on success, non-0 on error (in which
+ * case the message will be displayed inline as a received message). */
+int otrg_dialog_display_otr_message( const char *accountname,
+	const char *protocol, const char *username, const char *msg)
+{
+    return ui_ops->display_otr_message(accountname, protocol, username, msg);
+}
+
+/* Put up a Please Wait dialog.  This dialog can not be cancelled.
+ * Return a handle that must eventually be passed to
+ * otrg_dialog_private_key_wait_done. */
+OtrgDialogWaitHandle otrg_dialog_private_key_wait_start(const char *account,
+	const char *protocol)
+{
+    return ui_ops->private_key_wait_start(account, protocol);
+}
+
+/* End a Please Wait dialog. */
+void otrg_dialog_private_key_wait_done(OtrgDialogWaitHandle handle)
+{
+    ui_ops->private_key_wait_done(handle);
+}
+
+/* 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(us, ops, opdata, response_data, resp) with resp = 1.  If no,
+ * set resp = 0.  If the user destroys the dialog without answering, set
+ * resp = -1. */
+void otrg_dialog_unknown_fingerprint(OtrlUserState us, const char *accountname,
+	const char *protocol, const char *who, OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OtrlMessageAppOps *ops, void *opdata,
+	OTRConfirmResponse *response_data)
+{
+    ui_ops->unknown_fingerprint(us, accountname, protocol, who, kem,
+	    response_cb, ops, opdata, response_data);
+}
+
+/* Call this when a context transitions from (a state other than
+ * CONN_CONNECTED) to CONN_CONNECTED. */
+void otrg_dialog_connected(ConnContext *context)
+{
+    ui_ops->connected(context);
+}
+
+/* Call this when a context transitions from CONN_CONNECTED to
+ * (a state other than CONN_CONNECTED). */
+void otrg_dialog_disconnected(ConnContext *context)
+{
+    ui_ops->disconnected(context);
+}
+
+/* Call this when we receive a Key Exchange message that doesn't cause
+ * our state to change (because it was just the keys we knew already). */
+void otrg_dialog_stillconnected(ConnContext *context)
+{
+    ui_ops->stillconnected(context);
+}
+
+/* Set all OTR buttons to "sensitive" or "insensitive" as appropriate.
+ * Call this when accounts are logged in or out. */
+void otrg_dialog_resensitize_all(void)
+{
+    ui_ops->resensitize_all();
+}
+
+/* Set up the per-conversation information display */
+void otrg_dialog_new_conv(GaimConversation *conv)
+{
+    ui_ops->new_conv(conv);
+}
+
+/* Remove the per-conversation information display */
+void otrg_dialog_remove_conv(GaimConversation *conv)
+{
+    ui_ops->remove_conv(conv);
+}
diff --git a/dialogs.h b/dialogs.h
new file mode 100644
index 0000000..893441d
--- /dev/null
+++ b/dialogs.h
@@ -0,0 +1,143 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __OTRG_DIALOGS_H__
+#define __OTRG_DIALOGS_H__
+
+/* gaim headers */
+#include "notify.h"
+
+/* libotr headers */
+#include <libotr/proto.h>
+#include <libotr/message.h>
+
+typedef struct s_OtrgDialogWait *OtrgDialogWaitHandle;
+
+typedef struct {
+    void (*notify_message)(GaimNotifyMsgType type,
+	const char *accountname, const char *protocol, const char *username,
+	const char *title, const char *primary, const char *secondary);
+
+    int (*display_otr_message)(const char *accountname, const char *protocol,
+	    const char *username, const char *msg);
+
+    OtrgDialogWaitHandle (*private_key_wait_start)(const char *account,
+	const char *protocol);
+
+    void (*private_key_wait_done)(OtrgDialogWaitHandle handle);
+
+    void (*unknown_fingerprint)(OtrlUserState us, const char *accountname,
+	const char *protocol, const char *who, OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OtrlMessageAppOps *ops, void *opdata,
+	OTRConfirmResponse *response_data);
+
+    void (*connected)(ConnContext *context);
+
+    void (*disconnected)(ConnContext *context);
+
+    void (*stillconnected)(ConnContext *context);
+
+    void (*resensitize_all)(void);
+
+    void (*new_conv)(GaimConversation *conv);
+
+    void (*remove_conv)(GaimConversation *conv);
+} OtrgDialogUiOps;
+
+/* Set the UI ops */
+void otrg_dialog_set_ui_ops(const OtrgDialogUiOps *ops);
+
+/* Get the UI ops */
+const OtrgDialogUiOps *otrg_dialog_get_ui_ops(void);
+
+/* This is just like gaim_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. */
+void otrg_dialog_notify_message(GaimNotifyMsgType type,
+	const char *accountname, const char *protocol, const char *username,
+	const char *title, const char *primary, const char *secondary);
+
+/* Put up the error version of otrg_dialog_notify_message */
+void otrg_dialog_notify_error(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary);
+
+/* Put up the warning version of otrg_dialog_notify_message */
+void otrg_dialog_notify_warning(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary);
+
+/* Put up the info version of otrg_dialog_notify_message */
+void otrg_dialog_notify_info(const char *accountname, const char *protocol,
+	const char *username, const char *title, const char *primary,
+	const char *secondary);
+
+/* Display an OTR control message for the given accountname / protocol /
+ * username conversation.  Return 0 on success, non-0 on error (in which
+ * case the message will be displayed inline as a received message). */
+int otrg_dialog_display_otr_message( const char *accountname,
+	const char *protocol, const char *username, const char *msg);
+
+/* Put up a Please Wait dialog. This dialog can not be cancelled.
+ * Return a handle that must eventually be passed to
+ * otrg_dialog_private_key_wait_done. */
+OtrgDialogWaitHandle otrg_dialog_private_key_wait_start(const char *account,
+	const char *protocol);
+
+/* End a Please Wait dialog. */
+void otrg_dialog_private_key_wait_done(OtrgDialogWaitHandle handle);
+
+/* 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(us, ops, opdata, response_data, resp) with resp = 1.  If no,
+ * set resp = 0.  If the user destroys the dialog without answering, set
+ * resp = -1. */
+void otrg_dialog_unknown_fingerprint(OtrlUserState us, const char *accountname,
+	const char *protocol, const char *who, OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OtrlMessageAppOps *ops, void *opdata,
+	OTRConfirmResponse *response_data);
+
+/* Call this when a context transitions from (a state other than
+ * CONN_CONNECTED) to CONN_CONNECTED. */
+void otrg_dialog_connected(ConnContext *context);
+
+/* Call this when a context transitions from CONN_CONNECTED to
+ * (a state other than CONN_CONNECTED). */
+void otrg_dialog_disconnected(ConnContext *context);
+
+/* Call this when we receive a Key Exchange message that doesn't cause
+ * our state to change (because it was just the keys we knew already). */
+void otrg_dialog_stillconnected(ConnContext *context);
+
+/* Set all OTR buttons to "sensitive" or "insensitive" as appropriate.
+ * Call this when accounts are logged in or out. */
+void otrg_dialog_resensitize_all(void);
+
+/* Set up the per-conversation information display */
+void otrg_dialog_new_conv(GaimConversation *conv);
+
+/* Remove the per-conversation information display */
+void otrg_dialog_remove_conv(GaimConversation *conv);
+
+#endif
diff --git a/gtk-dialog.c b/gtk-dialog.c
new file mode 100644
index 0000000..cac56f9
--- /dev/null
+++ b/gtk-dialog.c
@@ -0,0 +1,562 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+/* gcrypt headers */
+#include <gcrypt.h>
+
+/* gaim headers */
+#include "stock.h"
+#include "plugin.h"
+#include "notify.h"
+#include "gtkconv.h"
+#include "util.h"
+
+/* libotr headers */
+#include <libotr/dh.h>
+#include <libotr/privkey.h>
+#include <libotr/proto.h>
+#include <libotr/message.h>
+#include <libotr/userstate.h>
+
+/* gaim-otr headers */
+#include "otr-plugin.h"
+#include "dialogs.h"
+#include "ui.h"
+
+static void message_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget)
+{
+    gtk_widget_destroy(GTK_WIDGET(widget));
+}
+
+static GtkWidget *create_dialog(GaimNotifyMsgType type, const char *title,
+	const char *primary, const char *secondary, int sensitive,
+	GtkWidget **labelp)
+{
+    GtkWidget *dialog;
+    GtkWidget *hbox;
+    GtkWidget *label;
+    GtkWidget *img = NULL;
+    char *label_text;
+    const char *icon_name = NULL;
+
+    switch (type) {
+	case GAIM_NOTIFY_MSG_ERROR:
+	    icon_name = GAIM_STOCK_DIALOG_ERROR;
+	    break;
+
+	case GAIM_NOTIFY_MSG_WARNING:
+	    icon_name = GAIM_STOCK_DIALOG_WARNING;
+	    break;
+
+	case GAIM_NOTIFY_MSG_INFO:
+	    icon_name = GAIM_STOCK_DIALOG_INFO;
+	    break;
+
+	default:
+	    icon_name = NULL;
+	    break;
+    }
+
+    if (icon_name != NULL) {
+	img = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+    }
+
+    dialog = gtk_dialog_new_with_buttons(title ? title : GAIM_ALERT_TITLE,
+					 NULL, 0, GTK_STOCK_OK,
+					 GTK_RESPONSE_ACCEPT, NULL);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT,
+	    sensitive);
+
+    gtk_window_set_accept_focus(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);
+
+    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);
+    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+
+    if (img != NULL) {
+	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);
+    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(hbox), label, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(dialog);
+
+    if (labelp) *labelp = label;
+    return dialog;
+}
+
+/* This is just like gaim_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. */
+static void otrg_gtk_dialog_notify_message(GaimNotifyMsgType 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);
+}
+
+struct s_OtrgDialogWait {
+    GtkWidget *dialog;
+    GtkWidget *label;
+};
+
+/* Put up a Please Wait dialog, with the "OK" button desensitized.
+ * Return a handle that must eventually be passed to
+ * otrg_dialog_private_key_wait_done. */
+static OtrgDialogWaitHandle otrg_gtk_dialog_private_key_wait_start(
+	const char *account, const char *protocol)
+{
+    GaimPlugin *p;
+    const char *title = "Generating private key";
+    const char *primary = "Please wait";
+    char *secondary;
+    const char *protocol_print;
+    GtkWidget *label;
+    GtkWidget *dialog;
+    OtrgDialogWaitHandle handle;
+
+    p = gaim_find_prpl(protocol);
+    protocol_print = (p ? p->info->name : "Unknown");
+	
+    /* Create the Please Wait... dialog */
+    secondary = g_strdup_printf("Generating private key for %s (%s)...",
+	    account, protocol_print);
+	
+    dialog = create_dialog(GAIM_NOTIFY_MSG_INFO, title, primary, secondary,
+	    0, &label);
+    handle = malloc(sizeof(struct s_OtrgDialogWait));
+    handle->dialog = dialog;
+    handle->label = label;
+
+    /* Make sure the dialog is actually displayed before doing any
+     * compute-intensive stuff. */
+    while (gtk_events_pending ()) {
+	gtk_main_iteration ();
+    }
+	
+    g_free(secondary);
+
+    return handle;
+}
+
+static int otrg_gtk_dialog_display_otr_message(const char *accountname,
+	const char *protocol, const char *username, const char *msg)
+{
+    /* See if there's a conversation window we can put this in. */
+    GaimAccount *account;
+    GaimConversation *conv;
+
+    account = gaim_accounts_find(accountname, protocol);
+    if (!account) return -1;
+
+    conv = gaim_find_conversation_with_account(username, account);
+    if (!conv) return -1;
+
+    gaim_conversation_write(conv, NULL, msg, GAIM_MESSAGE_SYSTEM, time(NULL));
+
+    return 0;
+}
+
+/* End a Please Wait dialog. */
+static void otrg_gtk_dialog_private_key_wait_done(OtrgDialogWaitHandle handle)
+{
+    const char *oldmarkup;
+    char *newmarkup;
+
+    oldmarkup = gtk_label_get_label(GTK_LABEL(handle->label));
+    newmarkup = g_strdup_printf("%s Done.", oldmarkup);
+
+    gtk_label_set_markup(GTK_LABEL(handle->label), newmarkup);
+    gtk_widget_show(handle->label);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(handle->dialog),
+	    GTK_RESPONSE_ACCEPT, 1);
+
+    g_free(newmarkup);
+    free(handle);
+}
+
+struct ufcbdata {
+    GtkDialog *dialog;
+    void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops, void *opdata,
+	    OTRConfirmResponse *response_data, int resp);
+    OtrlUserState us;
+    OtrlMessageAppOps *ops;
+    void *opdata;
+    OTRConfirmResponse *response_data;
+    int response;
+};
+
+static void unknown_fingerprint_destroy(GtkWidget *w, struct ufcbdata *cbdata)
+{
+    if (cbdata->response_cb) {
+	cbdata->response_cb(cbdata->us, cbdata->ops, cbdata->opdata,
+		cbdata->response_data, cbdata->response);
+    }
+    free(cbdata);
+}
+
+static void unknown_fingerprint_response(GtkWidget *w, int resp,
+	struct ufcbdata *cbdata)
+{
+    if (resp == GTK_RESPONSE_OK) {
+	cbdata->response = 1;
+    } else if (resp == GTK_RESPONSE_CANCEL) {
+	cbdata->response = 0;
+    }
+    gtk_widget_destroy(GTK_WIDGET(cbdata->dialog));
+}
+
+/* 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. */
+static void otrg_gtk_dialog_unknown_fingerprint(OtrlUserState us,
+	const char *accountname, const char *protocol, const char *who,
+	OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OtrlMessageAppOps *ops, void *opdata,
+	OTRConfirmResponse *response_data)
+{
+    char hash[45];
+    GtkWidget *img;
+    GtkWidget *dialog;
+    GtkWidget *hbox;
+    GtkWidget *label;
+    char *label_text;
+    struct ufcbdata *cbd = malloc(sizeof(struct ufcbdata));
+    const char *icon_name = GAIM_STOCK_DIALOG_WARNING;
+    GaimPlugin *p = gaim_find_prpl(protocol);
+    
+    img = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_DIALOG);
+    gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+
+    dialog = gtk_dialog_new_with_buttons( "Unknown Fingerprint",
+	    NULL, GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_OK, GTK_RESPONSE_OK,
+	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
+
+    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_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);
+    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+
+    if (img != NULL)
+	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+
+    otrl_privkey_hash_to_human(hash, kem->key_fingerprint);
+    label_text = g_strdup_printf("<span weight=\"bold\" size=\"larger\">%s "
+	    "(%s) has received an unknown fingerprint from %s:</span>\n\n"
+	    "%s\n\n"
+	    "Do you want to accept this fingerprint as valid?", accountname,
+	    (p && p->info->name) ? p->info->name : "Unknown", who, hash);
+
+    label = gtk_label_new(NULL);
+
+    gtk_label_set_markup(GTK_LABEL(label), 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(hbox), label, FALSE, FALSE, 0);
+    g_free(label_text);
+
+    cbd->dialog = GTK_DIALOG(dialog);
+    cbd->response_cb = response_cb;
+    cbd->us = us;
+    cbd->ops = ops;
+    cbd->opdata = opdata;
+    cbd->response_data = response_data;
+    cbd->response = -1;
+    g_signal_connect(G_OBJECT(dialog), "destroy",
+	    G_CALLBACK(unknown_fingerprint_destroy), cbd);
+    g_signal_connect(G_OBJECT(dialog), "response",
+	    G_CALLBACK(unknown_fingerprint_response), cbd);
+
+    gtk_widget_show_all(dialog);
+}
+
+static void dialog_update_label_conv(GaimConversation *conv, int is_private)
+{
+    GtkWidget *label;
+    GtkWidget *button;
+    GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
+    label = gaim_conversation_get_data(conv, "otr-label");
+    button = gaim_conversation_get_data(conv, "otr-button");
+    gtk_label_set_text(GTK_LABEL(label),
+	    is_private ? "OTR:\nPrivate" : "OTR:\nNot private");
+    gtk_tooltips_set_tip(gtkconv->tooltips, button,
+	    is_private ? "Refresh the private conversation"
+		       : "Start a private conversation", NULL);
+    /* Use any non-NULL value for "private", NULL for "not private" */
+    gaim_conversation_set_data(conv, "otr-private",
+	    is_private ? conv : NULL);
+}
+
+static void dialog_update_label(ConnContext *context, int is_private)
+{
+    GaimAccount *account;
+    GaimConversation *conv;
+
+    account = gaim_accounts_find(context->accountname, context->protocol);
+    if (!account) return;
+    conv = gaim_find_conversation_with_account(context->username, account);
+    if (!conv) return;
+    dialog_update_label_conv(conv, is_private);
+}
+
+/* Call this when a context transitions from (a state other than
+ * CONN_CONNECTED) to CONN_CONNECTED. */
+static void otrg_gtk_dialog_connected(ConnContext *context)
+{
+    char fingerprint[45];
+    unsigned char *sessionid;
+    char sess1[21], sess2[21];
+    char *primary = g_strdup_printf("Private connection with %s "
+	    "established.", context->username);
+    char *secondary;
+    int i;
+    SessionDirection dir = context->sesskeys[1][0].dir;
+
+    /* Make a human-readable version of the fingerprint */
+    otrl_privkey_hash_to_human(fingerprint,
+	    context->active_fingerprint->fingerprint);
+    /* Make a human-readable version of the sessionid (in two parts) */
+    sessionid = context->sesskeys[1][0].sessionid;
+    for(i=0;i<10;++i) sprintf(sess1+(2*i), "%02x", sessionid[i]);
+    sess1[20] = '\0';
+    for(i=0;i<10;++i) sprintf(sess2+(2*i), "%02x", sessionid[i+10]);
+    sess2[20] = '\0';
+    
+    secondary = g_strdup_printf("Fingerprint for %s:\n%s\n\n"
+	    "Secure id for this session:\n"
+	    "<span %s>%s</span> <span %s>%s</span>", context->username,
+	    fingerprint,
+	    dir == SESS_DIR_LOW ? "weight=\"bold\"" : "", sess1,
+	    dir == SESS_DIR_HIGH ? "weight=\"bold\"" : "", sess2);
+
+    otrg_dialog_notify_info(context->accountname, context->protocol,
+	    context->username, "Private connection established",
+	    primary, secondary);
+
+    g_free(primary);
+    g_free(secondary);
+    dialog_update_label(context, 1);
+}
+
+/* Call this when a context transitions from CONN_CONNECTED to
+ * (a state other than CONN_CONNECTED). */
+static void otrg_gtk_dialog_disconnected(ConnContext *context)
+{
+    char *primary = g_strdup_printf("Private connection with %s lost.",
+	    context->username);
+    otrg_dialog_notify_warning(context->accountname, context->protocol,
+	    context->username, "Private connection lost", primary, NULL);
+    g_free(primary);
+    dialog_update_label(context, 0);
+}
+
+/* Call this when we receive a Key Exchange message that doesn't cause
+ * our state to change (because it was just the keys we knew already). */
+static void otrg_gtk_dialog_stillconnected(ConnContext *context)
+{
+    char *secondary = g_strdup_printf("<span size=\"larger\">Successfully "
+	    "refreshed private connection with %s.</span>", context->username);
+    otrg_dialog_notify_info(context->accountname, context->protocol,
+	    context->username, "Refreshed private connection", NULL,
+	    secondary);
+    g_free(secondary);
+    dialog_update_label(context, 1);
+}
+
+/* This is called when the OTR button in the button box is clicked. */
+static void otrg_gtk_dialog_clicked_connect(GtkWidget *widget, gpointer data)
+{
+    const char *format;
+    char *buf;
+    GaimConversation *conv = data;
+
+    if (gaim_conversation_get_data(conv, "otr-private")) {
+	format = "Attempting to refresh the private conversation with %s...";
+    } else {
+	format = "Attempting to start a private conversation with %s...";
+    }
+    buf = g_strdup_printf(format, gaim_conversation_get_name(conv));
+    gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
+    g_free(buf);
+	
+    otrg_plugin_send_default_query_conv(conv);
+}
+
+static void dialog_resensitize(GaimConversation *conv);
+
+/* Set up the per-conversation information display */
+static void otrg_gtk_dialog_new_conv(GaimConversation *conv)
+{
+    GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
+    GaimAccount *account;
+    char *username;
+    const char *accountname, *proto;
+    ConnContext *context;
+    ConnectionState state;
+    GtkWidget *bbox;
+    GtkWidget *button;
+    GtkWidget *label;
+
+    /* Do nothing if this isn't an IM conversation */
+    if (gaim_conversation_get_type(conv) != GAIM_CONV_IM) return;
+
+    /* Do nothing if we're already set up */
+    if (gaim_conversation_get_data(conv, "otr-button")) return;
+
+    account = gaim_conversation_get_account(conv);
+    accountname = gaim_account_get_username(account);
+    proto = gaim_account_get_protocol_id(account);
+    username = g_strdup(
+	    gaim_normalize(account, gaim_conversation_get_name(conv)));
+
+    context = otrl_context_find(otrg_plugin_userstate, username, accountname,
+	    proto, 0, NULL, NULL, NULL);
+    state = context ? context->state : CONN_UNCONNECTED;
+    g_free(username);
+
+    bbox = gtkconv->bbox;
+    button = gtk_button_new();
+    label = gtk_label_new(NULL);
+    gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+    gtk_container_add(GTK_CONTAINER(button), label);
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gaim_conversation_set_data(conv, "otr-label", label);
+    gaim_conversation_set_data(conv, "otr-button", button);
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+	    GTK_SIGNAL_FUNC(otrg_gtk_dialog_clicked_connect), conv);
+
+    dialog_update_label_conv(conv, state == CONN_CONNECTED);
+    dialog_resensitize(conv);
+    gtk_widget_show_all(button);
+}
+
+/* Remove the per-conversation information display */
+static void otrg_gtk_dialog_remove_conv(GaimConversation *conv)
+{
+    GtkWidget *button;
+
+    /* Do nothing if this isn't an IM conversation */
+    if (gaim_conversation_get_type(conv) != GAIM_CONV_IM) return;
+
+    button = gaim_conversation_get_data(conv, "otr-button");
+    if (button) gtk_object_destroy(GTK_OBJECT(button));
+    g_hash_table_remove(conv->data, "otr-label");
+    g_hash_table_remove(conv->data, "otr-button");
+    g_hash_table_remove(conv->data, "otr-private");
+}
+
+/* Set the OTR button to "sensitive" or "insensitive" as appropriate. */
+static void dialog_resensitize(GaimConversation *conv)
+{
+    GaimAccount *account;
+    GaimConnection *connection;
+    GtkWidget *button;
+    const char *name;
+    OtrlPolicy policy;
+
+    /* Do nothing if this isn't an IM conversation */
+    if (gaim_conversation_get_type(conv) != GAIM_CONV_IM) return;
+
+    account = gaim_conversation_get_account(conv);
+    name = gaim_conversation_get_name(conv);
+    policy = otrg_ui_find_policy(account, name);
+
+    if (policy == OTRL_POLICY_NEVER) {
+	otrg_gtk_dialog_remove_conv(conv);
+    } else {
+	otrg_gtk_dialog_new_conv(conv);
+    }
+    button = gaim_conversation_get_data(conv, "otr-button");
+    if (!button) return;
+    if (account) {
+	connection = gaim_account_get_connection(account);
+	if (connection) {
+	    /* Set the button to "sensitive" */
+	    gtk_widget_set_sensitive(button, 1);
+	    return;
+	}
+    }
+    /* Set the button to "insensitive" */
+    gtk_widget_set_sensitive(button, 0);
+}
+
+/* Set all OTR buttons to "sensitive" or "insensitive" as appropriate.
+ * Call this when accounts are logged in or out. */
+static void otrg_gtk_dialog_resensitize_all(void)
+{
+    gaim_conversation_foreach(dialog_resensitize);
+}
+
+static const OtrgDialogUiOps gtk_dialog_ui_ops = {
+    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_connected,
+    otrg_gtk_dialog_disconnected,
+    otrg_gtk_dialog_stillconnected,
+    otrg_gtk_dialog_resensitize_all,
+    otrg_gtk_dialog_new_conv,
+    otrg_gtk_dialog_remove_conv
+};
+
+/* Get the GTK dialog UI ops */
+const OtrgDialogUiOps *otrg_gtk_dialog_get_ui_ops(void)
+{
+    return &gtk_dialog_ui_ops;
+}
diff --git a/gtk-dialog.h b/gtk-dialog.h
new file mode 100644
index 0000000..e1355b2
--- /dev/null
+++ b/gtk-dialog.h
@@ -0,0 +1,26 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __OTRG_GTK_DIALOG_H__
+#define __OTRG_GTK_DIALOG_H__
+
+/* Get the GTK dialog UI ops */
+const OtrgDialogUiOps *otrg_gtk_dialog_get_ui_ops(void);
+
+#endif
diff --git a/gtk-ui.c b/gtk-ui.c
new file mode 100644
index 0000000..0e01700
--- /dev/null
+++ b/gtk-ui.c
@@ -0,0 +1,904 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <gtk/gtk.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include <libotr/privkey.h>
+
+/* gaim headers */
+#include "util.h"
+#include "account.h"
+#include "notify.h"
+#include "gtkutils.h"
+
+/* gaim-otr headers */
+#include "dialogs.h"
+#include "ui.h"
+#include "otr-plugin.h"
+
+struct otroptionsdata {
+    GtkWidget *enablebox;
+    GtkWidget *automaticbox;
+    GtkWidget *onlyprivatebox;
+};
+
+static struct {
+    GtkWidget *accountmenu;
+    GtkWidget *fprint_label;
+    GtkWidget *generate_button;
+    GtkWidget *scrollwin;
+    GtkWidget *keylist;
+    gint sortcol, sortdir;
+    Fingerprint *selected_fprint;
+    GtkWidget *connect_button;
+    GtkWidget *disconnect_button;
+    GtkWidget *forget_button;
+    struct otroptionsdata oo;
+} ui_layout;
+
+static void account_menu_changed_cb(GtkWidget *item, GaimAccount *account,
+	void *data)
+{
+    const char *accountname;
+    const char *protocol;
+    GtkWidget *fprint = ui_layout.fprint_label;
+    char s[100];
+    char *fingerprint;
+    
+    if (account) {
+	char fingerprint_buf[45];
+	accountname = gaim_account_get_username(account);
+	protocol = gaim_account_get_protocol_id(account);
+	fingerprint = otrl_privkey_fingerprint(otrg_plugin_userstate,
+		fingerprint_buf, accountname, protocol);
+
+	if (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");
+	    if (ui_layout.generate_button)
+		gtk_widget_set_sensitive(ui_layout.generate_button, 1);
+	}
+    } else {
+	sprintf(s, "No account available");
+	if (ui_layout.generate_button)
+	    gtk_widget_set_sensitive(ui_layout.generate_button, 0);
+    }
+    if (fprint) {
+	gtk_label_set_text(GTK_LABEL(fprint), s);
+	gtk_widget_show(fprint);
+    }
+}
+
+static GtkWidget *accountmenu_get_selected_item(void)
+{
+    GtkWidget *menu;
+
+    menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(ui_layout.accountmenu));
+    return gtk_menu_get_active(GTK_MENU(menu));
+}
+
+static GaimAccount *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;
+    GaimAccount *account;
+    gpointer user_data;
+
+    item = accountmenu_get_selected_item();
+
+    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);
+}
+
+static void account_menu_added_removed_cb(GaimAccount *account, void *data)
+{
+    otrg_gtk_ui_update_fingerprint();
+}
+
+static void clist_all_unselected(void)
+{
+    gtk_widget_set_sensitive(ui_layout.connect_button, 0);
+    gtk_widget_set_sensitive(ui_layout.disconnect_button, 0);
+    gtk_widget_set_sensitive(ui_layout.forget_button, 0);
+    ui_layout.selected_fprint = NULL;
+}
+
+/* Update the keylist, if it's visible */
+static void otrg_gtk_ui_update_keylist(void)
+{
+    gchar *titles[4];
+    char hash[45];
+    ConnContext * context;
+    Fingerprint * fingerprint;
+    int selected_row = -1;
+
+    GtkWidget *keylist = ui_layout.keylist;
+
+    if (keylist == NULL)
+	return;
+
+    gtk_clist_freeze(GTK_CLIST(keylist));
+    gtk_clist_clear(GTK_CLIST(keylist));
+
+    for (context = otrg_plugin_userstate->context_root; context != NULL;
+	    context = context->next) {
+	int i;
+	GaimPlugin *p;
+	char *proto_name;
+	fingerprint = context->fingerprint_root.next;
+	/* If there's no fingerprint, don't add it to the known
+	 * fingerprints list */
+	while(fingerprint) {
+	    titles[0] = context->username;
+	    if (context->state == CONN_CONNECTED &&
+		    context->active_fingerprint != fingerprint) {
+		titles[1] = "Unused";
+	    } else {
+		titles[1] =
+		    (gchar *) otrl_context_statestr[context->state];
+	    }
+	    otrl_privkey_hash_to_human(hash, fingerprint->fingerprint);
+	    titles[2] = hash;
+	    p = gaim_find_prpl(context->protocol);
+	    proto_name = (p && p->info->name) ? p->info->name : "Unknown";
+	    titles[3] = g_strdup_printf("%s (%s)", context->accountname,
+		proto_name);
+	    i = gtk_clist_append(GTK_CLIST(keylist), titles);
+	    g_free(titles[3]);
+	    gtk_clist_set_row_data(GTK_CLIST(keylist), i, fingerprint);
+	    if (ui_layout.selected_fprint == fingerprint) {
+		selected_row = i;
+	    }
+	    fingerprint = fingerprint->next;
+	}
+    }
+
+    if (selected_row >= 0) {
+	gtk_clist_select_row(GTK_CLIST(keylist), selected_row, 0);
+    } else {
+	clist_all_unselected();
+    }
+
+    gtk_clist_sort(GTK_CLIST(keylist));
+
+    gtk_clist_thaw(GTK_CLIST(keylist));
+
+}
+
+static void generate(GtkWidget *widget, gpointer data)
+{
+    GaimAccount *account;
+    account = item_get_account(accountmenu_get_selected_item());
+	
+    if (account == NULL) return;
+	
+    otrg_plugin_create_privkey(gaim_account_get_username(account),
+	    gaim_account_get_protocol_id(account));
+}
+
+static void ui_destroyed(GtkObject *object)
+{
+    /* If this is called, we need to invalidate the stored pointers in
+     * the ui_layout struct. */
+    ui_layout.accountmenu = NULL;
+    ui_layout.fprint_label = NULL;
+    ui_layout.generate_button = NULL;
+    ui_layout.scrollwin = NULL;
+    ui_layout.keylist = NULL;
+    ui_layout.selected_fprint = NULL;
+    ui_layout.connect_button = NULL;
+    ui_layout.disconnect_button = NULL;
+    ui_layout.forget_button = NULL;
+    ui_layout.oo.enablebox = NULL;
+    ui_layout.oo.automaticbox = NULL;
+    ui_layout.oo.onlyprivatebox = NULL;
+}
+
+static void clist_selected(GtkWidget *widget, gint row, gint column,
+	GdkEventButton *event, gpointer data)
+{
+    int connect_sensitive = 0;
+    int disconnect_sensitive = 0;
+    int forget_sensitive = 0;
+    Fingerprint *f = gtk_clist_get_row_data(GTK_CLIST(ui_layout.keylist),
+	    row);
+    if (f && f->context->state == CONN_CONNECTED &&
+	    f->context->active_fingerprint == f) {
+	disconnect_sensitive = 1;
+    }
+    if (f && f->context->state == CONN_SETUP) {
+	disconnect_sensitive = 1;
+    }
+    if (f && f->context->state == CONN_CONNECTED &&
+	    f->context->active_fingerprint != f) {
+	forget_sensitive = 1;
+    }
+    if (f && f->context->state == CONN_UNCONNECTED) {
+	forget_sensitive = 1;
+    }
+    if (f && f->context->state == CONN_UNCONNECTED) {
+	connect_sensitive = 1;
+    }
+    gtk_widget_set_sensitive(ui_layout.connect_button,
+	    connect_sensitive);
+    gtk_widget_set_sensitive(ui_layout.disconnect_button,
+	    disconnect_sensitive);
+    gtk_widget_set_sensitive(ui_layout.forget_button, forget_sensitive);
+    ui_layout.selected_fprint = f;
+}
+
+static void clist_unselected(GtkWidget *widget, gint row, gint column,
+	GdkEventButton *event, gpointer data)
+{
+    clist_all_unselected();
+}
+
+static int fngsortval(Fingerprint *f)
+{
+    if (f->context->state == CONN_CONNECTED &&
+	    f->context->active_fingerprint == f) {
+	return 0;
+    } else if (f->context->state == CONN_SETUP) {
+	return 1;
+    } else if (f->context->state == CONN_UNCONNECTED) {
+	return 2;
+    } else {
+	return 3;
+    }
+}
+
+static gint statuscmp(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
+{
+    const GtkCListRow *a = ptr1;
+    const GtkCListRow *b = ptr2;
+    int as = fngsortval(a->data);
+    int bs = fngsortval(b->data);
+    return (as - bs);
+}
+
+static void clist_click_column(GtkCList *clist, gint column, gpointer data)
+{
+    if (ui_layout.sortcol == column) {
+	ui_layout.sortdir = -(ui_layout.sortdir);
+    } else {
+	ui_layout.sortcol = column;
+	ui_layout.sortdir = 1;
+    }
+
+    gtk_clist_set_sort_column(clist, ui_layout.sortcol);
+    gtk_clist_set_sort_type(clist,
+	    ui_layout.sortdir == 1 ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
+    if (column == 1) {
+	gtk_clist_set_compare_func(clist, statuscmp);
+    } else {
+	/* Just use the default compare function for the rest of the
+	 * columns */
+	gtk_clist_set_compare_func(clist, NULL);
+    }
+    gtk_clist_sort(clist);
+}
+
+static void connect_connection(GtkWidget *widget, gpointer data)
+{
+    /* Send an OTR Query to the other side. */
+    ConnContext *context;
+
+    if (ui_layout.selected_fprint == NULL) return;
+
+    context = ui_layout.selected_fprint->context;
+    otrg_ui_connect_connection(context);
+}
+
+static void disconnect_connection(GtkWidget *widget, gpointer data)
+{
+    /* Forget whatever state we've got with this context */
+    ConnContext *context;
+
+    if (ui_layout.selected_fprint == NULL) return;
+
+    context = ui_layout.selected_fprint->context;
+    if (context == NULL) return;
+	
+    /* Don't do anything with fingerprints other than the active one
+     * if we're in the CONNECTED state */
+    if (context->state == CONN_CONNECTED &&
+	    context->active_fingerprint != ui_layout.selected_fprint) {
+	return;
+    }
+	
+    otrg_ui_disconnect_connection(context);
+}
+
+static void forget_fingerprint(GtkWidget *widget, gpointer data)
+{
+    Fingerprint *fingerprint = ui_layout.selected_fprint;
+
+    otrg_ui_forget_fingerprint(fingerprint);
+}
+
+static void otroptions_clicked_cb(GtkButton *button, struct otroptionsdata *oo)
+{
+    gtk_widget_set_sensitive(oo->enablebox, TRUE);
+    if (gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(oo->enablebox))) {
+	gtk_widget_set_sensitive(oo->automaticbox, TRUE);
+	if (gtk_toggle_button_get_active(
+		    GTK_TOGGLE_BUTTON(oo->automaticbox))) {
+	    gtk_widget_set_sensitive(oo->onlyprivatebox, TRUE);
+	} else {
+	    gtk_widget_set_sensitive(oo->onlyprivatebox, FALSE);
+	}
+    } else {
+	gtk_widget_set_sensitive(oo->automaticbox, FALSE);
+	gtk_widget_set_sensitive(oo->onlyprivatebox, FALSE);
+    }
+}
+
+static void create_otroption_buttons(struct otroptionsdata *oo,
+	GtkWidget *vbox)
+{
+    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");
+
+    gtk_box_pack_start(GTK_BOX(vbox), oo->enablebox,
+	    FALSE, FALSE, 0);
+    tempbox1 = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), tempbox1,
+	    FALSE, FALSE, 0);
+    tempbox2 = gtk_vbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(tempbox1), tempbox2, FALSE, FALSE, 5);
+
+    gtk_box_pack_start(GTK_BOX(tempbox2), oo->automaticbox,
+	    FALSE, FALSE, 0);
+    tempbox1 = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(tempbox2), tempbox1, FALSE, FALSE, 0);
+    tempbox2 = gtk_vbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(tempbox1), tempbox2, FALSE, FALSE, 5);
+
+    gtk_box_pack_start(GTK_BOX(tempbox2), oo->onlyprivatebox,
+	    FALSE, FALSE, 0);
+
+    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);
+}
+
+/* Load the global OTR prefs */
+static void otrg_gtk_ui_global_prefs_load(gboolean *enabledp,
+	gboolean *automaticp, gboolean *onlyprivatep)
+{
+    if (gaim_prefs_exists("/OTR/enabled")) {
+	*enabledp = gaim_prefs_get_bool("/OTR/enabled");
+	*automaticp = gaim_prefs_get_bool("/OTR/automatic");
+	*onlyprivatep = gaim_prefs_get_bool("/OTR/onlyprivate");
+    } else {
+	*enabledp = TRUE;
+	*automaticp = TRUE;
+	*onlyprivatep = FALSE;
+    }
+}
+
+/* Save the global OTR prefs */
+static void otrg_gtk_ui_global_prefs_save(gboolean enabled,
+	gboolean automatic, gboolean onlyprivate)
+{
+    if (! gaim_prefs_exists("/OTR")) {
+	gaim_prefs_add_none("/OTR");
+    }
+    gaim_prefs_set_bool("/OTR/enabled", enabled);
+    gaim_prefs_set_bool("/OTR/automatic", automatic);
+    gaim_prefs_set_bool("/OTR/onlyprivate", onlyprivate);
+}
+
+/* Load the OTR prefs for a particular buddy */
+static void otrg_gtk_ui_buddy_prefs_load(GaimBuddy *buddy,
+	gboolean *usedefaultp, gboolean *enabledp, gboolean *automaticp,
+	gboolean *onlyprivatep)
+{
+    GaimBlistNode *node = &(buddy->node);
+
+    *usedefaultp = ! gaim_blist_node_get_bool(node, "OTR/overridedefault");
+
+    if (*usedefaultp) {
+	otrg_gtk_ui_global_prefs_load(enabledp, automaticp, onlyprivatep);
+    } else {
+	*enabledp = gaim_blist_node_get_bool(node, "OTR/enabled");
+	*automaticp = gaim_blist_node_get_bool(node, "OTR/automatic");
+	*onlyprivatep = gaim_blist_node_get_bool(node, "OTR/onlyprivate");
+    }
+}
+
+/* Save the OTR prefs for a particular buddy */
+static void otrg_gtk_ui_buddy_prefs_save(GaimBuddy *buddy,
+	gboolean usedefault, gboolean enabled, gboolean automatic,
+	gboolean onlyprivate)
+{
+    GaimBlistNode *node = &(buddy->node);
+
+    gaim_blist_node_set_bool(node, "OTR/overridedefault", !usedefault);
+    gaim_blist_node_set_bool(node, "OTR/enabled", enabled);
+    gaim_blist_node_set_bool(node, "OTR/automatic", automatic);
+    gaim_blist_node_set_bool(node, "OTR/onlyprivate", onlyprivate);
+}
+
+static void load_otroptions(struct otroptionsdata *oo)
+{
+    gboolean otrenabled;
+    gboolean otrautomatic;
+    gboolean otronlyprivate;
+
+    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate);
+
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->enablebox),
+	    otrenabled);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->automaticbox),
+	    otrautomatic);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oo->onlyprivatebox),
+	    otronlyprivate);
+
+    otroptions_clicked_cb(GTK_BUTTON(oo->enablebox), oo);
+}
+
+/* Create the privkeys UI, and pack it into the vbox */
+static void make_privkeys_ui(GtkWidget *vbox)
+{
+    GtkWidget *fbox;
+    GtkWidget *hbox;
+    GtkWidget *label;
+    GtkWidget *frame;
+
+    frame = gtk_frame_new("My private keys");
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    fbox = gtk_vbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(fbox), 10);
+    gtk_container_add(GTK_CONTAINER(frame), fbox);
+
+    hbox = gtk_hbox_new(FALSE, 5);
+    gtk_box_pack_start(GTK_BOX(fbox), hbox, FALSE, FALSE, 0);
+    label = gtk_label_new("Key for account:");
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+    ui_layout.accountmenu = gaim_gtk_account_option_menu_new(NULL, 1,
+	    G_CALLBACK(account_menu_changed_cb), NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), ui_layout.accountmenu, TRUE, TRUE, 0);
+
+    /* Make sure we notice if the menu changes because an account has
+     * been added or removed */
+    gaim_signal_connect(gaim_accounts_get_handle(), "account-added",
+	    ui_layout.accountmenu,
+	    GAIM_CALLBACK(account_menu_added_removed_cb), NULL);
+    gaim_signal_connect(gaim_accounts_get_handle(), "account-removed",
+	    ui_layout.accountmenu,
+	    GAIM_CALLBACK(account_menu_added_removed_cb), NULL);
+
+    ui_layout.fprint_label = gtk_label_new("");
+    gtk_label_set_selectable(GTK_LABEL(ui_layout.fprint_label), 1);
+    gtk_box_pack_start(GTK_BOX(fbox), ui_layout.fprint_label,
+	    FALSE, FALSE, 0);
+
+    ui_layout.generate_button = gtk_button_new();
+    gtk_signal_connect(GTK_OBJECT(ui_layout.generate_button), "clicked",
+	    GTK_SIGNAL_FUNC(generate), NULL);
+
+    label = gtk_label_new("Generate");
+    gtk_container_add(GTK_CONTAINER(ui_layout.generate_button), label);
+
+    otrg_gtk_ui_update_fingerprint();
+
+    gtk_box_pack_start(GTK_BOX(fbox), ui_layout.generate_button,
+	    FALSE, FALSE, 0);
+}
+
+/* Save the global OTR options whenever they're clicked */
+static void otroptions_save_cb(GtkButton *button, struct otroptionsdata *oo)
+{
+    otrg_gtk_ui_global_prefs_save(
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(oo->enablebox)),
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(oo->automaticbox)),
+	    gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(oo->onlyprivatebox)));
+
+    otrg_dialog_resensitize_all();
+}
+
+/* Make the options UI, and pack it into the vbox */
+static void make_options_ui(GtkWidget *vbox)
+{
+    GtkWidget *fbox;
+    GtkWidget *frame;
+
+    frame = gtk_frame_new("Default OTR Settings");
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    fbox = gtk_vbox_new(FALSE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(fbox), 10);
+    gtk_container_add(GTK_CONTAINER(frame), fbox);
+
+    create_otroption_buttons(&(ui_layout.oo), fbox);
+
+    load_otroptions(&(ui_layout.oo));
+
+    g_signal_connect(G_OBJECT(ui_layout.oo.enablebox), "clicked",
+		     G_CALLBACK(otroptions_save_cb), &(ui_layout.oo));
+    g_signal_connect(G_OBJECT(ui_layout.oo.automaticbox), "clicked",
+		     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));
+}
+
+/* Create the fingerprint UI, and pack it into the vbox */
+static void make_fingerprints_ui(GtkWidget *vbox)
+{
+    GtkWidget *hbox;
+    GtkWidget *label;
+    char *titles[4] = { "Screenname", "Status", "Fingerprint", "Account" };
+
+    ui_layout.scrollwin = gtk_scrolled_window_new(0, 0);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui_layout.scrollwin), 
+            GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+
+    ui_layout.keylist = gtk_clist_new_with_titles(4, titles);
+    gtk_clist_set_column_width(GTK_CLIST(ui_layout.keylist), 0, 90);
+    gtk_clist_set_column_width(GTK_CLIST(ui_layout.keylist), 1, 90);
+    gtk_clist_set_column_width(GTK_CLIST(ui_layout.keylist), 2, 400);
+    gtk_clist_set_column_width(GTK_CLIST(ui_layout.keylist), 3, 200);
+    gtk_clist_set_selection_mode(GTK_CLIST(ui_layout.keylist),
+	    GTK_SELECTION_SINGLE);
+    gtk_clist_column_titles_active(GTK_CLIST(ui_layout.keylist));
+
+    gtk_container_add(GTK_CONTAINER(ui_layout.scrollwin), ui_layout.keylist);
+    gtk_box_pack_start(GTK_BOX(vbox), ui_layout.scrollwin,
+	    TRUE, TRUE, 0);
+
+    otrg_gtk_ui_update_keylist();
+
+    hbox = gtk_hbox_new(FALSE, 5);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+
+    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
+
+    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");
+    gtk_container_add(GTK_CONTAINER(ui_layout.connect_button), label);
+    gtk_box_pack_start(GTK_BOX(hbox), ui_layout.connect_button,
+	    FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
+
+    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");
+    gtk_container_add(GTK_CONTAINER(ui_layout.disconnect_button), label);
+    gtk_box_pack_start(GTK_BOX(hbox), ui_layout.disconnect_button,
+	    FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
+
+    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");
+    gtk_container_add(GTK_CONTAINER(ui_layout.forget_button), label);
+    gtk_box_pack_start(GTK_BOX(hbox), ui_layout.forget_button,
+	    FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
+
+    gtk_signal_connect(GTK_OBJECT(vbox), "destroy",
+	    GTK_SIGNAL_FUNC(ui_destroyed), NULL);
+
+    /* Handle selections and deselections */
+    gtk_signal_connect(GTK_OBJECT(ui_layout.keylist), "select_row",
+	    GTK_SIGNAL_FUNC(clist_selected), NULL);
+    gtk_signal_connect(GTK_OBJECT(ui_layout.keylist), "unselect_row",
+	    GTK_SIGNAL_FUNC(clist_unselected), NULL);
+
+    /* Handle column sorting */
+    gtk_signal_connect(GTK_OBJECT(ui_layout.keylist), "click-column",
+	    GTK_SIGNAL_FUNC(clist_click_column), NULL);
+    ui_layout.sortcol = 0;
+    ui_layout.sortdir = 1;
+
+    gtk_widget_set_sensitive(ui_layout.connect_button, 0);
+    gtk_widget_set_sensitive(ui_layout.disconnect_button, 0);
+    gtk_widget_set_sensitive(ui_layout.forget_button, 0);
+    ui_layout.selected_fprint = NULL;
+}
+
+/* Construct the OTR UI widget */
+GtkWidget* otrg_gtk_ui_make_widget(GaimPlugin *plugin)
+{
+    GtkWidget *vbox = gtk_vbox_new(FALSE, 5);
+    GtkWidget *fingerprintbox = gtk_vbox_new(FALSE, 5);
+    GtkWidget *configbox = gtk_vbox_new(FALSE, 5);
+    GtkWidget *notebook = gtk_notebook_new();
+
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+    gtk_container_set_border_width(GTK_CONTAINER(fingerprintbox), 5);
+    gtk_container_set_border_width(GTK_CONTAINER(configbox), 5);
+
+    gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+    make_privkeys_ui(configbox);
+
+    make_options_ui(configbox);
+
+    /*
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
+    gtk_container_add(GTK_CONTAINER(confwindow), vbox);
+    */
+
+    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_widget_show_all(vbox);
+
+    return vbox;
+}
+
+struct cbdata {
+    GtkWidget *dialog;
+    GaimBuddy *buddy;
+    GtkWidget *defaultbox;
+    struct otroptionsdata oo;
+};
+
+static void default_clicked_cb(GtkButton *button, struct cbdata *data)
+{
+    gboolean defaultset =
+	gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->defaultbox));
+    if (defaultset) {
+	gtk_widget_set_sensitive(data->oo.enablebox, FALSE);
+	gtk_widget_set_sensitive(data->oo.automaticbox, FALSE);
+	gtk_widget_set_sensitive(data->oo.onlyprivatebox, FALSE);
+    } else {
+	otroptions_clicked_cb(button, &(data->oo));
+    }
+}
+
+static void load_buddyprefs(struct cbdata *data)
+{
+    gboolean usedefault, enabled, automatic, onlyprivate;
+
+    otrg_gtk_ui_buddy_prefs_load(data->buddy, &usedefault, &enabled,
+	    &automatic, &onlyprivate);
+
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->defaultbox),
+	    usedefault);
+
+    if (usedefault) {
+	/* Load the global defaults */
+	load_otroptions(&(data->oo));
+    } else {
+	/* We've got buddy-specific prefs */
+	gtk_toggle_button_set_active(
+		GTK_TOGGLE_BUTTON(data->oo.enablebox), enabled);
+	gtk_toggle_button_set_active(
+		GTK_TOGGLE_BUTTON(data->oo.automaticbox), automatic);
+	gtk_toggle_button_set_active(
+		GTK_TOGGLE_BUTTON(data->oo.onlyprivatebox), onlyprivate);
+    }
+
+    default_clicked_cb(GTK_BUTTON(data->defaultbox), data);
+}
+
+static void config_buddy_destroy_cb(GtkWidget *w, struct cbdata *data)
+{
+    free(data);
+}
+
+static void config_buddy_clicked_cb(GtkButton *button, struct cbdata *data)
+{
+    gboolean enabled = gtk_toggle_button_get_active(
+			     GTK_TOGGLE_BUTTON(data->oo.enablebox));
+    
+    /* Apply the changes */
+    otrg_gtk_ui_buddy_prefs_save(data->buddy,
+	 gtk_toggle_button_get_active(
+	     GTK_TOGGLE_BUTTON(data->defaultbox)),
+	 enabled,
+	 gtk_toggle_button_get_active(
+	     GTK_TOGGLE_BUTTON(data->oo.automaticbox)),
+	 gtk_toggle_button_get_active(
+	     GTK_TOGGLE_BUTTON(data->oo.onlyprivatebox)));
+
+    otrg_dialog_resensitize_all();
+}
+
+static void config_buddy_response_cb(GtkDialog *dialog, gint resp,
+	struct cbdata *data)
+{
+    gtk_widget_destroy(data->dialog);
+}
+
+static void otrg_gtk_ui_config_buddy(GaimBuddy *buddy)
+{
+    GtkWidget *dialog;
+    GtkWidget *label;
+    char *label_text;
+    struct cbdata *data = malloc(sizeof(struct cbdata));
+
+    if (!data) return;
+
+    dialog = gtk_dialog_new_with_buttons("OTR Settings",
+					 NULL, 0,
+					 GTK_STOCK_OK, GTK_RESPONSE_OK,
+					 NULL);
+    gtk_window_set_accept_focus(GTK_WINDOW(dialog), FALSE);
+    gtk_window_set_role(GTK_WINDOW(dialog), "otr_options");
+
+    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), 0);
+    gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 0);
+
+    data->dialog = dialog;
+    data->buddy = buddy;
+
+    /* Set the title */
+
+    label_text = g_strdup_printf("<span weight=\"bold\" size=\"larger\">"
+	    "OTR Settings for %s</span>", gaim_buddy_get_contact_alias(buddy));
+
+    label = gtk_label_new(NULL);
+
+    gtk_label_set_markup(GTK_LABEL(label), label_text);
+    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(GTK_DIALOG(dialog)->vbox), label,
+	    FALSE, FALSE, 5);
+
+    /* Make the cascaded checkboxes */
+
+    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);
+
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), gtk_hseparator_new(),
+	    FALSE, FALSE, 5);
+
+    create_otroption_buttons(&(data->oo), GTK_DIALOG(dialog)->vbox);
+
+    g_signal_connect(G_OBJECT(data->defaultbox), "clicked",
+		     G_CALLBACK(default_clicked_cb), data);
+    g_signal_connect(G_OBJECT(data->defaultbox), "clicked",
+		     G_CALLBACK(config_buddy_clicked_cb), data);
+    g_signal_connect(G_OBJECT(data->oo.enablebox), "clicked",
+		     G_CALLBACK(config_buddy_clicked_cb), data);
+    g_signal_connect(G_OBJECT(data->oo.automaticbox), "clicked",
+		     G_CALLBACK(config_buddy_clicked_cb), data);
+    g_signal_connect(G_OBJECT(data->oo.onlyprivatebox), "clicked",
+		     G_CALLBACK(config_buddy_clicked_cb), data);
+
+    /* Set the inital states of the buttons */
+    load_buddyprefs(data);
+
+    g_signal_connect(G_OBJECT(dialog), "destroy",
+		     G_CALLBACK(config_buddy_destroy_cb), data);
+    g_signal_connect(G_OBJECT(dialog), "response",
+		     G_CALLBACK(config_buddy_response_cb), data);
+
+    gtk_widget_show_all(dialog);
+}
+
+/* Calculate the policy for a particular account / username */
+static OtrlPolicy otrg_gtk_ui_find_policy(GaimAccount *account,
+	const char *name)
+{
+    GaimBuddy *buddy;
+    gboolean otrenabled, otrautomatic, otronlyprivate;
+    gboolean buddyusedefault, buddyenabled, buddyautomatic, buddyonlyprivate;
+    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+    
+    /* Get the default policy */
+    otrg_gtk_ui_global_prefs_load(&otrenabled, &otrautomatic, &otronlyprivate);
+
+    if (otrenabled) {
+	if (otrautomatic) {
+	    if (otronlyprivate) {
+		policy = OTRL_POLICY_ALWAYS;
+	    } else {
+		policy = OTRL_POLICY_OPPORTUNISTIC;
+	    }
+	} else {
+	    policy = OTRL_POLICY_MANUAL;
+	}
+    } else {
+	policy = OTRL_POLICY_NEVER;
+    }
+
+    buddy = gaim_find_buddy(account, name);
+    if (!buddy) return policy;
+
+    /* Get the buddy-specific policy, if present */
+    otrg_gtk_ui_buddy_prefs_load(buddy, &buddyusedefault, &buddyenabled,
+	    &buddyautomatic, &buddyonlyprivate);
+
+    if (buddyusedefault) return policy;
+
+    if (buddyenabled) {
+	if (buddyautomatic) {
+	    if (buddyonlyprivate) {
+		policy = OTRL_POLICY_ALWAYS;
+	    } else {
+		policy = OTRL_POLICY_OPPORTUNISTIC;
+	    }
+	} else {
+	    policy = OTRL_POLICY_MANUAL;
+	}
+    } else {
+	policy = OTRL_POLICY_NEVER;
+    }
+
+    return policy;
+}
+
+static const OtrgUiUiOps gtk_ui_ui_ops = {
+    otrg_gtk_ui_update_fingerprint,
+    otrg_gtk_ui_update_keylist,
+    otrg_gtk_ui_config_buddy,
+    otrg_gtk_ui_find_policy
+};
+
+/* Get the GTK UI ops */
+const OtrgUiUiOps *otrg_gtk_ui_get_ui_ops(void)
+{
+    return &gtk_ui_ui_ops;
+}
diff --git a/gtk-ui.h b/gtk-ui.h
new file mode 100644
index 0000000..c2c3654
--- /dev/null
+++ b/gtk-ui.h
@@ -0,0 +1,29 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __OTRG_GTK_UI_H__
+#define __OTRG_GTK_UI_H__
+
+/* Construct the GTK OTR UI widget */
+GtkWidget* otrg_gtk_ui_make_widget(GaimPlugin *plugin);
+
+/* Get the GTK UI ops */
+const OtrgUiUiOps *otrg_gtk_ui_get_ui_ops(void);
+
+#endif
diff --git a/makedist b/makedist
new file mode 100644
index 0000000..c10610b
--- /dev/null
+++ b/makedist
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Make the distribution tar.gz file from the CVS exported version
+
+autoreconf -s -i
+./configure --mandir=/usr/share/man --prefix=/usr
+fakeroot make dist
diff --git a/otr-plugin.c b/otr-plugin.c
new file mode 100644
index 0000000..8f0067b
--- /dev/null
+++ b/otr-plugin.c
@@ -0,0 +1,581 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  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>
+#include <string.h>
+#include <assert.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* gaim headers */
+#include "gaim.h"
+#include "notify.h"
+#include "version.h"
+#include "util.h"
+#include "debug.h"
+
+#ifdef USING_GTK
+/* gaim GTK headers */
+#include "gtkplugin.h"
+#endif
+
+/* libotr headers */
+#include <libotr/privkey.h>
+#include <libotr/proto.h>
+#include <libotr/message.h>
+#include <libotr/userstate.h>
+
+/* gaim-otr headers */
+#include "ui.h"
+#include "dialogs.h"
+#include "otr-plugin.h"
+
+#ifdef USING_GTK
+/* gaim-otr GTK headers */
+#include "gtk-ui.h"
+#include "gtk-dialog.h"
+#endif
+
+GaimPlugin *otrg_plugin_handle;
+
+/* We'll only use the one OtrlUserState. */
+OtrlUserState otrg_plugin_userstate = NULL;
+
+/* 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(GaimAccount *account, const char *recipient,
+	const char *message)
+{
+    GaimConnection *connection;
+
+    connection = gaim_account_get_connection(account);
+    if (!connection) {
+	const char *protocol = gaim_account_get_protocol_id(account);
+	const char *accountname = gaim_account_get_username(account);
+	GaimPlugin *p = gaim_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");
+	otrg_dialog_notify_error(accountname, protocol, recipient,
+		"Not connected", msg, NULL);
+	g_free(msg);
+	return;
+    }
+    serv_send_im(connection, recipient, message, 0);
+}
+
+static OtrlPolicy policy_cb(void *opdata, ConnContext *context)
+{
+    GaimAccount *account;
+    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
+
+    if (!context) return policy;
+
+    account = gaim_accounts_find(context->accountname, context->protocol);
+    if (!account) return policy;
+
+    return otrg_ui_find_policy(account, context->username);
+}
+
+static const char *protocol_name_cb(void *opdata, const char *protocol)
+{
+    GaimPlugin *p = gaim_find_prpl(protocol);
+    if (!p) return NULL;
+    return p->info->name;
+}
+
+static void protocol_name_free_cb(void *opdata, const char *protocol_name)
+{
+    /* Do nothing, since we didn't actually allocate any memory in
+     * protocol_name_cb. */
+}
+
+/* Generate a private key for the given accountname/protocol */
+void otrg_plugin_create_privkey(const char *accountname,
+	const char *protocol)
+{
+    OtrgDialogWaitHandle waithandle;
+
+    gchar *privkeyfile = g_build_filename(gaim_user_dir(), PRIVKEYFNAME, NULL);
+    if (!privkeyfile) {
+	fprintf(stderr, "Out of memory building filenames!\n");
+	return;
+    }
+
+    waithandle = otrg_dialog_private_key_wait_start(accountname, protocol);
+
+    /* Generate the key */
+    otrl_privkey_generate(otrg_plugin_userstate, privkeyfile,
+	    accountname, protocol);
+    g_free(privkeyfile);
+    otrg_ui_update_fingerprint();
+
+    /* Mark the dialog as done. */
+    otrg_dialog_private_key_wait_done(waithandle);
+}
+
+static void create_privkey_cb(void *opdata, const char *accountname,
+	const char *protocol)
+{
+    otrg_plugin_create_privkey(accountname, protocol);
+}
+
+static int is_logged_in_cb(void *opdata, const char *accountname,
+	const char *protocol, const char *recipient)
+{
+    GaimAccount *account;
+    GaimBuddy *buddy;
+
+    account = gaim_accounts_find(accountname, protocol);
+    if (!account) return -1;
+
+    buddy = gaim_find_buddy(account, recipient);
+    if (!buddy) return -1;
+
+    return (buddy->present == GAIM_BUDDY_ONLINE);
+}
+
+static void inject_message_cb(void *opdata, const char *accountname,
+	const char *protocol, const char *recipient, const char *message)
+{
+    GaimAccount *account = gaim_accounts_find(accountname, protocol);
+    if (!account) {
+	GaimPlugin *p = gaim_find_prpl(protocol);
+	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);
+	g_free(msg);
+	return;
+    }
+    otrg_plugin_inject_message(account, recipient, message);
+}
+
+static void notify_cb(void *opdata, OtrlNotifyLevel level,
+	const char *accountname, const char *protocol, const char *username,
+	const char *title, const char *primary, const char *secondary)
+{
+    GaimNotifyMsgType gaimlevel;
+
+    switch (level) {
+	case OTRL_NOTIFY_ERROR:
+	    gaimlevel = GAIM_NOTIFY_MSG_ERROR;
+	    break;
+	case OTRL_NOTIFY_WARNING:
+	    gaimlevel = GAIM_NOTIFY_MSG_WARNING;
+	    break;
+	case OTRL_NOTIFY_INFO:
+	    gaimlevel = GAIM_NOTIFY_MSG_INFO;
+	    break;
+    }
+
+    otrg_dialog_notify_message(gaimlevel, accountname, protocol,
+	    username, title, primary, secondary);
+}
+
+static int display_otr_message_cb(void *opdata, const char *accountname,
+	const char *protocol, const char *username, const char *msg)
+{
+    return otrg_dialog_display_otr_message(accountname, protocol,
+	    username, msg);
+}
+
+static void update_context_list_cb(void *opdata)
+{
+    otrg_ui_update_keylist();
+}
+
+/* Forward declaration because we need to use &ui_ops in this function */
+static void confirm_fingerprint_cb(OtrlUserState us, void *opdata,
+	const char *accountname, const char *protocol, const char *username,
+	OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OTRConfirmResponse *response_data);
+
+static void write_fingerprints_cb(void *opdata)
+{
+    gchar *storefile = g_build_filename(gaim_user_dir(), STOREFNAME, NULL);
+    otrl_privkey_write_fingerprints(otrg_plugin_userstate, storefile);
+    g_free(storefile);
+}
+
+static void gone_secure_cb(void *opdata, ConnContext *context)
+{
+    otrg_dialog_connected(context);
+}
+
+static void gone_insecure_cb(void *opdata, ConnContext *context)
+{
+    otrg_dialog_disconnected(context);
+}
+
+static void still_secure_cb(void *opdata, ConnContext *context, int is_reply)
+{
+    if (is_reply == 0) {
+	otrg_dialog_stillconnected(context);
+    }
+}
+
+static void log_message_cb(void *opdata, const char *message)
+{
+    gaim_debug_info("otr", message);
+}
+
+static OtrlMessageAppOps ui_ops = {
+    policy_cb,
+    create_privkey_cb,
+    is_logged_in_cb,
+    inject_message_cb,
+    notify_cb,
+    display_otr_message_cb,
+    update_context_list_cb,
+    protocol_name_cb,
+    protocol_name_free_cb,
+    confirm_fingerprint_cb,
+    write_fingerprints_cb,
+    gone_secure_cb,
+    gone_insecure_cb,
+    still_secure_cb,
+    log_message_cb
+};
+
+static void confirm_fingerprint_cb(OtrlUserState us, void *opdata,
+	const char *accountname, const char *protocol, const char *username,
+	OTRKeyExchangeMsg kem,
+	void (*response_cb)(OtrlUserState us, OtrlMessageAppOps *ops,
+	    void *opdata, OTRConfirmResponse *response_data, int resp),
+	OTRConfirmResponse *response_data)
+{
+    otrg_dialog_unknown_fingerprint(us, accountname, protocol, username, kem,
+	    response_cb, &ui_ops, opdata, response_data);
+}
+
+static void process_sending_im(GaimAccount *account, char *who, char **message,
+	void *m)
+{
+    char *newmessage = NULL;
+    const char *accountname = gaim_account_get_username(account);
+    const char *protocol = gaim_account_get_protocol_id(account);
+    char *username;
+    gcry_error_t err;
+
+    if (!who || !message || !*message)
+	return;
+
+    username = strdup(gaim_normalize(account, who));
+
+    err = otrl_message_sending(otrg_plugin_userstate, &ui_ops, NULL,
+	    accountname, protocol, username, *message, NULL, &newmessage,
+	    NULL, NULL);
+
+    if (err && newmessage == NULL) {
+	/* Be *sure* not to send out plaintext */
+	char *ourm = strdup("");
+	free(*message);
+	*message = ourm;
+    } else if (newmessage) {
+	char *ourm = malloc(strlen(newmessage) + 1);
+	if (ourm) {
+	    strcpy(ourm, newmessage);
+	}
+	otrl_message_free(newmessage);
+	free(*message);
+	*message = ourm;
+    }
+    free(username);
+}
+
+/* Send the default OTR Query message to the correspondent of the given
+ * context, from the given account.  [account is actually a
+ * GaimAccount*, but it's declared here as void* so this can be passed
+ * as a callback.] */
+void otrg_plugin_send_default_query(ConnContext *context, void *account)
+{
+    char *msg = otrl_proto_default_query_msg(context->accountname);
+    otrg_plugin_inject_message((GaimAccount *)account, context->username,
+	    msg ? msg : "?OTR?");
+    free(msg);
+}
+
+/* Send the default OTR Query message to the correspondent of the given
+ * conversation. */
+void otrg_plugin_send_default_query_conv(GaimConversation *conv)
+{
+    GaimAccount *account;
+    const char *username, *accountname;
+    char *msg;
+    
+    account = gaim_conversation_get_account(conv);
+    accountname = gaim_account_get_username(account);
+    username = gaim_conversation_get_name(conv);
+    
+    msg = otrl_proto_default_query_msg(accountname);
+    otrg_plugin_inject_message(account, username, msg ? msg : "?OTR?");
+    free(msg);
+}
+
+static gboolean process_receiving_im(GaimAccount *account, char **who, 
+        char **message, int *flags, void *m)
+{
+    char *newmessage = NULL;
+    OtrlTLV *tlvs = NULL;
+    OtrlTLV *tlv = NULL;
+    char *username;
+    gboolean res;
+    const char *accountname;
+    const char *protocol;
+
+    if (!who || !*who || !message || !*message)
+        return 0;
+
+    username = strdup(gaim_normalize(account, *who));
+    accountname = gaim_account_get_username(account);
+    protocol = gaim_account_get_protocol_id(account);
+
+    res = otrl_message_receiving(otrg_plugin_userstate, &ui_ops, NULL,
+	    accountname, protocol, username, *message,
+	    &newmessage, &tlvs, NULL, NULL);
+
+    if (newmessage) {
+	char *ourm = malloc(strlen(newmessage) + 1);
+	if (ourm) {
+	    strcpy(ourm, newmessage);
+	}
+	otrl_message_free(newmessage);
+	free(*message);
+	*message = ourm;
+    }
+
+    tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+    if (tlv) {
+	/* Notify the user that the other side disconnected. */
+
+	char *msg = g_strdup_printf("OTR: %s has closed his private "
+		"connection to you; you should do the same.", username);
+
+	if (msg) {
+	    otrg_dialog_display_otr_message(accountname, protocol,
+		    username, msg);
+	    g_free(msg);
+	}
+    }
+    
+    otrl_tlv_free(tlvs);
+
+    free(username);
+
+    /* If we're supposed to ignore this incoming message (because it's a
+     * protocol message), set it to NULL, so that other plugins that
+     * catch receiving-im-msg don't return 0, and cause it to be
+     * displayed anyway. */
+    if (res) {
+	free(*message);
+	*message = NULL;
+    }
+    return res;
+}
+
+static void process_conv_create(GaimConversation *conv, void *data)
+{
+    if (conv) otrg_dialog_new_conv(conv);
+}
+
+static void process_connection_change(GaimConnection *conn, void *data)
+{
+    /* If we log in or out of a connection, make sure all of the OTR
+     * buttons are in the appropriate sensitive/insensitive state. */
+    otrg_dialog_resensitize_all();
+}
+
+static void otr_options_cb(GaimBlistNode *node, gpointer user_data)
+{
+    /* We've already checked GAIM_BLIST_NODE_IS_BUDDY(node) */
+    GaimBuddy *buddy = (GaimBuddy *)node;
+
+    /* Modify the settings for this buddy */
+    otrg_ui_config_buddy(buddy);
+}
+
+static void supply_extended_menu(GaimBlistNode *node, GList **menu)
+{
+    GaimBlistNodeAction *act;
+
+    if (!GAIM_BLIST_NODE_IS_BUDDY(node)) return;
+
+     act = gaim_blist_node_action_new("OTR Settings", otr_options_cb, NULL);
+     *menu = g_list_append(*menu, act);
+}
+
+/* Disconnect a context, sending a notice to the other side, if
+ * appropriate. */
+void otrg_plugin_disconnect(ConnContext *context)
+{
+    otrl_message_disconnect(otrg_plugin_userstate, &ui_ops, NULL,
+	    context->accountname, context->protocol, context->username);
+}
+
+static gboolean otr_plugin_load(GaimPlugin *handle)
+{
+    gchar *privkeyfile = g_build_filename(gaim_user_dir(), PRIVKEYFNAME, NULL);
+    gchar *storefile = g_build_filename(gaim_user_dir(), STOREFNAME, NULL);
+    void *conv_handle = gaim_conversations_get_handle();
+    void *conn_handle = gaim_connections_get_handle();
+    void *blist_handle = gaim_blist_get_handle();
+
+    if (!privkeyfile || !storefile) {
+	g_free(privkeyfile);
+	g_free(storefile);
+	return 0;
+    }
+
+    otrg_plugin_handle = handle;
+
+    /* Make our OtrlUserState; we'll only use the one. */
+    otrg_plugin_userstate = otrl_userstate_create();
+
+    otrl_privkey_read(otrg_plugin_userstate, privkeyfile);
+    g_free(privkeyfile);
+    otrl_privkey_read_fingerprints(otrg_plugin_userstate, storefile,
+	    NULL, NULL);
+    g_free(storefile);
+
+    otrg_ui_update_fingerprint();
+
+    gaim_signal_connect(conv_handle, "sending-im-msg", otrg_plugin_handle,
+            GAIM_CALLBACK(process_sending_im), NULL);
+    gaim_signal_connect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
+            GAIM_CALLBACK(process_receiving_im), NULL);
+    gaim_signal_connect(conv_handle, "conversation-created",
+	    otrg_plugin_handle, GAIM_CALLBACK(process_conv_create), NULL);
+    gaim_signal_connect(conn_handle, "signed-on", otrg_plugin_handle,
+	    GAIM_CALLBACK(process_connection_change), NULL);
+    gaim_signal_connect(conn_handle, "signed-off", otrg_plugin_handle,
+	    GAIM_CALLBACK(process_connection_change), NULL);
+    gaim_signal_connect(blist_handle, "blist-node-extended-menu",
+	    otrg_plugin_handle, GAIM_CALLBACK(supply_extended_menu), NULL);
+
+    gaim_conversation_foreach(otrg_dialog_new_conv);
+
+    return 1;
+}
+
+static gboolean otr_plugin_unload(GaimPlugin *handle)
+{
+    void *conv_handle = gaim_conversations_get_handle();
+    void *conn_handle = gaim_connections_get_handle();
+    void *blist_handle = gaim_blist_get_handle();
+
+    /* Clean up all of our state. */
+    otrl_userstate_free(otrg_plugin_userstate);
+    otrg_plugin_userstate = NULL;
+
+    gaim_signal_disconnect(conv_handle, "sending-im-msg", otrg_plugin_handle,
+            GAIM_CALLBACK(process_sending_im));
+    gaim_signal_disconnect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
+            GAIM_CALLBACK(process_receiving_im));
+    gaim_signal_disconnect(conv_handle, "conversation-created",
+	    otrg_plugin_handle, GAIM_CALLBACK(process_conv_create));
+    gaim_signal_disconnect(conn_handle, "signed-on", otrg_plugin_handle,
+	    GAIM_CALLBACK(process_connection_change));
+    gaim_signal_disconnect(conn_handle, "signed-off", otrg_plugin_handle,
+	    GAIM_CALLBACK(process_connection_change));
+    gaim_signal_disconnect(blist_handle, "blist-node-extended-menu",
+	    otrg_plugin_handle, GAIM_CALLBACK(supply_extended_menu));
+
+    gaim_conversation_foreach(otrg_dialog_remove_conv);
+
+    return 1;
+}
+
+#ifdef USING_GTK
+
+static GaimGtkPluginUiInfo ui_info =
+{
+	otrg_gtk_ui_make_widget
+};
+
+#define UI_INFO &ui_info
+#define PLUGIN_TYPE GAIM_GTK_PLUGIN_TYPE
+
+#else
+
+#define UI_INFO NULL
+#define PLUGIN_TYPE ""
+
+#endif
+
+static GaimPluginInfo info =
+{
+	GAIM_PLUGIN_MAGIC,
+
+	/* We stick with the functions in the gaim 1.0.x API for
+	 * compatibility. */
+	1,                                                /* major version  */
+	0,                                                /* minor version  */
+
+	GAIM_PLUGIN_STANDARD,                             /* type           */
+	PLUGIN_TYPE,                                      /* ui_requirement */
+	0,                                                /* flags          */
+	NULL,                                             /* dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /* priority       */
+	"otr",                                            /* id             */
+	"Off-the-Record Messaging",                       /* name           */
+	GAIM_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.",
+	                                                  /* author         */
+	"Nikita Borisov and Ian Goldberg\n\t\t\t<otr at cypherpunks.ca>",
+	"http://www.cypherpunks.ca/otr/",                 /* homepage       */
+
+	otr_plugin_load,                                 /* load           */
+	otr_plugin_unload,                               /* unload         */
+	NULL,                                             /* destroy        */
+
+	UI_INFO,                                          /* ui_info        */
+	NULL,                                             /* extra_info     */
+	NULL,                                             /* prefs_info     */
+	NULL                                              /* actions        */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+    /* Set up the UI ops */
+#ifdef USING_GTK
+    otrg_ui_set_ui_ops(otrg_gtk_ui_get_ui_ops());
+    otrg_dialog_set_ui_ops(otrg_gtk_dialog_get_ui_ops());
+#endif
+
+    /* Initialize the OTR library */
+    OTRL_INIT;
+}
+
+GAIM_INIT_PLUGIN(otr, __init_plugin, info)
diff --git a/otr-plugin.h b/otr-plugin.h
new file mode 100644
index 0000000..558bbff
--- /dev/null
+++ b/otr-plugin.h
@@ -0,0 +1,61 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __OTRG_OTR_PLUGIN_H__
+#define __OTRG_OTR_PLUGIN_H__
+
+/* Gaim headers */
+#include "account.h"
+#include "plugin.h"
+
+/* libotr headers */
+#include <libotr/context.h>
+#include <libotr/userstate.h>
+
+#define PRIVKEYFNAME "otr.private_key"
+#define STOREFNAME "otr.fingerprints"
+
+extern GaimPlugin *otrg_plugin_handle;
+
+extern OtrlUserState otrg_plugin_userstate;
+
+/* 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(GaimAccount *account, const char *recipient,
+	const char *message);
+
+/* Generate a private key for the given accountname/protocol */
+void otrg_plugin_create_privkey(const char *accountname,
+	const char *protocol);
+
+/* Send the default OTR Query message to the correspondent of the given
+ * context, from the given account.  [account is actually a
+ * GaimAccount*, but it's declared here as void* so this can be passed
+ * as a callback.] */
+void otrg_plugin_send_default_query(ConnContext *context, void *account);
+
+/* Send the default OTR Query message to the correspondent of the given
+ * conversation. */
+void otrg_plugin_send_default_query_conv(GaimConversation *conv);
+
+/* Disconnect a context, sending a notice to the other side, if
+ * appropriate. */
+void otrg_plugin_disconnect(ConnContext *context);
+
+#endif
diff --git a/packaging/fedora/gaim-otr.spec b/packaging/fedora/gaim-otr.spec
new file mode 100644
index 0000000..2b1c44d
--- /dev/null
+++ b/packaging/fedora/gaim-otr.spec
@@ -0,0 +1,104 @@
+Summary: Off-The-Record Messaging plugin for GAIM
+Name: gaim-otr
+%define majver 2
+%define minver 0.2
+Version: %{majver}.%{minver}
+%define debug_package %{nil}
+%define ourrelease 1
+Release: %{ourrelease}
+Source: http://www.cypherpunks.ca/otr/gaim-otr-%{majver}.%{minver}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-buildroot
+Url: http://www.cypherpunks.ca/otr/
+Vendor: Nikita Borisov and Ian Goldberg <otr at cypherpunks.ca>
+Packager: Paul Wouters <paul at cypherpunks.ca>
+License: GPL
+Group: Applications/Internet
+Provides: gaim-otr
+Obsoletes: otr-plugin
+BuildRequires: glib2-devel, gtk2-devel, libgcrypt-devel >= 1.2.0, libgpg-error-devel, gaim >= 1.0.0, libotr-devel >= 2.0.2
+Requires: gaim >= 1.0.0, libgcrypt >= 1.2.0, gtk2 >= 2.4, libotr >= 2.0.2
+%define __spec_install_post /usr/lib/rpm/brp-compress || :
+
+%description 
+
+This is a gaim 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).
+
+OTR allows you to have private conversations over IM by providing:
+ - Encryption
+   - No one else can read your instant messages.
+ - Authentication
+   - You are assured the correspondent is who you think it is.
+ - Deniability
+   - The messages you send do _not_ have digital signatures that are
+     checkable by a third party.  Anyone can forge messages after a
+     conversation to make them look like they came from you.  However,
+     _during_ a conversation, your correspondent is assured the messages
+     he sees are authentic and unmodified.
+ - Perfect forward secrecy
+   - If you lose control of your private keys, no previous conversation
+     is compromised.
+
+For more information on Off-the-Record Messaging, see
+http://www.cypherpunks.ca/otr/
+
+%prep
+%setup -q -n gaim-otr-%{majver}.%{minver}
+
+%build
+%configure --prefix=%{_prefix} --libdir=%{_libdir} --mandir=%{_mandir}
+%{__make} \
+	CFLAGS="${RPM_OPT_FLAGS}" \
+	all
+
+%install
+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
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%files
+%defattr(-,root,root)
+%attr(0755,root,root) %{_libdir}/gaim/gaim-otr.so
+%doc README COPYING 
+
+%changelog
+* Tue May  3 2005 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 2.0.2.
+* Wed Feb 23 2005 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 2.0.1.
+* Tue Feb  8 2005 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 2.0.0.
+* Wed Feb  2 2005 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 1.99.0.
+* Wed Jan 19 2005 Paul Wouters <paul at cypherpunks.ca>
+- Split spec file from libotr and added dependancies
+* Tue Dec 21 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 1.0.2.
+* Fri Dec 17 2004 Paul Wouters <paul at cypherpunks.ca>
+- instll fix for x86_64
+* Sun Dec 12 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 1.0.0.
+* Fri Dec 10 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.9rc2. 
+* Thu Dec  9 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Added CFLAGS to "make all", removed DESTDIR
+* Wed Dec  8 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.9rc1. 
+* Fri Dec  3 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped to version 0.9.1. 
+* Wed Dec  1 2004 Paul Wouters <paul at cypherpunks.ca>
+- Bumped to version 0.9.0. 
+- Fixed install for tools and cos
+- Added Obsoletes: target for otr-plugin so rpm-Uhv gaim-otr removes it.
+* Mon Nov 22 2004 Ian Goldberg <otr at cypherpunks.ca>
+- Bumped version to 0.8.1
+* Sun Nov 21 2004 Paul Wouters <paul at cypherpunks.ca>
+- Initial version
+
diff --git a/packaging/windows/gaim-otr.nsi b/packaging/windows/gaim-otr.nsi
new file mode 100644
index 0000000..fda604d
--- /dev/null
+++ b/packaging/windows/gaim-otr.nsi
@@ -0,0 +1,212 @@
+; 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.
+; 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 gaim-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 "2.0.2"
+!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 "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\gaim-otr"
+InstallDirRegKey HKEY_LOCAL_MACHINE SOFTWARE\Gaim-otr "Install_Dir"
+;WriteRegStr HKLM "SOFTWARE\gaim-otr" "gaimdir" ""
+
+Var "GaimDir"
+
+ShowInstDetails show
+ShowUnInstDetails show
+
+Section "MainSection" SEC01
+;InstallDir "$PROGRAMFILES\Gaim\plugins"
+
+; uninstall previous gaim-otr install if found.
+Call UnInstOld
+ ;Check for gaim installation
+Call GetGaimInstPath
+WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "SOFTWARE\gaim-otr" "gaimdir" "$GaimDir"
+
+	SetOutPath "$INSTDIR"
+  SetOverwrite on
+  File "gaim-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 "README.Toolkit.txt"
+	File "README.txt"
+	File "Protocol.txt"
+	File "COPYING.txt"
+	File "COPYING.LIB.txt"
+	File "otr_mackey.exe"
+	File "otr_modify.exe"
+	File "otr_parse.exe"
+	File "otr_readforge.exe"
+	File "otr_remac.exe"
+	File "otr_sesskeys.exe"
+	File "gaim-otr.nsi"
+SectionEnd
+
+Section -AdditionalIcons
+  CreateDirectory "$SMPROGRAMS\Gaim-otr"
+  CreateShortCut "$SMPROGRAMS\Gaim-otr\Uninstall.lnk" "$INSTDIR\gaim-otr-uninst.exe"
+SectionEnd
+
+Section -Post
+  WriteUninstaller "$INSTDIR\gaim-otr-uninst.exe"
+  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
+  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\gaim-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\gaim-otr-uninst.exe"
+	Delete "$INSTDIR\README.Toolkit.txt"
+	Delete "$INSTDIR\README.txt"
+	Delete "$INSTDIR\Protocol.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\gaim-otr.nsi"
+  Delete "$SMPROGRAMS\Gaim-otr\Uninstall.lnk"
+  RMDir "$SMPROGRAMS\Gaim-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
+  MessageBox MB_OK|MB_ICONINFORMATION "Could not find gaim plugin directory, gaim-otr.dll not uninstalled!" IDOK ok
+dodelete:
+	Delete "$GaimDir\plugins\gaim-otr.dll"
+	IfFileExists "$GaimDir\plugins\gaim-otr.dll" 0 +2
+		MessageBox MB_OK|MB_ICONINFORMATION "gaim-otr.dll is busy. Probably Gaim is still running. Please delete $GaimDir\plugins\gaim-otr.dll manually."
+  DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
+  DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "SOFTWARE\Gaim-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\gaim-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  "Gaim-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 GAIM-OTR found, continuing."
+		
+FunctionEnd
+
+Function CopyDLL
+SetOverwrite try
+ClearErrors
+; 3 hours wasted so you guys don't need a reboot!
+; Rename /REBOOTOK "$INSTDIR\gaim-otr.dll" "$GaimDir\plugins\gaim-otr.dll"
+IfFileExists "$GaimDir\plugins\gaim-otr.dll" 0 copy ; remnant or uninstall prev version failed
+Delete "$GaimDir\plugins\gaim-otr.dll"
+copy:
+ClearErrors
+Rename "$INSTDIR\gaim-otr.dll" "$GaimDir\plugins\gaim-otr.dll"
+IfErrors dllbusy
+	Return
+dllbusy:
+	MessageBox MB_RETRYCANCEL "gaim-otr.dll is busy. Please close Gaim (including tray icon) and try again" IDCANCEL cancel
+	Delete "$GaimDir\plugins\gaim-otr.dll"
+	Goto copy
+	Return
+cancel:
+	Abort "Installation of gaim-otr aborted"
+FunctionEnd
diff --git a/ui.c b/ui.c
new file mode 100644
index 0000000..c152943
--- /dev/null
+++ b/ui.c
@@ -0,0 +1,139 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+
+/* gaim headers */
+#include "util.h"
+#include "account.h"
+
+/* libotr headers */
+#include <libotr/privkey.h>
+#include <libotr/proto.h>
+#include <libotr/message.h>
+
+/* gaim-otr headers */
+#include "ui.h"
+#include "dialogs.h"
+#include "otr-plugin.h"
+
+static const OtrgUiUiOps *ui_ops = NULL;
+
+/* Set the UI ops */
+void otrg_ui_set_ui_ops(const OtrgUiUiOps *ops)
+{
+    ui_ops = ops;
+}
+
+/* Get the UI ops */
+const OtrgUiUiOps *otrg_ui_get_ui_ops(void)
+{
+    return ui_ops;
+}
+
+/* Call this function when the DSA key is updated; it will redraw the
+ * UI, if visible. */
+void otrg_ui_update_fingerprint(void)
+{
+    if (ui_ops != NULL) {
+	ui_ops->update_fingerprint();
+    }
+}
+
+/* Update the keylist, if it's visible */
+void otrg_ui_update_keylist(void)
+{
+    if (ui_ops != NULL) {
+	ui_ops->update_keylist();
+    }
+}
+
+/* Send an OTR Query Message to attempt to start a connection */
+void otrg_ui_connect_connection(ConnContext *context)
+{
+    /* Send an OTR Query to the other side. */
+    GaimAccount *account;
+    char *msg;
+	
+    /* Only do anything for UNCONNECTED fingerprints */
+    if (context == NULL || context->state != CONN_UNCONNECTED) return;
+	
+    account = gaim_accounts_find(context->accountname, context->protocol);
+    if (!account) {
+	GaimPlugin *p = gaim_find_prpl(context->protocol);
+	msg = g_strdup_printf("Account %s (%s) could not be found",
+		  context->accountname,
+		  (p && p->info->name) ? p->info->name : "Unknown");
+	otrg_dialog_notify_error(context->accountname, context->protocol,
+		context->username, "Account not found", msg, NULL);
+	g_free(msg);
+	return;
+    }
+    otrg_plugin_send_default_query(context, account);	
+}
+
+/* Drop a context to UNCONNECTED state */
+void otrg_ui_disconnect_connection(ConnContext *context)
+{
+    /* Don't do anything with UNCONNECTED fingerprints */
+    if (context == NULL || context->state == CONN_UNCONNECTED) return;
+		
+    otrg_plugin_disconnect(context);
+    otrg_dialog_disconnected(context);	
+}
+
+/* Forget a fingerprint */
+void otrg_ui_forget_fingerprint(Fingerprint *fingerprint)
+{
+    ConnContext *context;
+    gchar *storefile;
+	
+    if (fingerprint == NULL) return;
+
+    /* Don't do anything with the active fingerprint if we're in the
+     * CONNECTED state. */
+    context = fingerprint->context;
+    if (context->state == CONN_CONNECTED &&
+	    context->active_fingerprint == fingerprint) return;
+	
+    otrl_context_forget_fingerprint(fingerprint, 1);
+    storefile = g_build_filename(gaim_user_dir(), STOREFNAME, NULL);
+    otrl_privkey_write_fingerprints(otrg_plugin_userstate, storefile);
+    g_free(storefile);
+	
+    otrg_ui_update_keylist();
+}
+
+/* Configure OTR for a particular buddy */
+void otrg_ui_config_buddy(GaimBuddy *buddy)
+{
+    if (ui_ops != NULL) {
+	ui_ops->config_buddy(buddy);
+    }
+}
+
+/* Calculate the policy for a particular account / username */
+OtrlPolicy otrg_ui_find_policy(GaimAccount *account, const char *name)
+{
+    if (ui_ops != NULL) {
+	return ui_ops->find_policy(account, name);
+    }
+    return OTRL_POLICY_DEFAULT;
+}
diff --git a/ui.h b/ui.h
new file mode 100644
index 0000000..6ea7ce7
--- /dev/null
+++ b/ui.h
@@ -0,0 +1,63 @@
+/*
+ *  Off-the-Record Messaging plugin for gaim
+ *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
+ *                           <otr at cypherpunks.ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of version 2 of the GNU General Public License as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __OTRG_UI_H__
+#define __OTRG_UI_H__
+
+#include <libotr/context.h>
+
+typedef struct {
+    void (*update_fingerprint)(void);
+
+    void (*update_keylist)(void);
+
+    void (*config_buddy)(GaimBuddy *buddy);
+
+    OtrlPolicy (*find_policy)(GaimAccount *account, const char *name);
+} OtrgUiUiOps;
+
+/* Set the UI ops */
+void otrg_ui_set_ui_ops(const OtrgUiUiOps *ops);
+
+/* Get the UI ops */
+const OtrgUiUiOps *otrg_ui_get_ui_ops(void);
+
+/* Call this function when the DSA key is updated; it will redraw the
+ * UI. */
+void otrg_ui_update_fingerprint(void);
+
+/* Update the keylist, if it's visible */
+void otrg_ui_update_keylist(void);
+
+/* Send an OTR Query Message to attempt to start a connection */
+void otrg_ui_connect_connection(ConnContext *context);
+
+/* Drop a context to UNCONNECTED state */
+void otrg_ui_disconnect_connection(ConnContext *context);
+
+/* Forget a fingerprint */
+void otrg_ui_forget_fingerprint(Fingerprint *fingerprint);
+
+/* Configure OTR for a particular buddy */
+void otrg_ui_config_buddy(GaimBuddy *buddy);
+
+/* Calculate the policy for a particular account / username */
+OtrlPolicy otrg_ui_find_policy(GaimAccount *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