pax_global_header00006660000000000000000000000064130735521060014514gustar00rootroot0000000000000052 comment=c27d6de85104216032a81e1564a85dc0d1bf0c99 hexchat-otr-0.2.2/000077500000000000000000000000001307355210600137435ustar00rootroot00000000000000hexchat-otr-0.2.2/.clang-format000066400000000000000000000001741307355210600163200ustar00rootroot00000000000000BasedOnStyle: Webkit BreakBeforeBraces: Allman UseTab: Always TabWidth: 4 SpaceBeforeParens: Always PointerAlignment: Right hexchat-otr-0.2.2/.editorconfig000066400000000000000000000002201307355210600164120ustar00rootroot00000000000000root = true [*] end_of_line = lf charset = utf-8 indent_style = tab indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true hexchat-otr-0.2.2/.gitignore000066400000000000000000000007531307355210600157400ustar00rootroot00000000000000# C *.o *.ko *.obj *.elf *.gch *.pch *.lib *.a *.la *.lo *.dll *.so *.so.* *.dylib *.exe *.out *.app *.i*86 *.x86_64 *.hex *.tar.xz # Autotools /autom4te.cache /aclocal.m4 /ar-lib /build-aux /compile /configure /depcomp /install-sh /ltmain.sh /missing /libtool /test-driver /config.* /test/test-suite.log .deps/ .libs/ stamp-h1 stamp-it Makefile Makefile.in Makefile.in.in /m4 /sub .dirstamp # Gettext /po/POTFILES /po/.intltool-merge-cache *.mo *.gmo *.pot # Misc *~ *.patch *.diff tags hexchat-otr-0.2.2/LICENSE000066400000000000000000000431031307355210600147510ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. , 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 Lesser General Public License instead of this License. hexchat-otr-0.2.2/Makefile.am000066400000000000000000000012761307355210600160050ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 hexchatlib_LTLIBRARIES = otr.la otr_la_SOURCES = \ src/otr_key.c \ src/otr_ops.c \ src/otr_util.c \ src/hexchat_otr.c \ src/hexchat-formats.c otr_la_CFLAGS = $(OTR_CFLAGS) $(GLIB_CFLAGS) $(GCRYPT_CFLAGS) otr_la_LIBADD = $(OTR_LIBS) $(GLIB_LIBS) $(GCRYPT_LIBS) otr_la_LDFLAGS = -avoid-version -module -export-symbols-regex hexchat_.* noinst_HEADERS = \ src/otr.h \ src/otr-formats.h \ src/hexchat_otr.h src/otr-formats.h src/hexchat-formats.c: src/formats.txt src/makeformats.py $(AM_V_GEN) python src/makeformats.py $< src BUILT_SOURCES = src/otr-formats.h src/hexchat-formats.c EXTRA_DIST = $(BUILT_SOURCES) autogen.sh LICENSE src/formats.txt src/makeformats.py hexchat-otr-0.2.2/Readme.md000066400000000000000000000015311307355210600154620ustar00rootroot00000000000000HexChat OTR =========== Adds off the record support to HexChat. Originally forked from irssi-otr and still a work in progress. Installation ------------ ### Dependencies - glib - gcrypt - libotr4 - hexchat ```sh ./autogen.sh make -s sudo make install ``` Usage ----- 1. Start a session with a user: ``` /query nick /otr start ``` If this is your first time it may take a while to generate a key. 2. Authenticate this user: At this point you need to verify this is the person you think. - If you know their fingerprint and it is correct: ``` /otr trust ``` - If you have previously agreed on a password: ``` /otr auth ``` - If you have neither of these: ``` /otr authq ``` 3. Start chatting: Everything should be secure at this point. When you are done: ``` /otr finish ``` hexchat-otr-0.2.2/autogen.sh000077500000000000000000000011641307355210600157460ustar00rootroot00000000000000#!/bin/sh # Run this to generate all the initial makefiles, etc. srcdir=`dirname $0` test -z "$srcdir" && srcdir=. (test -f $srcdir/configure.ac) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the top-level directory" exit 1 } aclocal --install -I m4 || exit 1 libtoolize --quiet --copy || exit 1 autoreconf --install -Wno-portability || exit 1 if [ "$NOCONFIGURE" = "" ]; then $srcdir/configure "$@" || exit 1 if [ "$1" = "--help" ]; then exit 0 else echo "Now type \`make\' to compile" || exit 1 fi else echo "Skipping configure process." fi set +x hexchat-otr-0.2.2/configure.ac000066400000000000000000000034361307355210600162370ustar00rootroot00000000000000AC_PREREQ([2.69]) AC_INIT([hexchat-otr], [0.2.2]) AC_CONFIG_SRCDIR([src/otr.h]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_PROG_CC AC_PROG_CC_C99 AC_PROG_INSTALL AC_PATH_PROG([GCRYPT_CONFIG], [libgcrypt-config], [no]) AM_INIT_AUTOMAKE([1.11 silent-rules tar-pax dist-xz no-dist-gzip subdir-objects no-define foreign -Wall]) AM_SILENT_RULES([yes]) AM_PROG_AR AC_DEFUN([HOTR_CHECK_MACRO], [m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined. Is ]$2[ installed?])])]) HOTR_CHECK_MACRO([AX_APPEND_COMPILE_FLAGS], [autoconf-archive]) LT_PREREQ([2.2.0]) LT_INIT PKG_PROG_PKG_CONFIG([0.28]) PKG_CHECK_MODULES([GLIB], [glib-2.0]) PKG_CHECK_MODULES([OTR], [libotr >= 4.0.0]) AC_MSG_CHECKING([for GCRYPT]) AS_IF([test "$GCRYPT_CONFIG" = "no"], [ AC_MSG_ERROR([no]) ], [ GCRYPT_CFLAGS="`$GCRYPT_CONFIG --cflags`" GCRYPT_LIBS="`$GCRYPT_CONFIG --libs`" AC_SUBST([GCRYPT_CFLAGS]) AC_SUBST([GCRYPT_LIBS]) AC_MSG_RESULT([yes]) ]) AC_MSG_CHECKING([for hexchat's plugin path]) AS_IF([$PKG_CONFIG hexchat-plugin --exists], [ hexchatlibdir="`$PKG_CONFIG --variable=hexchatlibdir hexchat-plugin`" AC_MSG_RESULT([$hexchatlibdir]) ], [ hexchatlibdir="$libdir/hexchat/plugins" AC_MSG_RESULT([not found, assuming $hexchatlibdir]) ]) AC_SUBST([hexchatlibdir]) AX_APPEND_COMPILE_FLAGS([ \ -std=gnu99 \ -funsigned-char \ -fstack-protector-strong \ -fPIE \ -fPIC \ -Wall \ -Wextra \ -Wconversion \ -Winline \ -Wno-padded \ -Wno-unused-parameter \ -Wstrict-prototypes \ -Wmissing-prototypes \ -Werror=implicit-function-declaration \ -Werror=pointer-arith \ -Werror=init-self \ -Werror=format=2 \ -Werror=missing-include-dirs \ -Werror=date-time \ ]) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT echo " $PACKAGE $VERSION install location: ${hexchatlibdir} " hexchat-otr-0.2.2/src/000077500000000000000000000000001307355210600145325ustar00rootroot00000000000000hexchat-otr-0.2.2/src/formats.txt000066400000000000000000000133421307355210600167510ustar00rootroot00000000000000Keys kg_failed Key generation for %s: failed: %s (%s) kg_completed Key generation for %s: completed in %d seconds. Reloading keys kg_aborted_dup Key generation for %s: aborted. Key generation for %s still in progress kg_aborted_dir Key generation for %s: aborted, failed creating directory %s: %s kg_mkdir created directory %s kg_pipe Key generation for %s: error creating pipe: %s kg_fork Key generation for %s: fork() error: %s kg_initiated Key generation for %s: initiated. This might take several minutes or on some systems even an hour. If you want to check that something is happening, see if there are two processes of your IRC client. kg_exited Key generation for %s: child terminated for unknown reason kg_exitsig Key generation for %s: child was killed by signal %s kg_pollerr Key generation for %s: error poll()ing child: %s kg_abort Key generation for %s: aborted kg_needacc I need an account name. Try something like /otr genkey mynick@irc.server.net kg_noabort No ongoing key generation to abort key_not_found no private keys found key_loaded private keys loaded key_load_error Error loading private keys: %s (%s) Fingerprints fp_saved fingerprints saved fp_save_error Error saving fingerprints: %s (%s) fp_not_found no fingerprints found fp_loaded fingerprints loaded fp_load_error Error loading fingerprints: %s (%s) fp_trust Trusting fingerprint from %s Instance Tags instag_saved instance tags saved instag_save_error Error saving instance tags: %s (%s) instag_not_found no instance tags found instag_loaded instance tags loaded instag_load_error Error loading instance tags: %s (%s) Callbacks ops_notify_bug BUG() in ops_notify ops_notify title: %s prim: %s sec: %s ops_display_bug BUG() in ops_display ops_display msg: %s ops_sec gone %%9secure%%9 ops_fpcomp Your peer is not authenticated. To make sure you're talking to the right guy you can either agree on a secret and use the authentication described in %9/otr auth%9, or use the traditional way and compare fingerprints over a secure line (e.g. telephone) and subsequently enter %9/otr trust%9. Your fingerprint is: %s. %s's fingerprint: %s ops_insec gone %9insecure%9 ops_still_reply still %%9secure%9 (is reply) ops_still_no_reply still %%9secure%9 (is not reply) ops_log log msg: %s ops_inject Couldn't inject message from %s for %s: %s ops_handle_msg Message event %s, msg %s SendingReceiving send_failed send failed: msg=%s send_change couldn't find context also OTR changed the outgoing message(BUG?) send_fragment failed to fragment message: msg=%s send_converted OTR converted sent message to %s receive_ignore_query ignoring rest of OTR default query msg receive_dequeued dequeued msg of length %d receive_queued queued msg of length %d receive_ignore ignoring protocol message of length %d, acc=%s, from=%s: %s receive_converted OTR converted received message otr_better_two %s has requested an Off-the-Record private conversation. However, you do not have a plugin to support that. otr_better_three See http://otr.cypherpunks.ca/ for more information. Context ctx_not_found couldn't find context: acc=%s nick=%s ctx_not_create couldn't create/find context: acc=%s from=%s Authentication auth_aborted_ongoing Ongoing authentication aborted auth_aborted Authentication aborted auth_responding Responding to authentication request... auth_initiated Initiated authentication... auth_have_old %s wanted to authenticate but an old authentication was still ongoing. Old authentication will be aborted, please try again. auth_peer %s wants to authenticate. Type /otr auth to complete. auth_peer_qa %s wants to authenticate and asked you \"%s\". Type /otr auth to complete. auth_peer_reply_wrong %s replied to an auth we didn't start. auth_peer_replied %s replied to our auth request... auth_peer_wrong_smp3 %s sent a wrong authentication message (SMP3). auth_peer_wrong_smp4 %s sent a wrong authentication message (SMP4). auth_successful Authentication successful! auth_failed Authentication failed! auth_needenc You need to establish an OTR session before you can authenticate. Commands cmd_otr We're alive cmd_qnotfound Failed: Can't get nick and server of current query window. (Or maybe you're doing this in the status window?) cmd_auth Please agree on a secret with your peer and then initiate the authentication with /otr auth or let him initiate. Should you initiate your peer will after a little while be instructed to enter the secret as well. Once he has done so the authentication will finish up. Should you have both typed in the same secret the authentication should be successful. cmd_debug_on Debug mode is on cmd_debug_off Debug mode is off cmd_finish Finished conversation with %s@%s. cmd_finishall_none No conversations to finish. cmd_version This is irc-otr version %s peer_finished %s has finished the OTR conversation. If you want to continue talking enter %9/otr finish%9 for plaintext or ?OTR? to restart OTR. Contexts ctx_ctx_unencrypted %%9%20s%%9 %30s plaintext ctx_ctx_encrypted %%9%20s%%9 %30s %gencrypted%n ctx_ctx_finished %%9%20s%%9 %30s finished ctx_ctx_unknown %%9%20s%%9 %30s unknown state(BUG?) ctx_fps_no %s %rnot authenticated%n ctx_fps_smp %s %gauthenticated%n via shared secret (SMP) ctx_fps_man %s %gauthenticated%n manually ctx_noctxs No active OTR contexts found Statusbar st_plaintext {sb plaintext} st_untrusted {sb %rOTR(not auth'ed)%n} st_trust_smp {sb %gOTR%n} st_trust_manual {sb %gOTR%n} st_smp_incoming {sb {hilight incoming auth request...}} st_smp_outgoing {sb {hilight awaiting auth reply...}} st_smp_finalize {sb {hilight finalizing auth...}} st_smp_unknown {sb {hilight unknown auth state!}} st_finished {sb finished} st_unknown {sb {hilight state unknown (BUG!)}} hexchat-otr-0.2.2/src/hexchat-formats.c000066400000000000000000000172271307355210600200040ustar00rootroot00000000000000 /* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include "otr.h" FORMAT_REC formats[] = { { MODULE_NAME, "otr" }, { NULL, "Keys" }, { "kg_failed", "Key generation for %s: failed: %s (%s)" }, { "kg_completed", "Key generation for %s: completed in %d seconds. Reloading keys" }, { "kg_aborted_dup", "Key generation for %s: aborted. Key generation for %s still in progress" }, { "kg_aborted_dir", "Key generation for %s: aborted, failed creating directory %s: %s" }, { "kg_mkdir", "created directory %s" }, { "kg_pipe", "Key generation for %s: error creating pipe: %s" }, { "kg_fork", "Key generation for %s: fork() error: %s" }, { "kg_initiated", "Key generation for %s: initiated. This might take several minutes or on some systems even an hour. If you want to check that something is happening, see if there are two processes of your IRC client." }, { "kg_exited", "Key generation for %s: child terminated for unknown reason" }, { "kg_exitsig", "Key generation for %s: child was killed by signal %s" }, { "kg_pollerr", "Key generation for %s: error poll()ing child: %s" }, { "kg_abort", "Key generation for %s: aborted" }, { "kg_needacc", "I need an account name. Try something like /otr genkey mynick@irc.server.net" }, { "kg_noabort", "No ongoing key generation to abort" }, { "key_not_found", "no private keys found" }, { "key_loaded", "private keys loaded" }, { "key_load_error", "Error loading private keys: %s (%s)" }, { NULL, "Fingerprints" }, { "fp_saved", "fingerprints saved" }, { "fp_save_error", "Error saving fingerprints: %s (%s)" }, { "fp_not_found", "no fingerprints found" }, { "fp_loaded", "fingerprints loaded" }, { "fp_load_error", "Error loading fingerprints: %s (%s)" }, { "fp_trust", "Trusting fingerprint from %s" }, { NULL, "Instance Tags" }, { "instag_saved", "instance tags saved" }, { "instag_save_error", "Error saving instance tags: %s (%s)" }, { "instag_not_found", "no instance tags found" }, { "instag_loaded", "instance tags loaded" }, { "instag_load_error", "Error loading instance tags: %s (%s)" }, { NULL, "Callbacks" }, { "ops_notify_bug", "BUG() in ops_notify" }, { "ops_notify", "title: %s prim: %s sec: %s" }, { "ops_display_bug", "BUG() in ops_display" }, { "ops_display", "msg: %s" }, { "ops_sec", "gone secure" }, { "ops_fpcomp", "Your peer is not authenticated. To make sure you're talking to the right guy you can either agree on a secret and use the authentication described in /otr auth, or use the traditional way and compare fingerprints over a secure line (e.g. telephone) and subsequently enter /otr trust. Your fingerprint is: %s. %s's fingerprint: %s" }, { "ops_insec", "gone insecure" }, { "ops_still_reply", "still secure (is reply)" }, { "ops_still_no_reply", "still secure (is not reply)" }, { "ops_log", "log msg: %s" }, { "ops_inject", "Couldn't inject message from %s for %s: %s" }, { "ops_handle_msg", "Message event %s, msg %s" }, { NULL, "SendingReceiving" }, { "send_failed", "send failed: msg=%s" }, { "send_change", "couldn't find context also OTR changed the outgoing message(BUG?)" }, { "send_fragment", "failed to fragment message: msg=%s" }, { "send_converted", "OTR converted sent message to %s" }, { "receive_ignore_query", "ignoring rest of OTR default query msg" }, { "receive_dequeued", "dequeued msg of length %d" }, { "receive_queued", "queued msg of length %d" }, { "receive_ignore", "ignoring protocol message of length %d, acc=%s, from=%s: %s" }, { "receive_converted", "OTR converted received message" }, { "otr_better_two", "%s has requested an Off-the-Record private conversation. However, you do not have a plugin to support that." }, { "otr_better_three", "See http://otr.cypherpunks.ca/ for more information." }, { NULL, "Context" }, { "ctx_not_found", "couldn't find context: acc=%s nick=%s" }, { "ctx_not_create", "couldn't create/find context: acc=%s from=%s" }, { NULL, "Authentication" }, { "auth_aborted_ongoing", "Ongoing authentication aborted" }, { "auth_aborted", "Authentication aborted" }, { "auth_responding", "Responding to authentication request..." }, { "auth_initiated", "Initiated authentication..." }, { "auth_have_old", "%s wanted to authenticate but an old authentication was still ongoing. Old authentication will be aborted, please try again." }, { "auth_peer", "%s wants to authenticate. Type /otr auth to complete." }, { "auth_peer_qa", "%s wants to authenticate and asked you \"%s\". Type /otr auth to complete." }, { "auth_peer_reply_wrong", "%s replied to an auth we didn't start." }, { "auth_peer_replied", "%s replied to our auth request..." }, { "auth_peer_wrong_smp3", "%s sent a wrong authentication message (SMP3)." }, { "auth_peer_wrong_smp4", "%s sent a wrong authentication message (SMP4)." }, { "auth_successful", "Authentication successful!" }, { "auth_failed", "Authentication failed!" }, { "auth_needenc", "You need to establish an OTR session before you can authenticate." }, { NULL, "Commands" }, { "cmd_otr", "We're alive" }, { "cmd_qnotfound", "Failed: Can't get nick and server of current query window. (Or maybe you're doing this in the status window?)" }, { "cmd_auth", "Please agree on a secret with your peer and then initiate the authentication with /otr auth or let him initiate. Should you initiate your peer will after a little while be instructed to enter the secret as well. Once he has done so the authentication will finish up. Should you have both typed in the same secret the authentication should be successful." }, { "cmd_debug_on", "Debug mode is on" }, { "cmd_debug_off", "Debug mode is off" }, { "cmd_finish", "Finished conversation with %s@%s." }, { "cmd_finishall_none", "No conversations to finish." }, { "cmd_version", "This is irc-otr version %s" }, { "peer_finished", "%s has finished the OTR conversation. If you want to continue talking enter /otr finish for plaintext or ?OTR? to restart OTR." }, { NULL, "Contexts" }, { "ctx_ctx_unencrypted", "%20s %30s plaintext" }, { "ctx_ctx_encrypted", "%20s %30s encrypted" }, { "ctx_ctx_finished", "%20s %30s finished" }, { "ctx_ctx_unknown", "%20s %30s unknown state(BUG?)" }, { "ctx_fps_no", "%s %rnot authenticated" }, { "ctx_fps_smp", "%s authenticated via shared secret (SMP)" }, { "ctx_fps_man", "%s authenticated manually" }, { "ctx_noctxs", "No active OTR contexts found" }, { NULL, "Statusbar" }, { "st_plaintext", "{sb plaintext}" }, { "st_untrusted", "{sb %rOTR(not auth'ed)}" }, { "st_trust_smp", "{sb OTR}" }, { "st_trust_manual", "{sb OTR}" }, { "st_smp_incoming", "{sb {hilight incoming auth request...}}" }, { "st_smp_outgoing", "{sb {hilight awaiting auth reply...}}" }, { "st_smp_finalize", "{sb {hilight finalizing auth...}}" }, { "st_smp_unknown", "{sb {hilight unknown auth state!}}" }, { "st_finished", "{sb finished}" }, { "st_unknown", "{sb {hilight state unknown (BUG!)}}" }, { NULL, NULL } }; G_STATIC_ASSERT (G_N_ELEMENTS(formats) - 1 == TXT_ST_UNKNOWN + 1); hexchat-otr-0.2.2/src/hexchat_otr.c000066400000000000000000000235161307355210600172150ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include "otr.h" int debug = 0; hexchat_plugin *ph; static GRegex *regex_nickignore = NULL; static char set_policy[512] = IO_DEFAULT_POLICY; static char set_policy_known[512] = IO_DEFAULT_POLICY_KNOWN; static char set_ignore[512] = IO_DEFAULT_IGNORE; static int set_finishonunload = TRUE; static int get_current_context_type (void) { return hexchat_list_int (ph, NULL, "type"); } static int extract_nick (char *nick, char *line, size_t nick_size) { char *excl; if (*line++ != ':') return FALSE; g_strlcpy (nick, line, nick_size); if ((excl = strchr (nick, '!'))) *excl = '\0'; return TRUE; } void irc_send_message (IRC_CTX *ircctx, const char *recipient, char *msg) { hexchat_commandf (ph, "PRIVMSG %s :%s", recipient, msg); } static void cmd_start (const char *nick) { if (get_current_context_type () != 3) { hexchat_print (ph, "OTR: You can only use OTR in a dialog\n"); return; } hexchat_commandf (ph, "quote PRIVMSG %s :?OTRv23?", nick); } static int cmd_otr (char *word[], char *word_eol[], void *userdata) { const char *own_nick = hexchat_get_info (ph, "nick"); char *target = (char *)hexchat_get_info (ph, "channel"); const char *network = hexchat_get_info (ph, "network"); IRC_CTX ircctxs = { .nick = (char *)own_nick, .address = (char *)network }, *ircctx = &ircctxs; char *cmd = word[2]; if (!cmd) { hexchat_command(ph, "help otr"); return HEXCHAT_EAT_ALL; } if (strcmp (cmd, "debug") == 0) { debug = !debug; otr_noticest (debug ? TXT_CMD_DEBUG_ON : TXT_CMD_DEBUG_OFF); } else if (strcmp (cmd, "start") == 0 || strcmp (cmd, "init") == 0) { cmd_start (target); } else if (strcmp (cmd, "version") == 0) { otr_noticest (TXT_CMD_VERSION, PVERSION); } else if (strcmp (cmd, "finish") == 0) { if (word[3] && *word[3]) otr_finish (NULL, NULL, word[3], TRUE); else otr_finish (ircctx, target, NULL, TRUE); } else if (strcmp (cmd, "trust") == 0) { if (word[3] && *word[3]) otr_trust (NULL, NULL, word[3]); else otr_trust (ircctx, target, NULL); } else if (strcmp (cmd, "authabort") == 0) { if (word[3] && *word[3]) otr_authabort (NULL, NULL, word[3]); else otr_authabort (ircctx, target, NULL); } else if (strcmp (cmd, "genkey") == 0) { if (word[3] && *word[3]) { if (strcmp (word[3], "abort") == 0) keygen_abort (FALSE); else if (strchr (word[3], '@')) keygen_run (word[3]); else otr_noticest (TXT_KG_NEEDACC); } else { otr_noticest (TXT_KG_NEEDACC); } } else if (strcmp (cmd, "auth") == 0) { if (!word[3] || !*word[3]) otr_notice (ircctx, target, TXT_CMD_AUTH); else if (word[4] && *word[4] && strchr (word[3], '@')) otr_auth (NULL, NULL, word[3], NULL, word_eol[4]); else otr_auth (ircctx, target, NULL, NULL, word_eol[3]); } else if (strcmp (cmd, "authq") == 0) { if (!word[3] || !*word[3] || !word[4] || !*word[4]) otr_notice (ircctx, target, TXT_CMD_AUTH); else if (word[5] && *word[5] && strchr (word[3], '@')) otr_auth (NULL, NULL, word[3], word[5], word[4]); else otr_auth (ircctx, target, NULL, word[4], word_eol[5]); } else if (strcmp (cmd, "set") == 0) { if (strcmp (word[3], "policy") == 0) { otr_setpolicies (word_eol[4], FALSE); g_strlcpy (set_policy, word_eol[4], sizeof(set_policy)); } else if (strcmp (word[3], "policy_known") == 0) { otr_setpolicies (word_eol[4], TRUE); g_strlcpy (set_policy_known, word_eol[4], sizeof(set_policy_known)); } else if (strcmp (word[3], "ignore") == 0) { if (regex_nickignore) g_regex_unref (regex_nickignore); regex_nickignore = g_regex_new (word_eol[4], 0, 0, NULL); g_strlcpy (set_ignore, word_eol[4], sizeof(set_ignore)); } else if (strcmp (word[3], "finishonunload") == 0) { set_finishonunload = (g_ascii_strcasecmp (word[4], "true") == 0); } else { hexchat_printf (ph, "policy: %s\n" "policy_known: %s\nignore: %s\n" "finishonunload: %s\n", set_policy, set_policy_known, set_ignore, set_finishonunload ? "true" : "false"); } } else hexchat_command(ph, "help otr"); return HEXCHAT_EAT_ALL; } static int hook_outgoing (char *word[], char *word_eol[], void *userdata) { const char *own_nick = hexchat_get_info (ph, "nick"); const char *channel = hexchat_get_info (ph, "channel"); const char *network = hexchat_get_info (ph, "network"); char newmsg[512]; char *otrmsg; IRC_CTX ircctx = { .nick = (char *)own_nick, .address = (char *)network }; if (get_current_context_type () != 3) /* Not PM */ return HEXCHAT_EAT_NONE; if (g_regex_match (regex_nickignore, channel, 0, NULL)) return HEXCHAT_EAT_NONE; otrmsg = otr_send (&ircctx, word_eol[1], channel); if (otrmsg == word_eol[1]) return HEXCHAT_EAT_NONE; hexchat_emit_print (ph, "Your Message", own_nick, word_eol[1], NULL, NULL); if (!otrmsg) return HEXCHAT_EAT_ALL; g_snprintf (newmsg, 511, "PRIVMSG %s :%s", channel, otrmsg); otrl_message_free (otrmsg); hexchat_command (ph, newmsg); return HEXCHAT_EAT_ALL; } static int hook_privmsg (char *word[], char *word_eol[], void *userdata) { char nick[256]; char *newmsg; const char *network = hexchat_get_info (ph, "network"); const char *own_nick = hexchat_get_info (ph, "nick"); const char *chantypes = hexchat_list_str (ph, NULL, "chantypes"); IRC_CTX ircctx = { .nick = (char *)own_nick, .address = (char *)network }; hexchat_context *query_ctx; if (strchr (chantypes, word[3][0]) != NULL) /* Ignore channels */ return HEXCHAT_EAT_NONE; if (!extract_nick (nick, word[1], sizeof(nick))) return HEXCHAT_EAT_NONE; if (g_regex_match (regex_nickignore, nick, 0, NULL)) return HEXCHAT_EAT_NONE; newmsg = otr_receive (&ircctx, word_eol[2], nick); if (!newmsg) { return HEXCHAT_EAT_ALL; } if (newmsg == word_eol[2]) { return HEXCHAT_EAT_NONE; } query_ctx = hexchat_find_context (ph, network, nick); if (query_ctx == NULL) { hexchat_commandf (ph, "query %s", nick); query_ctx = hexchat_find_context (ph, network, nick); } GRegex *regex_quot = g_regex_new (""", 0, 0, NULL); GRegex *regex_amp = g_regex_new ("&", 0, 0, NULL); GRegex *regex_lt = g_regex_new ("<", 0, 0, NULL); GRegex *regex_gt = g_regex_new (">", 0, 0, NULL); char *quotfix = g_regex_replace_literal (regex_quot, newmsg, -1, 0, "\"", 0, NULL); char *ampfix = g_regex_replace_literal (regex_amp, quotfix, -1, 0, "&", 0, NULL); char *ltfix = g_regex_replace_literal (regex_lt, ampfix, -1, 0, "<", 0, NULL); char *gtfix = g_regex_replace_literal (regex_gt, ltfix, -1, 0, ">", 0, NULL); newmsg = gtfix; g_regex_unref (regex_quot); g_regex_unref (regex_amp); g_regex_unref (regex_lt); g_regex_unref (regex_gt); if (query_ctx) hexchat_set_context (ph, query_ctx); hexchat_emit_print (ph, "Private Message", nick, newmsg, NULL, NULL); hexchat_command (ph, "GUI COLOR 2"); otrl_message_free (newmsg); return HEXCHAT_EAT_ALL; } void hexchat_plugin_get_info (char **name, char **desc, char **version, void **reserved) { *name = PNAME; *desc = PDESC; *version = PVERSION; } int hexchat_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg) { ph = plugin_handle; *plugin_name = PNAME; *plugin_desc = PDESC; *plugin_version = PVERSION; if (otrlib_init ()) return 0; hexchat_hook_server (ph, "PRIVMSG", HEXCHAT_PRI_NORM, hook_privmsg, 0); hexchat_hook_command (ph, "", HEXCHAT_PRI_NORM, hook_outgoing, 0, 0); hexchat_hook_command (ph, "otr", HEXCHAT_PRI_NORM, cmd_otr, OTR_HELP, 0); otr_setpolicies (IO_DEFAULT_POLICY, FALSE); otr_setpolicies (IO_DEFAULT_POLICY_KNOWN, TRUE); if (regex_nickignore) g_regex_unref (regex_nickignore); regex_nickignore = g_regex_new (IO_DEFAULT_IGNORE, 0, 0, NULL); hexchat_print (ph, "Hexchat OTR loaded successfully!\n"); return 1; } int hexchat_plugin_deinit (void) { g_regex_unref (regex_nickignore); if (set_finishonunload) otr_finishall (); otrlib_deinit (); return 1; } void otr_log(IRC_CTX *server, const char *nick, int level, const char *format, ...) { /* TODO: Implement me! */ } void printformat (IRC_CTX *ircctx, const char *nick, int lvl, int fnum, ...) { va_list params; va_start (params, fnum); char msg[LOGMAX], *s = msg; hexchat_context *find_query_ctx; char *network = NULL; if (ircctx) network = ircctx->address; if (network && nick) { find_query_ctx = hexchat_find_context (ph, network, nick); if (find_query_ctx == NULL) { /* no query window yet, let's open one */ hexchat_commandf (ph, "query %s", nick); find_query_ctx = hexchat_find_context (ph, network, nick); } } else { find_query_ctx = hexchat_find_context (ph, NULL, hexchat_get_info (ph, "network") ?: hexchat_get_info (ph, "network")); } hexchat_set_context (ph, find_query_ctx); #pragma GCC diagnostic ignored "-Wformat-nonliteral" if (g_vsnprintf (msg, sizeof(msg), formats[fnum].def, params) < 0) g_snprintf (msg, sizeof(msg), "internal error parsing error string (BUG)"); #pragma GCC diagnostic pop va_end (params); hexchat_printf (ph, "OTR: %s", s); } IRC_CTX *server_find_address (char *address) { static IRC_CTX ircctx; ircctx.address = address; return &ircctx; } hexchat-otr-0.2.2/src/hexchat_otr.h000066400000000000000000000053131307355210600172150ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include "config.h" #include #define MODULE_NAME "otr" #define PNAME "OTR" #define PDESC "Off-The-Record Messaging for Hexchat" #define PVERSION PACKAGE_VERSION #define OTR_HELP "OTR\n\ version: Prints version of plugin\n\ start: Starts an OTR chat (init also works)\n\ finish []: Finish an OTR chat\n\ trust []: Trusts the other user\n\ auth [] : Auths a user via password\n\ authq [] : Auths a user via question\n\ authabort []: Aborts auth in progress\n\ genkey [abort|]: Generates a new key\n\ set []: Changes settings, run without args for current values" #define MAX_FORMAT_PARAMS 10 struct _IRC_CTX { char *nick; char *address; }; typedef struct _IRC_CTX IRC_CTX; struct _FORMAT_REC { char *tag; char *def; }; typedef struct _FORMAT_REC FORMAT_REC; enum { MSGLEVEL_CRAP, MSGLEVEL_MSGS } lvls; extern hexchat_plugin *ph; /* plugin handle */ void printformat (IRC_CTX *ircctx, const char *nick, int lvl, int fnum, ...); #define otr_noticest(formatnum, ...) \ printformat (NULL, NULL, MSGLEVEL_MSGS, formatnum, ##__VA_ARGS__) #define otr_notice(server, nick, formatnum, ...) \ printformat (server, nick, MSGLEVEL_MSGS, formatnum, ##__VA_ARGS__) #define otr_infost(formatnum, ...) \ printformat (NULL, NULL, MSGLEVEL_CRAP, formatnum, ##__VA_ARGS__) #define otr_info(server, nick, formatnum, ...) \ printformat (server, nick, MSGLEVEL_CRAP, formatnum, ##__VA_ARGS__) #define otr_debug(server, nick, formatnum, ...) \ { \ if (debug) \ printformat (server, nick, MSGLEVEL_MSGS, formatnum, ##__VA_ARGS__); \ } #define IRCCTX_DUP(ircctx) g_memdup (ircctx, sizeof(IRC_CTX)); #define IRCCTX_ADDR(ircctx) ircctx->address #define IRCCTX_NICK(ircctx) ircctx->nick #define IRCCTX_FREE(ircctx) g_free (ircctx) hexchat-otr-0.2.2/src/makeformats.py000077500000000000000000000062501307355210600174230ustar00rootroot00000000000000#!/usr/bin/python # # Uli Meis # # Just a short script to generate our FORMAT_REC # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA import sys,os,re GPL_LICENSE = """ /* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ """ lines = map(lambda x: x.strip(),open(sys.argv[1],"r").readlines()) out_dir = sys.argv[2] if len(sys.argv) > 2 else "." hdr = open(os.path.join(out_dir, "otr-formats.h"), "w") srcx = open(os.path.join(out_dir, "hexchat-formats.c"), "w") srcx.write(GPL_LICENSE) hdr.write(GPL_LICENSE) srcx.write('#include "otr.h"\n'); srcx.write('FORMAT_REC formats[] = {\n\t') srcx.write('{ MODULE_NAME, "otr" }') hdr.write("enum\n{\n\t") hdr.write("TXT_OTR_MODULE_NAME") fills = 0 section = None for line in lines: srcx.write(",\n\t") e = line.split("\t") if len(e)==1: # Section name section = e[0] srcx.write("""{ NULL, "%s" }""" % (e[0])) hdr.write(",\n\tTXT_OTR_FILL_%d" % fills) fills += 1 continue params = [] fo = e[1] new = "" last=0 i=0 srcx.write("""{ "%s", "%s" """ % (e[0],fo.replace("%%9","").replace("%9","").replace("%g","").replace("%n",""))) for m in re.finditer("(^|[^%])%([0-9]*)[ds]",fo): if m.group()[-1]=='d': params += ['1'] else: params += ['0'] new += fo[last:m.start()+len(m.group(1))].replace('%%','%')+"$" if m.group(2): new+= "[%s]" % m.group(2) new += "%d" % i last = m.end() i += 1 new += fo[last:].replace('%%','%') e[1] = new e += [len(params)] + params #print "Handling line %s with elen %d" % (line,len(e)) premsg = "" if e[1][0] != "{" and section!="Nickignore" and section!="Contexts": premsg = "%9OTR%9: " srcx.write("}") hdr.write(",\n\t") hdr.write("TXT_%s" % e[0].upper()) hdr.write(""" }; extern FORMAT_REC formats[]; """) srcx.write(""", \t{ NULL, NULL } }; G_STATIC_ASSERT (G_N_ELEMENTS(formats) - 1 == TXT_ST_UNKNOWN + 1); """) hdr.close() srcx.close() hexchat-otr-0.2.2/src/otr-formats.h000066400000000000000000000053741307355210600171710ustar00rootroot00000000000000 /* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ enum { TXT_OTR_MODULE_NAME, TXT_OTR_FILL_0, TXT_KG_FAILED, TXT_KG_COMPLETED, TXT_KG_ABORTED_DUP, TXT_KG_ABORTED_DIR, TXT_KG_MKDIR, TXT_KG_PIPE, TXT_KG_FORK, TXT_KG_INITIATED, TXT_KG_EXITED, TXT_KG_EXITSIG, TXT_KG_POLLERR, TXT_KG_ABORT, TXT_KG_NEEDACC, TXT_KG_NOABORT, TXT_KEY_NOT_FOUND, TXT_KEY_LOADED, TXT_KEY_LOAD_ERROR, TXT_OTR_FILL_1, TXT_FP_SAVED, TXT_FP_SAVE_ERROR, TXT_FP_NOT_FOUND, TXT_FP_LOADED, TXT_FP_LOAD_ERROR, TXT_FP_TRUST, TXT_OTR_FILL_2, TXT_INSTAG_SAVED, TXT_INSTAG_SAVE_ERROR, TXT_INSTAG_NOT_FOUND, TXT_INSTAG_LOADED, TXT_INSTAG_LOAD_ERROR, TXT_OTR_FILL_3, TXT_OPS_NOTIFY_BUG, TXT_OPS_NOTIFY, TXT_OPS_DISPLAY_BUG, TXT_OPS_DISPLAY, TXT_OPS_SEC, TXT_OPS_FPCOMP, TXT_OPS_INSEC, TXT_OPS_STILL_REPLY, TXT_OPS_STILL_NO_REPLY, TXT_OPS_LOG, TXT_OPS_INJECT, TXT_OPS_HANDLE_MSG, TXT_OTR_FILL_4, TXT_SEND_FAILED, TXT_SEND_CHANGE, TXT_SEND_FRAGMENT, TXT_SEND_CONVERTED, TXT_RECEIVE_IGNORE_QUERY, TXT_RECEIVE_DEQUEUED, TXT_RECEIVE_QUEUED, TXT_RECEIVE_IGNORE, TXT_RECEIVE_CONVERTED, TXT_OTR_BETTER_TWO, TXT_OTR_BETTER_THREE, TXT_OTR_FILL_5, TXT_CTX_NOT_FOUND, TXT_CTX_NOT_CREATE, TXT_OTR_FILL_6, TXT_AUTH_ABORTED_ONGOING, TXT_AUTH_ABORTED, TXT_AUTH_RESPONDING, TXT_AUTH_INITIATED, TXT_AUTH_HAVE_OLD, TXT_AUTH_PEER, TXT_AUTH_PEER_QA, TXT_AUTH_PEER_REPLY_WRONG, TXT_AUTH_PEER_REPLIED, TXT_AUTH_PEER_WRONG_SMP3, TXT_AUTH_PEER_WRONG_SMP4, TXT_AUTH_SUCCESSFUL, TXT_AUTH_FAILED, TXT_AUTH_NEEDENC, TXT_OTR_FILL_7, TXT_CMD_OTR, TXT_CMD_QNOTFOUND, TXT_CMD_AUTH, TXT_CMD_DEBUG_ON, TXT_CMD_DEBUG_OFF, TXT_CMD_FINISH, TXT_CMD_FINISHALL_NONE, TXT_CMD_VERSION, TXT_PEER_FINISHED, TXT_OTR_FILL_8, TXT_CTX_CTX_UNENCRYPTED, TXT_CTX_CTX_ENCRYPTED, TXT_CTX_CTX_FINISHED, TXT_CTX_CTX_UNKNOWN, TXT_CTX_FPS_NO, TXT_CTX_FPS_SMP, TXT_CTX_FPS_MAN, TXT_CTX_NOCTXS, TXT_OTR_FILL_9, TXT_ST_PLAINTEXT, TXT_ST_UNTRUSTED, TXT_ST_TRUST_SMP, TXT_ST_TRUST_MANUAL, TXT_ST_SMP_INCOMING, TXT_ST_SMP_OUTGOING, TXT_ST_SMP_FINALIZE, TXT_ST_SMP_UNKNOWN, TXT_ST_FINISHED, TXT_ST_UNKNOWN }; extern FORMAT_REC formats[]; hexchat-otr-0.2.2/src/otr.h000066400000000000000000000132201307355210600155050ustar00rootroot00000000000000/* * Off-the-Record Messaging (OTR) module for the irssi IRC client * Copyright (C) 2008 Uli Meis * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include #include #include /* OTR */ #include #include #include #include /* glib */ #include #include #include /* hexchat */ #include "hexchat_otr.h" /* log stuff */ #define LOGMAX 1024 #define LVL_NOTICE 0 #define LVL_DEBUG 1 #define otr_logst(level, format, ...) \ otr_log (NULL, NULL, level, format, ##__VA_ARGS__) void otr_log (IRC_CTX *server, const char *to, int level, const char *format, ...); /* own */ #include "config.h" #include "otr-formats.h" /* * maybe this should be configurable? * I believe bitlbee has something >500. */ #define OTR_MAX_MSG_SIZE 400 /* otr protocol id */ #define PROTOCOLID "IRC" #define KEYFILE "/otr/otr.key" #define TMPKEYFILE "/otr/otr.key.tmp" #define FPSFILE "/otr/otr.fp" #define INSTAGFILE "/otr/otr.instag" /* some defaults */ #define IO_DEFAULT_POLICY "*@localhost opportunistic,*bitlbee* opportunistic,*@im.* opportunistic, *serv@irc* never" #define IO_DEFAULT_POLICY_KNOWN "* always" #define IO_DEFAULT_IGNORE "xmlconsole[0-9]*" static const char * const otr_msg_event_txt[] = { "NONE", "ENCRYPTION_REQUIRED", "ENCRYPTION_ERROR", "CONNECTION_ENDED", "SETUP_ERROR", "MSG_REFLECTED", "MSG_RESENT", "RCVDMSG_NOT_IN_PRIVATE", "RCVDMSG_UNREADABLE", "RCVDMSG_MALFORMED", "LOG_HEARTBEAT_RCVD", "LOG_HEARTBEAT_SENT", "RCVDMSG_GENERAL_ERR", "RCVDMSG_UNENCRYPTED", "RCVDMSG_UNRECOGNIZED", "RCVDMSG_FOR_OTHER_INSTANCE" }; static const char * const otr_status_txt[] = { "FINISHED", "TRUST_MANUAL", "TRUST_SMP", "SMP_ABORT", "SMP_STARTED", "SMP_RESPONDED", "SMP_INCOMING", "SMP_FINALIZE", "SMP_ABORTED", "PEER_FINISHED", "SMP_FAILED", "SMP_SUCCESS", "GONE_SECURE", "GONE_INSECURE", "CTX_UPDATE" }; /* returned by otr_getstatus */ enum { IO_ST_PLAINTEXT, IO_ST_FINISHED, IO_ST_SMP_INCOMING, IO_ST_SMP_OUTGOING, IO_ST_SMP_FINALIZE, IO_ST_UNKNOWN, IO_ST_UNTRUSTED=32, IO_ST_TRUST_MANUAL=64, IO_ST_TRUST_SMP=128, IO_ST_SMP_ONGOING= IO_ST_SMP_INCOMING|IO_ST_SMP_OUTGOING|IO_ST_SMP_FINALIZE }; /* given to otr_status_change */ enum { IO_STC_FINISHED, IO_STC_TRUST_MANUAL, IO_STC_TRUST_SMP, IO_STC_SMP_ABORT, IO_STC_SMP_STARTED, IO_STC_SMP_RESPONDED, IO_STC_SMP_INCOMING, IO_STC_SMP_FINALIZE, IO_STC_SMP_ABORTED, IO_STC_PEER_FINISHED, IO_STC_SMP_FAILED, IO_STC_SMP_SUCCESS, IO_STC_GONE_SECURE, IO_STC_GONE_INSECURE, IO_STC_CTX_UPDATE }; /* one for each OTR context (=communication pair) */ struct co_info { char *msgqueue; /* holds partially reconstructed base64 messages */ IRC_CTX *ircctx; /* irssi server object for this peer */ int received_smp_init; /* received SMP init msg */ int smp_failed; /* last SMP failed */ char better_msg_two[256]; /* what the second line of the "better" default query msg should like. Eat it up when it comes in */ int finished; /* true after you've /otr finished */ }; /* these are returned by /otr contexts */ struct fplist_ { char *fp; enum { NOAUTH, AUTHSMP, AUTHMAN } authby; struct fplist_ *next; }; struct ctxlist_ { char *username; char *accountname; enum { STUNENCRYPTED, STENCRYPTED, STFINISHED, STUNKNOWN } state; struct fplist_ *fplist; struct ctxlist_ *next; }; /* policy list generated from /set otr_policy */ struct plistentry { GPatternSpec *namepat; OtrlPolicy policy; }; /* used by the logging functions below */ extern int debug; void irc_send_message (IRC_CTX *ircctx, const char *recipient, char *msg); IRC_CTX *server_find_address (char *address); void otr_status_change (IRC_CTX *ircctx, const char *nick, int event); /* init stuff */ int otrlib_init (void); void otrlib_deinit (void); void otr_initops (void); void otr_setpolicies (const char *policies, int known); /* basic send/receive/status stuff */ char *otr_send (IRC_CTX *server, const char *msg, const char *to); char *otr_receive (IRC_CTX *server, const char *msg, const char *from); int otr_getstatus(IRC_CTX *ircctx, const char *nick); ConnContext *otr_getcontext (const char *accname, const char *nick, int create, void *data); /* user interaction */ void otr_trust (IRC_CTX *server, char *nick, const char *peername); void otr_finish (IRC_CTX *server, char *nick, const char *peername, int inquery); void otr_auth (IRC_CTX *server, char *nick, const char *peername, const char *question, const char *secret); void otr_authabort (IRC_CTX *server, char *nick, const char *peername); void otr_abort_auth(ConnContext *co, IRC_CTX *ircctx, const char *nick); struct ctxlist_ *otr_contexts (void); void otr_finishall (void); /* key/fingerprint stuff */ void keygen_run (const char *accname); void keygen_abort (int ignoreidle); void key_load (void); void fps_load (void); void otr_writefps (void); /* instance tags */ void instag_load (void); void otr_writeinstags (void); hexchat-otr-0.2.2/src/otr_key.c000066400000000000000000000174411307355210600163610ustar00rootroot00000000000000/* * Off-the-Record Messaging (OTR) module for the irssi IRC client * Copyright (C) 2008 Uli Meis * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #define _GNU_SOURCE #include "otr.h" #include #include #include #include extern OtrlUserState otr_state; typedef enum { KEYGEN_NO, KEYGEN_RUNNING } keygen_status_t; struct { keygen_status_t status; char *accountname; char *protocol; time_t started; GIOChannel *ch[2]; guint cpid, cwid; pid_t pid; } kg_st = { .status = KEYGEN_NO }; static inline const char *get_configdir (void) { return hexchat_get_info (ph, "configdir"); } static void keygen_childwatch (GPid pid, gint status, gpointer data) { struct pollfd pfd = { .fd = g_io_channel_unix_get_fd (kg_st.ch[0]), .events = POLLIN }; int ret; /* nothing to do if keygen_complete has already been called */ if (data) return; kg_st.pid = 0; ret = poll (&pfd, 1, 0); /* data is there, let's wait for keygen_complete to be called */ if (ret == 1) return; /* no data, report error and reset kg_st */ if (ret == 0) { if (WIFSIGNALED (status)) otr_noticest (TXT_KG_EXITSIG, kg_st.accountname, g_strsignal (WTERMSIG (status))); else otr_noticest (TXT_KG_EXITED, kg_st.accountname); } else if (ret == -1) otr_noticest (TXT_KG_POLLERR, kg_st.accountname, strerror (errno)); keygen_abort (FALSE); } /* * Installed as g_io_watch and called when the key generation * process finishs. */ static gboolean keygen_complete (GIOChannel *source, GIOCondition condition, gpointer data) { gcry_error_t err; const char *confdir = get_configdir (); char *filename = g_strconcat (confdir, KEYFILE, NULL); char *tmpfilename = g_strconcat (confdir, TMPKEYFILE, NULL); if (-1 == read (g_io_channel_unix_get_fd (kg_st.ch[0]), &err, sizeof(err))) { otr_noticest (TXT_KG_FAILED, kg_st.accountname, strerror(errno), "read failed"); } g_io_channel_shutdown (kg_st.ch[0], FALSE, NULL); g_io_channel_shutdown (kg_st.ch[1], FALSE, NULL); g_io_channel_unref (kg_st.ch[0]); g_io_channel_unref (kg_st.ch[1]); if (err) otr_noticest (TXT_KG_FAILED, kg_st.accountname, gcry_strerror (err), gcry_strsource (err)); else { /* reload keys */ otr_noticest (TXT_KG_COMPLETED, kg_st.accountname, time (NULL) - kg_st.started); rename (tmpfilename, filename); key_load (); } g_source_remove (kg_st.cwid); kg_st.cwid = g_child_watch_add (kg_st.pid, keygen_childwatch, (void *)1); kg_st.status = KEYGEN_NO; g_free (kg_st.accountname); g_free (filename); g_free (tmpfilename); return FALSE; } /* * Run key generation in a separate process (takes ages). * The other process will rewrite the key file, we shouldn't * change anything till it's done and we've reloaded the keys. */ void keygen_run (const char *accname) { gcry_error_t err; int ret; int fds[2]; char *filename = g_strconcat (get_configdir (), TMPKEYFILE, NULL); char *dir = g_path_get_dirname (filename); if (kg_st.status != KEYGEN_NO) { if (strcmp (accname, kg_st.accountname) != 0) otr_noticest (TXT_KG_ABORTED_DUP, accname, kg_st.accountname); return; } if (!g_file_test (dir, G_FILE_TEST_EXISTS)) { if (g_mkdir (dir, S_IRWXU)) { otr_noticest (TXT_KG_ABORTED_DIR, accname, dir, strerror (errno)); g_free (dir); g_free (filename); return; } else otr_noticest (TXT_KG_MKDIR, dir); } g_free (dir); if (pipe (fds) != 0) { otr_noticest (TXT_KG_PIPE, accname, strerror (errno)); g_free (filename); return; } kg_st.ch[0] = g_io_channel_unix_new (fds[0]); kg_st.ch[1] = g_io_channel_unix_new (fds[1]); kg_st.accountname = g_strdup (accname); kg_st.protocol = PROTOCOLID; kg_st.started = time (NULL); if ((ret = fork ())) { g_free (filename); if (ret == -1) { otr_noticest (TXT_KG_FORK, accname, strerror (errno)); return; } kg_st.status = KEYGEN_RUNNING; kg_st.pid = ret; otr_noticest (TXT_KG_INITIATED, accname); kg_st.cpid = g_io_add_watch (kg_st.ch[0], G_IO_IN, (GIOFunc)keygen_complete, NULL); kg_st.cwid = g_child_watch_add (kg_st.pid, keygen_childwatch, NULL); kg_st.started = time (NULL); return; } /* child */ err = otrl_privkey_generate (otr_state, filename, accname, PROTOCOLID); if (sizeof(err) != write (fds[1], &err, sizeof(err))) { perror("keygen_run - unable to write to stdout pipe"); } _exit (0); } /* * Abort ongoing key generation. */ void keygen_abort (int ignoreidle) { if (kg_st.status != KEYGEN_RUNNING) { if (!ignoreidle) otr_noticest (TXT_KG_NOABORT); return; } otr_noticest (TXT_KG_ABORT, kg_st.accountname); g_source_remove (kg_st.cpid); g_source_remove (kg_st.cwid); g_free (kg_st.accountname); if (kg_st.pid != 0) { kill (kg_st.pid, SIGTERM); g_child_watch_add (kg_st.pid, keygen_childwatch, (void *)1); } kg_st.status = KEYGEN_NO; } /* * Write fingerprints to file. */ void otr_writefps () { gcry_error_t err; char *filename = g_strconcat (get_configdir (), FPSFILE, NULL); err = otrl_privkey_write_fingerprints (otr_state, filename); if (err == GPG_ERR_NO_ERROR) { otr_noticest (TXT_FP_SAVED); } else { otr_noticest (TXT_FP_SAVE_ERROR, gcry_strerror (err), gcry_strsource (err)); } g_free (filename); } /* * Load private keys. */ void key_load () { gcry_error_t err; char *filename = g_strconcat (get_configdir (), KEYFILE, NULL); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { otr_noticest (TXT_KEY_NOT_FOUND); return; } err = otrl_privkey_read (otr_state, filename); if (err == GPG_ERR_NO_ERROR) { otr_noticest (TXT_KEY_LOADED); } else { otr_noticest (TXT_KEY_LOAD_ERROR, gcry_strerror (err), gcry_strsource (err)); } g_free (filename); } /* * Load fingerprints. */ void fps_load () { gcry_error_t err; char *filename = g_strconcat (get_configdir (), FPSFILE, NULL); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { otr_noticest (TXT_FP_NOT_FOUND); return; } err = otrl_privkey_read_fingerprints (otr_state, filename, NULL, NULL); if (err == GPG_ERR_NO_ERROR) { otr_noticest (TXT_FP_LOADED); } else { otr_noticest (TXT_FP_LOAD_ERROR, gcry_strerror (err), gcry_strsource (err)); } g_free (filename); } /* * Write instance tags to file. */ void otr_writeinstags(void) { gcry_error_t err; char *filename = g_strconcat(get_configdir (), INSTAGFILE, NULL); err = otrl_instag_write (otr_state, filename); if (err == GPG_ERR_NO_ERROR) { otr_noticest(TXT_INSTAG_SAVED); } else { otr_noticest(TXT_INSTAG_SAVE_ERROR, gcry_strerror(err), gcry_strsource(err)); } g_free(filename); } /* * Load instance tags. */ void instag_load(void) { gcry_error_t err; char *filename = g_strconcat(get_configdir (), INSTAGFILE, NULL); if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { otr_noticest(TXT_INSTAG_NOT_FOUND); return; } err = otrl_instag_read(otr_state, filename); if (err == GPG_ERR_NO_ERROR) { otr_noticest(TXT_INSTAG_LOADED); } else { otr_noticest(TXT_INSTAG_LOAD_ERROR, gcry_strerror(err), gcry_strsource(err)); } g_free(filename); } hexchat-otr-0.2.2/src/otr_ops.c000066400000000000000000000167111307355210600163710ustar00rootroot00000000000000/* * Off-the-Record Messaging (OTR) module for the irssi IRC client * Copyright (C) 2008 Uli Meis * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include "otr.h" OtrlMessageAppOps otr_ops; extern OtrlUserState otr_state; extern GSList *plistunknown, *plistknown; OtrlPolicy IO_DEFAULT_OTR_POLICY = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE; /* * Return policy for given context based on the otr_policy /setting */ static OtrlPolicy ops_policy (void *opdata, ConnContext *context) { struct co_info *coi = context->app_data; char *server = strchr (context->accountname, '@') + 1; OtrlPolicy op = IO_DEFAULT_OTR_POLICY; GSList *pl; char fullname[1024]; g_snprintf (fullname, sizeof(fullname), "%s@%s", context->username, server); /* loop through otr_policy */ if (plistunknown) { pl = plistunknown; do { struct plistentry *ple = pl->data; if (g_pattern_match_string (ple->namepat, fullname)) op = ple->policy; } while ((pl = g_slist_next (pl))); } if (plistknown && context->fingerprint_root.next) { pl = plistknown; /* loop through otr_policy_known */ do { struct plistentry *ple = pl->data; if (g_pattern_match_string (ple->namepat, fullname)) op = ple->policy; } while ((pl = g_slist_next (pl))); } if (coi && coi->finished && (op == OTRL_POLICY_OPPORTUNISTIC || op == OTRL_POLICY_ALWAYS)) op = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE; return op; } /* * Request for key generation. * The lib actually expects us to be finished before the call returns. * Since this can take more than an hour on some systems there isn't even * a point in trying... */ static void ops_create_privkey (void *opdata, const char *accountname, const char *protocol) { keygen_run (accountname); } /* * Inject OTR message. * Deriving the server is currently a hack, * need to derive the server from accountname. */ static void ops_inject_msg (void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { IRC_CTX *a_serv; char *msgcopy = g_strdup (message); /* OTR sometimes gives us multiple lines * (e.g. the default query (a.k.a. "better") message) */ g_strdelimit (msgcopy, "\n", ' '); a_serv = opdata; if (!a_serv) { otr_notice (a_serv, recipient, TXT_OPS_INJECT, accountname, recipient, message); } else { irc_send_message (a_serv, recipient, msgcopy); } g_free (msgcopy); } static void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err) { IRC_CTX *server = opdata; char *username = context->username; otr_debug (server, username, TXT_OPS_HANDLE_MSG, otr_msg_event_txt[msg_event], message); } /* * Gone secure. */ static void ops_secure (void *opdata, ConnContext *context) { struct co_info *coi = context->app_data; char *trust = context->active_fingerprint->trust ?: ""; char ownfp[45], peerfp[45]; otr_notice (coi->ircctx, context->username, TXT_OPS_SEC); if (*trust != '\0') return; /* not authenticated. * Let's print out the fingerprints for comparison */ otrl_privkey_hash_to_human (peerfp, context->active_fingerprint->fingerprint); otr_notice (coi->ircctx, context->username, TXT_OPS_FPCOMP, otrl_privkey_fingerprint (otr_state, ownfp, context->accountname, PROTOCOLID), context->username, peerfp); } /* * Gone insecure. */ static void ops_insecure (void *opdata, ConnContext *context) { struct co_info *coi = context->app_data; otr_notice (coi->ircctx, context->username, TXT_OPS_INSEC); } /* * Still secure? Need to find out what that means... */ static void ops_still_secure (void *opdata, ConnContext *context, int is_reply) { struct co_info *coi = context->app_data; otr_notice (coi->ircctx, context->username, is_reply ? TXT_OPS_STILL_REPLY : TXT_OPS_STILL_NO_REPLY); } /* * Really critical with IRC. * Unfortunately, we can't tell our peer which size to use. * (reminds me of MTU determination...) */ static int ops_max_msg (void *opdata, ConnContext *context) { return OTR_MAX_MSG_SIZE; } static void ops_create_instag (void *opdata, const char *accountname, const char *protocol) { otrl_instag_generate(otr_state, "/dev/null", accountname, protocol); otr_writeinstags(); } /* * Save fingerprint changes. */ static void ops_writefps (void *data) { otr_writefps (); } static int ops_is_logged_in (void *opdata, const char *accountname, const char *protocol, const char *recipient) { /*TODO register a handler for event 401 no such nick and set * a variable offline=TRUE. Reset it to false in otr_receive and * otr_send */ return TRUE; } void otr_status_change (IRC_CTX *ircctx, const char *nick, int event) { /* TODO: ? */ } static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question) { IRC_CTX *ircctx = (opdata); char *from = context->username; struct co_info *coi = context->app_data; coi->received_smp_init = (smp_event == OTRL_SMPEVENT_ASK_FOR_SECRET) || (smp_event == OTRL_SMPEVENT_ASK_FOR_ANSWER); switch (smp_event) { case OTRL_SMPEVENT_ASK_FOR_SECRET: otr_notice(ircctx, from, TXT_AUTH_PEER, from); otr_status_change(ircctx, from, IO_STC_SMP_INCOMING); break; case OTRL_SMPEVENT_ASK_FOR_ANSWER: otr_notice(ircctx, from, TXT_AUTH_PEER_QA, from, question); otr_status_change(ircctx, from, IO_STC_SMP_INCOMING); break; case OTRL_SMPEVENT_IN_PROGRESS: otr_notice(ircctx, from, TXT_AUTH_PEER_REPLIED, from); otr_status_change(ircctx, from, IO_STC_SMP_FINALIZE); break; case OTRL_SMPEVENT_SUCCESS: otr_notice(ircctx, from, TXT_AUTH_SUCCESSFUL); otr_status_change(ircctx, from, IO_STC_SMP_SUCCESS); break; case OTRL_SMPEVENT_ABORT: otr_abort_auth(context, ircctx, from); otr_status_change(ircctx, from, IO_STC_SMP_ABORTED); break; case OTRL_SMPEVENT_FAILURE: case OTRL_SMPEVENT_CHEATED: case OTRL_SMPEVENT_ERROR: otr_notice(ircctx, from, TXT_AUTH_FAILED); coi->smp_failed = TRUE; otr_status_change(ircctx, from, IO_STC_SMP_FAILED); break; default: otr_logst(MSGLEVEL_CRAP, "Received unknown SMP event"); break; } } /* * Initialize our OtrlMessageAppOps */ void otr_initops () { memset (&otr_ops, 0, sizeof(otr_ops)); otr_ops.policy = ops_policy; otr_ops.create_privkey = ops_create_privkey; otr_ops.inject_message = ops_inject_msg; otr_ops.handle_msg_event = ops_handle_msg_event; otr_ops.create_instag = ops_create_instag; otr_ops.handle_smp_event = ops_smp_event; otr_ops.gone_secure = ops_secure; otr_ops.gone_insecure = ops_insecure; otr_ops.still_secure = ops_still_secure; otr_ops.max_message_size = ops_max_msg; otr_ops.write_fingerprints = ops_writefps; otr_ops.is_logged_in = ops_is_logged_in; } hexchat-otr-0.2.2/src/otr_util.c000066400000000000000000000377461307355210600165600ustar00rootroot00000000000000/* * Off-the-Record Messaging (OTR) module for the irssi IRC client * Copyright (C) 2008 Uli Meis * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA */ #include "otr.h" #include OtrlUserState otr_state = NULL; extern OtrlMessageAppOps otr_ops; static int otrinited = FALSE; GSList *plistunknown = NULL; GSList *plistknown = NULL; GRegex *regex_policies = NULL; #define MSGQUEUE_LEN 4096 /* * init otr lib. */ int otrlib_init () { if (!otrinited) { OTRL_INIT; otrinited = TRUE; } otr_state = otrl_userstate_create (); /* load keys and fingerprints */ instag_load (); key_load (); fps_load (); otr_initops (); regex_policies = g_regex_new ("([^,]+) (never|manual|handlews|opportunistic|always)" "(,|$)", 0, 0, NULL); return otr_state == NULL; } /* * deinit otr lib. */ void otrlib_deinit () { if (otr_state) { otr_writefps (); otrl_userstate_free (otr_state); otr_state = NULL; } keygen_abort (TRUE); otr_setpolicies ("", FALSE); otr_setpolicies ("", TRUE); g_regex_unref (regex_policies); } /* * Free our app data. */ static void context_free_app_info (void *data) { struct co_info *coi = data; if (coi->msgqueue) { g_free (coi->msgqueue); } if (coi->ircctx) IRCCTX_FREE (coi->ircctx); } /* * Add app data to context. * See struct co_info for details. */ static void context_add_app_info (void *data, ConnContext *co) { IRC_CTX *ircctx = IRCCTX_DUP (data); struct co_info *coi = g_malloc (sizeof(struct co_info)); memset (coi, 0, sizeof(struct co_info)); co->app_data = coi; co->app_data_free = context_free_app_info; coi->ircctx = ircctx; #pragma GCC diagnostic ignored "-Wformat-nonliteral" g_snprintf (coi->better_msg_two, sizeof(coi->better_msg_two), formats[TXT_OTR_BETTER_TWO].def, co->accountname); #pragma GCC diagnostic pop } /* * Get a context from a pair. */ ConnContext *otr_getcontext (const char *accname, const char *nick, int create, void *data) { ConnContext *co = otrl_context_find ( otr_state, nick, accname, PROTOCOLID, OTRL_INSTAG_BEST, create, FALSE, context_add_app_info, data); /* context came from a fingerprint */ if (co && data && !co->app_data) context_add_app_info (data, co); return co; } /* * Hand the given message to OTR. * Returns NULL if OTR handled the message and * the original message otherwise. */ char *otr_send (IRC_CTX *ircctx, const char *msg, const char *to) { const char *nick = IRCCTX_NICK (ircctx); const char *address = IRCCTX_ADDR (ircctx); gcry_error_t err; char *newmessage = NULL; ConnContext *co; char accname[256]; g_snprintf (accname, sizeof(accname), "%s@%s", nick, address); if (!(co = otr_getcontext (accname, to, TRUE, ircctx))) { otr_noticest (TXT_CTX_NOT_CREATE, accname, to); return NULL; } err = otrl_message_sending ( otr_state, &otr_ops, ircctx, accname, PROTOCOLID, to, OTRL_INSTAG_BEST, msg, NULL, &newmessage, OTRL_FRAGMENT_SEND_ALL, &co, context_add_app_info, ircctx); if (err != 0) { otr_notice (ircctx, to, TXT_SEND_FAILED, msg); return NULL; } if (newmessage == NULL) return (char *)msg; return NULL; } struct ctxlist_ *otr_contexts () { ConnContext *context; Fingerprint *fprint; struct ctxlist_ *ctxlist = NULL, *ctxhead = NULL; struct fplist_ *fplist, *fphead; char fp[41]; char *trust; int i; for (context = otr_state->context_root; context; context = context->next) { if (!ctxlist) ctxhead = ctxlist = g_malloc0 (sizeof(struct ctxlist_)); else ctxlist = ctxlist->next = g_malloc0 (sizeof(struct ctxlist_)); switch (context->msgstate) { case OTRL_MSGSTATE_PLAINTEXT: ctxlist->state = STUNENCRYPTED; break; case OTRL_MSGSTATE_ENCRYPTED: ctxlist->state = STENCRYPTED; break; case OTRL_MSGSTATE_FINISHED: ctxlist->state = STFINISHED; break; default: ctxlist->state = STUNKNOWN; break; } ctxlist->username = context->username; ctxlist->accountname = context->accountname; fplist = fphead = NULL; for (fprint = context->fingerprint_root.next; fprint; fprint = fprint->next) { if (!fplist) fphead = fplist = g_malloc0 (sizeof(struct fplist_)); else fplist = fplist->next = g_malloc0 (sizeof(struct fplist_)); trust = fprint->trust ?: ""; for (i = 0; i < 20; ++i) sprintf (fp + i * 2, "%02x", fprint->fingerprint[i]); fplist->fp = g_strdup (fp); if (*trust == '\0') fplist->authby = NOAUTH; else if (strcmp (trust, "smp") == 0) fplist->authby = AUTHSMP; else fplist->authby = AUTHMAN; } ctxlist->fplist = fphead; } return ctxhead; } /* * Get the OTR status of this conversation. */ int otr_getstatus(IRC_CTX *ircctx, const char *nick) { ConnContext *co; char accname[128]; struct co_info *coi; g_snprintf (accname, sizeof(accname), "%s@%s", ircctx->nick, ircctx->address); if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) { return IO_ST_PLAINTEXT; } coi = co->app_data; switch (co->msgstate) { case OTRL_MSGSTATE_PLAINTEXT: return IO_ST_PLAINTEXT; case OTRL_MSGSTATE_ENCRYPTED: { char *trust = co->active_fingerprint->trust; int ex = co->smstate->nextExpected; int code = 0; switch (ex) { case OTRL_SMP_EXPECT1: if (coi->received_smp_init) code = IO_ST_SMP_INCOMING; break; case OTRL_SMP_EXPECT2: code = IO_ST_SMP_OUTGOING; break; case OTRL_SMP_EXPECT3: case OTRL_SMP_EXPECT4: code = IO_ST_SMP_FINALIZE; break; default: otr_logst( MSGLEVEL_CRAP, "Encountered unknown SMP state in libotr, please let maintainers know"); return IO_ST_UNKNOWN; } if (trust && (*trust != '\0')) code |= strcmp(trust, "smp") == 0 ? IO_ST_TRUST_SMP : IO_ST_TRUST_MANUAL; else code |= IO_ST_UNTRUSTED; return code; } case OTRL_MSGSTATE_FINISHED: return IO_ST_FINISHED; default: otr_logst( MSGLEVEL_CRAP, "BUG Found! Please write us a mail and describe how you got here"); return IO_ST_UNKNOWN; } } /* * Finish the conversation. */ void otr_finish (IRC_CTX *ircctx, char *nick, const char *peername, int inquery) { ConnContext *co; char accname[128]; struct co_info *coi; char *pserver = NULL; if (peername) { pserver = strchr (peername, '@'); if (!pserver) return; ircctx = server_find_address (pserver + 1); if (!ircctx) return; *pserver = '\0'; nick = (char *)peername; } g_snprintf (accname, sizeof(accname), "%s@%s", IRCCTX_NICK (ircctx), IRCCTX_ADDR (ircctx)); if (!(co = otr_getcontext (accname, nick, FALSE, NULL))) { if (inquery) otr_noticest (TXT_CTX_NOT_FOUND, accname, nick); if (peername) *pserver = '@'; return; } otrl_message_disconnect (otr_state, &otr_ops, ircctx, accname, PROTOCOLID, nick, OTRL_INSTAG_BEST); if (inquery) { otr_info (ircctx, nick, TXT_CMD_FINISH, nick, IRCCTX_ADDR (ircctx)); } else { otr_infost (TXT_CMD_FINISH, nick, IRCCTX_ADDR (ircctx)); } coi = co->app_data; /* finish if /otr finish has been issued. Reset if * we're called cause the query window has been closed. */ if (coi) coi->finished = inquery; /* write the finished into the master as well */ co = otrl_context_find( otr_state, nick, accname, PROTOCOLID, OTRL_INSTAG_MASTER, FALSE, NULL, NULL, NULL); coi = co->app_data; if (coi) coi->finished = inquery; if (peername) *pserver = '@'; } void otr_finishall () { ConnContext *context; int finished = 0; for (context = otr_state->context_root; context; context = context->next) { struct co_info *coi = context->app_data; if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) continue; otrl_message_disconnect (otr_state, &otr_ops, coi->ircctx, context->accountname, PROTOCOLID, context->username, OTRL_INSTAG_BEST); otr_infost (TXT_CMD_FINISH, context->username, IRCCTX_ADDR (coi->ircctx)); finished++; } if (!finished) otr_infost (TXT_CMD_FINISHALL_NONE); } /* * Trust our peer. */ void otr_trust (IRC_CTX *ircctx, char *nick, const char *peername) { ConnContext *co; char accname[128]; struct co_info *coi; char *pserver = NULL; if (peername) { pserver = strchr (peername, '@'); if (!pserver) return; ircctx = server_find_address (pserver + 1); if (!ircctx) return; *pserver = '\0'; nick = (char *)peername; } g_snprintf (accname, sizeof(accname), "%s@%s", IRCCTX_NICK (ircctx), IRCCTX_ADDR (ircctx)); if (!(co = otr_getcontext (accname, nick, FALSE, NULL))) { otr_noticest (TXT_CTX_NOT_FOUND, accname, nick); if (peername) *pserver = '@'; return; } otrl_context_set_trust (co->active_fingerprint, "manual"); coi = co->app_data; coi->smp_failed = FALSE; otr_notice (ircctx, nick, TXT_FP_TRUST, nick); if (peername) *pserver = '@'; } /* * Abort any ongoing SMP authentication. */ void otr_abort_auth (ConnContext *co, IRC_CTX *ircctx, const char *nick) { struct co_info *coi; coi = co->app_data; coi->received_smp_init = FALSE; otr_notice (ircctx, nick, co->smstate->nextExpected != OTRL_SMP_EXPECT1 ? TXT_AUTH_ABORTED_ONGOING : TXT_AUTH_ABORTED); otrl_message_abort_smp (otr_state, &otr_ops, ircctx, co); } /* * implements /otr authabort */ void otr_authabort (IRC_CTX *ircctx, char *nick, const char *peername) { ConnContext *co; char accname[128]; char *pserver = NULL; if (peername) { pserver = strchr (peername, '@'); if (!pserver) return; ircctx = server_find_address (pserver + 1); if (!ircctx) return; *pserver = '\0'; nick = (char *)peername; } g_snprintf (accname, sizeof(accname), "%s@%s", IRCCTX_NICK (ircctx), IRCCTX_ADDR (ircctx)); if (!(co = otr_getcontext (accname, nick, FALSE, NULL))) { otr_noticest (TXT_CTX_NOT_FOUND, accname, nick); if (peername) *pserver = '@'; return; } otr_abort_auth (co, ircctx, nick); if (peername) *pserver = '@'; } /* * Initiate or respond to SMP authentication. */ void otr_auth (IRC_CTX *ircctx, char *nick, const char *peername, const char *question, const char *secret) { ConnContext *co; char accname[128]; struct co_info *coi; char *pserver = NULL; if (peername) { pserver = strchr (peername, '@'); if (!pserver) return; ircctx = server_find_address (pserver + 1); if (!ircctx) return; *pserver = '\0'; nick = (char *)peername; } g_snprintf (accname, sizeof(accname), "%s@%s", IRCCTX_NICK (ircctx), IRCCTX_ADDR (ircctx)); if (!(co = otr_getcontext (accname, nick, FALSE, NULL))) { otr_noticest (TXT_CTX_NOT_FOUND, accname, nick); if (peername) *pserver = '@'; return; } if (co->msgstate != OTRL_MSGSTATE_ENCRYPTED) { otr_notice (ircctx, nick, TXT_AUTH_NEEDENC); return; } coi = co->app_data; /* Aborting an ongoing auth */ if (co->smstate->nextExpected != OTRL_SMP_EXPECT1) otr_abort_auth (co, ircctx, nick); coi->smp_failed = FALSE; /* reset trust level */ if (co->active_fingerprint) { char *trust = co->active_fingerprint->trust; if (trust && (*trust != '\0')) { otrl_context_set_trust (co->active_fingerprint, ""); otr_writefps (); } } if (!coi->received_smp_init) if (question) otrl_message_initiate_smp_q( otr_state, &otr_ops, ircctx, co, question, (unsigned char*)secret, strlen (secret)); else otrl_message_initiate_smp ( otr_state, &otr_ops, ircctx, co, (unsigned char *)secret, strlen (secret)); else otrl_message_respond_smp ( otr_state, &otr_ops, ircctx, co, (unsigned char *)secret, strlen (secret)); otr_notice (ircctx, nick, coi->received_smp_init ? TXT_AUTH_RESPONDING : TXT_AUTH_INITIATED); if (peername) *pserver = '@'; } /* * Hand the given message to OTR. * Returns NULL if its an OTR protocol message and * the (possibly) decrypted message otherwise. */ char *otr_receive (IRC_CTX *ircctx, const char *msg, const char *from) { int ignore_message; char *newmessage = NULL; char accname[256]; ConnContext *co; struct co_info *coi; OtrlTLV *tlvs; g_snprintf (accname, sizeof(accname), "%s@%s", IRCCTX_NICK (ircctx), IRCCTX_ADDR (ircctx)); if (!(co = otr_getcontext (accname, from, TRUE, ircctx))) { otr_noticest (TXT_CTX_NOT_CREATE, accname, from); return NULL; } coi = co->app_data; /* Really lame but I don't see how you could do this in a generic * way unless the IRC server would somehow mark continuation messages. */ if ((strcmp (msg, coi->better_msg_two) == 0) || (strcmp (msg, formats[TXT_OTR_BETTER_THREE].def) == 0)) { otr_debug (ircctx, from, TXT_RECEIVE_IGNORE_QUERY); return NULL; } /* The server might have split lines that were too long * (bitlbee does that). The heuristic is simple: If we can find ?OTR: * in the message but it doesn't end with a ".", queue it and wait * for the rest. */ if (coi->msgqueue) { /* already something in the queue */ g_strlcat (coi->msgqueue, msg, MSGQUEUE_LEN); /* wait for more? */ if ((strlen (msg) > OTR_MAX_MSG_SIZE) && (msg[strlen (msg) - 1] != '.') && (msg[strlen (msg) - 1] != ',')) return NULL; otr_debug (ircctx, from, TXT_RECEIVE_DEQUEUED, strlen (coi->msgqueue)); msg = coi->msgqueue; coi->msgqueue = NULL; /* this is freed thru our caller by otrl_message_free. * Currently ok since that just uses free(). */ } else if (strstr (msg, "?OTR:") && (strlen (msg) > OTR_MAX_MSG_SIZE) && (msg[strlen (msg) - 1] != '.') && (msg[strlen (msg) - 1] != ',')) { coi->msgqueue = g_malloc (MSGQUEUE_LEN); g_strlcpy (coi->msgqueue, msg, MSGQUEUE_LEN); otr_debug (ircctx, from, TXT_RECEIVE_QUEUED, strlen (msg)); return NULL; } ignore_message = otrl_message_receiving ( otr_state, &otr_ops, ircctx, accname, PROTOCOLID, from, msg, &newmessage, &tlvs, &co, NULL, NULL); if (tlvs) { OtrlTLV *tlv = otrl_tlv_find (tlvs, OTRL_TLV_DISCONNECTED); if (tlv) { otr_status_change (ircctx, from, IO_STC_PEER_FINISHED); otr_notice (ircctx, from, TXT_PEER_FINISHED, from); } } if (ignore_message) { otr_debug (ircctx, from, TXT_RECEIVE_IGNORE, strlen (msg), accname, from, msg); return NULL; } if (newmessage) otr_debug (ircctx, from, TXT_RECEIVE_CONVERTED); return newmessage ?: (char *)msg; } void otr_setpolicies (const char *policies, int known) { GMatchInfo *match_info; GSList *plist = known ? plistknown : plistunknown; if (plist) { GSList *p = plist; do { struct plistentry *ple = p->data; g_pattern_spec_free (ple->namepat); g_free (p->data); } while ((p = g_slist_next (p))); g_slist_free (plist); plist = NULL; } g_regex_match (regex_policies, policies, 0, &match_info); while (g_match_info_matches (match_info)) { struct plistentry *ple = (struct plistentry *)g_malloc0 (sizeof(struct plistentry)); char *pol = g_match_info_fetch (match_info, 2); ple->namepat = g_pattern_spec_new (g_match_info_fetch (match_info, 1)); switch (*pol) { case 'n': ple->policy = OTRL_POLICY_NEVER; break; case 'm': ple->policy = OTRL_POLICY_MANUAL; break; case 'h': ple->policy = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE; break; case 'o': ple->policy = OTRL_POLICY_OPPORTUNISTIC; break; case 'a': ple->policy = OTRL_POLICY_ALWAYS; break; } plist = g_slist_append (plist, ple); g_free (pol); g_match_info_next (match_info, NULL); } g_match_info_free (match_info); if (known) plistknown = plist; else plistunknown = plist; }