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