├── AUTHORS ├── bootstrap ├── help ├── Makefile.am └── otr ├── .gitignore ├── Makefile.am ├── src ├── Makefile.am ├── module.h ├── otr-formats.c ├── otr-formats.h ├── utils.h ├── cmd.h ├── key.h ├── irssi-otr.h ├── otr.h ├── utils.c ├── key.c ├── module.c ├── cmd.c ├── otr-ops.c └── otr.c ├── irssi-otr.spec.in ├── ChangeLog ├── configure.ac ├── config └── libotr.m4 ├── README.md ├── INSTALL └── LICENSE /AUTHORS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -x 4 | if [ ! -e config ]; then 5 | mkdir config 6 | fi 7 | 8 | autoreconf -i -s 9 | -------------------------------------------------------------------------------- /help/Makefile.am: -------------------------------------------------------------------------------- 1 | # The day Irssi will be able to check in $(prefix), this will change. Until 2 | # then, it's hardcoded. 3 | helpdir = $(datarootdir)/irssi/help 4 | help_DATA = otr 5 | 6 | EXTRA_DIST = otr 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.swp 4 | *.o 5 | *.swo 6 | *.pyc 7 | Makefile 8 | .libs/ 9 | .deps/ 10 | *~ 11 | *.la 12 | *.lo 13 | Makefile.in 14 | *.loT 15 | *.info 16 | *.bz2 17 | *.tar 18 | configure 19 | aclocal.m4 20 | autom4te.cache/ 21 | config.h 22 | config.h.in 23 | config.log 24 | config.status 25 | stamp-h1 26 | libtool 27 | tags 28 | cscope.* 29 | 30 | config/ 31 | !config/libotr.m4 32 | 33 | irssi-otr.spec 34 | rpmbuild/ 35 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = $(PACKAGE_CFLAGS) 2 | 3 | ACLOCAL_AMFLAGS = -I config 4 | 5 | SUBDIRS = help \ 6 | src 7 | 8 | EXTRA_DIST = README.md LICENSE ChangeLog bootstrap 9 | 10 | EXTRA_DIST += irssi-otr.spec.in 11 | 12 | RPM_DIRS = --define "_sourcedir `pwd`" \ 13 | --define "_rpmdir `pwd`/rpmbuild" \ 14 | --define "_specdir `pwd`" \ 15 | --define "_builddir `pwd`/rpmbuild" \ 16 | --define "_srcrpmdir `pwd`/rpmbuild" 17 | 18 | rpm: dist-bzip2 irssi-otr.spec 19 | rpmbuild $(RPM_DIRS) -ba irssi-otr.spec 20 | srpm: dist-bzip2 irssi-otr.spec 21 | rpmbuild $(RPM_DIRS) -bs irssi-otr.spec 22 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = $(LIBOTR_CFLAGS) $(LIBGCRYPT_CFLAGS) $(PACKAGE_FLAGS) 2 | 3 | IRSSI_DIST=$(oldincludedir)/irssi 4 | IRSSI_INCLUDE = -I$(IRSSI_DIST) \ 5 | -I$(IRSSI_DIST)/src \ 6 | -I$(IRSSI_DIST)/src/fe-common/core \ 7 | -I$(IRSSI_DIST)/src/core \ 8 | -I$(IRSSI_DIST)/src/fe-text \ 9 | -I$(IRSSI_DIST)/src/irc \ 10 | -I$(IRSSI_DIST)/src/irc/core \ 11 | -I$(IRSSI_DIST)/src/irc/dcc \ 12 | -I$(IRSSI_DIST)/src/irc/notifylist 13 | 14 | AM_CPPFLAGS = -I$(top_srcdir)/src $(IRSSI_INCLUDE) 15 | 16 | plugindir = $(IRSSI_MODULE_DIR) 17 | plugin_LTLIBRARIES = libotr.la 18 | 19 | libotr_la_SOURCES = otr-formats.c otr-formats.h \ 20 | key.c key.h cmd.c cmd.h otr.c otr-ops.c \ 21 | utils.h utils.c otr.h module.c module.h irssi-otr.h 22 | 23 | libotr_la_LDFLAGS = -avoid-version -module 24 | libotr_la_LDFLAGS += $(LIBOTR_LIBS) $(LIBGCRYPT_LIBS) -lpthread 25 | 26 | install-data-hook: 27 | chmod 644 $(DESTDIR)/$(plugindir)/libotr.so 28 | rm $(DESTDIR)/$(plugindir)/libotr.la 29 | 30 | uninstall-hook: 31 | rm $(DESTDIR)/$(plugindir)/libotr.so 32 | -------------------------------------------------------------------------------- /src/module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) module for the irssi IRC client 3 | * 4 | * Copyright (C) 2012 - David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #ifndef IRSSI_OTR_MODULE 22 | #define IRSSI_OTR_MODULE 23 | 24 | void sig_message_private(SERVER_REC *server, const char *msg, 25 | const char *nick, const char *address); 26 | 27 | #endif /* IRSSI_OTR_MODULE */ 28 | -------------------------------------------------------------------------------- /irssi-otr.spec.in: -------------------------------------------------------------------------------- 1 | Name: irssi-otr 2 | Version: @PACKAGE_VERSION@ 3 | Release: 1%{?dist} 4 | Summary: Off-The-Record Messaging plugin for irssi 5 | Group: Applications/Internet 6 | License: GPLv2+ 7 | URL: https://github.com/cryptodotis/irssi-otr 8 | Source0: %{name}-%{version}.tar.bz2 9 | 10 | BuildRequires: glib2-devel >= 2.13 11 | BuildRequires: irssi-devel 12 | BuildRequires: libotr-devel >= 4.1.0 13 | BuildRequires: pkgconfig 14 | Requires: irssi 15 | 16 | %description 17 | This provides module which implements Off-The-Record (OTR) 18 | Messaging for the irssi IRC client. 19 | 20 | %prep 21 | %setup -q 22 | 23 | %build 24 | #./bootstrap 25 | %configure --with-irssi-module-dir=%{_libdir}/irssi/modules 26 | make %{?_smp_mflags} 27 | 28 | %install 29 | make install DESTDIR=%{buildroot} 30 | # needed for debuginfo subpackage 31 | chmod +x %{buildroot}%{_libdir}/irssi/modules/libotr.so 32 | 33 | %files 34 | %doc README.md LICENSE ChangeLog 35 | %{_libdir}/irssi/modules/libotr.so 36 | %{_datadir}/irssi/help/otr 37 | 38 | %changelog 39 | * Sun Mar 09 2014 Martin Milata - 1.0-1 40 | - Initial package based on Conrad Meyer's spec for irc-otr. 41 | -------------------------------------------------------------------------------- /src/otr-formats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) - 2012 David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #include "otr.h" 22 | #include "otr-formats.h" 23 | 24 | FORMAT_REC otr_formats[] = { 25 | { MODULE_NAME, "OTR", 0 }, 26 | 27 | /* Status bar format. */ 28 | { NULL, "Statusbar", 0 } , 29 | 30 | { "stb_plaintext", "{sb plaintext}", 0}, 31 | { "stb_finished", "{sb %yfinished%n}", 0}, 32 | { "stb_unknown", "{sb {hilight state unknown (BUG!)}}", 0}, 33 | { "stb_untrusted", "{sb %GOTR%n (%runverified%n)}", 0}, 34 | { "stb_trust", "{sb %GOTR%n}", 0}, 35 | 36 | /* Last element. */ 37 | { NULL, NULL, 0 } 38 | }; 39 | -------------------------------------------------------------------------------- /src/otr-formats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) - 2012 David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #ifndef IRSSI_OTR_FORMATS_H 22 | #define IRSSI_OTR_FORMATS_H 23 | 24 | #include "irssi-otr.h" 25 | 26 | /* 27 | * Must be in sync with the otr_formats array. 28 | */ 29 | enum otr_status_format { 30 | TXT_OTR_MODULE_NAME = 0, 31 | TXT_OTR_FILL_1 = 1, 32 | TXT_STB_PLAINTEXT = 2, 33 | TXT_STB_FINISHED = 3, 34 | TXT_STB_UNKNOWN = 4, 35 | TXT_STB_UNTRUSTED = 5, 36 | TXT_STB_TRUST = 6, 37 | }; 38 | 39 | extern FORMAT_REC otr_formats[]; 40 | 41 | #endif /* IRSSI_OTR_FORMATS_H */ 42 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2012 - David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #ifndef IRSSI_OTR_UTILS_H 22 | #define IRSSI_OTR_UTILS_H 23 | 24 | void utils_free_args(char ***argv, int argc); 25 | void utils_extract_command(const char *data, char **_cmd); 26 | void utils_explode_args(const char *_data, char ***_argv, int *_argc); 27 | int utils_io_extract_smp(const char *data, char **question, char **secret); 28 | void utils_string_to_upper(char *string); 29 | int utils_auth_extract_secret(const char *_data, char **secret); 30 | void utils_hash_parts_to_readable_hash(const char **parts, char *dst); 31 | char *utils_trim_string(char *s); 32 | char *utils_escape_message(char *s); 33 | 34 | #endif /* IRSSI_OTR_UTILS_H */ 35 | -------------------------------------------------------------------------------- /src/cmd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #ifndef IRSSI_OTR_CMD_H 23 | #define IRSSI_OTR_CMD_H 24 | 25 | #include "otr.h" 26 | 27 | /* 28 | * The /otr commands structure. 29 | */ 30 | struct irssi_commands { 31 | const char *name; 32 | void (*func)(struct otr_user_state *ustate, SERVER_REC *irssi, 33 | const char *target, const void *data); 34 | }; 35 | 36 | /* 37 | * This is called once the command is received and then dispatch to the correct 38 | * func() of the right irssi_commands. 39 | */ 40 | void cmd_generic(struct otr_user_state *ustate, SERVER_REC *irssi, 41 | const char *target, char *cmd, const void *data); 42 | 43 | #endif /* IRSSI_OTR_CMD_H */ 44 | -------------------------------------------------------------------------------- /src/key.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2012 - David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #ifndef IRSSI_OTR_KEY_H 22 | #define IRSSI_OTR_KEY_H 23 | 24 | #include "otr.h" 25 | 26 | /* 27 | * Status of key generation. 28 | */ 29 | enum key_gen_status { 30 | KEY_GEN_IDLE = 0, 31 | KEY_GEN_RUNNING = 1, 32 | KEY_GEN_FINISHED = 2, 33 | KEY_GEN_ERROR = 3, 34 | }; 35 | 36 | /* 37 | * Data of the state of key generation. 38 | */ 39 | struct key_gen_data { 40 | struct otr_user_state *ustate; 41 | char *account_name; 42 | char *key_file_path; 43 | enum key_gen_status status; 44 | gcry_error_t gcry_error; 45 | void *newkey; 46 | }; 47 | 48 | void key_gen_check(void); 49 | void key_gen_run(struct otr_user_state *ustate, const char *account_name); 50 | void key_load(struct otr_user_state *ustate); 51 | void key_load_fingerprints(struct otr_user_state *ustate); 52 | void key_write_fingerprints(struct otr_user_state *ustate); 53 | void key_write_instags(struct otr_user_state *ustate); 54 | 55 | #endif /* IRSSI_OTR_KEY_H */ 56 | -------------------------------------------------------------------------------- /src/irssi-otr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) module for the irssi IRC client 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #ifndef IRSSI_IRSSI_OTR_H 23 | #define IRSSI_IRSSI_OTR_H 24 | 25 | #define UOFF_T_LONG_LONG 1 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | /* Ease our life a bit. */ 45 | #define get_client_config_dir get_irssi_dir 46 | 47 | #define IRSSI_CONN_ADDR(i) i->connrec->address 48 | #define IRSSI_NICK(i) i->nick 49 | 50 | #define OTR_IRSSI_MSG_PREFIX "%9OTR%9: " 51 | 52 | /* 53 | * Irssi macros for printing text to console. 54 | */ 55 | #define IRSSI_MSG(fmt, ...) \ 56 | do { \ 57 | printtext(NULL, NULL, MSGLEVEL_MSGS, OTR_IRSSI_MSG_PREFIX fmt, \ 58 | ## __VA_ARGS__); \ 59 | } while (0) 60 | #define IRSSI_INFO(irssi, username, fmt, ...) \ 61 | do { \ 62 | printtext(irssi, username, MSGLEVEL_CRAP, OTR_IRSSI_MSG_PREFIX fmt, \ 63 | ## __VA_ARGS__); \ 64 | } while (0) 65 | #define IRSSI_NOTICE(irssi, username, fmt, ...) \ 66 | do { \ 67 | printtext(irssi, username, MSGLEVEL_MSGS, OTR_IRSSI_MSG_PREFIX fmt, \ 68 | ## __VA_ARGS__); \ 69 | } while (0) 70 | #define IRSSI_DEBUG(fmt, ...) \ 71 | do { \ 72 | if (debug) { \ 73 | printtext(NULL, NULL, MSGLEVEL_MSGS, OTR_IRSSI_MSG_PREFIX fmt, \ 74 | ## __VA_ARGS__); \ 75 | } \ 76 | } while (0) 77 | 78 | #endif /* IRSSI_IRSSI_OTR_H */ 79 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2016-12-07 irssi-otr 1.0.2 2 | * libotr >= 4.1.0 in configure.ac and spec file 3 | * Merge remote-tracking branch 'ailin/abicheck' 4 | * Don't use INCLUDES anymore and use system includedir for irssi headers 5 | * Merge remote-tracking branch 'anarcat/prefix' 6 | * Fix formatting in tronic escape message patch 7 | * Merge remote-tracking branch 'tronic/master' 8 | * respect $PREFIX on install 9 | * implement ABI check for irssi 0.8.18 10 | * Fix: update README replacing old info 11 | * use https link to otr.cypherpunks.ca instead of http redirect 12 | * Add missing #include. 13 | * Fix my broken NULL pointer check (and some segfaults). 14 | * Remove unused variable. 15 | * Detect the libotr-emitted HTML-formatted init string and replace it with a description customized for IRC and irssi-otr. 16 | * Remove linebreaks from libotr messages to avoid sending random IRC commands. 17 | 18 | 2015-01-03 irssi-otr 1.0.1 19 | * Fix: bad comment for cmd_generic 20 | * Add cscope files to gitignore 21 | * Fix: change project URL in configure.ac 22 | * Fix: assertion when server record is NULL 23 | * Add spec file for building RPMs 24 | * Add additional files into dist tarball 25 | * Make irssi module directory configurable 26 | 27 | 2014-02-12 irssi-otr 1.0.0 28 | * Fix: typo in notice message of SMP event 29 | * Fix: otr info help syntax 30 | * Fix: otr info printing every user fingerprints 31 | * Update help page with otr info command 32 | * Updated README.md with otr fingerprint info 33 | * Add /otr info command 34 | * Merge pull request #38 from KwadroNaut/master 35 | * language, gender 36 | * Fix typo and add important notice to README 37 | * Fix: remove double quotes around a NULL value 38 | * Fix: remove gmodule useless linked library flags 39 | * Fix: remove .so on uninstall and set +x the .so on install 40 | 41 | 2013-05-09 irssi-otr 1.0.0-alpha2 42 | * Merge pull request #30 from anarcat/master 43 | * cleanup automake voodoo 44 | * Fix: explicitly set ptr to NULL on asprintf error 45 | * Fix: handle IRC /me marker on msg receive 46 | * Fix: small typo in README.md 47 | * Fix: memory leak on error path 48 | * Fix: remove useless value 49 | * Fix: handle zero length string in rtrim() 50 | * Support split OTR message on receive. Bitlbee support. 51 | * Fix: print right nick name on otr finish 52 | * Merge pull request #18 from cbab/master 53 | * Fix: Automake install-data-hook must respect $DESTDIR 54 | * Fix: missing pthread header include 55 | * Fix: install help in /usr 56 | * Fix: move NULL free out of end label 57 | 58 | 2012-12-02 irssi-otr 1.0.0-alpha1 59 | * Initial import of the alpha1 version 60 | * Major refactoring from the last version 61 | * Only supports libotr4 62 | 63 | Version 0.3 64 | * create queries for OTR messages if otr_createqueries is set 65 | * finish conversations on unload unless otr_finishonunload is off 66 | * added settings otr_policy, otr_policy_known and otr_ignore 67 | * fixed two segfault sources 68 | * key generation now operates on a temp file 69 | * the .irssi/otr dir now gets created with mode 700 70 | * commands now take an optional nick@server argument 71 | (for single message window users) 72 | * changed loglevel of otr_log (heartbeats) and otr_finish 73 | * moved to the new public statusbar-item.h header 74 | 75 | Version 0.2 76 | 77 | * fixed multiple server problem. 78 | * fixed fragmentation problem (seen with pidgin over IRC). 79 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([irssi-otr],[1.0.2],[dgoulet@ev0ke.net],[],[https://github.com/cryptodotis/irssi-otr]) 2 | AC_CONFIG_AUX_DIR([config]) 3 | AC_CANONICAL_TARGET 4 | AC_CANONICAL_HOST 5 | AC_CONFIG_MACRO_DIR([config]) 6 | AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip]) 7 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 8 | 9 | AC_PROG_GREP 10 | AC_PROG_CC 11 | 12 | # We do not want to create a .a for the module, so disable by default. 13 | AM_DISABLE_STATIC 14 | AM_PROG_LIBTOOL 15 | 16 | # Check for pthread 17 | AC_CHECK_LIB([pthread], [pthread_create], [], 18 | [AC_MSG_ERROR([Cannot find libpthread. Use [LDFLAGS]=-Ldir to specify its location.])] 19 | ) 20 | 21 | AC_ARG_WITH([irssi-headers], 22 | AS_HELP_STRING([--with-irssi-headers], 23 | [Directory containing the Irssi header files]), 24 | [IRSSI_HEADER_DIR="$withval"], 25 | [IRSSI_HEADER_DIR="\"\""]) 26 | 27 | # Check for Glib. It needs to be installed anyway or this macro will not be defined. 28 | AM_PATH_GLIB_2_0([2.22.0], [], 29 | [AC_MSG_ERROR([Glib 2.22 is required in order to compile. 30 | Please install the Glib development files.])], [gmodule]) 31 | 32 | AM_PATH_LIBGCRYPT(1:1.2.0,,AC_MSG_ERROR(libgcrypt 1.2.0 or newer is required.)) 33 | 34 | AM_PATH_LIBOTR(4.1.0, [], [AC_MSG_ERROR([libotr 4.1.0 or newer is required.])]) 35 | 36 | pkg_modules="gmodule-2.0 >= 2.0.0" 37 | PKG_CHECK_MODULES(GMODULE, [$pkg_modules]) 38 | 39 | LIBS="$LIBS" 40 | 41 | PACKAGE_CFLAGS="$GMODULE_CFLAGS -Wall -Werror=format-security" 42 | AC_SUBST(PACKAGE_CFLAGS) 43 | 44 | # Checking only a few Irssi headers is enough to tell that everything is 45 | # available. This is broken I know but tell that to the irssi guys to NOT use 46 | # cluster fu*** of headers and local inclusion system wide... 47 | CPPFLAGS="$CPPFLAGS $PACKAGE_CFLAGS -I$IRSSI_HEADER_DIR" 48 | AC_CHECK_HEADERS([\ 49 | irssi/src/common.h \ 50 | irssi/src/core/commands.h \ 51 | irssi/src/core/modules.h \ 52 | irssi/src/core/servers.h \ 53 | irssi/src/core/signals.h \ 54 | irssi/src/core/levels.h \ 55 | irssi/src/core/queries.h \ 56 | irssi/src/core/settings.h \ 57 | ], [], [AC_MSG_ERROR([Irssi Header files are needed])], 58 | [ 59 | #include 60 | #ifdef HAVE_IRSSI_SRC_COMMON_H 61 | #include 62 | #endif 63 | ]) 64 | 65 | LT_INIT 66 | 67 | CFLAGS="-Wall $CFLAGS -g -fno-strict-aliasing -fstack-protector-all -D_FORTIFY_SOURCE=2 -O1 -Wl,-z,relro,-z,now -fPIE -pie $PACKAGE_CFLAGS" 68 | 69 | DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_builddir) -I\$(top_builddir)/src -I\$(top_builddir)/include" 70 | AC_SUBST(DEFAULT_INCLUDES) 71 | 72 | # Irssi searches for modules in $HOME/.irssi/modules and $(libdir)/irssi/modules 73 | # where the value of $(libdir) may depend on your distribution, architecture, 74 | # and whether irssi was installed from package or from source. 75 | # The hardcoded value should work for most systems but you need to override it for 76 | # e.g. x86_64 Fedora. 77 | AC_ARG_WITH([irssi-module-dir], 78 | AS_HELP_STRING([--with-irssi-module-dir=DIR], [Irssi module directory]), 79 | [IRSSI_MODULE_DIR="$withval"], 80 | [IRSSI_MODULE_DIR='${libdir}/irssi/modules']) 81 | 82 | AC_SUBST(IRSSI_MODULE_DIR) 83 | 84 | AC_CONFIG_FILES([ 85 | Makefile 86 | help/Makefile 87 | src/Makefile 88 | irssi-otr.spec 89 | ]) 90 | 91 | AC_OUTPUT 92 | 93 | # 94 | # Mini-report on what will be built 95 | # 96 | AS_ECHO() 97 | 98 | # Target architecture we're building for 99 | target_arch=$host_cpu 100 | [ 101 | for f in $CFLAGS; do 102 | if test $f = "-m32"; then 103 | target_arch="32-bit" 104 | elif test $f = "-m64"; then 105 | target_arch="64-bit" 106 | fi 107 | done 108 | ] 109 | AS_ECHO_N("Target architecture: ") 110 | AS_ECHO($target_arch) 111 | 112 | # Print the bindir and libdir this `make install' will install into. 113 | AS_ECHO() 114 | AS_ECHO_N("Module will be installed in: ") 115 | AS_ECHO("`eval eval echo $IRSSI_MODULE_DIR`") 116 | AS_ECHO() 117 | -------------------------------------------------------------------------------- /help/otr: -------------------------------------------------------------------------------- 1 | 2 | OTR %|[OPTION] 3 | 4 | Command to control the OTR module. Without an option, this help is printed. 5 | 6 | This help contains three sections which are %9options, quickstart and files.%n 7 | 8 | To add the OTR status bar (highly recommended): 9 | 10 | %9/statusbar window add otr%n 11 | 12 | %9Options:%n 13 | 14 | AUTH 15 | Start or respond to an authentication process. 16 | 17 | AUTHQ [<[question]>] 18 | Start a SMP authentication process. The question MUST be enclosed in 19 | brackets and followed by the secret with at least one white space between 20 | the question and secret. 21 | 22 | Example: %9/otr authq [My question is] rest is the secret%n 23 | 24 | AUTHABORT 25 | Abort an ongoing authentication process. 26 | 27 | CONTEXTS 28 | List known contexts which basically list the known fingerprints and their 29 | state. 30 | 31 | DEBUG 32 | Turn on debugging. 33 | 34 | DISTRUST [] 35 | Distrust a specific fingerprint. This command can be done inside a private 36 | window for which the current fingerprint of the other person will be used 37 | or else set fp to a human readable OTR fingerprint available with the above 38 | contexts command. 39 | 40 | Examples: %9/otr distrust 487FFADA 5073FEDD C5AB5C14 5BB6C1FF 6D40D48A%n 41 | 42 | FINISH 43 | End the OTR session. This MUST be done inside a private conversation 44 | window. 45 | 46 | FORGET [] 47 | Forget a specific fingerprint (deleted from the known fingerprints). The 48 | behavior is the same as the distrust command explained above. 49 | 50 | GENKEY @ 51 | Generate OTR keys for a given account name. This is done automatically 52 | if someone tries to establish a secure session. 53 | 54 | This process is done in a background thread and can take an arbitrary 55 | amount of time. The completion is checked when another irssi event is 56 | catched. Unfortunately, for technical reasons, the thread can't notify the 57 | irssi main window so a status check is done at each irssi events and a 58 | message is printed if the key is ready. 59 | 60 | HELP 61 | Print this help. 62 | 63 | INFO 64 | Display the OTR fingerprint(s) of all your account(s). 65 | 66 | INIT 67 | Initialize an OTR conversation within a private conversation window. 68 | 69 | TRUST [] 70 | Trust a specific fingerprint. The behavior is the same as the forget and 71 | distrust commands explained above. 72 | 73 | VERSION 74 | Print the version of the OTR module. 75 | 76 | %9Quickstart:%n 77 | 78 | Start a private conversation with the person you want to initiate a secure 79 | session. Once in the private message window: 80 | 81 | %9/otr init%n 82 | 83 | Key generation should start if no key is found for your account name. Once the 84 | process is done, either type a message which should automatically start the 85 | session or redo the init command. 86 | 87 | Time to authenticate the person. Either use a shared secret exchange through 88 | phone or GPG-signed email or use the socialist millionaire problem mechanism 89 | (SMP) which is basically to ask a question for which the answer can only be 90 | known by the other person. 91 | 92 | %9/otr auth %n OR %9/otr authq [A question] %n 93 | 94 | Or to respond to an authentication: 95 | 96 | %9/otr auth %n 97 | 98 | %9Files:%n 99 | 100 | This otr modules creates a directory in %9$HOME/.irssi/otr%n and creates three 101 | files: 102 | 103 | * %9otr.key%n 104 | Contains your OTR private key(s). NEVER shared this directory with someone 105 | else unless you know what you are doing. 106 | 107 | * %9otr.fp%n 108 | The known fingerprints with their _trust_ status. 109 | 110 | * %9otr.instag 111 | Instance tag of the libotr. This should NEVER be copied to an other 112 | computer. If unsure, just ignore this file. 113 | 114 | For more information on OTR, see https://otr.cypherpunks.ca/ 115 | 116 | -------------------------------------------------------------------------------- /config/libotr.m4: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl Off-the-Record Messaging library 3 | dnl Copyright (C) 2004-2007 Ian Goldberg, Chris Alexander, Nikita Borisov 4 | dnl 5 | dnl 6 | dnl This library is free software; you can redistribute it and/or 7 | dnl modify it under the terms of version 2.1 of the GNU Lesser General 8 | dnl Public License as published by the Free Software Foundation. 9 | dnl 10 | dnl This library is distributed in the hope that it will be useful, 11 | dnl but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | dnl Lesser General Public License for more details. 14 | dnl 15 | dnl You should have received a copy of the GNU Lesser General Public 16 | dnl License along with this library; if not, write to the Free Software 17 | dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | dnl 19 | 20 | dnl AM_PATH_LIBOTR([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) 21 | dnl Test for libotr, and define LIBOTR_CFLAGS and LIBOTR_LIBS as appropriate. 22 | dnl enables arguments --with-libotr-prefix= 23 | dnl --with-libotr-inc-prefix= 24 | dnl 25 | dnl You must already have found libgcrypt with AM_PATH_LIBGCRYPT 26 | dnl 27 | dnl Adapted from alsa.m4, originally by 28 | dnl Richard Boulton 29 | dnl Christopher Lansdown 30 | dnl Jaroslav Kysela 31 | 32 | AC_DEFUN([AM_PATH_LIBOTR], 33 | [dnl Save the original CFLAGS, LDFLAGS, and LIBS 34 | libotr_save_CFLAGS="$CFLAGS" 35 | libotr_save_LDFLAGS="$LDFLAGS" 36 | libotr_save_LIBS="$LIBS" 37 | libotr_found=yes 38 | 39 | dnl 40 | dnl Get the cflags and libraries for libotr 41 | dnl 42 | AC_ARG_WITH(libotr-prefix, 43 | [ --with-libotr-prefix=PFX Prefix where libotr is installed(optional)], 44 | [libotr_prefix="$withval"], [libotr_prefix=""]) 45 | 46 | AC_ARG_WITH(libotr-inc-prefix, 47 | [ --with-libotr-inc-prefix=PFX Prefix where libotr includes are (optional)], 48 | [libotr_inc_prefix="$withval"], [libotr_inc_prefix=""]) 49 | 50 | dnl Add any special include directories 51 | AC_MSG_CHECKING(for libotr CFLAGS) 52 | if test "$libotr_inc_prefix" != "" ; then 53 | LIBOTR_CFLAGS="$LIBOTR_CFLAGS -I$libotr_inc_prefix" 54 | CFLAGS="$CFLAGS $LIBOTR_CFLAGS" 55 | fi 56 | AC_MSG_RESULT($LIBOTR_CFLAGS) 57 | 58 | dnl add any special lib dirs 59 | AC_MSG_CHECKING(for libotr LIBS) 60 | if test "$libotr_prefix" != "" ; then 61 | LIBOTR_LIBS="$LIBOTR_LIBS -L$libotr_prefix" 62 | LDFLAGS="$LDFLAGS $LIBOTR_LIBS" 63 | fi 64 | 65 | dnl add the libotr library 66 | LIBOTR_LIBS="$LIBOTR_LIBS -lotr" 67 | LIBS="$LIBOTR_LIBS $LIBS" 68 | AC_MSG_RESULT($LIBOTR_LIBS) 69 | 70 | dnl Check for a working version of libotr that is of the right version. 71 | min_libotr_version=ifelse([$1], ,3.0.0,$1) 72 | no_libotr="" 73 | libotr_min_major_version=`echo $min_libotr_version | \ 74 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` 75 | libotr_min_minor_version=`echo $min_libotr_version | \ 76 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` 77 | libotr_min_sub_version=`echo $min_libotr_version | \ 78 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` 79 | AC_MSG_CHECKING(for libotr headers version $libotr_min_major_version.x >= $min_libotr_version) 80 | 81 | AC_LANG_SAVE 82 | AC_LANG_C 83 | AC_TRY_COMPILE([ 84 | #include 85 | #include 86 | ], [ 87 | # if(OTRL_VERSION_MAJOR != $libotr_min_major_version) 88 | # error not present 89 | # else 90 | 91 | # if(OTRL_VERSION_MINOR > $libotr_min_minor_version) 92 | exit(0); 93 | # else 94 | # if(OTRL_VERSION_MINOR < $libotr_min_minor_version) 95 | # error not present 96 | # endif 97 | 98 | # if(OTRL_VERSION_SUB < $libotr_min_sub_version) 99 | # error not present 100 | # endif 101 | # endif 102 | # endif 103 | exit(0); 104 | ], 105 | [AC_MSG_RESULT(found.)], 106 | [AC_MSG_RESULT(not present.) 107 | ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libotr not found.)]) 108 | libotr_found=no] 109 | ) 110 | AC_LANG_RESTORE 111 | 112 | dnl Now that we know that we have the right version, let's see if we have the library and not just the headers. 113 | AC_CHECK_LIB([otr], [otrl_message_receiving],, 114 | [ifelse([$3], , [AC_MSG_ERROR(No linkable libotr was found.)]) 115 | libotr_found=no], 116 | $LIBGCRYPT_LIBS 117 | ) 118 | 119 | LDFLAGS="$libotr_save_LDFLAGS" 120 | LIBS="$libotr_save_LIBS" 121 | 122 | if test "x$libotr_found" = "xyes" ; then 123 | ifelse([$2], , :, [$2]) 124 | else 125 | LIBOTR_CFLAGS="" 126 | LIBOTR_LIBS="" 127 | ifelse([$3], , :, [$3]) 128 | fi 129 | 130 | dnl That should be it. Now just export our symbols: 131 | AC_SUBST(LIBOTR_CFLAGS) 132 | AC_SUBST(LIBOTR_LIBS) 133 | ]) 134 | 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | irssi-otr 2 | ========= 3 | 4 | Libotr (https://otr.cypherpunks.ca) support for Irssi. 5 | 6 | **Mailing list**: otr-dev@lists.cypherpunks.ca 7 | 8 | First of all, we strongly recommend to set this option to speed up any OTR 9 | commands or sessions. 10 | 11 | `/set cmd_queue_speed 1msec` 12 | 13 | The default value of irssi is much higher and used to avoid excess flood on IRC 14 | servers. However, with the message size this module is using and rate of a 15 | normal conversation, it seems OK to set this limit. Please inform us if it 16 | causes problems. 17 | 18 | Future works is to handle IRC excess flood inside this module. 19 | 20 | Requirements 21 | --------- 22 | 23 | * libotr 4.1.x - [Download 24 | Link](https://otr.cypherpunks.ca/index.php#downloads) 25 | 26 | * irssi-dev >= 0.8.15 - [Download Link](http://www.irssi.org/download) 27 | 28 | * glib2.0 Development package 29 | 30 | * libgcrypt >= 1.5.0 31 | 32 | * automake, autoconf, libtool 33 | 34 | Installation 35 | --------- 36 | 37 | Run the following commands to compile and install. 38 | 39 | `$ ./bootstrap` 40 | 41 | `$ ./configure --prefix="/usr"` 42 | 43 | `$ make && make install` 44 | 45 | Quick Start 46 | --------- 47 | 48 | 1. `/load otr` in the Irssi main window. 49 | 2. Open a chat window with your buddy. 50 | 3. `/otr init`, initiate OTR session. 51 | 52 | If no key is found for your user and server, the key generation will be 53 | launched. 54 | 55 | You should see `OTR: Gone secure` and you are ready to communicate over OTR. 56 | 57 | Instructions 58 | --------- 59 | 60 | To load the OTR module at startup, use the following and make sure 61 | **otr.so** is located in the **modules/** directory of the Irssi home 62 | (usually ~/.irssi). 63 | 64 | `echo "load otr" >> ~/.irssi/startup` 65 | 66 | Once loaded, we recommend you add the OTR status bar allowing you to see the 67 | OTR state of private conversation. 68 | 69 | `/statusbar window add otr` 70 | 71 | Possible states are: 72 | 73 | * Plaintext 74 | * Finished 75 | * OTR 76 | * OTR (unverified) 77 | 78 | #### Key Generation #### 79 | 80 | Key generation happens in a separate process and its duration mainly depends 81 | on the available entropy. If **no** key is detected for the current user and server, 82 | the keys will be generated automatically for you. Or else, you can run: 83 | 84 | `/otr genkey nickname@server-addr-fqdn` 85 | 86 | Once done, you should see a message in the irssi main window indicating 87 | completion. 88 | 89 | `OTR: Key generation for completed in X seconds. Reloading keys.` 90 | 91 | The default OTR policy irssi-otr is now something between manual and 92 | opportunistic. Manual means you have to start it yourself by issuing a `/otr 93 | init` command and opportunistic means both peers send some magic whitespaces 94 | and start OTR once they receive these whitespaces from the other side. 95 | 96 | Irssi-otr uses a mode in between where we are not sending whitespaces as an 97 | announcement (as in opportunistic) but we still handle whitespaces if we see it 98 | from the other side. Therefore if your peer uses opportunistic the handshake 99 | should still start automatically once he writes something. 100 | 101 | To display the fingerprint of the OTR key you just generated, one can type `/otr info` to show the fingerprint. 102 | 103 | #### Authentication #### 104 | 105 | In order to be sure you are communicating with the right person you can do two 106 | things to autenticate him or her. 107 | 108 | 1. Use a **shared secret** previously decided between both parties or 109 | exchanged, **ideally** in person. Use the following command to iniate 110 | or respond to an authentication request. 111 | 112 | `/otr auth SHARED_SECRET` 113 | 114 | 2. The second method is to use the **[socialist millionaire 115 | problem](https://en.wikipedia.org/wiki/Socialist_millionaire)** (SMP) 116 | mechanism which consist of asking the other party a question for which him 117 | or her will only be able to respond with the correct answer. 118 | 119 | `/otr authq [YOUR QUESTION HERE] SHARED_SECRET` 120 | 121 | And respond with the command on number 1 above. 122 | 123 | 3. The third way is to trust manually. Exchange your fingerprint with the other 124 | party over a telephone or GPG-signed email for instance. 125 | 126 | `/otr trust [FP]` 127 | 128 | You can either type this command in the private conversation window of the 129 | buddy fingerprint you want to trust or enter the **FP** argument which is 130 | the five parts of the human readable fingerprint available via the `/otr 131 | contexts` command. 132 | 133 | For example: `/otr trust 487FFADA 5073FEDD C5AB5C14 5BB6C1FF 6D40D48A` 134 | 135 | You can abort an ongoing authentication at any time by using this command. 136 | 137 | `/otr authabort` 138 | 139 | To **distrust** a fingerprint for whatever reason you may have, use the 140 | following command which is like the trust command above. 141 | 142 | `/otr distrust [FP]` 143 | 144 | To completely **forget** a fingerprint meaning it will be erased from the OTR 145 | fingerprints file. Again, same as trust/distrust command, you can either enter 146 | the five parts of the fingerprint or execute the command in the private 147 | conversation window. 148 | 149 | `/otr forget [FP]` 150 | 151 | #### Finishing a Session #### 152 | 153 | If the window is closed, a **finish** action is triggered informing the other 154 | hand that you have ended the private session. The status bar will indicate 155 | `plaintext` if so. 156 | 157 | You can also use the `/otr finish` command to end the OTR session without 158 | closing the window. 159 | 160 | If your buddy finishes the session, you will be notified and the status bar 161 | will indicate `finished` in yellow. 162 | 163 | #### Other commands #### 164 | 165 | * Print the irssi-otr module version. 166 | 167 | `/otr version` 168 | 169 | * List all OTR contexts and their status. 170 | 171 | `/otr contexts` 172 | 173 | Irssi Files 174 | --------- 175 | 176 | In **/otr/otr.{key,fp}** you'll find the fingerprints and your 177 | private keys (should you at any point be interested). There is also the 178 | **otr.instag** file which is of no importance for you and used by libotr. 179 | 180 | -------------------------------------------------------------------------------- /src/otr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #ifndef IRSSI_OTR_OTR_H 23 | #define IRSSI_OTR_OTR_H 24 | 25 | /* Libotr */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "irssi-otr.h" 32 | #include "utils.h" 33 | 34 | /* irssi module name */ 35 | #define MODULE_NAME "otr" 36 | 37 | /* 38 | * XXX: Maybe this should be configurable? 39 | */ 40 | #define OTR_MAX_MSG_SIZE 400 41 | 42 | /* OTR protocol id */ 43 | #define OTR_PROTOCOL_ID "IRC" 44 | 45 | #define OTR_DIR "/otr" 46 | #define OTR_KEYFILE OTR_DIR "/otr.key" 47 | #define OTR_FINGERPRINTS_FILE OTR_DIR "/otr.fp" 48 | #define OTR_INSTAG_FILE OTR_DIR "/otr.instag" 49 | 50 | /* 51 | * Specified in OTR protocol version 3. See: 52 | * http://www.cypherpunks.ca/otr/Protocol-v3-4.0.0.html 53 | */ 54 | #define OTR_MSG_BEGIN_TAG "?OTR:" 55 | #define OTR_MSG_END_TAG '.' 56 | 57 | #define OTR_MSG_HELP \ 58 | " This is a request for an Off-the-Record private conversation. " \ 59 | "However, you do not have a plugin to support that. " \ 60 | "If you are using Irssi, please install irssi-otr (irssi-plugin-otr)." 61 | 62 | /* IRC /me command marker and len. */ 63 | #define OTR_IRC_MARKER_ME "/me " 64 | #define OTR_IRC_MARKER_ME_LEN sizeof(OTR_IRC_MARKER_ME) - 1 65 | 66 | /* 67 | * Memory allocation zeroed. Really useful! 68 | */ 69 | #define zmalloc(x) calloc(1, x) 70 | 71 | /* Irssi otr user state */ 72 | struct otr_user_state { 73 | OtrlUserState otr_state; 74 | }; 75 | 76 | /* 77 | * Peer OTR internal context. 78 | */ 79 | struct otr_peer_context { 80 | /* The SMP event status. Used for the Irssi status bar. */ 81 | OtrlSMPEvent smp_event; 82 | /* Did the SMP secret was asked so are we in a responder state? */ 83 | unsigned int ask_secret; 84 | /* 85 | * The fingerprint of the private message OTR session. This is useful for 86 | * the forget command for which we can recover the fingerprint 87 | * automatically. 88 | */ 89 | Fingerprint *active_fingerprint; 90 | /* 91 | * If needed, used to reconstruct the full message from fragmentation. 92 | * Bitlbee for instance does that where we receive a *long* OTR message 93 | * split in multiple PRIVMSG so we need to reconstruct it. 94 | */ 95 | char *full_msg; 96 | /* Size of full_msg. Note this is the allocated memory size. */ 97 | size_t msg_size; 98 | /* Len of the actual string in full_msg NOT counting the NULL byte. */ 99 | size_t msg_len; 100 | }; 101 | 102 | /* given to otr_status_change */ 103 | enum otr_status_event { 104 | OTR_STATUS_FINISHED, 105 | OTR_STATUS_TRUST_MANUAL, 106 | OTR_STATUS_TRUST_SMP, 107 | OTR_STATUS_SMP_ABORT, 108 | OTR_STATUS_SMP_STARTED, 109 | OTR_STATUS_SMP_RESPONDED, 110 | OTR_STATUS_SMP_INCOMING, 111 | OTR_STATUS_SMP_FINALIZE, 112 | OTR_STATUS_SMP_ABORTED, 113 | OTR_STATUS_PEER_FINISHED, 114 | OTR_STATUS_SMP_FAILED, 115 | OTR_STATUS_SMP_SUCCESS, 116 | OTR_STATUS_GONE_SECURE, 117 | OTR_STATUS_GONE_INSECURE, 118 | OTR_STATUS_CTX_UPDATE 119 | }; 120 | 121 | enum otr_msg_status { 122 | OTR_MSG_ORIGINAL = 1, 123 | OTR_MSG_WAIT_MORE = 2, 124 | OTR_MSG_USE_QUEUE = 3, 125 | OTR_MSG_ERROR = 4, 126 | }; 127 | 128 | /* there can be only one */ 129 | extern struct otr_user_state *user_state_global; 130 | 131 | /* Libotr ops functions */ 132 | extern OtrlMessageAppOps otr_ops; 133 | 134 | /* Active debug or not */ 135 | extern int debug; 136 | 137 | void irssi_send_message(SERVER_REC *irssi, const char *recipient, 138 | const char *message); 139 | void otr_status_change(SERVER_REC *irssi, const char *nick, 140 | enum otr_status_event event); 141 | 142 | /* init stuff */ 143 | 144 | struct otr_user_state *otr_init_user_state(void); 145 | void otr_free_user_state(struct otr_user_state *ustate); 146 | 147 | void otr_lib_init(); 148 | void otr_lib_uninit(); 149 | 150 | void otr_control_timer(unsigned int interval, void *opdata); 151 | 152 | /* Message transport. */ 153 | int otr_send(SERVER_REC *irssi, const char *msg, const char *to, 154 | char **otr_msg); 155 | int otr_receive(SERVER_REC *irssi, const char *msg, 156 | const char *from, char **new_msg); 157 | 158 | /* User interaction */ 159 | void otr_finish(SERVER_REC *irssi, const char *nick); 160 | void otr_auth(SERVER_REC *irssi, const char *nick, const char *question, 161 | const char *secret); 162 | void otr_auth_abort(SERVER_REC *irssi, const char *nick); 163 | void otr_contexts(struct otr_user_state *ustate); 164 | void otr_finishall(struct otr_user_state *ustate); 165 | void otr_forget(SERVER_REC *irssi, const char *nick, char *str_fp, 166 | struct otr_user_state *ustate); 167 | void otr_distrust(SERVER_REC *irssi, const char *nick, char *str_fp, 168 | struct otr_user_state *ustate); 169 | void otr_trust(SERVER_REC *irssi, const char *nick, char *str_fp, 170 | struct otr_user_state *ustate); 171 | 172 | enum otr_status_format otr_get_status_format(SERVER_REC *irssi, 173 | const char *nick); 174 | 175 | struct otr_peer_context *otr_create_peer_context(void); 176 | ConnContext *otr_find_context(SERVER_REC *irssi, const char *nick, int create); 177 | Fingerprint *otr_find_hash_fingerprint_from_human(const char *human_fp, 178 | struct otr_user_state *ustate); 179 | 180 | #endif /* IRSSI_OTR_OTR_H */ 181 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2012 - David Goulet 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License as published by the Free 8 | * Software Foundation; either version 2 of the License, or (at your option) 9 | * any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program; if not, write to the Free Software Foundation, Inc., 51 18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "otr.h" 25 | #include "utils.h" 26 | 27 | /* 28 | * Left trim a string. 29 | */ 30 | static char *ltrim(char *s) 31 | { 32 | assert(s); 33 | 34 | while (isspace(*s)) { 35 | s++; 36 | } 37 | return s; 38 | } 39 | 40 | /* 41 | * Right trim a string. 42 | */ 43 | static char *rtrim(char *s) 44 | { 45 | size_t len; 46 | char *back; 47 | 48 | assert(s); 49 | 50 | len = strlen(s); 51 | if (len == 0) { 52 | goto end; 53 | } 54 | 55 | back = s + len; 56 | 57 | /* Move up to the first non whitespace character. */ 58 | while (isspace(*--back)); 59 | /* Remove whitespace(s) from the string. */ 60 | *(back + 1) = '\0'; 61 | 62 | end: 63 | return s; 64 | } 65 | 66 | /* 67 | * Trim whitespaces in front and back of the string. 68 | */ 69 | char *utils_trim_string(char *s) 70 | { 71 | assert(s); 72 | 73 | return rtrim(ltrim(s)); 74 | } 75 | 76 | /* 77 | * Convert invalid characters (newlines from libotr) into spaces. Also change 78 | * the init help string because IRC does not support HTML. 79 | */ 80 | char *utils_escape_message(char *s) 81 | { 82 | char const *helpmsg = OTR_MSG_HELP; 83 | size_t i; 84 | 85 | if (strncmp(s, "?OTR", 4) == 0 && 86 | strstr(s, " has requested an 0) { 252 | argc++; 253 | have_arg = 1; 254 | } 255 | 256 | c = cmd_offset; 257 | while ((c = strchr(c + 1, ' '))) { 258 | /* Skip consecutive spaces. */ 259 | if (*(c + 1) == ' ') { 260 | continue; 261 | } 262 | argc++; 263 | have_arg = 1; 264 | } 265 | 266 | /* No args, only spaces encountered. */ 267 | if (!have_arg) { 268 | argc = 0; 269 | goto error; 270 | } 271 | 272 | argv = zmalloc(argc * sizeof(char *)); 273 | if (!argv) { 274 | goto error; 275 | } 276 | 277 | /* Ignore first command */ 278 | c = strtok(cmd_offset, " "); 279 | while (c != NULL) { 280 | argv[i] = strdup(c); 281 | c = strtok(NULL, " "); 282 | i++; 283 | } 284 | 285 | *_argv = argv; 286 | 287 | error: 288 | *_argc = argc; 289 | free(data); 290 | return; 291 | } 292 | 293 | /* 294 | * Free an argv array. Usually, call this after using utils_explode_args. 295 | */ 296 | void utils_free_args(char ***argv, int argc) 297 | { 298 | int i; 299 | char **args; 300 | 301 | assert(argv); 302 | 303 | /* Nothing to free. */ 304 | if (argc == 0) { 305 | return; 306 | } 307 | 308 | args = *argv; 309 | 310 | for (i = 0; i < argc; i++) { 311 | if (args[i]) { 312 | free(args[i]); 313 | } 314 | } 315 | 316 | free(args); 317 | } 318 | 319 | /* 320 | * Extract otr command from an irssi command string. 321 | * 322 | * Ex: /otr auth my_secret, _cmd is set to "auth" 323 | */ 324 | void utils_extract_command(const char *data, char **_cmd) 325 | { 326 | char *s, *cmd = NULL; 327 | 328 | assert(data); 329 | assert(_cmd); 330 | 331 | /* Search for the first whitespace. */ 332 | s = strchr(data, ' '); 333 | if (s) { 334 | cmd = strndup(data, s - data); 335 | if (!cmd) { 336 | goto error; 337 | } 338 | } else { 339 | cmd = strdup(data); 340 | } 341 | 342 | *_cmd = cmd; 343 | 344 | error: 345 | return; 346 | } 347 | 348 | /* 349 | * String to uppercase. Done inplace! 350 | */ 351 | void utils_string_to_upper(char *string) 352 | { 353 | int i = 0; 354 | char c; 355 | 356 | assert(string); 357 | 358 | while (string[i]) { 359 | c = string[i]; 360 | string[i] = toupper(c); 361 | i++; 362 | } 363 | } 364 | 365 | /* 366 | * Convert a fingerprint string of this format contained in parts: 367 | * d81d8363 f6d6090a c2632a53 352dadfa fd296a87 368 | * to a privkey hash_to_human format of libotr: 369 | * D81D8363 F6D6090A C2632A53 352DADFA FD296A87 370 | * 371 | * Stores the result in dst which is basically regroup the string and upper 372 | * case it. The dst argument must be equal or larger than 373 | * OTRL_PRIVKEY_FPRINT_HUMAN_LEN. 374 | */ 375 | void utils_hash_parts_to_readable_hash(const char **parts, char *dst) 376 | { 377 | int ret; 378 | 379 | /* Safety net. This is a code flow error. */ 380 | assert(parts && parts[0] && parts[1] && parts[2] && parts[3] && parts[4]); 381 | assert(dst); 382 | 383 | ret = snprintf(dst, OTRL_PRIVKEY_FPRINT_HUMAN_LEN, "%s %s %s %s %s", 384 | parts[0], parts[1], parts[2], parts[3], parts[4]); 385 | if (ret < 0) { 386 | goto error; 387 | } 388 | 389 | /* In place upper case full string. */ 390 | utils_string_to_upper(dst); 391 | 392 | error: 393 | return; 394 | } 395 | -------------------------------------------------------------------------------- /src/key.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "key.h" 34 | 35 | /* 36 | * Key generation data for the thread in charge of creating the key. 37 | */ 38 | static struct key_gen_data key_gen_state = { 39 | .status = KEY_GEN_IDLE, 40 | .gcry_error = GPG_ERR_NO_ERROR, 41 | }; 42 | 43 | static pthread_t keygen_thread; 44 | 45 | /* 46 | * Build file path concatenate to the irssi config dir. 47 | */ 48 | static char *file_path_build(const char *path) 49 | { 50 | int ret; 51 | char *filename; 52 | 53 | if (!path) { 54 | path = ""; 55 | } 56 | 57 | /* Either NULL or the filename is returned here which is valid. */ 58 | ret = asprintf(&filename, "%s%s", get_client_config_dir(), path); 59 | if (ret < 0) { 60 | filename = NULL; 61 | } 62 | 63 | return filename; 64 | } 65 | 66 | /* 67 | * Reset key generation state and status is IDLE. 68 | */ 69 | static void reset_key_gen_state(void) 70 | { 71 | /* Safety. */ 72 | if (key_gen_state.key_file_path) { 73 | free(key_gen_state.key_file_path); 74 | } 75 | 76 | /* Pointer dup when key_gen_run is called. */ 77 | if (key_gen_state.account_name) { 78 | free(key_gen_state.account_name); 79 | } 80 | 81 | /* Nullify everything. */ 82 | memset(&key_gen_state, 0, sizeof(key_gen_state)); 83 | key_gen_state.status = KEY_GEN_IDLE; 84 | key_gen_state.gcry_error = GPG_ERR_NO_ERROR; 85 | } 86 | 87 | /* 88 | * Generate OTR key. Thread in the background. 89 | * 90 | * NOTE: NO irssi interaction should be done here like emitting signals or else 91 | * it causes a segfaults of libperl. 92 | */ 93 | static void *generate_key(void *data) 94 | { 95 | gcry_error_t err; 96 | 97 | assert(key_gen_state.newkey); 98 | 99 | key_gen_state.status = KEY_GEN_RUNNING; 100 | 101 | err = otrl_privkey_generate_calculate(key_gen_state.newkey); 102 | if (err != GPG_ERR_NO_ERROR) { 103 | key_gen_state.status = KEY_GEN_ERROR; 104 | key_gen_state.gcry_error = err; 105 | goto error; 106 | } 107 | 108 | key_gen_state.status = KEY_GEN_FINISHED; 109 | 110 | error: 111 | return NULL; 112 | } 113 | 114 | /* 115 | * Check key generation state and print message to user according to state. 116 | */ 117 | void key_gen_check(void) 118 | { 119 | gcry_error_t err; 120 | 121 | switch (key_gen_state.status) { 122 | case KEY_GEN_FINISHED: 123 | err = otrl_privkey_generate_finish(key_gen_state.ustate->otr_state, 124 | key_gen_state.newkey, key_gen_state.key_file_path); 125 | if (err != GPG_ERR_NO_ERROR) { 126 | IRSSI_MSG("Key generation finish state failed. Err: %s", 127 | gcry_strerror(err)); 128 | } else { 129 | IRSSI_MSG("Key generation for %9%s%n completed", 130 | key_gen_state.account_name); 131 | } 132 | reset_key_gen_state(); 133 | break; 134 | case KEY_GEN_ERROR: 135 | IRSSI_MSG("Key generation for %9%s%n failed. Err: %s (%d)", 136 | key_gen_state.account_name, 137 | gcry_strerror(key_gen_state.gcry_error), 138 | key_gen_state.gcry_error); 139 | reset_key_gen_state(); 140 | break; 141 | case KEY_GEN_RUNNING: 142 | case KEY_GEN_IDLE: 143 | /* Do nothing */ 144 | break; 145 | }; 146 | } 147 | 148 | /* 149 | * Run key generation in a seperate process (takes ages). The other process 150 | * will rewrite the key file, we shouldn't change anything till it's done and 151 | * we've reloaded the keys. 152 | */ 153 | void key_gen_run(struct otr_user_state *ustate, const char *account_name) 154 | { 155 | int ret; 156 | gcry_error_t err; 157 | 158 | assert(ustate); 159 | assert(account_name); 160 | 161 | if (key_gen_state.status != KEY_GEN_IDLE) { 162 | IRSSI_INFO(NULL, NULL, "Key generation for %s is still in progress. ", 163 | "Please wait until completion before creating a new key.", 164 | key_gen_state.account_name); 165 | goto error_status; 166 | } 167 | 168 | /* Make sure the pointer does not go away during the proess. */ 169 | key_gen_state.account_name = strdup(account_name); 170 | key_gen_state.ustate = ustate; 171 | 172 | /* Creating key file path. */ 173 | key_gen_state.key_file_path = file_path_build(OTR_KEYFILE); 174 | if (!key_gen_state.key_file_path) { 175 | IRSSI_INFO(NULL, NULL, "Key generation failed. ENOMEM"); 176 | goto error; 177 | } 178 | 179 | IRSSI_MSG("Key generation started for %9%s%n", key_gen_state.account_name); 180 | 181 | err = otrl_privkey_generate_start(ustate->otr_state, account_name, 182 | OTR_PROTOCOL_ID, &key_gen_state.newkey); 183 | if (err != GPG_ERR_NO_ERROR || !key_gen_state.newkey) { 184 | IRSSI_MSG("Key generation start failed. Err: %s", gcry_strerror(err)); 185 | goto error; 186 | } 187 | 188 | ret = pthread_create(&keygen_thread, NULL, generate_key, NULL); 189 | if (ret < 0) { 190 | IRSSI_MSG("Key generation failed. Thread failure: %s", 191 | strerror(errno)); 192 | goto error; 193 | } 194 | 195 | return; 196 | 197 | error: 198 | reset_key_gen_state(); 199 | error_status: 200 | return; 201 | } 202 | 203 | /* 204 | * Write fingerprints to file. 205 | */ 206 | void key_write_fingerprints(struct otr_user_state *ustate) 207 | { 208 | gcry_error_t err; 209 | char *filename; 210 | 211 | assert(ustate); 212 | 213 | filename = file_path_build(OTR_FINGERPRINTS_FILE); 214 | if (!filename) { 215 | goto error_filename; 216 | } 217 | 218 | err = otrl_privkey_write_fingerprints(ustate->otr_state, filename); 219 | if (err == GPG_ERR_NO_ERROR) { 220 | IRSSI_DEBUG("Fingerprints saved to %9%s%9", filename); 221 | } else { 222 | IRSSI_DEBUG("Error writing fingerprints: %d (%d)", 223 | gcry_strerror(err), gcry_strsource(err)); 224 | } 225 | 226 | free(filename); 227 | error_filename: 228 | return; 229 | } 230 | 231 | /* 232 | * Write instance tags to file. 233 | */ 234 | void key_write_instags(struct otr_user_state *ustate) 235 | { 236 | gcry_error_t err; 237 | char *filename; 238 | 239 | assert(ustate); 240 | 241 | filename = file_path_build(OTR_INSTAG_FILE); 242 | if (!filename) { 243 | goto error_filename; 244 | } 245 | 246 | err = otrl_instag_write(ustate->otr_state, filename); 247 | if (err == GPG_ERR_NO_ERROR) { 248 | IRSSI_DEBUG("Instance tags saved in %9%s%9", filename); 249 | } else { 250 | IRSSI_DEBUG("Error saving instance tags: %d (%d)", 251 | gcry_strerror(err), gcry_strsource(err)); 252 | } 253 | 254 | free(filename); 255 | error_filename: 256 | return; 257 | } 258 | 259 | /* 260 | * Load private keys. 261 | */ 262 | void key_load(struct otr_user_state *ustate) 263 | { 264 | int ret; 265 | gcry_error_t err; 266 | char *filename; 267 | 268 | assert(ustate); 269 | 270 | filename = file_path_build(OTR_KEYFILE); 271 | if (!filename) { 272 | goto error_filename; 273 | } 274 | 275 | ret = access(filename, F_OK); 276 | if (ret < 0) { 277 | IRSSI_DEBUG("No private keys found in %9%s%9", filename); 278 | goto end; 279 | } 280 | 281 | err = otrl_privkey_read(ustate->otr_state, filename); 282 | if (err == GPG_ERR_NO_ERROR) { 283 | IRSSI_DEBUG("Private keys loaded from %9%s%9", filename); 284 | } else { 285 | IRSSI_DEBUG("Error loading private keys: %d (%d)", 286 | gcry_strerror(err), gcry_strsource(err)); 287 | } 288 | 289 | end: 290 | free(filename); 291 | error_filename: 292 | return; 293 | } 294 | 295 | /* 296 | * Load fingerprints. 297 | */ 298 | void key_load_fingerprints(struct otr_user_state *ustate) 299 | { 300 | int ret; 301 | gcry_error_t err; 302 | char *filename; 303 | 304 | assert(ustate); 305 | 306 | filename = file_path_build(OTR_FINGERPRINTS_FILE); 307 | if (!filename) { 308 | goto error_filename; 309 | } 310 | 311 | ret = access(filename, F_OK); 312 | if (ret < 0) { 313 | IRSSI_DEBUG("No fingerprints found in %9%s%9", filename); 314 | goto end; 315 | } 316 | 317 | err = otrl_privkey_read_fingerprints(ustate->otr_state, filename, NULL, 318 | NULL); 319 | if (err == GPG_ERR_NO_ERROR) { 320 | IRSSI_DEBUG("Fingerprints loaded from %9%s%9", filename); 321 | } else { 322 | IRSSI_DEBUG("Error loading fingerprints: %d (%d)", 323 | gcry_strerror(err), gcry_strsource(err)); 324 | } 325 | 326 | end: 327 | free(filename); 328 | error_filename: 329 | return; 330 | } 331 | -------------------------------------------------------------------------------- /src/module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) module for the irssi IRC client 3 | * 4 | * Copyright (C) 2008 Uli Meis 5 | * 2012 David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "cmd.h" 32 | #include "key.h" 33 | #include "otr.h" 34 | #include "otr-formats.h" 35 | #include "utils.h" 36 | 37 | GCRY_THREAD_OPTION_PTHREAD_IMPL; 38 | 39 | static const char *signal_args_otr_event[] = { 40 | "iobject", "string", "string", NULL 41 | }; 42 | 43 | int debug = FALSE; 44 | 45 | /* 46 | * Need this to decode arguments in perl signal handlers. Maybe irssi should 47 | * install perl/perl-signals.h which is where this definition comes from? 48 | */ 49 | void perl_signal_register(const char *signal, const char **args); 50 | 51 | /* 52 | * Global state for the user. Init when the module loads. 53 | */ 54 | struct otr_user_state *user_state_global; 55 | 56 | /* 57 | * Pipes all outgoing private messages through OTR 58 | */ 59 | static void sig_server_sendmsg(SERVER_REC *server, const char *target, 60 | const char *msg, void *target_type_p) 61 | { 62 | int ret; 63 | char *otrmsg = NULL; 64 | 65 | key_gen_check(); 66 | 67 | if (GPOINTER_TO_INT(target_type_p) != SEND_TARGET_NICK) { 68 | goto end; 69 | } 70 | 71 | /* Critical section. On error, message MUST NOT be sent */ 72 | ret = otr_send(server, msg, target, &otrmsg); 73 | if (ret) { 74 | signal_stop(); 75 | goto end; 76 | } 77 | 78 | if (!otrmsg) { 79 | /* Send original message */ 80 | signal_continue(4, server, target, msg, target_type_p); 81 | } else { 82 | /* Send encrypted message */ 83 | signal_continue(4, server, target, otrmsg, target_type_p); 84 | } 85 | 86 | end: 87 | otrl_message_free(otrmsg); 88 | return; 89 | } 90 | 91 | /* 92 | * Pipes all incoming private messages through OTR 93 | */ 94 | void sig_message_private(SERVER_REC *server, const char *msg, 95 | const char *nick, const char *address) 96 | { 97 | int ret; 98 | char *new_msg = NULL; 99 | 100 | key_gen_check(); 101 | 102 | ret = otr_receive(server, msg, nick, &new_msg); 103 | if (ret) { 104 | signal_stop(); 105 | goto end; 106 | } 107 | 108 | if (!new_msg) { 109 | /* This message was not OTR */ 110 | signal_continue(4, server, msg, nick, address); 111 | } else { 112 | /* 113 | * Check for /me IRC marker and if so, handle it so the user does not 114 | * receive a message beginning with /me but rather let irssi handle it 115 | * as a IRC action. 116 | */ 117 | if (!strncmp(new_msg, OTR_IRC_MARKER_ME, OTR_IRC_MARKER_ME_LEN)) { 118 | signal_stop(); 119 | signal_emit("message irc action", 5, server, 120 | new_msg + OTR_IRC_MARKER_ME_LEN, nick, address, nick); 121 | } else { 122 | /* OTR received message */ 123 | signal_continue(4, server, new_msg, nick, address); 124 | } 125 | } 126 | 127 | end: 128 | otrl_message_free(new_msg); 129 | return; 130 | } 131 | 132 | /* 133 | * Finish an OTR conversation when its query is closed. 134 | */ 135 | static void sig_query_destroyed(QUERY_REC *query) 136 | { 137 | if (query && query->server && query->server->connrec) { 138 | otr_finish(query->server, query->name); 139 | } 140 | } 141 | 142 | /* 143 | * Handle /me IRC command. 144 | */ 145 | static void cmd_me(const char *data, IRC_SERVER_REC *server, 146 | WI_ITEM_REC *item) 147 | { 148 | int ret; 149 | const char *target; 150 | char *msg, *otrmsg = NULL; 151 | QUERY_REC *query; 152 | 153 | query = QUERY(item); 154 | 155 | key_gen_check(); 156 | 157 | if (!query || !query->server) { 158 | goto end; 159 | } 160 | 161 | CMD_IRC_SERVER(server); 162 | if (!IS_IRC_QUERY(query)) { 163 | goto end; 164 | } 165 | 166 | if (!server || !server->connected) { 167 | cmd_return_error(CMDERR_NOT_CONNECTED); 168 | } 169 | 170 | target = window_item_get_target(item); 171 | 172 | ret = asprintf(&msg, OTR_IRC_MARKER_ME "%s", data); 173 | if (ret < 0) { 174 | goto end; 175 | } 176 | 177 | /* Critical section. On error, message MUST NOT be sent */ 178 | ret = otr_send(query->server, msg, target, &otrmsg); 179 | free(msg); 180 | 181 | if (!otrmsg) { 182 | goto end; 183 | } 184 | 185 | signal_stop(); 186 | 187 | if (otrmsg) { 188 | /* Send encrypted message */ 189 | irssi_send_message(SERVER(server), target, otrmsg); 190 | otrl_message_free(otrmsg); 191 | } 192 | 193 | signal_emit("message irc own_action", 3, server, data, item->visible_name); 194 | 195 | end: 196 | return; 197 | } 198 | 199 | /* 200 | * Handle the "/otr" command. 201 | */ 202 | static void cmd_otr(const char *data, void *server, WI_ITEM_REC *item) 203 | { 204 | char *cmd = NULL; 205 | QUERY_REC *query; 206 | 207 | query = QUERY(item); 208 | 209 | /* Check key generation state. */ 210 | key_gen_check(); 211 | 212 | if (*data == '\0') { 213 | IRSSI_INFO(NULL, NULL, "Alive!"); 214 | goto end; 215 | } 216 | 217 | utils_extract_command(data, &cmd); 218 | if (!cmd) { 219 | /* ENOMEM and cmd is untouched. */ 220 | goto end; 221 | } 222 | 223 | if (query && query->server && query->server->connrec) { 224 | cmd_generic(user_state_global, query->server, query->name, cmd, data); 225 | } else { 226 | cmd_generic(user_state_global, NULL, NULL, cmd, data); 227 | } 228 | 229 | statusbar_items_redraw("otr"); 230 | 231 | free(cmd); 232 | 233 | end: 234 | return; 235 | } 236 | 237 | /* 238 | * Optionally finish conversations on /quit. We're already doing this on unload 239 | * but the quit handler terminates irc connections before unloading. 240 | */ 241 | static void cmd_quit(const char *data, void *server, WI_ITEM_REC *item) 242 | { 243 | otr_finishall(user_state_global); 244 | } 245 | 246 | /* 247 | * Handle otr statusbar of irssi. 248 | */ 249 | static void otr_statusbar(struct SBAR_ITEM_REC *item, int get_size_only) 250 | { 251 | WI_ITEM_REC *wi = active_win->active; 252 | QUERY_REC *query = QUERY(wi); 253 | enum otr_status_format formatnum = TXT_OTR_MODULE_NAME; 254 | 255 | if (query && query->server && query->server->connrec) { 256 | formatnum = otr_get_status_format(query->server, query->name); 257 | } 258 | 259 | statusbar_item_default_handler(item, get_size_only, 260 | formatnum ? otr_formats[formatnum].def : "", " ", FALSE); 261 | } 262 | 263 | /* 264 | * Create otr module directory if none exists. 265 | */ 266 | static int create_module_dir(void) 267 | { 268 | int ret; 269 | char *dir_path = NULL; 270 | 271 | /* Create ~/.irssi/otr directory. */ 272 | ret = asprintf(&dir_path, "%s%s", get_client_config_dir(), OTR_DIR); 273 | if (ret < 0) { 274 | IRSSI_MSG("Unable to allocate home dir path."); 275 | goto error_alloc; 276 | } 277 | 278 | ret = access(dir_path, F_OK); 279 | if (ret < 0) { 280 | ret = mkdir(dir_path, S_IRWXU); 281 | if (ret < 0) { 282 | IRSSI_MSG("Unable to create %s directory.", dir_path); 283 | goto error; 284 | } 285 | } 286 | 287 | error: 288 | free(dir_path); 289 | error_alloc: 290 | return ret; 291 | } 292 | 293 | void irssi_send_message(SERVER_REC *irssi, const char *recipient, 294 | const char *msg) 295 | { 296 | /* 297 | * Apparently, there are cases where the server record is NULL which has 298 | * been reported with the irssi xmpp plugin. In that case, just return an 299 | * do nothing. 300 | */ 301 | if (!irssi) { 302 | return; 303 | } 304 | 305 | irssi->send_message(irssi, recipient, msg, 306 | GPOINTER_TO_INT(SEND_TARGET_NICK)); 307 | } 308 | 309 | /* 310 | * irssi init() 311 | */ 312 | void otr_init(void) 313 | { 314 | int ret; 315 | 316 | module_register(MODULE_NAME, "core"); 317 | 318 | theme_register(otr_formats); 319 | 320 | ret = create_module_dir(); 321 | if (ret < 0) { 322 | return; 323 | } 324 | 325 | gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); 326 | 327 | otr_lib_init(); 328 | 329 | user_state_global = otr_init_user_state(); 330 | if (!user_state_global) { 331 | IRSSI_MSG("Unable to allocate user global state"); 332 | return; 333 | } 334 | 335 | signal_add_first("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg); 336 | signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); 337 | signal_add("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); 338 | 339 | command_bind("otr", NULL, (SIGNAL_FUNC) cmd_otr); 340 | command_bind_first("quit", NULL, (SIGNAL_FUNC) cmd_quit); 341 | command_bind_irc_first("me", NULL, (SIGNAL_FUNC) cmd_me); 342 | 343 | statusbar_item_register("otr", NULL, otr_statusbar); 344 | statusbar_items_redraw("window"); 345 | 346 | perl_signal_register("otr event", signal_args_otr_event); 347 | } 348 | 349 | /* 350 | * irssi deinit() 351 | */ 352 | void otr_deinit(void) 353 | { 354 | signal_remove("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg); 355 | signal_remove("message private", (SIGNAL_FUNC) sig_message_private); 356 | signal_remove("query destroyed", (SIGNAL_FUNC) sig_query_destroyed); 357 | 358 | command_unbind("otr", (SIGNAL_FUNC) cmd_otr); 359 | command_unbind("quit", (SIGNAL_FUNC) cmd_quit); 360 | command_unbind("me", (SIGNAL_FUNC) cmd_me); 361 | 362 | statusbar_item_unregister("otr"); 363 | 364 | otr_finishall(user_state_global); 365 | 366 | /* Remove glib timer if any. */ 367 | otr_control_timer(0, NULL); 368 | 369 | otr_free_user_state(user_state_global); 370 | 371 | otr_lib_uninit(); 372 | 373 | theme_unregister(); 374 | } 375 | 376 | #ifdef IRSSI_ABI_VERSION 377 | /* 378 | * irssi abicheck() 379 | */ 380 | void otr_abicheck(int *version) 381 | { 382 | *version = IRSSI_ABI_VERSION; 383 | } 384 | #endif 385 | -------------------------------------------------------------------------------- /src/cmd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | #include 24 | #include 25 | 26 | #include "cmd.h" 27 | #include "key.h" 28 | 29 | /* 30 | * /otr debug 31 | */ 32 | static void _cmd_debug(struct otr_user_state *ustate, SERVER_REC *irssi, 33 | const char *target, const void *data) 34 | { 35 | debug = !debug; 36 | if (debug) { 37 | IRSSI_INFO(NULL, NULL, "Debug on"); 38 | } else { 39 | IRSSI_INFO(NULL, NULL, "Debug off"); 40 | } 41 | } 42 | 43 | /* 44 | * /otr version 45 | */ 46 | static void _cmd_version(struct otr_user_state *ustate, SERVER_REC *irssi, 47 | const char *target, const void *data) 48 | { 49 | IRSSI_INFO(NULL, NULL, "OTR module version: " VERSION); 50 | } 51 | 52 | /* 53 | * /otr help 54 | */ 55 | static void _cmd_help(struct otr_user_state *ustate, SERVER_REC *irssi, 56 | const char *target, const void *data) 57 | { 58 | int ret; 59 | char *cmd_line; 60 | 61 | ret = asprintf(&cmd_line, "%sHELP otr", settings_get_str("cmdchars")); 62 | if (ret < 0) { 63 | return; 64 | } 65 | 66 | /* Call /help otr instread of duplicating the text output. */ 67 | signal_emit("send command", 3, cmd_line, irssi, NULL); 68 | 69 | free(cmd_line); 70 | } 71 | 72 | /* 73 | * /otr finish 74 | */ 75 | static void _cmd_finish(struct otr_user_state *ustate, SERVER_REC *irssi, 76 | const char *target, const void *data) 77 | { 78 | if (!irssi || !target) { 79 | IRSSI_NOTICE(irssi, target, 80 | "Failed: Can't get nick and server of current query window. " 81 | "(Or maybe you're doing this in the status window?)"); 82 | goto end; 83 | } 84 | 85 | otr_finish(irssi, target); 86 | 87 | end: 88 | return; 89 | } 90 | 91 | /* 92 | * /otr trust [FP] 93 | */ 94 | static void _cmd_trust(struct otr_user_state *ustate, SERVER_REC *irssi, 95 | const char *target, const void *data) 96 | { 97 | int argc; 98 | char **argv; 99 | char str_fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], *fp = NULL; 100 | 101 | utils_explode_args(data, &argv, &argc); 102 | 103 | if (argc == 5) { 104 | utils_hash_parts_to_readable_hash((const char **) argv, str_fp); 105 | fp = str_fp; 106 | } else if (!irssi || (irssi && argc != 0)) { 107 | /* If no IRSSI or some arguments (not 5), bad command. */ 108 | IRSSI_NOTICE(irssi, target, "Usage %9/otr trust [FP]%9 " 109 | "where FP is the five part of the fingerprint listed by " 110 | "%9/otr contexts%9 or do the command inside an OTR session " 111 | "private message window."); 112 | goto end; 113 | } 114 | 115 | otr_trust(irssi, target, fp, ustate); 116 | 117 | end: 118 | utils_free_args(&argv, argc); 119 | return; 120 | } 121 | 122 | /* 123 | * /otr authabort 124 | */ 125 | static void _cmd_authabort(struct otr_user_state *ustate, SERVER_REC *irssi, 126 | const char *target, const void *data) 127 | { 128 | if (!irssi || !target) { 129 | IRSSI_NOTICE(irssi, target, 130 | "Failed: Can't get nick and server of current query window. " 131 | "(Or maybe you're doing this in the status window?)"); 132 | goto end; 133 | } 134 | 135 | otr_auth_abort(irssi, target); 136 | 137 | end: 138 | return; 139 | } 140 | 141 | /* 142 | * /otr genkey mynick@irc.server.net 143 | */ 144 | static void _cmd_genkey(struct otr_user_state *ustate, SERVER_REC *irssi, 145 | const char *target, const void *data) 146 | { 147 | int argc; 148 | char **argv; 149 | 150 | utils_explode_args(data, &argv, &argc); 151 | 152 | if (argc) { 153 | if (strchr(argv[0], '@')) { 154 | key_gen_run(ustate, argv[0]); 155 | } else { 156 | IRSSI_INFO(NULL, NULL, "I need an account name. " 157 | "Try something like /otr genkey mynick@irc.server.net"); 158 | } 159 | } else { 160 | IRSSI_INFO(NULL, NULL, "I need an account name. " 161 | "Try something like /otr genkey mynick@irc.server.net"); 162 | } 163 | 164 | utils_free_args(&argv, argc); 165 | } 166 | 167 | /* 168 | * Authentication with a question. 169 | * 170 | * /otr authq [QUESTION] SECRET 171 | */ 172 | static void _cmd_authq(struct otr_user_state *ustate, SERVER_REC *irssi, 173 | const char *target, const void *data) 174 | { 175 | int ret; 176 | char *question = NULL, *secret = NULL; 177 | 178 | if (!irssi || !target) { 179 | IRSSI_NOTICE(irssi, target, 180 | "Failed: Can't get nick and server of current query window. " 181 | "(Or maybe you're doing this in the status window?)"); 182 | goto end; 183 | } 184 | 185 | /* 186 | * Returns a negative value if the command arguments are not formatted 187 | * correctly or missing. Note, an empty question or secret is valid. 188 | */ 189 | ret = utils_io_extract_smp(data, &question, &secret); 190 | if (ret < 0) { 191 | IRSSI_NOTICE(irssi, target, "Usage: %9/otr authq [QUESTION] " 192 | "SECRET%9"); 193 | goto end; 194 | } 195 | 196 | otr_auth(irssi, target, question, secret); 197 | 198 | free(question); 199 | free(secret); 200 | 201 | end: 202 | return; 203 | } 204 | 205 | /* 206 | * /otr auth SECRET 207 | */ 208 | static void _cmd_auth(struct otr_user_state *ustate, SERVER_REC *irssi, 209 | const char *target, const void *data) 210 | { 211 | int ret; 212 | char *secret = NULL; 213 | 214 | if (!irssi || !target) { 215 | IRSSI_NOTICE(irssi, target, 216 | "Failed: Can't get nick and server of current query window. " 217 | "(Or maybe you're doing this in the status window?)"); 218 | goto error; 219 | } 220 | 221 | ret = utils_auth_extract_secret(data, &secret); 222 | if (ret < 0) { 223 | IRSSI_NOTICE(irssi, target, "Huh... I need a secret here James."); 224 | goto error; 225 | } 226 | 227 | otr_auth(irssi, target, NULL, secret); 228 | free(secret); 229 | 230 | error: 231 | return; 232 | } 233 | 234 | /* 235 | * /otr contexts 236 | */ 237 | static void _cmd_contexts(struct otr_user_state *ustate, SERVER_REC *irssi, 238 | const char *target, const void *data) 239 | { 240 | otr_contexts(ustate); 241 | } 242 | 243 | /* 244 | * /otr init 245 | */ 246 | static void _cmd_init(struct otr_user_state *ustate, SERVER_REC *irssi, 247 | const char *target, const void *data) 248 | { 249 | ConnContext *ctx; 250 | 251 | /* No server object, just ignore the request */ 252 | if (!irssi || !target) { 253 | IRSSI_NOTICE(irssi, target, 254 | "Failed: Can't get nick and server of current query window. " 255 | "(Or maybe you're doing this in the status window?)"); 256 | goto end; 257 | } 258 | 259 | ctx = otr_find_context(irssi, target, 0); 260 | if (ctx && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { 261 | IRSSI_NOTICE(irssi, target, "Already secure!"); 262 | goto end; 263 | } 264 | 265 | IRSSI_NOTICE(irssi, target, "Initiating OTR session..."); 266 | 267 | /* 268 | * Irssi does not handle well the HTML tag in the default OTR query message 269 | * so just send the OTR tag instead. Contact me for a better fix! :) 270 | */ 271 | irssi_send_message(irssi, target, "?OTRv23?"); 272 | 273 | end: 274 | return; 275 | } 276 | 277 | /* 278 | * /otr forget [FP] 279 | */ 280 | static void _cmd_forget(struct otr_user_state *ustate, SERVER_REC *irssi, 281 | const char *target, const void *data) 282 | { 283 | int argc; 284 | char **argv; 285 | char str_fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], *fp = NULL; 286 | 287 | utils_explode_args(data, &argv, &argc); 288 | 289 | if (argc == 5) { 290 | utils_hash_parts_to_readable_hash((const char **) argv, str_fp); 291 | fp = str_fp; 292 | } else if (!irssi || (irssi && argc != 0)) { 293 | /* If no IRSSI or some arguments (not 5), bad command. */ 294 | IRSSI_NOTICE(irssi, target, "Usage %9/otr forget [FP]%9 " 295 | "where FP is the five part of the fingerprint listed by " 296 | "%9/otr contexts%9 or do the command inside an OTR session " 297 | "private message window"); 298 | goto error; 299 | } 300 | 301 | /* Trigger the forget action. */ 302 | otr_forget(irssi, target, fp, ustate); 303 | 304 | error: 305 | utils_free_args(&argv, argc); 306 | return; 307 | } 308 | 309 | /* 310 | * /otr distrust [FP] 311 | */ 312 | static void _cmd_distrust(struct otr_user_state *ustate, SERVER_REC *irssi, 313 | const char *target, const void *data) 314 | { 315 | int argc; 316 | char **argv; 317 | char str_fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], *fp = NULL; 318 | 319 | utils_explode_args(data, &argv, &argc); 320 | 321 | if (argc == 5) { 322 | utils_hash_parts_to_readable_hash((const char **) argv, str_fp); 323 | fp = str_fp; 324 | } else if (!irssi || (irssi && argc != 0)) { 325 | /* If no IRSSI or some arguments (not 5), bad command. */ 326 | IRSSI_NOTICE(irssi, target, "Usage %9/otr distrust [FP]%9 " 327 | "where FP is the five part of the fingerprint listed by " 328 | "%9/otr contexts%9 or do the command inside an OTR session " 329 | "private message window"); 330 | goto error; 331 | } 332 | 333 | /* Trigger the forget action. */ 334 | otr_distrust(irssi, target, fp, ustate); 335 | 336 | error: 337 | utils_free_args(&argv, argc); 338 | return; 339 | } 340 | 341 | /* 342 | * /otr info 343 | */ 344 | static void _cmd_info(struct otr_user_state *ustate, SERVER_REC *irssi, 345 | const char *target, const void *data) 346 | { 347 | unsigned int fp_found = 0; 348 | char ownfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 349 | OtrlPrivKey *key; 350 | 351 | for (key = user_state_global->otr_state->privkey_root; key != NULL; 352 | key = key->next) { 353 | otrl_privkey_fingerprint(user_state_global->otr_state, ownfp, 354 | key->accountname, OTR_PROTOCOL_ID); 355 | IRSSI_NOTICE(irssi, target, "%B%s%n fingerprint:", 356 | key->accountname, ownfp); 357 | IRSSI_NOTICE(irssi, target, "%g%s%n", ownfp); 358 | fp_found = 1; 359 | } 360 | 361 | if (!fp_found) { 362 | IRSSI_NOTICE(irssi, target, "No key found!"); 363 | } 364 | } 365 | 366 | static struct irssi_commands cmds[] = { 367 | { "version", _cmd_version }, 368 | { "debug", _cmd_debug }, 369 | { "help", _cmd_help }, 370 | { "init", _cmd_init }, 371 | { "finish", _cmd_finish }, 372 | { "trust", _cmd_trust }, 373 | { "distrust", _cmd_distrust }, 374 | { "forget", _cmd_forget }, 375 | { "authabort", _cmd_authabort }, 376 | { "auth", _cmd_auth }, 377 | { "authq", _cmd_authq }, 378 | { "genkey", _cmd_genkey }, 379 | { "contexts", _cmd_contexts }, 380 | { "info", _cmd_info }, 381 | { NULL, NULL }, 382 | { NULL, NULL } 383 | }; 384 | 385 | /* 386 | * Entry point for all other commands. 387 | */ 388 | void cmd_generic(struct otr_user_state *ustate, SERVER_REC *irssi, 389 | const char *target, char *cmd, const void *data) 390 | { 391 | struct irssi_commands *commands = cmds; 392 | 393 | assert(cmd); 394 | 395 | do { 396 | if (strcmp(commands->name, cmd) == 0) { 397 | commands->func(ustate, irssi, target, data); 398 | goto end; 399 | } 400 | } while ((++commands)->name); 401 | 402 | IRSSI_NOTICE(irssi, target, "Unknown command %9%s%n", cmd); 403 | 404 | end: 405 | return; 406 | } 407 | -------------------------------------------------------------------------------- /src/otr-ops.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * Copyright (C) 2008 Uli Meis 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 18 | */ 19 | 20 | #include 21 | 22 | #include "key.h" 23 | #include "module.h" 24 | 25 | static OtrlPolicy OTR_DEFAULT_POLICY = 26 | OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE; 27 | 28 | /* 29 | * Return default policy for now. 30 | */ 31 | static OtrlPolicy ops_policy(void *opdata, ConnContext *context) 32 | { 33 | return OTR_DEFAULT_POLICY; 34 | } 35 | 36 | /* 37 | * Request for key generation. 38 | * 39 | * The lib actually expects us to be finished before the call returns. Since 40 | * this can take more than an hour on some systems there isn't even a point in 41 | * trying... 42 | */ 43 | static void ops_create_privkey(void *opdata, const char *accountname, 44 | const char *protocol) 45 | { 46 | key_gen_run(user_state_global, accountname); 47 | } 48 | 49 | /* 50 | * Inject OTR message. 51 | */ 52 | static void ops_inject_msg(void *opdata, const char *accountname, 53 | const char *protocol, const char *recipient, const char *message) 54 | { 55 | SERVER_REC *irssi = opdata; 56 | 57 | IRSSI_DEBUG("Inject msg:\n[%s]", message); 58 | irssi_send_message(irssi, recipient, message); 59 | } 60 | 61 | /* 62 | * Gone secure. 63 | */ 64 | static void ops_secure(void *opdata, ConnContext *context) 65 | { 66 | int ret; 67 | char ownfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 68 | char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 69 | SERVER_REC *irssi = opdata; 70 | struct otr_peer_context *opc; 71 | 72 | assert(context); 73 | /* This should *really* not happened */ 74 | assert(context->msgstate == OTRL_MSGSTATE_ENCRYPTED); 75 | 76 | IRSSI_NOTICE(irssi, context->username, "Gone %9secure%9"); 77 | otr_status_change(irssi, context->username, OTR_STATUS_GONE_SECURE); 78 | 79 | opc = context->app_data; 80 | opc->active_fingerprint = context->active_fingerprint; 81 | 82 | ret = otrl_context_is_fingerprint_trusted(context->active_fingerprint); 83 | if (ret) { 84 | /* Secure and trusted */ 85 | goto end; 86 | } 87 | 88 | /* Not authenticated. Let's print out the fingerprints for comparison. */ 89 | otrl_privkey_hash_to_human(peerfp, 90 | context->active_fingerprint->fingerprint); 91 | otrl_privkey_fingerprint(user_state_global->otr_state, ownfp, 92 | context->accountname, OTR_PROTOCOL_ID); 93 | 94 | IRSSI_NOTICE(irssi, context->username, "Your peer is not " 95 | "authenticated. To make sure you're talking to the right person you can " 96 | "either agree on a secret and use the authentication command " 97 | "%9/otr auth%9 or %9/otr authq [QUESTION] SECRET%9. You can also " 98 | "use the traditional way and compare fingerprints " 99 | "(e.g. telephone or GPG-signed mail) and subsequently enter " 100 | "%9/otr trust%9."); 101 | 102 | IRSSI_NOTICE(irssi, context->username, "Your fingerprint is: %y%s%n", 103 | ownfp); 104 | IRSSI_NOTICE(irssi, context->username, "%9%s's%9 fingerprint is: %r%s%n", 105 | context->username, peerfp); 106 | 107 | end: 108 | return; 109 | } 110 | 111 | /* 112 | * Gone insecure. 113 | */ 114 | static void ops_insecure(void *opdata, ConnContext *context) 115 | { 116 | SERVER_REC *irssi = opdata; 117 | 118 | IRSSI_NOTICE(irssi, context->username, "Gone %rinsecure%r"); 119 | otr_status_change(irssi, context->username, OTR_STATUS_GONE_INSECURE); 120 | } 121 | 122 | /* 123 | * Really critical with IRC. Unfortunately, we can't tell our peer which size 124 | * to use. 125 | */ 126 | static int ops_max_msg(void *opdata, ConnContext *context) 127 | { 128 | return OTR_MAX_MSG_SIZE; 129 | } 130 | 131 | static void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event, 132 | ConnContext *context, const char *message, gcry_error_t err) 133 | { 134 | SERVER_REC *server = opdata; 135 | char *username = context->username; 136 | 137 | switch (msg_event) { 138 | case OTRL_MSGEVENT_NONE: 139 | break; 140 | case OTRL_MSGEVENT_ENCRYPTION_REQUIRED: 141 | IRSSI_NOTICE(server, username, "%yEncryption is required.%n"); 142 | break; 143 | case OTRL_MSGEVENT_ENCRYPTION_ERROR: 144 | IRSSI_NOTICE(server, username, "An error occurred when " 145 | "encrypting your message. The message was NOT sent."); 146 | break; 147 | case OTRL_MSGEVENT_CONNECTION_ENDED: 148 | IRSSI_NOTICE(server, username, "%9%s%9 has already closed the " 149 | "connection to you.", username); 150 | break; 151 | case OTRL_MSGEVENT_SETUP_ERROR: 152 | if (!err) { 153 | err = GPG_ERR_INV_VALUE; 154 | } 155 | switch (err) { 156 | case GPG_ERR_INV_VALUE: 157 | IRSSI_NOTICE(server, username, "Error setting up private " 158 | "conversation: Malformed message received"); 159 | break; 160 | default: 161 | IRSSI_NOTICE(server, username, "Error up private " 162 | "conversation: %s", gcry_strerror(err)); 163 | break; 164 | } 165 | break; 166 | case OTRL_MSGEVENT_MSG_REFLECTED: 167 | IRSSI_NOTICE(server, username, "Receiving our own OTR messages. " 168 | "You are either trying to talk to yourself, or someone is " 169 | "reflecting your messages back at you."); 170 | break; 171 | case OTRL_MSGEVENT_MSG_RESENT: 172 | IRSSI_NOTICE(server, username, "The last message to %9%s%9 " 173 | "was resent: %s", username, message); 174 | break; 175 | case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE: 176 | IRSSI_NOTICE(server, username, "The encrypted message received " 177 | "from %s is unreadable, as you are not currently communicating " 178 | "privately.", username); 179 | break; 180 | case OTRL_MSGEVENT_RCVDMSG_UNREADABLE: 181 | IRSSI_NOTICE(server, username, "We received an unreadable " 182 | "encrypted message from %s.", username); 183 | break; 184 | case OTRL_MSGEVENT_RCVDMSG_MALFORMED: 185 | IRSSI_NOTICE(server, username, "We received a malformed data " 186 | "message from %s.", username); 187 | break; 188 | case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD: 189 | IRSSI_DEBUG("Heartbeat received from %s.", username); 190 | break; 191 | case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT: 192 | IRSSI_DEBUG("Heartbeat sent to %s.", username); 193 | break; 194 | case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR: 195 | IRSSI_NOTICE(server, username, "General Error: %s.", message); 196 | break; 197 | case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED: 198 | IRSSI_NOTICE(server, username, 199 | "The following message from %9%s%9 was NOT " 200 | "encrypted.", username); 201 | /* 202 | * This is a hack I found to send the message in a private window of 203 | * the username without creating an infinite loop since the 'message 204 | * private' signal is hijacked in this module. If someone is able to 205 | * clean this up with a more elegant solution, by all means PLEASE 206 | * submit a patch or email me a better way. 207 | */ 208 | signal_remove("message private", (SIGNAL_FUNC) sig_message_private); 209 | signal_emit("message private", 4, server, message, username, 210 | IRSSI_CONN_ADDR(server)); 211 | signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); 212 | break; 213 | case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED: 214 | IRSSI_NOTICE(server, username, "Unrecognized OTR message " 215 | "received from %s.", username); 216 | break; 217 | case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE: 218 | IRSSI_DEBUG("%s has sent a message for a different instance.", 219 | username); 220 | break; 221 | } 222 | } 223 | 224 | /* 225 | * A context changed. 226 | */ 227 | static void ops_up_ctx_list(void *opdata) 228 | { 229 | otr_status_change(opdata, NULL, OTR_STATUS_CTX_UPDATE); 230 | } 231 | 232 | /* 233 | * Save fingerprint changes. 234 | */ 235 | static void ops_write_fingerprints(void *data) 236 | { 237 | key_write_fingerprints(user_state_global); 238 | } 239 | 240 | static int ops_is_logged_in(void *opdata, const char *accountname, 241 | const char *protocol, const char *recipient) 242 | { 243 | int ret; 244 | SERVER_REC *irssi = opdata; 245 | 246 | if (irssi) { 247 | /* Logged in */ 248 | ret = 1; 249 | } else { 250 | /* Not */ 251 | ret = 0; 252 | } 253 | 254 | IRSSI_DEBUG("User %s %s logged in", accountname, 255 | (ret == 0) ? "not" : ""); 256 | 257 | return ret; 258 | } 259 | 260 | static void ops_create_instag(void *opdata, const char *accountname, 261 | const char *protocol) 262 | { 263 | otrl_instag_generate(user_state_global->otr_state, "/dev/null", 264 | accountname, protocol); 265 | key_write_instags(user_state_global); 266 | } 267 | 268 | static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event, 269 | ConnContext *context, unsigned short progress_percent, char *question) 270 | { 271 | SERVER_REC *irssi = opdata; 272 | const char *from = context->username; 273 | struct otr_peer_context *opc = context->app_data; 274 | 275 | /* 276 | * Without a peer context, we can't update the status bar. Code flow error 277 | * if none is found. This context is created automatically by an otrl_* 278 | * call or if non existent when returned from 279 | * otrl_message_sending/receiving. 280 | */ 281 | assert(opc); 282 | 283 | opc->smp_event = smp_event; 284 | 285 | switch (smp_event) { 286 | case OTRL_SMPEVENT_ASK_FOR_SECRET: 287 | IRSSI_NOTICE(irssi, from, "%9%s%9 wants to authenticate. " 288 | "Type %9/otr auth %9 to complete.", from); 289 | opc->ask_secret = 1; 290 | otr_status_change(irssi, from, OTR_STATUS_SMP_INCOMING); 291 | break; 292 | case OTRL_SMPEVENT_ASK_FOR_ANSWER: 293 | IRSSI_NOTICE(irssi, from, "%9%s%9 wants to authenticate and " 294 | "asked this question:", from); 295 | IRSSI_NOTICE(irssi, from, "%b>%n %y%s%n", question); 296 | IRSSI_NOTICE(irssi, from, "Type %9/otr auth %9 to complete."); 297 | opc->ask_secret = 1; 298 | otr_status_change(irssi, from, OTR_STATUS_SMP_INCOMING); 299 | break; 300 | case OTRL_SMPEVENT_IN_PROGRESS: 301 | IRSSI_NOTICE(irssi, from, "%9%s%9 replied to your auth request", 302 | from); 303 | otr_status_change(irssi, from, OTR_STATUS_SMP_FINALIZE); 304 | break; 305 | case OTRL_SMPEVENT_SUCCESS: 306 | IRSSI_NOTICE(irssi, from, "%gAuthentication successful.%n"); 307 | otr_status_change(irssi, from, OTR_STATUS_SMP_SUCCESS); 308 | break; 309 | case OTRL_SMPEVENT_ABORT: 310 | otr_auth_abort(irssi, context->username); 311 | otr_status_change(irssi, from, OTR_STATUS_SMP_ABORTED); 312 | break; 313 | case OTRL_SMPEVENT_FAILURE: 314 | case OTRL_SMPEVENT_CHEATED: 315 | case OTRL_SMPEVENT_ERROR: 316 | IRSSI_NOTICE(irssi, from, "%RAuthentication failed%n"); 317 | otr_status_change(irssi, from, OTR_STATUS_SMP_FAILED); 318 | break; 319 | default: 320 | IRSSI_NOTICE(irssi, from, "Received unknown SMP event. " 321 | "Ignoring"); 322 | break; 323 | } 324 | } 325 | 326 | /* 327 | * timer_control callback. 328 | */ 329 | static void ops_timer_control(void *opdata, unsigned int interval) 330 | { 331 | otr_control_timer(interval, opdata); 332 | } 333 | 334 | /* 335 | * Handle otr error message. 336 | */ 337 | static const char *ops_otr_error_message(void *opdata, ConnContext *context, 338 | OtrlErrorCode code) 339 | { 340 | char *msg = NULL; 341 | 342 | switch (code) { 343 | case OTRL_ERRCODE_NONE: 344 | break; 345 | case OTRL_ERRCODE_ENCRYPTION_ERROR: 346 | msg = strdup("Error occurred encrypting message."); 347 | break; 348 | case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE: 349 | if (context) { 350 | msg = strdup("You sent encrypted data which was unexpected"); 351 | } 352 | break; 353 | case OTRL_ERRCODE_MSG_UNREADABLE: 354 | msg = strdup("You transmitted an unreadable encrypted message"); 355 | break; 356 | case OTRL_ERRCODE_MSG_MALFORMED: 357 | msg = strdup("You transmitted a malformed data message."); 358 | break; 359 | } 360 | 361 | return msg; 362 | } 363 | 364 | /* 365 | * Free otr error message callback. 366 | */ 367 | static void ops_otr_error_message_free(void *opdata, const char *err_msg) 368 | { 369 | if (err_msg) { 370 | free((char *)err_msg); 371 | } 372 | } 373 | 374 | /* 375 | * Assign OTR message operations. 376 | */ 377 | OtrlMessageAppOps otr_ops = { 378 | ops_policy, 379 | ops_create_privkey, 380 | ops_is_logged_in, 381 | ops_inject_msg, 382 | ops_up_ctx_list, 383 | NULL, /* new_fingerprint */ 384 | ops_write_fingerprints, 385 | ops_secure, 386 | ops_insecure, 387 | NULL, /* still_secure */ 388 | ops_max_msg, 389 | NULL, /* account_name */ 390 | NULL, /* account_name_free */ 391 | NULL, /* received_symkey */ 392 | ops_otr_error_message, 393 | ops_otr_error_message_free, 394 | NULL, /* resent_msg_prefix */ 395 | NULL, /* resent_msg_prefix_free */ 396 | ops_smp_event, 397 | ops_handle_msg_event, 398 | ops_create_instag, 399 | NULL, /* convert_msg */ 400 | NULL, /* convert_free */ 401 | ops_timer_control, 402 | }; 403 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 5 | 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 6 | 7 | Copying and distribution of this file, with or without modification, 8 | are permitted in any medium without royalty provided the copyright 9 | notice and this notice are preserved. This file is offered as-is, 10 | without warranty of any kind. 11 | 12 | Basic Installation 13 | ================== 14 | 15 | Briefly, the shell commands `./configure; make; make install' should 16 | configure, build, and install this package. The following 17 | more-detailed instructions are generic; see the `README' file for 18 | instructions specific to this package. Some packages provide this 19 | `INSTALL' file but do not implement all of the features documented 20 | below. The lack of an optional feature in a given package is not 21 | necessarily a bug. More recommendations for GNU packages can be found 22 | in *note Makefile Conventions: (standards)Makefile Conventions. 23 | 24 | The `configure' shell script attempts to guess correct values for 25 | various system-dependent variables used during compilation. It uses 26 | those values to create a `Makefile' in each directory of the package. 27 | It may also create one or more `.h' files containing system-dependent 28 | definitions. Finally, it creates a shell script `config.status' that 29 | you can run in the future to recreate the current configuration, and a 30 | file `config.log' containing compiler output (useful mainly for 31 | debugging `configure'). 32 | 33 | It can also use an optional file (typically called `config.cache' 34 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 35 | the results of its tests to speed up reconfiguring. Caching is 36 | disabled by default to prevent problems with accidental use of stale 37 | cache files. 38 | 39 | If you need to do unusual things to compile the package, please try 40 | to figure out how `configure' could check whether to do them, and mail 41 | diffs or instructions to the address given in the `README' so they can 42 | be considered for the next release. If you are using the cache, and at 43 | some point `config.cache' contains results you don't want to keep, you 44 | may remove or edit it. 45 | 46 | The file `configure.ac' (or `configure.in') is used to create 47 | `configure' by a program called `autoconf'. You need `configure.ac' if 48 | you want to change it or regenerate `configure' using a newer version 49 | of `autoconf'. 50 | 51 | The simplest way to compile this package is: 52 | 53 | 1. `cd' to the directory containing the package's source code and type 54 | `./configure' to configure the package for your system. 55 | 56 | Running `configure' might take a while. While running, it prints 57 | some messages telling which features it is checking for. 58 | 59 | 2. Type `make' to compile the package. 60 | 61 | 3. Optionally, type `make check' to run any self-tests that come with 62 | the package, generally using the just-built uninstalled binaries. 63 | 64 | 4. Type `make install' to install the programs and any data files and 65 | documentation. When installing into a prefix owned by root, it is 66 | recommended that the package be configured and built as a regular 67 | user, and only the `make install' phase executed with root 68 | privileges. 69 | 70 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 71 | this time using the binaries in their final installed location. 72 | This target does not install anything. Running this target as a 73 | regular user, particularly if the prior `make install' required 74 | root privileges, verifies that the installation completed 75 | correctly. 76 | 77 | 6. You can remove the program binaries and object files from the 78 | source code directory by typing `make clean'. To also remove the 79 | files that `configure' created (so you can compile the package for 80 | a different kind of computer), type `make distclean'. There is 81 | also a `make maintainer-clean' target, but that is intended mainly 82 | for the package's developers. If you use it, you may have to get 83 | all sorts of other programs in order to regenerate files that came 84 | with the distribution. 85 | 86 | 7. Often, you can also type `make uninstall' to remove the installed 87 | files again. In practice, not all packages have tested that 88 | uninstallation works correctly, even though it is required by the 89 | GNU Coding Standards. 90 | 91 | 8. Some packages, particularly those that use Automake, provide `make 92 | distcheck', which can by used by developers to test that all other 93 | targets like `make install' and `make uninstall' work correctly. 94 | This target is generally not run by end users. 95 | 96 | Compilers and Options 97 | ===================== 98 | 99 | Some systems require unusual options for compilation or linking that 100 | the `configure' script does not know about. Run `./configure --help' 101 | for details on some of the pertinent environment variables. 102 | 103 | You can give `configure' initial values for configuration parameters 104 | by setting variables in the command line or in the environment. Here 105 | is an example: 106 | 107 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 108 | 109 | *Note Defining Variables::, for more details. 110 | 111 | Compiling For Multiple Architectures 112 | ==================================== 113 | 114 | You can compile the package for more than one kind of computer at the 115 | same time, by placing the object files for each architecture in their 116 | own directory. To do this, you can use GNU `make'. `cd' to the 117 | directory where you want the object files and executables to go and run 118 | the `configure' script. `configure' automatically checks for the 119 | source code in the directory that `configure' is in and in `..'. This 120 | is known as a "VPATH" build. 121 | 122 | With a non-GNU `make', it is safer to compile the package for one 123 | architecture at a time in the source code directory. After you have 124 | installed the package for one architecture, use `make distclean' before 125 | reconfiguring for another architecture. 126 | 127 | On MacOS X 10.5 and later systems, you can create libraries and 128 | executables that work on multiple system types--known as "fat" or 129 | "universal" binaries--by specifying multiple `-arch' options to the 130 | compiler but only a single `-arch' option to the preprocessor. Like 131 | this: 132 | 133 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 134 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 135 | CPP="gcc -E" CXXCPP="g++ -E" 136 | 137 | This is not guaranteed to produce working output in all cases, you 138 | may have to build one architecture at a time and combine the results 139 | using the `lipo' tool if you have problems. 140 | 141 | Installation Names 142 | ================== 143 | 144 | By default, `make install' installs the package's commands under 145 | `/usr/local/bin', include files under `/usr/local/include', etc. You 146 | can specify an installation prefix other than `/usr/local' by giving 147 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 148 | absolute file name. 149 | 150 | You can specify separate installation prefixes for 151 | architecture-specific files and architecture-independent files. If you 152 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 153 | PREFIX as the prefix for installing programs and libraries. 154 | Documentation and other data files still use the regular prefix. 155 | 156 | In addition, if you use an unusual directory layout you can give 157 | options like `--bindir=DIR' to specify different values for particular 158 | kinds of files. Run `configure --help' for a list of the directories 159 | you can set and what kinds of files go in them. In general, the 160 | default for these options is expressed in terms of `${prefix}', so that 161 | specifying just `--prefix' will affect all of the other directory 162 | specifications that were not explicitly provided. 163 | 164 | The most portable way to affect installation locations is to pass the 165 | correct locations to `configure'; however, many packages provide one or 166 | both of the following shortcuts of passing variable assignments to the 167 | `make install' command line to change installation locations without 168 | having to reconfigure or recompile. 169 | 170 | The first method involves providing an override variable for each 171 | affected directory. For example, `make install 172 | prefix=/alternate/directory' will choose an alternate location for all 173 | directory configuration variables that were expressed in terms of 174 | `${prefix}'. Any directories that were specified during `configure', 175 | but not in terms of `${prefix}', must each be overridden at install 176 | time for the entire installation to be relocated. The approach of 177 | makefile variable overrides for each directory variable is required by 178 | the GNU Coding Standards, and ideally causes no recompilation. 179 | However, some platforms have known limitations with the semantics of 180 | shared libraries that end up requiring recompilation when using this 181 | method, particularly noticeable in packages that use GNU Libtool. 182 | 183 | The second method involves providing the `DESTDIR' variable. For 184 | example, `make install DESTDIR=/alternate/directory' will prepend 185 | `/alternate/directory' before all installation names. The approach of 186 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 187 | does not work on platforms that have drive letters. On the other hand, 188 | it does better at avoiding recompilation issues, and works well even 189 | when some directory options were not specified in terms of `${prefix}' 190 | at `configure' time. 191 | 192 | Optional Features 193 | ================= 194 | 195 | If the package supports it, you can cause programs to be installed 196 | with an extra prefix or suffix on their names by giving `configure' the 197 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 198 | 199 | Some packages pay attention to `--enable-FEATURE' options to 200 | `configure', where FEATURE indicates an optional part of the package. 201 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 202 | is something like `gnu-as' or `x' (for the X Window System). The 203 | `README' should mention any `--enable-' and `--with-' options that the 204 | package recognizes. 205 | 206 | For packages that use the X Window System, `configure' can usually 207 | find the X include and library files automatically, but if it doesn't, 208 | you can use the `configure' options `--x-includes=DIR' and 209 | `--x-libraries=DIR' to specify their locations. 210 | 211 | Some packages offer the ability to configure how verbose the 212 | execution of `make' will be. For these packages, running `./configure 213 | --enable-silent-rules' sets the default to minimal output, which can be 214 | overridden with `make V=1'; while running `./configure 215 | --disable-silent-rules' sets the default to verbose, which can be 216 | overridden with `make V=0'. 217 | 218 | Particular systems 219 | ================== 220 | 221 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 222 | CC is not installed, it is recommended to use the following options in 223 | order to use an ANSI C compiler: 224 | 225 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 226 | 227 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 228 | 229 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 230 | parse its `' header file. The option `-nodtk' can be used as 231 | a workaround. If GNU CC is not installed, it is therefore recommended 232 | to try 233 | 234 | ./configure CC="cc" 235 | 236 | and if that doesn't work, try 237 | 238 | ./configure CC="cc -nodtk" 239 | 240 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 241 | directory contains several dysfunctional programs; working variants of 242 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 243 | in your `PATH', put it _after_ `/usr/bin'. 244 | 245 | On Haiku, software installed for all users goes in `/boot/common', 246 | not `/usr/local'. It is recommended to use the following options: 247 | 248 | ./configure --prefix=/boot/common 249 | 250 | Specifying the System Type 251 | ========================== 252 | 253 | There may be some features `configure' cannot figure out 254 | automatically, but needs to determine by the type of machine the package 255 | will run on. Usually, assuming the package is built to be run on the 256 | _same_ architectures, `configure' can figure that out, but if it prints 257 | a message saying it cannot guess the machine type, give it the 258 | `--build=TYPE' option. TYPE can either be a short name for the system 259 | type, such as `sun4', or a canonical name which has the form: 260 | 261 | CPU-COMPANY-SYSTEM 262 | 263 | where SYSTEM can have one of these forms: 264 | 265 | OS 266 | KERNEL-OS 267 | 268 | See the file `config.sub' for the possible values of each field. If 269 | `config.sub' isn't included in this package, then this package doesn't 270 | need to know the machine type. 271 | 272 | If you are _building_ compiler tools for cross-compiling, you should 273 | use the option `--target=TYPE' to select the type of system they will 274 | produce code for. 275 | 276 | If you want to _use_ a cross compiler, that generates code for a 277 | platform different from the build platform, you should specify the 278 | "host" platform (i.e., that on which the generated programs will 279 | eventually be run) with `--host=TYPE'. 280 | 281 | Sharing Defaults 282 | ================ 283 | 284 | If you want to set default values for `configure' scripts to share, 285 | you can create a site shell script called `config.site' that gives 286 | default values for variables like `CC', `cache_file', and `prefix'. 287 | `configure' looks for `PREFIX/share/config.site' if it exists, then 288 | `PREFIX/etc/config.site' if it exists. Or, you can set the 289 | `CONFIG_SITE' environment variable to the location of the site script. 290 | A warning: not all `configure' scripts look for a site script. 291 | 292 | Defining Variables 293 | ================== 294 | 295 | Variables not defined in a site shell script can be set in the 296 | environment passed to `configure'. However, some packages may run 297 | configure again during the build, and the customized values of these 298 | variables may be lost. In order to avoid this problem, you should set 299 | them in the `configure' command line, using `VAR=value'. For example: 300 | 301 | ./configure CC=/usr/local2/bin/gcc 302 | 303 | causes the specified `gcc' to be used as the C compiler (unless it is 304 | overridden in the site shell script). 305 | 306 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 307 | an Autoconf bug. Until the bug is fixed you can use this workaround: 308 | 309 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 310 | 311 | `configure' Invocation 312 | ====================== 313 | 314 | `configure' recognizes the following options to control how it 315 | operates. 316 | 317 | `--help' 318 | `-h' 319 | Print a summary of all of the options to `configure', and exit. 320 | 321 | `--help=short' 322 | `--help=recursive' 323 | Print a summary of the options unique to this package's 324 | `configure', and exit. The `short' variant lists options used 325 | only in the top level, while the `recursive' variant lists options 326 | also present in any nested packages. 327 | 328 | `--version' 329 | `-V' 330 | Print the version of Autoconf used to generate the `configure' 331 | script, and exit. 332 | 333 | `--cache-file=FILE' 334 | Enable the cache: use and save the results of the tests in FILE, 335 | traditionally `config.cache'. FILE defaults to `/dev/null' to 336 | disable caching. 337 | 338 | `--config-cache' 339 | `-C' 340 | Alias for `--cache-file=config.cache'. 341 | 342 | `--quiet' 343 | `--silent' 344 | `-q' 345 | Do not print messages saying which checks are being made. To 346 | suppress all normal output, redirect it to `/dev/null' (any error 347 | messages will still be shown). 348 | 349 | `--srcdir=DIR' 350 | Look for the package's source code in directory DIR. Usually 351 | `configure' can determine that directory automatically. 352 | 353 | `--prefix=DIR' 354 | Use DIR as the installation prefix. *note Installation Names:: 355 | for more details, including other options available for fine-tuning 356 | the installation locations. 357 | 358 | `--no-create' 359 | `-n' 360 | Run the configure checks, but stop before creating any output 361 | files. 362 | 363 | `configure' also accepts some other, not widely useful, options. Run 364 | `configure --help' for more details. 365 | 366 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/otr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Off-the-Record Messaging (OTR) modules for IRC 3 | * 4 | * Copyright (C) 2008 - Uli Meis 5 | * 2012 - David Goulet 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 51 19 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | #include 24 | #include 25 | #include 26 | 27 | #include "otr-formats.h" 28 | #include "key.h" 29 | 30 | static const char *statusbar_txt[] = { 31 | "FINISHED", 32 | "TRUST_MANUAL", 33 | "TRUST_SMP", 34 | "SMP_ABORT", 35 | "SMP_STARTED", 36 | "SMP_RESPONDED", 37 | "SMP_INCOMING", 38 | "SMP_FINALIZE", 39 | "SMP_ABORTED", 40 | "PEER_FINISHED", 41 | "SMP_FAILED", 42 | "SMP_SUCCESS", 43 | "GONE_SECURE", 44 | "GONE_INSECURE", 45 | "CTX_UPDATE" 46 | }; 47 | 48 | /* Glib timer for otr. */ 49 | static guint otr_timerid; 50 | 51 | /* 52 | * Allocate and return a string containing the account name of the Irssi server 53 | * record. 54 | * 55 | * Return: nick@myserver.net 56 | */ 57 | static char *create_account_name(SERVER_REC *irssi) 58 | { 59 | int ret; 60 | char *accname; 61 | 62 | assert(irssi); 63 | 64 | /* Valid or NULL, the caller should handle this */ 65 | ret = asprintf(&accname, "%s@%s", IRSSI_NICK(irssi), 66 | IRSSI_CONN_ADDR(irssi)); 67 | if (ret < 0) { 68 | IRSSI_INFO(NULL, NULL, "Unable to allocate account name."); 69 | /* 70 | * As stated in asprintf(3), if an error occurs, the contents of the 71 | * passed pointer is undefined. Force it to NULL here. 72 | */ 73 | accname = NULL; 74 | } 75 | 76 | return accname; 77 | } 78 | 79 | /* 80 | * Load instance tags. 81 | */ 82 | static void instag_load(struct otr_user_state *ustate) 83 | { 84 | int ret; 85 | char *filename; 86 | gcry_error_t err; 87 | 88 | assert(ustate); 89 | 90 | /* Getting the otr instance filename path */ 91 | ret = asprintf(&filename, "%s%s", get_client_config_dir(), 92 | OTR_INSTAG_FILE); 93 | if (ret < 0) { 94 | goto error_filename; 95 | } 96 | 97 | ret = access(filename, F_OK); 98 | if (ret < 0) { 99 | IRSSI_DEBUG("no instance tags found at %9%s%9", filename); 100 | goto end; 101 | } 102 | 103 | err = otrl_instag_read(ustate->otr_state, filename); 104 | if (err == GPG_ERR_NO_ERROR) { 105 | IRSSI_DEBUG("Instance tags loaded from %9%s%9", filename); 106 | } else { 107 | IRSSI_DEBUG("Error loading instance tags: %d (%d)", 108 | gcry_strerror(err), gcry_strsource(err)); 109 | } 110 | 111 | end: 112 | free(filename); 113 | error_filename: 114 | return; 115 | } 116 | 117 | /* 118 | * Free otr peer context. Callback passed to libotr. 119 | */ 120 | static void destroy_peer_context_cb(void *data) 121 | { 122 | struct otr_peer_context *opc = data; 123 | 124 | if (opc) { 125 | free(opc); 126 | } 127 | 128 | IRSSI_DEBUG("Peer context freed"); 129 | } 130 | 131 | /* 132 | * Allocate otr peer context. Callback passed to libotr. 133 | */ 134 | static void add_peer_context_cb(void *data, ConnContext *context) 135 | { 136 | struct otr_peer_context *opc; 137 | 138 | opc = otr_create_peer_context(); 139 | if (!opc) { 140 | return; 141 | } 142 | 143 | opc->active_fingerprint = context->active_fingerprint; 144 | 145 | context->app_data = opc; 146 | context->app_data_free = destroy_peer_context_cb; 147 | 148 | IRSSI_DEBUG("Peer context created for %s", context->username); 149 | } 150 | 151 | /* 152 | * Find Irssi server record by account name. 153 | */ 154 | static SERVER_REC *find_irssi_by_account_name(const char *accname) 155 | { 156 | GSList *tmp; 157 | size_t nick_len; 158 | char *address, *nick = NULL; 159 | SERVER_REC *server, *srv = NULL; 160 | 161 | assert(accname); 162 | 163 | address = strchr(accname, '@'); 164 | if (!address) { 165 | goto error; 166 | } 167 | 168 | /* Calculate the nickname length. */ 169 | nick_len = address - accname; 170 | 171 | /* Allocate right size for the nickname plus the NULL terminated byte. */ 172 | nick = malloc(nick_len + 1); 173 | if (!nick) { 174 | /* ENOMEM */ 175 | goto error; 176 | } 177 | 178 | /* Get the nick from the account name. */ 179 | strncpy(nick, accname, nick_len); 180 | nick[nick_len] = '\0'; 181 | 182 | /* Move after the @ */ 183 | address++; 184 | 185 | for (tmp = servers; tmp; tmp = tmp->next) { 186 | server = tmp->data; 187 | if (g_ascii_strncasecmp(server->connrec->address, address, 188 | strlen(server->connrec->address)) == 0 && 189 | strncmp(server->nick, nick, strlen(nick)) == 0) { 190 | srv = server; 191 | break; 192 | } 193 | } 194 | 195 | free(nick); 196 | 197 | error: 198 | return srv; 199 | } 200 | 201 | /* 202 | * Check if fingerprint is in an encrypted context. 203 | * 204 | * Return 1 if it does, else 0. 205 | */ 206 | static int check_fp_encrypted_msgstate(Fingerprint *fp) 207 | { 208 | int ret; 209 | ConnContext *context; 210 | 211 | assert(fp); 212 | 213 | /* Loop on all fingerprint's context(es). */ 214 | for (context = fp->context; 215 | context != NULL && context->m_context == fp->context; 216 | context = context->next) { 217 | if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED && 218 | context->active_fingerprint == fp) { 219 | ret = 1; 220 | goto end; 221 | } 222 | } 223 | 224 | /* No state is encrypted. */ 225 | ret = 0; 226 | 227 | end: 228 | return ret; 229 | } 230 | 231 | /* 232 | * Timer called from the glib main loop and set up by the timer_control 233 | * callback of libotr. 234 | */ 235 | static gboolean timer_fired_cb(gpointer data) 236 | { 237 | otrl_message_poll(user_state_global->otr_state, &otr_ops, NULL); 238 | return TRUE; 239 | } 240 | 241 | void otr_control_timer(unsigned int interval, void *opdata) 242 | { 243 | if (otr_timerid) { 244 | g_source_remove(otr_timerid); 245 | otr_timerid = 0; 246 | } 247 | 248 | if (interval > 0) { 249 | otr_timerid = g_timeout_add_seconds(interval, timer_fired_cb, opdata); 250 | } 251 | } 252 | 253 | /* 254 | * Find context from nickname and irssi server record. 255 | */ 256 | ConnContext *otr_find_context(SERVER_REC *irssi, const char *nick, int create) 257 | { 258 | char *accname = NULL; 259 | ConnContext *ctx = NULL; 260 | 261 | assert(irssi); 262 | assert(nick); 263 | 264 | accname = create_account_name(irssi); 265 | if (!accname) { 266 | goto error; 267 | } 268 | 269 | ctx = otrl_context_find(user_state_global->otr_state, nick, accname, 270 | OTR_PROTOCOL_ID, OTRL_INSTAG_BEST, create, NULL, 271 | add_peer_context_cb, irssi); 272 | 273 | free(accname); 274 | 275 | error: 276 | return ctx; 277 | } 278 | 279 | /* 280 | * Create otr peer context. 281 | */ 282 | struct otr_peer_context *otr_create_peer_context(void) 283 | { 284 | return zmalloc(sizeof(struct otr_peer_context)); 285 | } 286 | 287 | /* 288 | * Return a newly allocated OTR user state. 289 | */ 290 | struct otr_user_state *otr_init_user_state(void) 291 | { 292 | struct otr_user_state *ous = NULL; 293 | 294 | ous = zmalloc(sizeof(*ous)); 295 | if (!ous) { 296 | goto error; 297 | } 298 | 299 | ous->otr_state = otrl_userstate_create(); 300 | 301 | instag_load(ous); 302 | 303 | /* Load keys and fingerprints. */ 304 | key_load(ous); 305 | key_load_fingerprints(ous); 306 | 307 | error: 308 | return ous; 309 | } 310 | 311 | /* 312 | * Destroy otr user state. 313 | */ 314 | void otr_free_user_state(struct otr_user_state *ustate) 315 | { 316 | if (ustate->otr_state) { 317 | otrl_userstate_free(ustate->otr_state); 318 | ustate->otr_state = NULL; 319 | } 320 | 321 | free(ustate); 322 | } 323 | 324 | /* 325 | * init otr lib. 326 | */ 327 | void otr_lib_init() 328 | { 329 | OTRL_INIT; 330 | } 331 | 332 | /* 333 | * deinit otr lib. 334 | */ 335 | void otr_lib_uninit() 336 | { 337 | } 338 | 339 | /* 340 | * Hand the given message to OTR. 341 | * 342 | * Return 0 if the message was successfully handled or else a negative value. 343 | */ 344 | int otr_send(SERVER_REC *irssi, const char *msg, const char *to, char **otr_msg) 345 | { 346 | gcry_error_t err; 347 | char *accname = NULL; 348 | ConnContext *ctx = NULL; 349 | 350 | assert(irssi); 351 | 352 | accname = create_account_name(irssi); 353 | if (!accname) { 354 | goto error; 355 | } 356 | 357 | IRSSI_DEBUG("Sending message..."); 358 | 359 | err = otrl_message_sending(user_state_global->otr_state, &otr_ops, 360 | irssi, accname, OTR_PROTOCOL_ID, to, OTRL_INSTAG_BEST, msg, NULL, otr_msg, 361 | OTRL_FRAGMENT_SEND_ALL_BUT_LAST, &ctx, add_peer_context_cb, irssi); 362 | if (err) { 363 | IRSSI_NOTICE(irssi, to, "Send failed."); 364 | goto error; 365 | } 366 | 367 | /* Modify some libotr messages (OTR init in particular) to work better 368 | * with IRC. */ 369 | if (*otr_msg) { 370 | utils_escape_message(*otr_msg); 371 | } 372 | 373 | IRSSI_DEBUG("Message sent..."); 374 | 375 | /* Add peer context to OTR context if none exists. */ 376 | if (ctx && !ctx->app_data) { 377 | add_peer_context_cb(irssi, ctx); 378 | } 379 | 380 | free(accname); 381 | return 0; 382 | 383 | error: 384 | free(accname); 385 | return -1; 386 | } 387 | 388 | /* 389 | * List otr contexts to the main Irssi windows. 390 | */ 391 | void otr_contexts(struct otr_user_state *ustate) 392 | { 393 | char human_fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN], *trust; 394 | ConnContext *ctx, *c_iter; 395 | Fingerprint *fp; 396 | 397 | assert(ustate); 398 | 399 | if (!ustate->otr_state->context_root) { 400 | IRSSI_INFO(NULL, NULL, "No active OTR contexts found"); 401 | goto end; 402 | } 403 | 404 | IRSSI_MSG("[ %KUser%n - %KAccount%n - %KStatus%n - %KFingerprint%n - " 405 | "%KTrust%n ]"); 406 | 407 | /* Iterate over all contextes of the user state. */ 408 | for (ctx = ustate->otr_state->context_root; ctx != NULL; ctx = ctx->next) { 409 | OtrlMessageState best_mstate = OTRL_MSGSTATE_PLAINTEXT; 410 | 411 | /* Skip master context. */ 412 | if (ctx != ctx->m_context) { 413 | continue; 414 | } 415 | 416 | for (fp = ctx->fingerprint_root.next; fp != NULL; fp = fp->next) { 417 | int used = 0; 418 | char *username, *accountname; 419 | 420 | username = ctx->username; 421 | accountname = ctx->accountname; 422 | 423 | for (c_iter = ctx->m_context; 424 | c_iter && c_iter->m_context == ctx->m_context; 425 | c_iter = c_iter->next) { 426 | /* Print account name, username and msgstate. */ 427 | if (c_iter->active_fingerprint == fp) { 428 | used = 1; 429 | 430 | if (c_iter->msgstate == OTRL_MSGSTATE_ENCRYPTED) { 431 | best_mstate = OTRL_MSGSTATE_ENCRYPTED; 432 | } else if (c_iter->msgstate == OTRL_MSGSTATE_FINISHED && 433 | best_mstate == OTRL_MSGSTATE_PLAINTEXT) { 434 | best_mstate = OTRL_MSGSTATE_FINISHED; 435 | } 436 | } 437 | } 438 | 439 | if (used) { 440 | switch (best_mstate) { 441 | case OTRL_MSGSTATE_ENCRYPTED: 442 | IRSSI_MSG("%b>%n %9%s%9 - %B%s%n - %GEncrypted%n -", 443 | accountname, username); 444 | break; 445 | case OTRL_MSGSTATE_PLAINTEXT: 446 | IRSSI_MSG("%b>%n %9%s%9 - %B%s%n - Plaintext -", 447 | accountname, username); 448 | break; 449 | case OTRL_MSGSTATE_FINISHED: 450 | IRSSI_MSG("%b>%n %9%s%9 - %B%s%n - %yFinished%n -", 451 | accountname, username); 452 | break; 453 | default: 454 | IRSSI_MSG("%b>%n %9%s%9 - %B%s%n - Unknown -", accountname, 455 | username); 456 | break; 457 | }; 458 | } else { 459 | IRSSI_MSG("%b>%n %9%s%9 - %B%s%n - Unused -", accountname, 460 | username); 461 | } 462 | 463 | /* Hash fingerprint to human. */ 464 | otrl_privkey_hash_to_human(human_fp, fp->fingerprint); 465 | 466 | trust = fp->trust; 467 | if (trust && trust[0] != '\0') { 468 | if (strncmp(trust, "smp", 3) == 0) { 469 | IRSSI_MSG(" %g%s%n - SMP", human_fp); 470 | } else { 471 | IRSSI_MSG(" %g%s%n - Manual", human_fp); 472 | } 473 | } else { 474 | IRSSI_MSG(" %r%s%n - Unverified", human_fp); 475 | } 476 | } 477 | } 478 | 479 | end: 480 | return; 481 | } 482 | 483 | /* 484 | * Finish the conversation. 485 | */ 486 | void otr_finish(SERVER_REC *irssi, const char *nick) 487 | { 488 | ConnContext *ctx; 489 | 490 | assert(irssi); 491 | assert(nick); 492 | 493 | ctx = otr_find_context(irssi, nick, FALSE); 494 | if (!ctx) { 495 | IRSSI_INFO(irssi, nick, "Nothing to do"); 496 | goto end; 497 | } 498 | 499 | otrl_message_disconnect(user_state_global->otr_state, &otr_ops, irssi, 500 | ctx->accountname, OTR_PROTOCOL_ID, nick, ctx->their_instance); 501 | 502 | otr_status_change(irssi, nick, OTR_STATUS_FINISHED); 503 | 504 | IRSSI_INFO(irssi, nick, "Finished conversation with %9%s%9", 505 | nick); 506 | 507 | end: 508 | return; 509 | } 510 | 511 | /* 512 | * Finish all otr contexts. 513 | */ 514 | void otr_finishall(struct otr_user_state *ustate) 515 | { 516 | ConnContext *context; 517 | SERVER_REC *irssi; 518 | 519 | assert(ustate); 520 | 521 | for (context = ustate->otr_state->context_root; context; 522 | context = context->next) { 523 | /* Only finish encrypted session. */ 524 | if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) { 525 | continue; 526 | } 527 | 528 | irssi = find_irssi_by_account_name(context->accountname); 529 | if (!irssi) { 530 | IRSSI_DEBUG("Unable to find server window for account %s", 531 | context->accountname); 532 | continue; 533 | } 534 | 535 | otr_finish(irssi, context->username); 536 | } 537 | } 538 | 539 | /* 540 | * Trust our peer. 541 | */ 542 | void otr_trust(SERVER_REC *irssi, const char *nick, char *str_fp, 543 | struct otr_user_state *ustate) 544 | { 545 | char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 546 | struct otr_peer_context *opc; 547 | ConnContext *ctx; 548 | Fingerprint *fp_trust; 549 | 550 | assert(ustate); 551 | 552 | if (!irssi && !str_fp) { 553 | IRSSI_NOTICE(NULL, nick, "Need a fingerprint!"); 554 | goto error; 555 | } 556 | 557 | /* No human string fingerprint given. */ 558 | if (!str_fp) { 559 | ctx = otr_find_context(irssi, nick, FALSE); 560 | if (!ctx) { 561 | goto error; 562 | } 563 | 564 | opc = ctx->app_data; 565 | /* Always NEED a peer context or else code error. */ 566 | assert(opc); 567 | 568 | fp_trust = ctx->active_fingerprint; 569 | } else { 570 | fp_trust = otr_find_hash_fingerprint_from_human(str_fp, ustate); 571 | } 572 | 573 | if (fp_trust) { 574 | int ret; 575 | 576 | ret = otrl_context_is_fingerprint_trusted(fp_trust); 577 | if (ret) { 578 | IRSSI_NOTICE(irssi, nick, "Already trusted!"); 579 | goto end; 580 | } 581 | 582 | /* Trust level is manual at this point. */ 583 | otrl_context_set_trust(fp_trust, "manual"); 584 | key_write_fingerprints(ustate); 585 | 586 | otr_status_change(irssi, nick, OTR_STATUS_TRUST_MANUAL); 587 | 588 | otrl_privkey_hash_to_human(peerfp, fp_trust->fingerprint); 589 | IRSSI_NOTICE(irssi, nick, "Fingerprint %g%s%n trusted!", peerfp); 590 | } else { 591 | IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n NOT found", 592 | (str_fp != NULL) ? str_fp : ""); 593 | } 594 | 595 | end: 596 | error: 597 | return; 598 | } 599 | 600 | /* 601 | * implements /otr authabort 602 | */ 603 | void otr_auth_abort(SERVER_REC *irssi, const char *nick) 604 | { 605 | ConnContext *ctx; 606 | 607 | assert(irssi); 608 | assert(nick); 609 | 610 | ctx = otr_find_context(irssi, nick, FALSE); 611 | if (!ctx) { 612 | IRSSI_NOTICE(irssi, nick, "Context for %9%s%9 not found.", nick); 613 | goto end; 614 | } 615 | 616 | otrl_message_abort_smp(user_state_global->otr_state, &otr_ops, irssi, ctx); 617 | otr_status_change(irssi, nick, OTR_STATUS_SMP_ABORT); 618 | 619 | if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { 620 | IRSSI_NOTICE(irssi, nick, "%rOngoing authentication aborted%n"); 621 | } else { 622 | IRSSI_NOTICE(irssi, nick, "%rAuthentication aborted%n"); 623 | } 624 | 625 | end: 626 | return; 627 | } 628 | 629 | /* 630 | * Initiate or respond to SMP authentication. 631 | */ 632 | void otr_auth(SERVER_REC *irssi, const char *nick, const char *question, 633 | const char *secret) 634 | { 635 | int ret; 636 | size_t secret_len = 0; 637 | ConnContext *ctx; 638 | struct otr_peer_context *opc; 639 | 640 | assert(irssi); 641 | assert(nick); 642 | 643 | ctx = otr_find_context(irssi, nick, 0); 644 | if (!ctx) { 645 | IRSSI_NOTICE(irssi, nick, "Context for %9%s%9 not found.", nick); 646 | goto end; 647 | } 648 | 649 | opc = ctx->app_data; 650 | /* Again, code flow error. */ 651 | assert(opc); 652 | 653 | if (ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED) { 654 | IRSSI_INFO(irssi, nick, 655 | "You need to establish an OTR session before you " 656 | "can authenticate."); 657 | goto end; 658 | } 659 | 660 | /* Aborting an ongoing auth */ 661 | if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { 662 | otr_auth_abort(irssi, nick); 663 | } 664 | 665 | /* reset trust level */ 666 | if (ctx->active_fingerprint) { 667 | ret = otrl_context_is_fingerprint_trusted(ctx->active_fingerprint); 668 | if (!ret) { 669 | otrl_context_set_trust(ctx->active_fingerprint, ""); 670 | key_write_fingerprints(user_state_global); 671 | } 672 | } 673 | 674 | /* Libotr allows empty secret. */ 675 | if (secret) { 676 | secret_len = strlen(secret); 677 | } 678 | 679 | if (opc->ask_secret) { 680 | otrl_message_respond_smp(user_state_global->otr_state, &otr_ops, 681 | irssi, ctx, (unsigned char *) secret, secret_len); 682 | otr_status_change(irssi, nick, OTR_STATUS_SMP_RESPONDED); 683 | IRSSI_NOTICE(irssi, nick, "%yResponding to authentication...%n"); 684 | } else { 685 | if (question) { 686 | otrl_message_initiate_smp_q(user_state_global->otr_state, 687 | &otr_ops, irssi, ctx, question, (unsigned char *) secret, 688 | secret_len); 689 | } else { 690 | otrl_message_initiate_smp(user_state_global->otr_state, 691 | &otr_ops, irssi, ctx, (unsigned char *) secret, secret_len); 692 | } 693 | otr_status_change(irssi, nick, OTR_STATUS_SMP_STARTED); 694 | IRSSI_NOTICE(irssi, nick, "%yInitiated authentication...%n"); 695 | } 696 | 697 | opc->ask_secret = 0; 698 | 699 | end: 700 | return; 701 | } 702 | 703 | /* 704 | * For the given message we received through irssi, check if we need to queue 705 | * it for the case where that message is part of a bigger OTR full message. 706 | * This can happen with bitlbee for instance where OTR message are split in 707 | * different PRIVMSG. 708 | * 709 | * This uses a "queue" in the peer context so it's it very important to have 710 | * the peer context associated with the message (nickname + irssi object). 711 | * 712 | * Return an otr_msg_status code indicating the caller what to do with the msg. 713 | * OTR_MSG_ERROR indicates an error probably memory related. OTR_MSG_WAIT_MORE 714 | * tells the caller to NOT send out the message since we are waiting for more 715 | * to complete the OTR original message. OTR_MSG_ORIGINAL tell the caller to 716 | * simply use the original message. OTR_MSG_USE_QUEUE indicates that full_msg 717 | * can be used containing the reconstructed message. The caller SHOULD free(3) 718 | * this pointer after use. 719 | */ 720 | static enum otr_msg_status enqueue_otr_fragment(const char *msg, 721 | struct otr_peer_context *opc, char **full_msg) 722 | { 723 | enum otr_msg_status ret; 724 | size_t msg_len; 725 | 726 | assert(msg); 727 | assert(opc); 728 | 729 | /* We are going to use it quite a bit so ease our life a bit. */ 730 | msg_len = strlen(msg); 731 | 732 | if (opc->full_msg) { 733 | if (msg_len > (opc->msg_size - opc->msg_len)) { 734 | char *tmp_ptr; 735 | 736 | /* Realloc memory if there is not enough space. */ 737 | tmp_ptr = realloc(opc->full_msg, opc->msg_size + msg_len + 1); 738 | if (!tmp_ptr) { 739 | free(opc->full_msg); 740 | opc->full_msg = NULL; 741 | ret = OTR_MSG_ERROR; 742 | goto end; 743 | } 744 | opc->full_msg = tmp_ptr; 745 | opc->msg_size += msg_len + 1; 746 | } 747 | 748 | /* Copy msg to full message since we already have a part pending. */ 749 | strncpy(opc->full_msg + opc->msg_len, msg, msg_len); 750 | opc->msg_len += msg_len; 751 | opc->full_msg[opc->msg_len] = '\0'; 752 | 753 | IRSSI_DEBUG("Partial OTR message added to queue: %s", msg); 754 | 755 | /* 756 | * Are we waiting for more? If the message ends with a ".", the 757 | * transmission has ended else we have to wait for more. 758 | */ 759 | if (msg[msg_len - 1] != OTR_MSG_END_TAG) { 760 | ret = OTR_MSG_WAIT_MORE; 761 | goto end; 762 | } 763 | 764 | /* 765 | * Dup the string with enough space for the NULL byte since we are 766 | * about to free it before passing it to the caller. 767 | */ 768 | *full_msg = strndup(opc->full_msg, opc->msg_len + 1); 769 | /* Reset everything. */ 770 | free(opc->full_msg); 771 | opc->full_msg = NULL; 772 | opc->msg_size = opc->msg_len = 0; 773 | ret = OTR_MSG_USE_QUEUE; 774 | goto end; 775 | } else { 776 | char *pos; 777 | 778 | /* 779 | * Try to find the OTR message tag at the _beginning_of the packet and 780 | * check if this packet is not the end with the end tag of OTR "." 781 | */ 782 | pos = strstr(msg, OTR_MSG_BEGIN_TAG); 783 | if (pos && (pos == msg) && msg[msg_len - 1] != OTR_MSG_END_TAG) { 784 | /* Allocate full message buffer with an extra for NULL byte. */ 785 | opc->full_msg = zmalloc((msg_len * 2) + 1); 786 | if (!opc->full_msg) { 787 | ret = OTR_MSG_ERROR; 788 | goto end; 789 | } 790 | /* Copy full message with NULL terminated byte. */ 791 | strncpy(opc->full_msg, msg, msg_len); 792 | opc->msg_len += msg_len; 793 | opc->msg_size += ((msg_len * 2) + 1); 794 | opc->full_msg[opc->msg_len] = '\0'; 795 | ret = OTR_MSG_WAIT_MORE; 796 | IRSSI_DEBUG("Partial OTR message begins the queue: %s", msg); 797 | goto end; 798 | } 799 | 800 | /* Use original message. */ 801 | ret = OTR_MSG_ORIGINAL; 802 | goto end; 803 | } 804 | 805 | end: 806 | return ret; 807 | } 808 | 809 | /* 810 | * Hand the given message to OTR. 811 | * 812 | * Returns 0 if its an OTR protocol message or else negative value. 813 | */ 814 | int otr_receive(SERVER_REC *irssi, const char *msg, const char *from, 815 | char **new_msg) 816 | { 817 | int ret = -1; 818 | char *accname = NULL, *full_msg = NULL; 819 | const char *recv_msg = NULL; 820 | OtrlTLV *tlvs; 821 | ConnContext *ctx; 822 | struct otr_peer_context *opc; 823 | 824 | assert(irssi); 825 | 826 | accname = create_account_name(irssi); 827 | if (!accname) { 828 | goto error; 829 | } 830 | 831 | IRSSI_DEBUG("Receiving message..."); 832 | 833 | ctx = otr_find_context(irssi, from, 1); 834 | if (!ctx) { 835 | goto error; 836 | } 837 | 838 | /* Add peer context to OTR context if none exists */ 839 | if (!ctx->app_data) { 840 | add_peer_context_cb(irssi, ctx); 841 | } 842 | 843 | opc = ctx->app_data; 844 | assert(opc); 845 | 846 | ret = enqueue_otr_fragment(msg, opc, &full_msg); 847 | switch (ret) { 848 | case OTR_MSG_ORIGINAL: 849 | recv_msg = msg; 850 | break; 851 | case OTR_MSG_WAIT_MORE: 852 | ret = 1; 853 | goto error; 854 | case OTR_MSG_USE_QUEUE: 855 | recv_msg = full_msg; 856 | break; 857 | case OTR_MSG_ERROR: 858 | ret = -1; 859 | goto error; 860 | } 861 | 862 | ret = otrl_message_receiving(user_state_global->otr_state, 863 | &otr_ops, irssi, accname, OTR_PROTOCOL_ID, from, recv_msg, new_msg, 864 | &tlvs, &ctx, add_peer_context_cb, irssi); 865 | if (ret) { 866 | IRSSI_DEBUG("Ignoring message of length %d from %s to %s.\n" 867 | "%s", strlen(msg), from, accname, msg); 868 | } else { 869 | if (*new_msg) { 870 | IRSSI_DEBUG("Converted received message."); 871 | } 872 | } 873 | 874 | /* Check for disconnected message */ 875 | OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); 876 | if (tlv) { 877 | otr_status_change(irssi, from, OTR_STATUS_PEER_FINISHED); 878 | IRSSI_NOTICE(irssi, from, "%9%s%9 has finished the OTR " 879 | "conversation. If you want to continue talking enter " 880 | "%9/otr finish%9 for plaintext or %9/otr init%9 to restart.", 881 | from); 882 | } 883 | 884 | otrl_tlv_free(tlvs); 885 | 886 | IRSSI_DEBUG("Message received."); 887 | 888 | error: 889 | if (full_msg) { 890 | free(full_msg); 891 | } 892 | free(accname); 893 | return ret; 894 | } 895 | 896 | /* 897 | * Get the OTR status of this conversation. 898 | */ 899 | enum otr_status_format otr_get_status_format(SERVER_REC *irssi, 900 | const char *nick) 901 | { 902 | int ret; 903 | enum otr_status_format code; 904 | ConnContext *ctx = NULL; 905 | 906 | assert(irssi); 907 | 908 | ctx = otr_find_context(irssi, nick, FALSE); 909 | if (!ctx) { 910 | code = TXT_STB_PLAINTEXT; 911 | goto end; 912 | } 913 | 914 | switch (ctx->msgstate) { 915 | case OTRL_MSGSTATE_PLAINTEXT: 916 | code = TXT_STB_PLAINTEXT; 917 | break; 918 | case OTRL_MSGSTATE_ENCRYPTED: 919 | /* Begin by checking trust. */ 920 | ret = otrl_context_is_fingerprint_trusted(ctx->active_fingerprint); 921 | if (ret) { 922 | code = TXT_STB_TRUST; 923 | } else { 924 | code = TXT_STB_UNTRUSTED; 925 | } 926 | break; 927 | case OTRL_MSGSTATE_FINISHED: 928 | code = TXT_STB_FINISHED; 929 | break; 930 | default: 931 | IRSSI_NOTICE(irssi, nick, "BUG Found! " 932 | "Please write us a mail and describe how you got here"); 933 | code = TXT_STB_UNKNOWN; 934 | break; 935 | } 936 | 937 | end: 938 | if (ctx) { 939 | IRSSI_DEBUG("Code: %d, state: %d, sm_prog_state: %d, auth state: %d", 940 | code, ctx->msgstate, ctx->smstate->sm_prog_state, 941 | ctx->auth.authstate); 942 | } 943 | return code; 944 | } 945 | 946 | /* 947 | * Change status bar text for a given nickname. 948 | */ 949 | void otr_status_change(SERVER_REC *irssi, const char *nick, 950 | enum otr_status_event event) 951 | { 952 | statusbar_items_redraw("otr"); 953 | signal_emit("otr event", 3, irssi, nick, statusbar_txt[event]); 954 | } 955 | 956 | /* 957 | * Search for a OTR Fingerprint object from the given human readable string and 958 | * return a pointer to the object if found else NULL. 959 | */ 960 | Fingerprint *otr_find_hash_fingerprint_from_human(const char *human_fp, 961 | struct otr_user_state *ustate) 962 | { 963 | char str_fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 964 | Fingerprint *fp = NULL, *fp_iter = NULL; 965 | ConnContext *context; 966 | 967 | /* Loop on all context of the user state */ 968 | for (context = ustate->otr_state->context_root; context != NULL; 969 | context = context->next) { 970 | /* Loop on all fingerprint of the context */ 971 | for (fp_iter = context->fingerprint_root.next; fp_iter; 972 | fp_iter = fp_iter->next) { 973 | otrl_privkey_hash_to_human(str_fp, fp_iter->fingerprint); 974 | /* Compare human fingerprint given in argument to the current. */ 975 | if (strncmp(str_fp, human_fp, sizeof(str_fp)) == 0) { 976 | fp = otrl_context_find_fingerprint(context, 977 | fp_iter->fingerprint, 0, NULL); 978 | goto end; 979 | } 980 | } 981 | } 982 | 983 | end: 984 | return fp; 985 | } 986 | 987 | /* 988 | * Forget a fingerprint. 989 | * 990 | * If str_fp is not NULL, it must be on the OTR human format like this: 991 | * "487FFADA 5073FEDD C5AB5C14 5BB6C1FF 6D40D48A". If str_fp is NULL, get the 992 | * context of the target nickname, check for the OTR peer context active 993 | * fingerprint and forget this one if possible. 994 | */ 995 | void otr_forget(SERVER_REC *irssi, const char *nick, char *str_fp, 996 | struct otr_user_state *ustate) 997 | { 998 | int ret; 999 | char fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 1000 | Fingerprint *fp_forget; 1001 | ConnContext *ctx = NULL; 1002 | struct otr_peer_context *opc; 1003 | 1004 | if (!irssi && !str_fp) { 1005 | IRSSI_NOTICE(NULL, nick, "Need a fingerprint!"); 1006 | goto error; 1007 | } 1008 | 1009 | /* No human string fingerprint given. */ 1010 | if (!str_fp) { 1011 | ctx = otr_find_context(irssi, nick, FALSE); 1012 | if (!ctx) { 1013 | goto error; 1014 | } 1015 | 1016 | opc = ctx->app_data; 1017 | /* Always NEED a peer context or else code error. */ 1018 | assert(opc); 1019 | 1020 | fp_forget = opc->active_fingerprint; 1021 | } else { 1022 | fp_forget = otr_find_hash_fingerprint_from_human(str_fp, ustate); 1023 | } 1024 | 1025 | if (fp_forget) { 1026 | /* Don't do anything if context is in encrypted state. */ 1027 | ret = check_fp_encrypted_msgstate(fp_forget); 1028 | if (ret) { 1029 | IRSSI_NOTICE(irssi, nick, "Fingerprint " 1030 | "context is still encrypted. Finish the OTR " 1031 | "session before forgetting a fingerprint " 1032 | "(%9/otr finish%9)."); 1033 | goto end; 1034 | } 1035 | 1036 | otrl_privkey_hash_to_human(fp, fp_forget->fingerprint); 1037 | /* Forget fp and context if it's the only one remaining. */ 1038 | otrl_context_forget_fingerprint(fp_forget, 1); 1039 | /* Update fingerprints file. */ 1040 | key_write_fingerprints(ustate); 1041 | IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n forgotten.", 1042 | fp); 1043 | } else { 1044 | IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n NOT found", 1045 | (str_fp != NULL) ? str_fp : ""); 1046 | } 1047 | 1048 | end: 1049 | error: 1050 | return; 1051 | } 1052 | 1053 | /* 1054 | * Distrust a fingerprint. 1055 | * 1056 | * If str_fp is not NULL, it must be on the OTR human format like this: 1057 | * "487FFADA 5073FEDD C5AB5C14 5BB6C1FF 6D40D48A". If str_fp is NULL, get the 1058 | * context of the target nickname, check for the OTR peer context active 1059 | * fingerprint and distrust it. 1060 | */ 1061 | void otr_distrust(SERVER_REC *irssi, const char *nick, char *str_fp, 1062 | struct otr_user_state *ustate) 1063 | { 1064 | int ret; 1065 | char fp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; 1066 | Fingerprint *fp_distrust; 1067 | ConnContext *ctx; 1068 | struct otr_peer_context *opc; 1069 | 1070 | if (!irssi && !str_fp) { 1071 | IRSSI_NOTICE(NULL, nick, "Need a fingerprint!"); 1072 | goto error; 1073 | } 1074 | 1075 | /* No human string fingerprint given. */ 1076 | if (!str_fp) { 1077 | ctx = otr_find_context(irssi, nick, FALSE); 1078 | if (!ctx) { 1079 | goto error; 1080 | } 1081 | 1082 | opc = ctx->app_data; 1083 | /* Always NEED a peer context or else code error. */ 1084 | assert(opc); 1085 | 1086 | fp_distrust = opc->active_fingerprint; 1087 | } else { 1088 | fp_distrust = otr_find_hash_fingerprint_from_human(str_fp, ustate); 1089 | } 1090 | 1091 | if (fp_distrust) { 1092 | ret = otrl_context_is_fingerprint_trusted(fp_distrust); 1093 | if (!ret) { 1094 | /* Fingerprint already not trusted. Do nothing. */ 1095 | IRSSI_NOTICE(irssi, nick, "Already not trusting it!"); 1096 | goto end; 1097 | } 1098 | 1099 | otrl_privkey_hash_to_human(fp, fp_distrust->fingerprint); 1100 | otrl_context_set_trust(fp_distrust, ""); 1101 | /* Update fingerprints file. */ 1102 | key_write_fingerprints(ustate); 1103 | IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n distrusted.", 1104 | fp); 1105 | } else { 1106 | IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n NOT found", 1107 | (str_fp != NULL) ? str_fp : ""); 1108 | } 1109 | 1110 | end: 1111 | error: 1112 | return; 1113 | } 1114 | --------------------------------------------------------------------------------