[Pkg-privacy-commits] [irssi-plugin-otr] 86/267: Add up to date source files

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:41:31 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch debian
in repository irssi-plugin-otr.

commit 94ae4bfafde29a2618db04f35a96ded20678760f
Author: David Goulet <dgoulet at ev0ke.net>
Date:   Fri Nov 2 16:34:04 2012 -0400

    Add up to date source files
    
    Signed-off-by: David Goulet <dgoulet at ev0ke.net>
---
 src/Makefile.am   |  20 ++
 src/io-config.h   |   3 +
 src/io_set.c      |  62 ++++
 src/io_util.c     | 349 +++++++++++++++++++++
 src/irssi_otr.c   | 302 ++++++++++++++++++
 src/irssi_otr.h   |  83 +++++
 src/otr-formats.c | 112 +++++++
 src/otr-formats.h | 111 +++++++
 src/otr.h         | 247 +++++++++++++++
 src/otr_key.c     | 364 ++++++++++++++++++++++
 src/otr_ops.c     | 398 ++++++++++++++++++++++++
 src/otr_util.c    | 915 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 2966 insertions(+)

diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..6699607
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,20 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+IRSSI_DIST=/usr/include/irssi
+IRSSI_INCLUDE = -I$(IRSSI_DIST) \
+				-I$(IRSSI_DIST)/src \
+				-I$(IRSSI_DIST)/src/fe-common/core \
+				-I$(IRSSI_DIST)/src/core \
+				-I$(IRSSI_DIST)/src/fe-text \
+				-I$(IRSSI_DIST)/src/irc \
+				-I$(IRSSI_DIST)/src/irc/core \
+				-I$(IRSSI_DIST)/src/irc/dcc \
+				-I$(IRSSI_DIST)/src/irc/notifylist
+
+INCLUDES = -I$(top_srcdir)/src $(IRSSI_INCLUDE)
+
+pkglib_LTLIBRARIES = libotr.la
+
+libotr_la_SOURCES = otr_key.c otr_util.c otr_ops.c io_set.c io_util.c \
+					otr.h irssi_otr.c irssi_otr.h otr-formats.c otr-formats.h
+libotr_la_LDFLAGS = -module -shared
diff --git a/src/io-config.h b/src/io-config.h
new file mode 100644
index 0000000..950502b
--- /dev/null
+++ b/src/io-config.h
@@ -0,0 +1,3 @@
+#define HAVE_STRSIGNAL
+#define HAVE_GREGEX_H
+#define IRCOTR_VERSION "git-fd11e699d9cb8d2f230251b23c037f1fd6372e14"
diff --git a/src/io_set.c b/src/io_set.c
new file mode 100644
index 0000000..997e078
--- /dev/null
+++ b/src/io_set.c
@@ -0,0 +1,62 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2009  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "otr.h"
+
+#ifdef HAVE_GREGEX_H
+extern GRegex *regex_nickignore;
+#endif
+
+char set_policy[512] = IO_DEFAULT_POLICY;
+char set_policy_known[512] = IO_DEFAULT_POLICY_KNOWN;
+char set_ignore[512] = IO_DEFAULT_IGNORE;
+int set_finishonunload = TRUE;
+
+void cmd_set(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[], char *argv_eol[],
+	    char *target) {
+	char *setting, *value;
+
+	if (argc) {
+		setting = argv[0];
+		value = argv[1] ? : "";
+	}
+
+	if (!argc) {
+		otr_logst(MSGLEVEL_CRAP,"policy: %s\n"
+			  "policy_known: %s\nignore: %s\n"
+			  "finishonunload: %s\n",
+			  set_policy,set_policy_known,set_ignore,
+			  set_finishonunload ? "true" : "false");
+	} else if (strcmp(setting,"policy")==0) {
+		otr_setpolicies(ioustate,value,FALSE);
+		strcpy(set_policy,value);
+	} else if (strcmp(setting,"policy_known")==0) {
+		otr_setpolicies(ioustate,value,TRUE);
+		strcpy(set_policy_known,value);
+	} else if (strcmp(setting,"ignore")==0) {
+#ifdef HAVE_GREGEX_H
+		if (regex_nickignore)
+			g_regex_unref(regex_nickignore);
+		regex_nickignore = g_regex_new(value,0,0,NULL);
+		strcpy(set_ignore,value);
+#endif
+	} else if (strcmp(setting,"finishonunload")==0) {
+		set_finishonunload = (strcasecmp(value,"true")==0);
+	}
+}
diff --git a/src/io_util.c b/src/io_util.c
new file mode 100644
index 0000000..4d1c71b
--- /dev/null
+++ b/src/io_util.c
@@ -0,0 +1,349 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "otr.h"
+
+char *otr_status_txt[] = {
+	"FINISHED",
+	"TRUST_MANUAL",
+	"TRUST_SMP",
+	"SMP_ABORT",
+	"SMP_STARTED",
+	"SMP_RESPONDED",
+	"SMP_INCOMING",
+	"SMP_FINALIZE",
+	"SMP_ABORTED",
+	"PEER_FINISHED",
+	"SMP_FAILED",
+	"SMP_SUCCESS",
+	"GONE_SECURE",
+	"GONE_INSECURE",
+	"CTX_UPDATE"
+};
+
+char *otr_msg_event_txt[] = {
+	"NONE",
+	"ENCRYPTION_REQUIRED",
+	"ENCRYPTION_ERROR",
+	"CONNECTION_ENDED",
+	"SETUP_ERROR",
+	"MSG_REFLECTED",
+	"MSG_RESENT",
+	"RCVDMSG_NOT_IN_PRIVATE",
+	"RCVDMSG_UNREADABLE",
+	"RCVDMSG_MALFORMED",
+	"LOG_HEARTBEAT_RCVD",
+	"LOG_HEARTBEAT_SENT",
+	"RCVDMSG_GENERAL_ERR",
+	"RCVDMSG_UNENCRYPTED",
+	"RCVDMSG_UNRECOGNIZED",
+	"RCVDMSG_FOR_OTHER_INSTANCE"
+};
+
+int extract_nick(char *nick, char *line)
+{
+	char *excl;
+
+	if (*line++ != ':')
+		return FALSE;
+
+	strcpy(nick, line);
+
+	if ((excl = strchr(nick, '!')))
+		*excl = '\0';
+
+	return TRUE;
+}
+
+int cmd_generic(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		char *argv_eol[],
+		char *target)
+{
+	char *cmd;
+	struct _cmds *commands = cmds;
+
+	if (!argc) {
+		otr_noticest(TXT_CMD_OTR);
+		return TRUE;
+	}
+
+	cmd = argv[0];
+
+	argv++;
+	argv_eol++;
+	argc--;
+
+	do {
+		if (strcmp(commands->name, cmd) == 0) {
+			commands->cmdfunc(ioustate, ircctx, argc, argv,
+					  argv_eol,
+					  target);
+			return TRUE;
+		}
+	} while ((++commands)->name);
+
+	return FALSE;
+}
+
+void cmd_debug(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	       char *argv_eol[],
+	       char *target)
+{
+	debug = !debug;
+	otr_noticest(debug ? TXT_CMD_DEBUG_ON : TXT_CMD_DEBUG_OFF);
+}
+
+void cmd_version(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		 char *argv_eol[],
+		 char *target)
+{
+	otr_noticest(TXT_CMD_VERSION, IRCOTR_VERSION);
+}
+
+void cmd_help(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	      char *argv_eol[],
+	      char *target)
+{
+	otr_log(ircctx, target, MSGLEVEL_CRAP, otr_help);
+}
+
+void cmd_finish(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		char *argv_eol[],
+		char *target)
+{
+	if (argc)
+		otr_finish(NULL, NULL, argv[0], TRUE);
+	else if (ircctx && target)
+		otr_finish(ircctx, target, NULL, TRUE);
+	else
+		otr_noticest(TXT_CMD_QNOTFOUND);
+}
+
+void cmd_trust(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	       char *argv_eol[],
+	       char *target)
+{
+	if (argc)
+		otr_trust(NULL, NULL, argv[0]);
+	else if (ircctx && target)
+		otr_trust(ircctx, target, NULL);
+	else
+		otr_noticest(TXT_CMD_QNOTFOUND);
+}
+
+void cmd_authabort(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc,
+		   char *argv[], char *argv_eol[],
+		   char *target)
+{
+	if (argc)
+		otr_authabort(NULL, NULL, argv[0]);
+	else if (ircctx && target)
+		otr_authabort(ircctx, target, NULL);
+	else
+		otr_noticest(TXT_CMD_QNOTFOUND);
+}
+
+void cmd_genkey(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		char *argv_eol[],
+		char *target)
+{
+	if (argc) {
+		if (strcmp(argv[0], "abort") == 0)
+			keygen_abort(ioustate, FALSE);
+		else if (strchr(argv[0], '@'))
+			keygen_run(ioustate, argv[0]);
+		else
+			otr_noticest(TXT_KG_NEEDACC);
+	} else {
+		otr_noticest(TXT_KG_NEEDACC);
+	}
+}
+
+void _cmd_auth(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	       char *argv_eol[],
+	       char *target,
+	       int qanda)
+{
+	char *accountname = NULL;
+	char *question = NULL;
+	char *secret;
+
+	/* have args? */
+	if (argc < (qanda ? 2 : 1)) {
+		otr_notice(ircctx, target,
+			   TXT_CMD_AUTH);
+		return;
+	}
+
+	/* have buddy? */
+	if (!(ircctx && target)) {
+		accountname = strchr(argv[0], '@');
+		if (!accountname) {
+			otr_noticest(TXT_CMD_QNOTFOUND);
+			return;
+		}
+		ircctx = NULL;
+		target = NULL;
+		argv++; argv_eol++; argc--;
+	}
+
+	/* have question? */
+	if (qanda) {
+		question = argv[0];
+		argv++; argv_eol++; argc--;
+	}
+
+	secret = argv_eol[0];
+
+	otr_auth(ircctx, target, accountname, question, secret);
+}
+
+void cmd_authq(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	       char *argv_eol[],
+	       char *target)
+{
+	_cmd_auth(ioustate, ircctx, argc, argv, argv_eol, target, TRUE);
+}
+
+void cmd_auth(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+	      char *argv_eol[],
+	      char *target)
+{
+	_cmd_auth(ioustate, ircctx, argc, argv, argv_eol, target, FALSE);
+}
+
+/*
+ * /otr contexts
+ */
+void cmd_contexts(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		  char *argv_eol[],
+		  char *target)
+{
+	struct ctxlist_ *ctxlist = otr_contexts(ioustate), *ctxnext = ctxlist;
+	struct fplist_ *fplist, *fpnext;
+
+	if (!ctxlist)
+		otr_infost(TXT_CTX_NOCTXS);
+
+	while (ctxlist) {
+		otr_infost(TXT_CTX_CTX_UNENCRYPTED + ctxlist->state,
+			   ctxlist->username,
+			   ctxlist->accountname);
+
+		fplist = ctxlist->fplist;
+		while (fplist) {
+			otr_infost(TXT_CTX_FPS_NO + fplist->authby,
+				   fplist->fp);
+			fplist = fplist->next;
+		}
+		ctxlist = ctxlist->next;
+	}
+	while ((ctxlist = ctxnext)) {
+		ctxnext = ctxlist->next;
+		fpnext = ctxlist->fplist;
+		while ((fplist = fpnext)) {
+			fpnext = fplist->next;
+			g_free(fplist->fp);
+			g_free(fplist);
+		}
+		g_free(ctxlist);
+	}
+}
+
+struct _cmds cmds[] = {
+	{ "version", cmd_version },
+	{ "debug", cmd_debug },
+	{ "help", cmd_help },
+	{ "finish", cmd_finish },
+	{ "trust", cmd_trust },
+	{ "authabort", cmd_authabort },
+	{ "auth", cmd_auth },
+#ifndef LIBOTR3
+	{ "authq", cmd_authq },
+#endif
+	{ "genkey", cmd_genkey },
+	{ "contexts", cmd_contexts },
+	{ NULL, NULL },
+	{ NULL, NULL }
+};
+
+void io_explode_args(const char *args, char ***argvp, char ***argv_eolp,
+		     int *argcp)
+{
+	char **argv, **argv_eol;
+	char *s = (char*)args;
+	int argc = 1, i;
+
+	while ((s = strchr(s + 1, ' ')))
+		argc++;
+
+	argv = (char**)malloc(sizeof(char *) * argc);
+	argv_eol = (char**)malloc(sizeof(char *) * argc);
+
+	s = (char*)args;
+	argv_eol[0] = strdup(args);
+	i = 0;
+	while (++i < argc)
+		argv_eol[i] = strchr(argv_eol[i - 1], ' ') + 1;
+
+	argv[0] = strtok(strdup(args), " ");
+	i = 1;
+	while (i < argc) {
+		argv[i++] = strtok(NULL, " ");
+		otr_logst(MSGLEVEL_CRAP, "arg %d: %s", i, argv[i - 1]);
+	}
+
+	*argvp = argv;
+	*argv_eolp = argv_eol;
+	*argcp = argc;
+}
+
+/*
+ * Get a format describing the OTR status of this conversation.
+ */
+int otr_getstatus_format(IRC_CTX *ircctx, const char *nick)
+{
+	int status = otr_getstatus(ircctx, nick);
+
+	if (status & (IO_ST_SMP_ONGOING)) {
+		/* we don't care about the trust level in that case */
+		status = status & IO_ST_SMP_ONGOING;
+	}
+
+	switch (status) {
+	case IO_ST_PLAINTEXT:
+		return TXT_ST_PLAINTEXT;
+	case IO_ST_FINISHED:
+		return TXT_ST_FINISHED;
+	case IO_ST_UNTRUSTED:
+		return TXT_ST_UNTRUSTED;
+	case IO_ST_SMP_INCOMING:
+		return TXT_ST_SMP_INCOMING;
+	case IO_ST_SMP_OUTGOING:
+		return TXT_ST_SMP_OUTGOING;
+	case IO_ST_SMP_FINALIZE:
+		return TXT_ST_SMP_FINALIZE;
+	case IO_ST_TRUST_MANUAL:
+		return TXT_ST_TRUST_MANUAL;
+	case IO_ST_TRUST_SMP:
+		return TXT_ST_TRUST_SMP;
+	default:
+		return TXT_ST_SMP_UNKNOWN;
+	}
+}
diff --git a/src/irssi_otr.c b/src/irssi_otr.c
new file mode 100644
index 0000000..1f0df0e
--- /dev/null
+++ b/src/irssi_otr.c
@@ -0,0 +1,302 @@
+/*
+ * Off-the-Record Messaging (OTR) module for the irssi IRC client
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "otr.h"
+
+int debug = FALSE;
+
+static const char *signal_args_otr_event[] = {
+	"iobject", "string", "string", "NULL" };
+
+#ifdef HAVE_GREGEX_H
+GRegex *regex_nickignore = NULL;
+#endif
+
+/* need this to decode arguments in perl signal handlers. Maybe irssi should
+ * install perl/perl-signals.h which is where this definition comes from? */
+void perl_signal_register(const char *signal, const char **args);
+
+static IOUSTATE *ioustate;
+
+void irc_send_message(IRC_CTX *ircctx, const char *recipient, char *msg) {
+	ircctx->send_message(
+		ircctx,recipient,msg,GPOINTER_TO_INT(SEND_TARGET_NICK));
+}
+
+/*
+ * Pipes all outgoing private messages through OTR
+ */
+static void sig_server_sendmsg(SERVER_REC *server, const char *target,
+			       const char *msg, void *target_type_p)
+{
+	if (GPOINTER_TO_INT(target_type_p)==SEND_TARGET_NICK) {
+		char *otrmsg;
+
+#ifdef HAVE_GREGEX_H
+		if (g_regex_match(regex_nickignore,target,0,NULL))
+			return;
+#endif
+		otrmsg = otr_send(server,msg,target);
+		if (otrmsg&&(otrmsg!=msg)) {
+			signal_continue(4,server,target,otrmsg,target_type_p);
+			otrl_message_free(otrmsg);
+		} else if (!otrmsg)
+			signal_stop();
+	}
+}
+
+/*
+ * Pipes all incoming private messages through OTR
+ */
+static void sig_message_private(SERVER_REC *server, const char *msg,
+				const char *nick, const char *address)
+{
+	char *newmsg;
+
+#ifdef HAVE_GREGEX_H
+	if (g_regex_match(regex_nickignore,nick,0,NULL))
+		return;
+#endif
+
+	newmsg = otr_receive(server,msg,nick);
+
+	if (newmsg&&(newmsg!=msg)) {
+		signal_continue(4,server,newmsg,nick,address);
+		otrl_message_free(newmsg);
+	} else if (newmsg==NULL)
+		signal_stop();
+}
+
+/*
+ * Finish an OTR conversation when its query is closed.
+ */
+static void sig_query_destroyed(QUERY_REC *query) {
+	if (query&&query->server&&query->server->connrec) {
+		otr_finish(query->server,query->name,NULL,FALSE);
+	}
+}
+
+/*
+ * /otr
+ */
+static void cmd_otr(const char *data,void *server,WI_ITEM_REC *item) 
+{
+	char **argv, **argv_eol;
+	int argc;
+	QUERY_REC *query = QUERY(item);
+
+	if (*data == '\0') {
+		otr_noticest(TXT_CMD_OTR);
+		return;
+	}
+
+	io_explode_args(data,&argv,&argv_eol,&argc);
+
+	if (query&&query->server&&query->server->connrec) {
+		cmd_generic(ioustate,query->server,argc,argv,argv_eol,query->name);
+	} else {
+		cmd_generic(ioustate,NULL,argc,argv,argv_eol,NULL);
+	}
+
+	statusbar_items_redraw("otr");
+
+	g_free(argv_eol[0]);
+	g_free(argv_eol);
+	g_free(argv);
+}
+
+/*
+ * Optionally finish conversations on /quit. We're already doing this on unload
+ * but the quit handler terminates irc connections before unloading.
+ */
+static void cmd_quit(const char *data, void *server, WI_ITEM_REC *item)
+{
+	if (settings_get_bool("otr_finishonunload"))
+		otr_finishall(ioustate);
+}
+
+/*
+ * otr statusbar
+ */
+static void otr_statusbar(struct SBAR_ITEM_REC *item, int get_size_only)
+{
+	WI_ITEM_REC *wi = active_win->active;
+	QUERY_REC *query = QUERY(wi);
+	int formatnum=0;
+
+	if (query&&query->server&&query->server->connrec)
+		formatnum = otr_getstatus_format(query->server,query->name);
+
+	statusbar_item_default_handler(
+		item, 
+		get_size_only, 
+		formatnum ? formats[formatnum].def : ""," ",FALSE);
+}
+
+void otr_query_create(SERVER_REC *server, const char *nick)
+{
+	if (!server||!nick||
+	    !settings_get_bool("otr_createqueries")||
+	    query_find(server, nick))
+		return;
+
+	irc_query_create(server->tag, nick, TRUE);
+}
+
+static void read_settings(void)
+{
+	otr_setpolicies(ioustate,settings_get_str("otr_policy"),FALSE);
+	otr_setpolicies(ioustate,settings_get_str("otr_policy_known"),TRUE);
+#ifdef HAVE_GREGEX_H
+	if (regex_nickignore)
+		g_regex_unref(regex_nickignore);
+	regex_nickignore = g_regex_new(settings_get_str("otr_ignore"),0,0,NULL);
+#endif
+
+}
+
+void otr_status_change(IRC_CTX *ircctx, const char *nick, int event)
+{
+	statusbar_items_redraw("otr");
+	signal_emit("otr event",3,ircctx,nick,otr_status_txt[event]);
+}
+
+/*
+ * irssi init()
+ */
+void otr_init(void)
+{
+	module_register(MODULE_NAME, "core");
+
+	theme_register(formats);
+
+	if (otrlib_init())
+		return;
+
+	ioustate = otr_init_user("one to rule them all");
+
+	signal_add_first("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
+	signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
+	signal_add("query destroyed", (SIGNAL_FUNC) sig_query_destroyed);
+
+	command_bind("otr", NULL, (SIGNAL_FUNC) cmd_otr);
+
+	command_bind_first("quit", NULL, (SIGNAL_FUNC) cmd_quit);
+
+	settings_add_str("otr", "otr_policy",IO_DEFAULT_POLICY);
+	settings_add_str("otr", "otr_policy_known",IO_DEFAULT_POLICY_KNOWN);
+	settings_add_str("otr", "otr_ignore",IO_DEFAULT_IGNORE);
+	settings_add_bool("otr", "otr_finishonunload",TRUE);
+	settings_add_bool("otr", "otr_createqueries",TRUE);
+	read_settings();
+	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+	statusbar_item_register("otr", NULL, otr_statusbar);
+
+	statusbar_items_redraw("window");
+
+	perl_signal_register("otr event",signal_args_otr_event);
+}
+
+/*
+ * irssi deinit()
+ */
+void otr_deinit(void)
+{
+#ifdef HAVE_GREGEX_H
+	g_regex_unref(regex_nickignore);
+#endif
+
+	signal_remove("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
+	signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+	signal_remove("query destroyed", (SIGNAL_FUNC) sig_query_destroyed);
+
+	command_unbind("otr", (SIGNAL_FUNC) cmd_otr);
+
+	command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
+
+	signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+	statusbar_item_unregister("otr");
+
+	if (settings_get_bool("otr_finishonunload"))
+		otr_finishall(ioustate);
+
+	otr_deinit_user(ioustate);
+
+	otrlib_deinit();
+
+	theme_unregister();
+}
+
+IRC_CTX *ircctx_by_peername(const char *peername, char *nick)
+{
+        GSList *tmp;
+	char pname[256];
+	char *address;
+
+	strcpy(pname,peername);
+
+	address = strchr(pname,'@');
+
+	if (!address)
+		return NULL;
+
+	*address = '\0';
+	strcpy(nick,pname);
+	*address++ = '@';
+
+        for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+                SERVER_REC *server = tmp->data;
+
+                if (g_strcasecmp(server->connrec->address, address) == 0)
+                        return server;
+        }
+
+        return NULL;
+}
+
+char *lvlstring[] = { 
+	"NOTICE",
+	"DEBUG"
+};
+
+
+void otr_log(IRC_CTX *server, const char *nick, 
+	     int level, const char *format, ...) {
+	va_list params;
+	va_start( params, format );
+	char msg[LOGMAX], *s = msg;
+
+	if ((level==LVL_DEBUG)&&!debug)
+		return;
+
+	s += sprintf(s,"%s","%9OTR%9");
+
+	if (level!=LVL_NOTICE)	
+		s += sprintf(s,"(%s)",lvlstring[level]);
+
+	s += sprintf(s,": ");
+
+	if( vsnprintf( s, LOGMAX, format, params ) < 0 )
+		sprintf( s, "internal error parsing error string (BUG)" );
+	va_end( params );
+
+	printtext(server, nick, MSGLEVEL_MSGS, msg);
+}
diff --git a/src/irssi_otr.h b/src/irssi_otr.h
new file mode 100644
index 0000000..4756ea1
--- /dev/null
+++ b/src/irssi_otr.h
@@ -0,0 +1,83 @@
+/*
+ * Off-the-Record Messaging (OTR) module for the irssi IRC client
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#define UOFF_T_LONG_LONG 1
+
+#include <src/common.h>
+#include <src/core/commands.h>
+#include <src/core/modules.h>
+#include <src/core/servers.h>
+#include <src/core/signals.h>
+#include <src/core/levels.h>
+#include <src/core/queries.h>
+#include <src/fe-common/core/printtext.h>
+#include <src/fe-common/core/fe-windows.h>
+#include <src/fe-common/core/module-formats.h>
+#include <src/core/modules.h>
+#include <src/core/settings.h>
+#include <src/irc/core/irc.h>
+#include <src/irc/core/irc-queries.h>
+
+#include <src/fe-text/statusbar-item.h>
+
+#define IRC_CTX SERVER_REC
+
+#define get_client_config_dir get_irssi_dir
+
+static IRC_CTX *IRCCTX_DUP(IRC_CTX *ircctx) __attribute__ ((unused));
+
+static IRC_CTX *IRCCTX_DUP(IRC_CTX *ircctx) {
+	server_ref(ircctx);
+	return ircctx;
+}
+
+static IRC_CTX *IRCCTX_FREE(IRC_CTX *ircctx) __attribute__ ((unused));
+
+static IRC_CTX *IRCCTX_FREE(IRC_CTX *ircctx)
+{
+	server_unref(ircctx);
+	return ircctx;
+}
+
+void otr_query_create(IRC_CTX *ircctx, const char *nick);
+
+#define IRCCTX_ADDR(ircctx) ircctx->connrec->address
+#define IRCCTX_NICK(ircctx) ircctx->nick
+#define IRCCTX_ACCNAME(accname,ircctx) sprintf(accname, "%s@%s", ircctx->nick, ircctx->connrec->address)
+#define IRCCTX_IO_US(ircctx) (&ioustate_uniq)
+#define IO_CREATE_US(user) (&ioustate_uniq)
+
+#define otr_noticest(formatnum,...) \
+	printformat(NULL,NULL,MSGLEVEL_MSGS, formatnum, ## __VA_ARGS__)
+
+#define otr_notice(ircctx,nick,formatnum,...) { \
+	otr_query_create(ircctx,nick); \
+	printformat(ircctx,nick,MSGLEVEL_MSGS, formatnum, ## __VA_ARGS__);}
+
+#define otr_infost(formatnum,...) \
+	printformat(NULL,NULL,MSGLEVEL_CRAP, formatnum, ## __VA_ARGS__)
+
+#define otr_info(server,nick,formatnum,...) { \
+	otr_query_create(ircctx,nick); \
+	printformat(ircctx,nick,MSGLEVEL_CRAP, formatnum, ## __VA_ARGS__);}
+
+#define otr_debug(ircctx,nick,formatnum,...) { \
+	if (debug) { \
+		otr_query_create(ircctx,nick); \
+		printformat(ircctx,nick,MSGLEVEL_MSGS, formatnum, ## __VA_ARGS__); } }
diff --git a/src/otr-formats.c b/src/otr-formats.c
new file mode 100644
index 0000000..9ac326e
--- /dev/null
+++ b/src/otr-formats.c
@@ -0,0 +1,112 @@
+#include "otr.h"
+char *otr_help = "%9- OTR help -%9\n-{ Original author: http://irssi-otr.tuxfamily.org/\n\n-{ Forked to fix bugs, software is not stable at this moment, do not rely on it for strong security!\n-{Original readme is the READ_ME file.\n-{https://crypto.is/ project.\n%9- End of OTR help -%9";
+FORMAT_REC formats[] = {
+{ MODULE_NAME, "otr", 0}
+,
+{ NULL, "Keys", 0 }
+,
+{ "kg_failed", "%9OTR%9: Key generation for $0: failed: $1 ($2)", 3, { 0, 0, 0 }},
+{ "kg_completed", "%9OTR%9: Key generation for $0: completed in $1 seconds. Reloading keys", 2, { 0, 1 }},
+{ "kg_aborted_dup", "%9OTR%9: Key generation for $0: aborted. Key generation for $1 still in progress", 2, { 0, 0 }},
+{ "kg_aborted_dir", "%9OTR%9: Key generation for $0: aborted, failed creating directory $1: $2", 3, { 0, 0, 0 }},
+{ "kg_mkdir", "%9OTR%9: created directory $0", 1, { 0 }},
+{ "kg_pipe", "%9OTR%9: Key generation for $0: error creating pipe: $1", 2, { 0, 0 }},
+{ "kg_fork", "%9OTR%9: Key generation for $0: fork() error: $1", 2, { 0, 0 }},
+{ "kg_initiated", "%9OTR%9: Key generation for $0: initiated. This might take several minutes or on some systems even an hour. If you wanna check that something is happening, see if there are two processes of your IRC client.", 1, { 0 }},
+{ "kg_exited", "%9OTR%9: Key generation for $0: child terminated for unknown reason", 1, { 0 }},
+{ "kg_exitsig", "%9OTR%9: Key generation for $0: child was killed by signal $1", 2, { 0, 0 }},
+{ "kg_pollerr", "%9OTR%9: Key generation for $0: error poll()ing child: $1", 2, { 0, 0 }},
+{ "kg_abort", "%9OTR%9: Key generation for $0: aborted", 1, { 0 }},
+{ "kg_needacc", "%9OTR%9: I need an account name. Try something like /otr genkey mynick at irc.server.net", 0},
+{ "kg_noabort", "%9OTR%9: No ongoing key generation to abort", 0},
+{ "key_not_found", "%9OTR%9: no private keys found", 0},
+{ "key_loaded", "%9OTR%9: private keys loaded", 0},
+{ "key_load_error", "%9OTR%9: Error loading private keys: $0 ($1)", 2, { 0, 0 }},
+{ NULL, "Fingerprints", 0 }
+,
+{ "fp_saved", "%9OTR%9: fingerprints saved", 0},
+{ "fp_save_error", "%9OTR%9: Error saving fingerprints: $0 ($1)", 2, { 0, 0 }},
+{ "fp_not_found", "%9OTR%9: no fingerprints found", 0},
+{ "fp_loaded", "%9OTR%9: fingerprints loaded", 0},
+{ "fp_load_error", "%9OTR%9: Error loading fingerprints: $0 ($1)", 2, { 0, 0 }},
+{ "fp_trust", "%9OTR%9: Trusting fingerprint from $0", 1, { 0 }},
+{ NULL, "Callbacks", 0 }
+,
+{ "ops_notify_bug", "%9OTR%9: BUG() in ops_notify", 0},
+{ "ops_notify", "%9OTR%9: title: $0 prim: $1 sec: $2", 3, { 0, 0, 0 }},
+{ "ops_display_bug", "%9OTR%9: BUG() in ops_display", 0},
+{ "ops_display", "%9OTR%9: msg: $0", 1, { 0 }},
+{ "ops_sec", "%9OTR%9: gone %9secure%9", 0},
+{ "ops_fpcomp", "%9OTR%9: Your peer is not authenticated. To make sure you're talking to the right guy you can either agree on a secret and use the authentication described in %9/otr auth%9, or use the traditional way and compare fingerprints over a secure line (e.g. telephone) and subsequently enter %9/otr trust%9.  Your fingerprint is: $0. $1's fingerprint: $2", 3, { 0, 0, 0 }},
+{ "ops_insec", "%9OTR%9: gone %9insecure%9", 0},
+{ "ops_still_reply", "%9OTR%9: still %9secure%9 (is reply)", 0},
+{ "ops_still_no_reply", "%9OTR%9: still %9secure%9 (is not reply)", 0},
+{ "ops_log", "%9OTR%9: log msg: $0", 1, { 0 }},
+{ "ops_inject", "%9OTR%9: Couldn't inject message from $0 for $1: $2", 3, { 0, 0, 0 }},
+{ NULL, "SendingReceiving", 0 }
+,
+{ "send_failed", "%9OTR%9: send failed: msg=$0", 1, { 0 }},
+{ "send_change", "%9OTR%9: couldn't find context also OTR changed the outgoing message(BUG?)", 0},
+{ "send_fragment", "%9OTR%9: failed to fragment message: msg=$0", 1, { 0 }},
+{ "send_converted", "%9OTR%9: OTR converted sent message to $0", 1, { 0 }},
+{ "receive_ignore_query", "%9OTR%9: ignoring rest of OTR default query msg", 0},
+{ "receive_dequeued", "%9OTR%9: dequeued msg of length $0", 1, { 1 }},
+{ "receive_queued", "%9OTR%9: queued msg of length $0", 1, { 1 }},
+{ "receive_ignore", "%9OTR%9: ignoring protocol message of length $0, acc=$1, from=$2: $3", 4, { 1, 0, 0, 0 }},
+{ "receive_converted", "%9OTR%9: OTR converted received message", 0},
+{ "otr_better_two", "%9OTR%9: <b>$0</b> has requested an <a href=\"http://otr.cypherpunks.ca/\">Off-the-Record private conversation</a>.  However, you do not have a plugin to support that.", 1, { 0 }},
+{ "otr_better_three", "%9OTR%9: See <a href=\"http://otr.cypherpunks.ca/\">http://otr.cypherpunks.ca/</a> for more information.", 0},
+{ NULL, "Context", 0 }
+,
+{ "ctx_not_found", "%9OTR%9: couldn't find context: acc=$0 nick=$1", 2, { 0, 0 }},
+{ "ctx_not_create", "%9OTR%9: couldn't create/find context: acc=$0 from=$1", 2, { 0, 0 }},
+{ NULL, "Authentication", 0 }
+,
+{ "auth_aborted_ongoing", "%9OTR%9: Ongoing authentication aborted", 0},
+{ "auth_aborted", "%9OTR%9: Authentication aborted", 0},
+{ "auth_responding", "%9OTR%9: Responding to authentication request...", 0},
+{ "auth_initiated", "%9OTR%9: Initiated authentication...", 0},
+{ "auth_have_old", "%9OTR%9: $0 wanted to authenticate but an old authentication was still ongoing.  Old authentication will be aborted, please try again.", 1, { 0 }},
+{ "auth_peer", "%9OTR%9: $0 wants to authenticate. Type /otr auth <your-shared-secret> to complete.", 1, { 0 }},
+{ "auth_peer_reply_wrong", "%9OTR%9: $0 replied to an auth we didn't start.", 1, { 0 }},
+{ "auth_peer_replied", "%9OTR%9: $0 replied to our auth request...", 1, { 0 }},
+{ "auth_peer_wrong_smp3", "%9OTR%9: $0 sent a wrong authentication message (SMP3).", 1, { 0 }},
+{ "auth_peer_wrong_smp4", "%9OTR%9: $0 sent a wrong authentication message (SMP4).", 1, { 0 }},
+{ "auth_successful", "%9OTR%9: Authentication successful!", 0},
+{ "auth_failed", "%9OTR%9: Authentication failed!", 0},
+{ "auth_needenc", "%9OTR%9: You need to establish an OTR session before you can authenticate.", 0},
+{ NULL, "Commands", 0 }
+,
+{ "cmd_otr", "%9OTR%9: We're alive", 0},
+{ "cmd_qnotfound", "%9OTR%9: Failed: Can't get nick and server of current query window. (Or maybe you're doing this in the status window?)", 0},
+{ "cmd_auth", "%9OTR%9: Please agree on a secret with your peer and then initiate the authentication with /otr auth <secret> or let him initiate. Should you initiate your peer will after a little while be instructed to enter the secret as well. Once he has done so the authentication will finish up. Should you have both typed in the same secret the authentication should be successful.", 0},
+{ "cmd_debug_on", "%9OTR%9: Debug mode is on", 0},
+{ "cmd_debug_off", "%9OTR%9: Debug mode is off", 0},
+{ "cmd_finish", "%9OTR%9: Finished conversation with $0@$1.", 2, { 0, 0 }},
+{ "cmd_finishall_none", "%9OTR%9: No conversations to finish.", 0},
+{ "cmd_version", "%9OTR%9: This is irc-otr version $0", 1, { 0 }},
+{ "peer_finished", "%9OTR%9: $0 has finished the OTR conversation. If you want to continue talking enter %9/otr finish%9 for plaintext or ?OTR? to restart OTR.", 1, { 0 }},
+{ NULL, "Contexts", 0 }
+,
+{ "ctx_ctx_unencrypted", "%9$[20]0%9    $[30]1    plaintext", 2, { 0, 0 }},
+{ "ctx_ctx_encrypted", "%9$[20]0%9    $[30]1    %gencrypted%n", 2, { 0, 0 }},
+{ "ctx_ctx_finished", "%9$[20]0%9    $[30]1    finished", 2, { 0, 0 }},
+{ "ctx_ctx_unknown", "%9$[20]0%9    $[30]1    unknown state(BUG?)", 2, { 0, 0 }},
+{ "ctx_fps_no", "$0 %rnot authenticated%n", 1, { 0 }},
+{ "ctx_fps_smp", "$0 %gauthenticated%n via shared secret (SMP)", 1, { 0 }},
+{ "ctx_fps_man", "$0 %gauthenticated%n manually", 1, { 0 }},
+{ "ctx_noctxs", "No active OTR contexts found", 0},
+{ NULL, "Statusbar", 0 }
+,
+{ "st_plaintext", "{sb plaintext}", 0},
+{ "st_untrusted", "{sb %rOTR(not auth'ed)%n}", 0},
+{ "st_trust_smp", "{sb %gOTR%n}", 0},
+{ "st_trust_manual", "{sb %gOTR%n}", 0},
+{ "st_smp_incoming", "{sb {hilight incoming auth request...}}", 0},
+{ "st_smp_outgoing", "{sb {hilight awaiting auth reply...}}", 0},
+{ "st_smp_finalize", "{sb {hilight finalizing auth...}}", 0},
+{ "st_smp_unknown", "{sb {hilight unknown auth state!}}", 0},
+{ "st_finished", "{sb finished}", 0},
+{ "st_unknown", "{sb {hilight state unknown (BUG!)}}", 0},
+{ NULL, NULL, 0 }
+};
diff --git a/src/otr-formats.h b/src/otr-formats.h
new file mode 100644
index 0000000..b1adacb
--- /dev/null
+++ b/src/otr-formats.h
@@ -0,0 +1,111 @@
+extern char *otr_help;
+
+enum {
+TXT_OTR_MODULE_NAME,
+TXT_OTR_FILL_0,
+TXT_KG_FAILED,
+TXT_KG_COMPLETED,
+TXT_KG_ABORTED_DUP,
+TXT_KG_ABORTED_DIR,
+TXT_KG_MKDIR,
+TXT_KG_PIPE,
+TXT_KG_FORK,
+TXT_KG_INITIATED,
+TXT_KG_EXITED,
+TXT_KG_EXITSIG,
+TXT_KG_POLLERR,
+TXT_KG_ABORT,
+TXT_KG_NEEDACC,
+TXT_KG_NOABORT,
+TXT_KEY_NOT_FOUND,
+TXT_KEY_LOADED,
+TXT_KEY_LOAD_ERROR,
+TXT_OTR_FILL_1,
+TXT_FP_SAVED,
+TXT_FP_SAVE_ERROR,
+TXT_FP_NOT_FOUND,
+TXT_FP_LOADED,
+TXT_FP_LOAD_ERROR,
+TXT_FP_TRUST,
+TXT_OTR_FILL_2,
+TXT_INSTAG_SAVED,
+TXT_INSTAG_SAVE_ERROR,
+TXT_INSTAG_NOT_FOUND,
+TXT_INSTAG_LOADED,
+TXT_INSTAG_LOAD_ERROR,
+TXT_OTR_FILL_3,
+TXT_OPS_NOTIFY_BUG,
+TXT_OPS_NOTIFY,
+TXT_OPS_DISPLAY_BUG,
+TXT_OPS_DISPLAY,
+TXT_OPS_SEC,
+TXT_OPS_FPCOMP,
+TXT_OPS_INSEC,
+TXT_OPS_STILL_REPLY,
+TXT_OPS_STILL_NO_REPLY,
+TXT_OPS_LOG,
+TXT_OPS_INJECT,
+TXT_OPS_HANDLE_MSG,
+TXT_OTR_FILL_4,
+TXT_SEND_FAILED,
+TXT_SEND_CHANGE,
+TXT_SEND_FRAGMENT,
+TXT_SEND_CONVERTED,
+TXT_RECEIVE_IGNORE_QUERY,
+TXT_RECEIVE_DEQUEUED,
+TXT_RECEIVE_QUEUED,
+TXT_RECEIVE_IGNORE,
+TXT_RECEIVE_CONVERTED,
+TXT_OTR_BETTER_TWO,
+TXT_OTR_BETTER_THREE,
+TXT_OTR_FILL_5,
+TXT_CTX_NOT_FOUND,
+TXT_CTX_NOT_CREATE,
+TXT_OTR_FILL_6,
+TXT_AUTH_ABORTED_ONGOING,
+TXT_AUTH_ABORTED,
+TXT_AUTH_RESPONDING,
+TXT_AUTH_INITIATED,
+TXT_AUTH_HAVE_OLD,
+TXT_AUTH_PEER,
+TXT_AUTH_PEER_QA,
+TXT_AUTH_PEER_REPLY_WRONG,
+TXT_AUTH_PEER_REPLIED,
+TXT_AUTH_PEER_WRONG_SMP3,
+TXT_AUTH_PEER_WRONG_SMP4,
+TXT_AUTH_SUCCESSFUL,
+TXT_AUTH_FAILED,
+TXT_AUTH_NEEDENC,
+TXT_OTR_FILL_7,
+TXT_CMD_OTR,
+TXT_CMD_QNOTFOUND,
+TXT_CMD_AUTH,
+TXT_CMD_DEBUG_ON,
+TXT_CMD_DEBUG_OFF,
+TXT_CMD_FINISH,
+TXT_CMD_FINISHALL_NONE,
+TXT_CMD_VERSION,
+TXT_PEER_FINISHED,
+TXT_OTR_FILL_8,
+TXT_CTX_CTX_UNENCRYPTED,
+TXT_CTX_CTX_ENCRYPTED,
+TXT_CTX_CTX_FINISHED,
+TXT_CTX_CTX_UNKNOWN,
+TXT_CTX_FPS_NO,
+TXT_CTX_FPS_SMP,
+TXT_CTX_FPS_MAN,
+TXT_CTX_NOCTXS,
+TXT_OTR_FILL_9,
+TXT_ST_PLAINTEXT,
+TXT_ST_UNTRUSTED,
+TXT_ST_TRUST_SMP,
+TXT_ST_TRUST_MANUAL,
+TXT_ST_SMP_INCOMING,
+TXT_ST_SMP_OUTGOING,
+TXT_ST_SMP_FINALIZE,
+TXT_ST_SMP_UNKNOWN,
+TXT_ST_FINISHED,
+TXT_ST_UNKNOWN
+};
+
+extern FORMAT_REC formats[];
diff --git a/src/otr.h b/src/otr.h
new file mode 100644
index 0000000..f716f79
--- /dev/null
+++ b/src/otr.h
@@ -0,0 +1,247 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* OTR */
+
+#include <libotr/proto.h>
+#include <libotr/context.h>
+#include <libotr/message.h>
+#include <libotr/privkey.h>
+
+/* glib */
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+
+#include "irssi_otr.h"
+
+/* user state */
+
+typedef struct {
+	OtrlUserState otr_state;
+	GSList *plistunknown;
+	GSList *plistknown;
+} IOUSTATE;
+
+#ifndef TARGET_BITLBEE
+/* there can be only one */
+extern IOUSTATE ioustate_uniq;
+#endif
+
+/* log stuff */
+
+#define LOGMAX 1024
+
+#define LVL_NOTICE  0
+#define LVL_DEBUG   1
+
+#define otr_logst(level, format, ...) \
+	otr_log(NULL, NULL, level, format, ## __VA_ARGS__)
+
+void otr_log(IRC_CTX *server, const char *to,
+	     int level, const char *format, ...);
+
+/* own */
+
+#include "io-config.h"
+
+/* irssi module name */
+#define MODULE_NAME "otr"
+
+#include "otr-formats.h"
+
+/*
+ * maybe this should be configurable?
+ * I believe bitlbee has something >500.
+ */
+#define OTR_MAX_MSG_SIZE 400
+
+/* otr protocol id */
+#define PROTOCOLID "IRC"
+
+#define KEYFILE    "/otr/otr.key"
+#define TMPKEYFILE "/otr/otr.key.tmp"
+#define FPSFILE    "/otr/otr.fp"
+#define INSTAGFILE "/otr/otr.instag"
+
+/* some defaults */
+#define IO_DEFAULT_POLICY \
+	"*@localhost opportunistic,*bitlbee* opportunistic,*@im.* opportunistic, *serv at irc* never"
+#define IO_DEFAULT_POLICY_KNOWN "* always"
+#define IO_DEFAULT_IGNORE "xmlconsole[0-9]*"
+
+/* used as a prefix for /me messages.
+ * This makes it readable and sensible for
+ * people not on IRC (i.e. in case of a gateway
+ * like bitlbee)
+ */
+#define IRCACTIONMARK "/me "
+#define IRCACTIONMARKLEN 4
+
+/* one for each OTR context (=communication pair) */
+struct co_info {
+	char *msgqueue;                 /* holds partially reconstructed base64
+	                                   messages */
+	IRC_CTX *ircctx;                /* irssi server object for this peer */
+	int received_smp_init;          /* received SMP init msg */
+	int smp_failed;                 /* last SMP failed */
+	char better_msg_two[256];       /* what the second line of the "better"
+	                                   default query msg should like. Eat it
+	                                   up when it comes in */
+	int finished;                   /* true after you've /otr finished */
+};
+
+/* these are returned by /otr contexts */
+
+struct fplist_ {
+	char *fp;
+	enum { NOAUTH, AUTHSMP, AUTHMAN } authby;
+	struct fplist_ *next;
+};
+
+struct ctxlist_ {
+	char *username;
+	char *accountname;
+	enum { STUNENCRYPTED, STENCRYPTED, STFINISHED, STUNKNOWN } state;
+	struct fplist_ *fplist;
+	struct ctxlist_ *next;
+};
+
+/* returned by otr_getstatus */
+enum {
+	IO_ST_PLAINTEXT,
+	IO_ST_FINISHED,
+	IO_ST_SMP_INCOMING,
+	IO_ST_SMP_OUTGOING,
+	IO_ST_SMP_FINALIZE,
+	IO_ST_UNKNOWN,
+	IO_ST_UNTRUSTED=32,
+	IO_ST_TRUST_MANUAL=64,
+	IO_ST_TRUST_SMP=128,
+	IO_ST_SMP_ONGOING=
+		IO_ST_SMP_INCOMING | IO_ST_SMP_OUTGOING | IO_ST_SMP_FINALIZE
+};
+
+/* given to otr_status_change */
+enum {
+	IO_STC_FINISHED,
+	IO_STC_TRUST_MANUAL,
+	IO_STC_TRUST_SMP,
+	IO_STC_SMP_ABORT,
+	IO_STC_SMP_STARTED,
+	IO_STC_SMP_RESPONDED,
+	IO_STC_SMP_INCOMING,
+	IO_STC_SMP_FINALIZE,
+	IO_STC_SMP_ABORTED,
+	IO_STC_PEER_FINISHED,
+	IO_STC_SMP_FAILED,
+	IO_STC_SMP_SUCCESS,
+	IO_STC_GONE_SECURE,
+	IO_STC_GONE_INSECURE,
+	IO_STC_CTX_UPDATE
+};
+
+/* libotr4 handle_msg_event */
+extern char *otr_msg_event_txt[];
+
+/* the above as text for scripting */
+extern char *otr_status_txt[];
+
+/* policy list generated from /set otr_policy */
+
+struct plistentry {
+	GPatternSpec *namepat;
+	OtrlPolicy policy;
+};
+
+/* used by the logging functions below */
+extern int debug;
+
+void irc_send_message(IRC_CTX *ircctx, const char *recipient, char *msg);
+void otr_status_change(IRC_CTX *ircctx, const char *nick, int event);
+IRC_CTX *ircctx_by_peername(const char *peername, char *nick);
+
+/* init stuff */
+
+int otrlib_init();
+void otrlib_deinit();
+void otr_initops();
+void otr_setpolicies(IOUSTATE *ioustate, const char *policies, int known);
+IOUSTATE *otr_init_user(char *user);
+void otr_deinit_user(IOUSTATE *ioustate);
+
+/* basic send/receive/status stuff */
+
+char *otr_send(IRC_CTX *server, const char *msg, const char *to);
+char *otr_receive(IRC_CTX *server, const char *msg, const char *from);
+int otr_getstatus(IRC_CTX *ircctx, const char *nick);
+ConnContext *otr_getcontext(const char *accname, const char *nick, int create,
+			    IRC_CTX *ircctx);
+
+/* user interaction */
+
+void otr_trust(IRC_CTX *server, char *nick, const char *peername);
+void otr_finish(IRC_CTX *server, char *nick, const char *peername,
+		int inquery);
+void otr_auth(IRC_CTX *server, char *nick, const char *peername,
+	      const char *question, const char *secret);
+void otr_authabort(IRC_CTX *server, char *nick, const char *peername);
+void otr_abort_auth(ConnContext *co, IRC_CTX *ircctx, const char *nick);
+struct ctxlist_ *otr_contexts(IOUSTATE *ioustate);
+void otr_finishall(IOUSTATE *ioustate);
+
+
+/* key/fingerprint stuff */
+
+void keygen_run(IOUSTATE *ioustate, const char *accname);
+void keygen_abort(IOUSTATE *ioustate, int ignoreidle);
+void key_load(IOUSTATE *ioustate);
+void fps_load(IOUSTATE *ioustate);
+void otr_writefps(IOUSTATE *ioustate);
+
+#ifndef LIBOTR3
+/* instance tags */
+void instag_load(IOUSTATE *ioustate);
+void otr_writeinstags(IOUSTATE *ioustate);
+#endif
+
+int extract_nick(char *nick, char *line);
+
+struct _cmds {
+	char *name;
+	void (*cmdfunc)(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc,
+			char *argv[], char *argv_eol[], char *target);
+};
+
+/* see io_util.c */
+#define CMDCOUNT 9
+extern struct _cmds cmds[];
+
+int cmd_generic(IOUSTATE *ioustate, IRC_CTX *ircctx, int argc, char *argv[],
+		char *argv_eol[],
+		char *target);
+int otr_getstatus_format(IRC_CTX *ircctx, const char *nick);
+
+void io_explode_args(const char *args, char ***argvp, char ***argv_eolp,
+		     int *argcp);
diff --git a/src/otr_key.c b/src/otr_key.c
new file mode 100644
index 0000000..1082bc5
--- /dev/null
+++ b/src/otr_key.c
@@ -0,0 +1,364 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#define _GNU_SOURCE
+
+#include "otr.h"
+
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <signal.h>
+
+typedef enum { KEYGEN_NO, KEYGEN_RUNNING } keygen_status_t;
+
+struct {
+	keygen_status_t status;
+	char *accountname;
+	char *protocol;
+	time_t started;
+	GIOChannel *ch[2];
+	guint cpid;
+	guint cwid;
+	pid_t pid;
+	IOUSTATE *ioustate;
+} kg_st = { .status = KEYGEN_NO };
+
+void keygen_childwatch(GPid pid, gint status, gpointer data)
+{
+	struct pollfd pfd = {
+		.fd = g_io_channel_unix_get_fd(kg_st.ch[0]),
+		.events = POLLIN
+	};
+	int ret;
+
+	/* nothing to do if keygen_complete has already been called */
+	if (data)
+		return;
+
+	kg_st.pid = 0;
+
+	ret = poll(&pfd, 1, 0);
+
+	/* data is there, let's wait for keygen_complete to be called */
+	if (ret == 1)
+		return;
+
+	/* no data, report error and reset kg_st */
+	if (ret == 0) {
+		if (WIFSIGNALED(status)) {
+			char sigstr[16];
+
+			sprintf(sigstr,
+#ifndef HAVE_STRSIGNAL
+				"%d", WTERMSIG(status));
+#else
+				"%s", strsignal(WTERMSIG(status)));
+#endif
+			otr_noticest(TXT_KG_EXITSIG,
+				     kg_st.accountname,
+				     sigstr);
+		}else
+			otr_noticest(TXT_KG_EXITED, kg_st.accountname);
+	} else if (ret == -1)
+		otr_noticest(TXT_KG_POLLERR, kg_st.accountname,
+			     strerror(errno));
+
+	keygen_abort(kg_st.ioustate, FALSE);
+}
+
+/*
+ * Installed as g_io_watch and called when the key generation
+ * process finishs.
+ */
+gboolean keygen_complete(GIOChannel *source, GIOCondition condition,
+			 gpointer data)
+{
+	gcry_error_t err;
+	const char *clconfdir = get_client_config_dir();
+	char *filename = g_strconcat(clconfdir, KEYFILE, NULL);
+	char *tmpfilename = g_strconcat(clconfdir, TMPKEYFILE, NULL);
+
+	read(g_io_channel_unix_get_fd(kg_st.ch[0]), &err, sizeof(err));
+
+	g_source_remove(kg_st.cpid);
+	g_io_channel_shutdown(kg_st.ch[0], FALSE, NULL);
+	g_io_channel_shutdown(kg_st.ch[1], FALSE, NULL);
+	g_io_channel_unref(kg_st.ch[0]);
+	g_io_channel_unref(kg_st.ch[1]);
+
+	if (err)
+		otr_noticest(TXT_KG_FAILED,
+			     kg_st.accountname,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	else {
+		/* reload keys */
+		otr_noticest(TXT_KG_COMPLETED,
+			     kg_st.accountname,
+			     time(NULL) - kg_st.started);
+		rename(tmpfilename, filename);
+		//otrl_privkey_forget_all(otr_state); <-- done by lib
+		key_load(kg_st.ioustate);
+	}
+
+	g_source_remove(kg_st.cwid);
+	kg_st.cwid = g_child_watch_add(kg_st.pid, keygen_childwatch, (void*)1);
+
+	kg_st.status = KEYGEN_NO;
+	g_free(kg_st.accountname);
+
+	g_free(filename);
+	g_free(tmpfilename);
+
+	return FALSE;
+}
+
+/*
+ * Run key generation in a seperate process (takes ages).
+ * The other process will rewrite the key file, we shouldn't
+ * change anything till it's done and we've reloaded the keys.
+ */
+void keygen_run(IOUSTATE *ioustate, const char *accname)
+{
+	gcry_error_t err;
+	int ret;
+	int fds[2];
+	char *filename = g_strconcat(
+		get_client_config_dir(), TMPKEYFILE,
+		NULL), *filenamedup = g_strdup(
+		filename);
+	char *dir = dirname(filenamedup);
+
+	if (kg_st.status != KEYGEN_NO) {
+		if (strcmp(accname, kg_st.accountname) != 0)
+			otr_noticest(TXT_KG_ABORTED_DUP,
+				     accname, kg_st.accountname);
+		return;
+	}
+
+	if (!g_file_test(dir, G_FILE_TEST_EXISTS)) {
+		if (g_mkdir(dir, S_IRWXU)) {
+			otr_noticest(TXT_KG_ABORTED_DIR,
+				     accname, dir, strerror(errno));
+			g_free(dir);
+			g_free(filename);
+			return;
+		} else
+			otr_noticest(TXT_KG_MKDIR, dir);
+	}
+	g_free(filenamedup);
+
+	if (pipe(fds) != 0) {
+		otr_noticest(TXT_KG_PIPE,
+			     accname, strerror(errno));
+		g_free(filename);
+		return;
+	}
+
+	kg_st.ch[0] = g_io_channel_unix_new(fds[0]);
+	kg_st.ch[1] = g_io_channel_unix_new(fds[1]);
+
+	kg_st.accountname = g_strdup(accname);
+	kg_st.ioustate = ioustate;
+	kg_st.protocol = PROTOCOLID;
+	kg_st.started = time(NULL);
+
+	if ((ret = fork())) {
+		g_free(filename);
+		if (ret == -1) {
+			otr_noticest(TXT_KG_FORK,
+				     accname, strerror(errno));
+			return;
+		}
+
+		kg_st.status = KEYGEN_RUNNING;
+		kg_st.pid = ret;
+
+		otr_noticest(TXT_KG_INITIATED,
+			     accname);
+
+		kg_st.cpid = g_io_add_watch(kg_st.ch[0], G_IO_IN,
+					    (GIOFunc)keygen_complete, NULL);
+		kg_st.cwid = g_child_watch_add(kg_st.pid, keygen_childwatch,
+					       NULL);
+
+		kg_st.started = time(NULL);
+		return;
+	}
+
+	/* child */
+
+	err = otrl_privkey_generate(ioustate->otr_state, filename, accname,
+				    PROTOCOLID);
+	write(fds[1], &err, sizeof(err));
+
+	g_free(filename);
+	_exit(0);
+}
+
+/*
+ * Abort ongoing key generation.
+ */
+void keygen_abort(IOUSTATE *ioustate, int ignoreidle)
+{
+	if (kg_st.status != KEYGEN_RUNNING) {
+		if (!ignoreidle)
+			otr_noticest(TXT_KG_NOABORT);
+		return;
+	}
+
+	otr_noticest(TXT_KG_ABORT, kg_st.accountname);
+
+	g_source_remove(kg_st.cpid);
+	g_source_remove(kg_st.cwid);
+	g_free(kg_st.accountname);
+
+	if (kg_st.pid != 0) {
+		kill(kg_st.pid, SIGTERM);
+		g_child_watch_add(kg_st.pid, keygen_childwatch, (void*)1);
+	}
+
+	kg_st.status = KEYGEN_NO;
+}
+
+/*
+ * Write fingerprints to file.
+ */
+void otr_writefps(IOUSTATE *ioustate)
+{
+	gcry_error_t err;
+	char *filename = g_strconcat(get_client_config_dir(), FPSFILE, NULL);
+
+	err = otrl_privkey_write_fingerprints(ioustate->otr_state, filename);
+
+	if (err == GPG_ERR_NO_ERROR) {
+		otr_noticest(TXT_FP_SAVED);
+	} else {
+		otr_noticest(TXT_FP_SAVE_ERROR,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	}
+	g_free(filename);
+}
+
+#ifndef LIBOTR3
+/*
+ * Write instance tags to file.
+ */
+void otr_writeinstags(IOUSTATE *ioustate)
+{
+	gcry_error_t err;
+	char *filename = g_strconcat(
+		get_client_config_dir(), INSTAGFILE, NULL);
+
+	err = otrl_instag_write(ioustate->otr_state, filename);
+
+	if (err == GPG_ERR_NO_ERROR) {
+		otr_noticest(TXT_INSTAG_SAVED);
+	} else {
+		otr_noticest(TXT_INSTAG_SAVE_ERROR,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	}
+	g_free(filename);
+}
+#endif
+
+/*
+ * Load private keys.
+ */
+void key_load(IOUSTATE *ioustate)
+{
+	gcry_error_t err;
+	char *filename = g_strconcat(get_client_config_dir(), KEYFILE, NULL);
+
+	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+		otr_noticest(TXT_KEY_NOT_FOUND);
+		return;
+	}
+
+	err = otrl_privkey_read(ioustate->otr_state, filename);
+
+	if (err == GPG_ERR_NO_ERROR) {
+		otr_noticest(TXT_KEY_LOADED);
+	} else {
+		otr_noticest(TXT_KEY_LOAD_ERROR,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	}
+	g_free(filename);
+}
+
+/*
+ * Load fingerprints.
+ */
+void fps_load(IOUSTATE *ioustate)
+{
+	gcry_error_t err;
+	char *filename = g_strconcat(get_client_config_dir(), FPSFILE, NULL);
+
+	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+		otr_noticest(TXT_FP_NOT_FOUND);
+		return;
+	}
+
+	err =
+		otrl_privkey_read_fingerprints(ioustate->otr_state, filename,
+					       NULL,
+					       NULL);
+
+	if (err == GPG_ERR_NO_ERROR) {
+		otr_noticest(TXT_FP_LOADED);
+	} else {
+		otr_noticest(TXT_FP_LOAD_ERROR,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	}
+	g_free(filename);
+}
+
+#ifndef LIBOTR3
+/*
+ * Load instance tags.
+ */
+void instag_load(IOUSTATE *ioustate)
+{
+	gcry_error_t err;
+	char *filename = g_strconcat(
+		get_client_config_dir(), INSTAGFILE, NULL);
+
+	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+		otr_noticest(TXT_INSTAG_NOT_FOUND);
+		return;
+	}
+
+	err = otrl_instag_read(ioustate->otr_state, filename);
+
+	if (err == GPG_ERR_NO_ERROR) {
+		otr_noticest(TXT_INSTAG_LOADED);
+	} else {
+		otr_noticest(TXT_INSTAG_LOAD_ERROR,
+			     gcry_strerror(err),
+			     gcry_strsource(err));
+	}
+	g_free(filename);
+}
+#endif
diff --git a/src/otr_ops.c b/src/otr_ops.c
new file mode 100644
index 0000000..6b0bdb6
--- /dev/null
+++ b/src/otr_ops.c
@@ -0,0 +1,398 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "otr.h"
+
+OtrlMessageAppOps otr_ops;
+
+OtrlPolicy IO_DEFAULT_OTR_POLICY =
+	OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE;
+
+/*
+ * Return policy for given context based on the otr_policy /setting
+ */
+OtrlPolicy ops_policy(void *opdata, ConnContext *context)
+{
+	struct co_info *coi = context->app_data;
+	char *server = strchr(context->accountname, '@') + 1;
+	OtrlPolicy op = IO_DEFAULT_OTR_POLICY;
+	GSList *pl;
+	char fullname[1024];
+	IOUSTATE *ioustate = IRCCTX_IO_US(coi->ircctx);
+
+	sprintf(fullname, "%s@%s", context->username, server);
+
+	/* loop through otr_policy */
+
+	if (ioustate->plistunknown) {
+		pl = ioustate->plistunknown;
+		do {
+			struct plistentry *ple = pl->data;
+
+			if (g_pattern_match_string(ple->namepat, fullname))
+				op = ple->policy;
+		} while ((pl = g_slist_next(pl)));
+	}
+
+	if (ioustate->plistknown && context->fingerprint_root.next) {
+		pl = ioustate->plistknown;
+
+		/* loop through otr_policy_known */
+
+		do {
+			struct plistentry *ple = pl->data;
+
+			if (g_pattern_match_string(ple->namepat, fullname))
+				op = ple->policy;
+		} while ((pl = g_slist_next(pl)));
+	}
+
+	if (coi && coi->finished &&
+	    (op == OTRL_POLICY_OPPORTUNISTIC ||
+	     op == OTRL_POLICY_ALWAYS))
+		op = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE;
+	return op;
+}
+
+/*
+ * Request for key generation.
+ * The lib actually expects us to be finished before the call returns.
+ * Since this can take more than an hour on some systems there isn't even
+ * a point in trying...
+ */
+void ops_create_privkey(void *opdata, const char *accountname,
+			const char *protocol)
+{
+	IRC_CTX *ircctx __attribute__((unused)) = opdata;
+
+	keygen_run(IRCCTX_IO_US(ircctx), accountname);
+}
+
+/*
+ * Inject OTR message.
+ * Deriving the server is currently a hack,
+ * need to derive the server from accountname.
+ */
+void ops_inject_msg(void *opdata, const char *accountname,
+		    const char *protocol, const char *recipient,
+		    const char *message)
+{
+	IRC_CTX *a_serv;
+	char *msgcopy = g_strdup(message);
+
+	/* OTR sometimes gives us multiple lines
+	 * (e.g. the default query (a.k.a. "better") message) */
+	g_strdelimit(msgcopy, "\n", ' ');
+	a_serv = opdata;
+	if (!a_serv) {
+		char nick[256];
+		a_serv = ircctx_by_peername(accountname, nick);
+	}
+	if (!a_serv) {
+		otr_notice(a_serv, recipient, TXT_OPS_INJECT,
+			   accountname, recipient, message);
+	} else {
+		otr_logst(MSGLEVEL_CRAP, "%d: INJECT %s", time(NULL), msgcopy);
+		irc_send_message(a_serv, recipient, msgcopy);
+	}
+	g_free(msgcopy);
+}
+
+/*
+ * OTR notification. Haven't seen one yet.
+ */
+void ops_notify(void *opdata, OtrlNotifyLevel level, const char *accountname,
+		const char *protocol, const char *username,
+		const char *title, const char *primary,
+		const char *secondary)
+{
+	ConnContext *co = otr_getcontext(accountname, username, FALSE, NULL);
+	IRC_CTX *server = opdata;
+	struct co_info *coi;
+	if (co) {
+		coi = co->app_data;
+		server = coi->ircctx;
+	} else
+		otr_notice(server, username, TXT_OPS_NOTIFY_BUG);
+
+	otr_notice(server, username, TXT_OPS_NOTIFY,
+		   title, primary, secondary);
+}
+
+#ifdef HAVE_GREGEX_H
+
+/* This is kind of messy. */
+const char *convert_otr_msg(const char *msg)
+{
+	GRegex *regex_bold = g_regex_new("</?i([ /][^>]*)?>", 0, 0, NULL);
+	GRegex *regex_del = g_regex_new("</?b([ /][^>]*)?>", 0, 0, NULL);
+	gchar *msgnohtml =
+		g_regex_replace_literal(regex_del, msg, -1, 0, "", 0, NULL);
+
+	msg = g_regex_replace_literal(regex_bold, msgnohtml, -1, 0, "*", 0,
+				      NULL);
+
+	g_free(msgnohtml);
+	g_regex_unref(regex_del);
+	g_regex_unref(regex_bold);
+
+	return msg;
+}
+
+#endif
+
+/*
+ * OTR message. E.g. "following has been transmitted in clear: ...".
+ * We're trying to kill the ugly HTML.
+ */
+int ops_display_msg(void *opdata, const char *accountname,
+		    const char *protocol, const char *username,
+		    const char *msg)
+{
+	ConnContext *co = otr_getcontext(accountname, username, FALSE, opdata);
+	IRC_CTX *server = opdata;
+	struct co_info *coi;
+
+	if (co) {
+		coi = co->app_data;
+		server = coi->ircctx;
+	} else
+		otr_notice(server, username, TXT_OPS_DISPLAY_BUG);
+
+#ifdef HAVE_GREGEX_H
+	msg = convert_otr_msg(msg);
+	otr_notice(server, username, TXT_OPS_DISPLAY, msg);
+	g_free((char*)msg);
+#else
+	otr_notice(server, username, TXT_OPS_DISPLAY, msg);
+#endif
+
+	return 0;
+}
+
+/*
+ * Gone secure.
+ */
+void ops_secure(void *opdata, ConnContext *context)
+{
+	struct co_info *coi = context->app_data;
+	char * trust = context->active_fingerprint->trust ? : "";
+	char ownfp[45], peerfp[45];
+
+	otr_notice(coi->ircctx,
+		   context->username, TXT_OPS_SEC);
+	otr_status_change(coi->ircctx, context->username, IO_STC_GONE_SECURE);
+
+	//TODO: pull master context
+	coi->finished = FALSE;
+
+	if (*trust != '\0')
+		return;
+
+	/* not authenticated.
+	 * Let's print out the fingerprints for comparison */
+
+	otrl_privkey_hash_to_human(peerfp,
+				   context->active_fingerprint->fingerprint);
+
+	otr_notice(coi->ircctx, context->username, TXT_OPS_FPCOMP,
+		   otrl_privkey_fingerprint(IRCCTX_IO_US(coi->ircctx)->
+					    otr_state,
+					    ownfp,
+					    context->accountname,
+					    PROTOCOLID),
+		   context->username,
+		   peerfp);
+}
+
+/*
+ * Gone insecure.
+ */
+void ops_insecure(void *opdata, ConnContext *context)
+{
+	struct co_info *coi = context->app_data;
+	otr_notice(coi->ircctx,
+		   context->username, TXT_OPS_INSEC);
+	otr_status_change(coi->ircctx, context->username,
+			  IO_STC_GONE_INSECURE);
+}
+
+/*
+ * Still secure? Need to find out what that means...
+ */
+void ops_still_secure(void *opdata, ConnContext *context, int is_reply)
+{
+	struct co_info *coi = context->app_data;
+	otr_notice(coi->ircctx,
+		   context->username, is_reply ?
+		   TXT_OPS_STILL_REPLY :
+		   TXT_OPS_STILL_NO_REPLY);
+}
+
+/*
+ * OTR log message. IIRC heartbeats are of this category.
+ */
+void ops_log(void *opdata, const char *message)
+{
+	otr_infost(TXT_OPS_LOG, message);
+}
+
+/*
+ * Really critical with IRC.
+ * Unfortunately, we can't tell our peer which size to use.
+ * (reminds me of MTU determination...)
+ */
+int ops_max_msg(void *opdata, ConnContext *context)
+{
+	return OTR_MAX_MSG_SIZE;
+}
+
+#ifndef LIBOTR3
+void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event,
+			  ConnContext *context, const char *message,
+			  gcry_error_t err)
+{
+	IRC_CTX *server = opdata;
+	char *username = context->username;
+
+	otr_debug(server,
+		  username,
+		  TXT_OPS_HANDLE_MSG,
+		  otr_msg_event_txt[msg_event], message);
+}
+#endif
+
+/*
+ * A context changed.
+ * I believe this is not happening for the SMP expects.
+ */
+void ops_up_ctx_list(void *opdata)
+{
+	otr_status_change(opdata, NULL, IO_STC_CTX_UPDATE);
+}
+
+/*
+ * Save fingerprint changes.
+ */
+void ops_writefps(void *data)
+{
+	IRC_CTX *ircctx __attribute__((unused)) = data;
+
+	otr_writefps(IRCCTX_IO_US(ircctx));
+}
+
+int ops_is_logged_in(void *opdata, const char *accountname,
+		     const char *protocol, const char *recipient)
+{
+	/*TODO register a handler for event 401 no such nick and set
+	 * a variable offline=TRUE. Reset it to false in otr_receive and
+	 * otr_send */
+	return TRUE;
+}
+
+#ifndef LIBOTR3
+void ops_create_instag(void *opdata, const char *accountname,
+		       const char *protocol)
+{
+	otrl_instag_generate(IRCCTX_IO_US(
+				     ircctx)->otr_state, "/dev/null",
+			     accountname, protocol);
+	otr_writeinstags(IRCCTX_IO_US(ircctx));
+}
+
+void ops_smp_event(void *opdata, OtrlSMPEvent smp_event,
+		   ConnContext *context, unsigned short progress_percent,
+		   char *question)
+{
+	IRC_CTX *ircctx = (opdata);
+	char *from = context->username;
+	struct co_info *coi = context->app_data;
+
+	coi->received_smp_init =
+		(smp_event == OTRL_SMPEVENT_ASK_FOR_SECRET) ||
+		(smp_event == OTRL_SMPEVENT_ASK_FOR_ANSWER);
+
+	switch (smp_event) {
+	case OTRL_SMPEVENT_ASK_FOR_SECRET:
+		otr_notice(ircctx, from, TXT_AUTH_PEER,
+			   from);
+		otr_status_change(ircctx, from, IO_STC_SMP_INCOMING);
+		break;
+	case OTRL_SMPEVENT_ASK_FOR_ANSWER:
+		otr_notice(ircctx, from, TXT_AUTH_PEER_QA, from, question);
+		otr_status_change(ircctx, from, IO_STC_SMP_INCOMING);
+		break;
+	case OTRL_SMPEVENT_IN_PROGRESS:
+		otr_notice(ircctx, from,
+			   TXT_AUTH_PEER_REPLIED,
+			   from);
+		otr_status_change(ircctx, from, IO_STC_SMP_FINALIZE);
+		break;
+	case OTRL_SMPEVENT_SUCCESS:
+		otr_notice(ircctx, from,
+			   TXT_AUTH_SUCCESSFUL);
+		otr_status_change(ircctx, from, IO_STC_SMP_SUCCESS);
+		break;
+	case OTRL_SMPEVENT_ABORT:
+		otr_abort_auth(context, ircctx, from);
+		otr_status_change(ircctx, from, IO_STC_SMP_ABORTED);
+		break;
+	case OTRL_SMPEVENT_FAILURE:
+	case OTRL_SMPEVENT_CHEATED:
+	case OTRL_SMPEVENT_ERROR:
+		otr_notice(ircctx, from, TXT_AUTH_FAILED);
+		coi->smp_failed = TRUE;
+		otr_status_change(ircctx, from, IO_STC_SMP_FAILED);
+		break;
+	default:
+		otr_logst(MSGLEVEL_CRAP, "Received unknown SMP event");
+		break;
+	}
+}
+
+#endif
+
+/*
+ * Initialize our OtrlMessageAppOps
+ */
+void otr_initops()
+{
+	memset(&otr_ops, 0, sizeof(otr_ops));
+
+	otr_ops.policy = ops_policy;
+	otr_ops.create_privkey = ops_create_privkey;
+	otr_ops.inject_message = ops_inject_msg;
+	otr_ops.gone_secure = ops_secure;
+	otr_ops.gone_insecure = ops_insecure;
+	otr_ops.still_secure = ops_still_secure;
+	otr_ops.max_message_size = ops_max_msg;
+	otr_ops.update_context_list = ops_up_ctx_list;
+	otr_ops.write_fingerprints = ops_writefps;
+	otr_ops.is_logged_in = ops_is_logged_in;
+
+#ifdef LIBOTR3
+	otr_ops.notify = ops_notify;
+	otr_ops.display_otr_message = ops_display_msg;
+	otr_ops.log_message = ops_log;
+#else
+	otr_ops.handle_msg_event = ops_handle_msg_event;
+	otr_ops.create_instag = ops_create_instag;
+	otr_ops.handle_smp_event = ops_smp_event;
+#endif
+}
diff --git a/src/otr_util.c b/src/otr_util.c
new file mode 100644
index 0000000..987ab6d
--- /dev/null
+++ b/src/otr_util.c
@@ -0,0 +1,915 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008  Uli Meis <a.sporto+bee at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "otr.h"
+
+#include <gcrypt.h>
+
+extern OtrlMessageAppOps otr_ops;
+static int otrinited = FALSE;
+
+#ifdef TARGET_BITLBEE
+GHashTable *ioustates;
+#else
+IOUSTATE ioustate_uniq = { 0, 0, 0 };
+#endif
+
+#ifdef HAVE_GREGEX_H
+GRegex *regex_policies;
+#endif
+
+IOUSTATE *otr_init_user(char *user)
+{
+	IOUSTATE *ioustate = IO_CREATE_US(user);
+
+	ioustate->otr_state = otrl_userstate_create();
+
+	/* load keys and fingerprints */
+
+#ifndef LIBOTR3
+	instag_load(ioustate);
+#endif
+	key_load(ioustate);
+	fps_load(ioustate);
+
+	return ioustate;
+}
+
+void otr_deinit_user(IOUSTATE *ioustate)
+{
+	keygen_abort(ioustate, TRUE);
+
+	if (ioustate->otr_state) {
+		otr_writefps(ioustate);
+		otrl_userstate_free(ioustate->otr_state);
+		ioustate->otr_state = NULL;
+	}
+	otr_setpolicies(ioustate, "", FALSE);
+	otr_setpolicies(ioustate, "", TRUE);
+}
+
+/*
+ * init otr lib.
+ */
+int otrlib_init()
+{
+	if (!otrinited) {
+		/* apparently used in pidgin-otr to
+		 * force gcrypt to /dev/urandom */
+		/*
+		   gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+		   gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+		 */
+		OTRL_INIT;
+		otrinited = TRUE;
+	}
+
+#ifdef TARGET_BITLBEE
+	ioustates = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+					  otr_deinit_user);
+#endif
+
+	otr_initops();
+
+#ifdef HAVE_GREGEX_H
+	regex_policies =
+		g_regex_new(
+			"([^,]+) (never|manual|handlews|opportunistic|always)"
+			"(,|$)", 0, 0, NULL);
+#endif
+
+	return 0;
+}
+
+/*
+ * deinit otr lib.
+ */
+void otrlib_deinit()
+{
+#ifdef HAVE_GREGEX_H
+	g_regex_unref(regex_policies);
+#endif
+
+#ifdef TARGET_BITLBEE
+	g_hash_table_destroy(ioustates);
+#endif
+}
+
+
+/*
+ * Free our app data.
+ */
+void context_free_app_info(void *data)
+{
+	struct co_info *coi = data;
+	if (coi->msgqueue) {
+		g_free(coi->msgqueue);
+	}
+	if (coi->ircctx)
+		IRCCTX_FREE(coi->ircctx);
+}
+
+/*
+ * Add app data to context.
+ * See struct co_info for details.
+ */
+void context_add_app_info(void *data, ConnContext *co)
+{
+	IRC_CTX *ircctx = IRCCTX_DUP(data);
+	struct co_info *coi = g_malloc(sizeof(struct co_info));
+
+	memset(coi, 0, sizeof(struct co_info));
+	co->app_data = coi;
+	co->app_data_free = context_free_app_info;
+
+	coi->ircctx = ircctx;
+	sprintf(coi->better_msg_two, formats[TXT_OTR_BETTER_TWO].def,
+		co->accountname);
+}
+
+/*
+ * Get a context from a pair.
+ */
+ConnContext *otr_getcontext(const char *accname, const char *nick,
+			    int create, IRC_CTX *ircctx)
+{
+	ConnContext *co = otrl_context_find(
+		IRCCTX_IO_US(ircctx)->otr_state,
+		nick,
+		accname,
+		PROTOCOLID,
+#ifndef LIBOTR3
+		OTRL_INSTAG_BEST,
+#endif
+		create,
+		NULL,
+		context_add_app_info,
+		ircctx);
+
+	/* context came from a fingerprint */
+	if (co && ircctx && !co->app_data)
+		context_add_app_info(ircctx, co);
+
+	return co;
+}
+
+/*
+ * Hand the given message to OTR.
+ * Returns NULL if OTR handled the message and
+ * the original message otherwise.
+ */
+char *otr_send(IRC_CTX *ircctx, const char *msg, const char *to)
+{
+	gcry_error_t err;
+	char *newmessage = NULL;
+	ConnContext *co;
+	char accname[256];
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	otr_logst(MSGLEVEL_CRAP, "%d: send...", time(NULL));
+	err = otrl_message_sending(
+		IRCCTX_IO_US(ircctx)->otr_state,
+		&otr_ops,
+		ircctx,
+		accname,
+		PROTOCOLID,
+		to,
+#ifndef LIBOTR3
+		OTRL_INSTAG_BEST,
+#endif
+		msg,
+		NULL,
+		&newmessage,
+#ifndef LIBOTR3
+		OTRL_FRAGMENT_SEND_ALL,
+		&co,
+#endif
+		context_add_app_info,
+		ircctx);
+	otr_logst(MSGLEVEL_CRAP, "%d: sent", time(NULL));
+
+	if (err != 0) {
+		otr_notice(ircctx, to, TXT_SEND_FAILED, msg);
+		return NULL;
+	}
+
+	if (newmessage == NULL)
+		return (char*)msg;
+
+	/* OTR message. Need to do fragmentation */
+
+#ifdef LIBOTR3
+	if (!(co = otr_getcontext(accname, to, FALSE, ircctx))) {
+		otr_notice(ircctx, to, TXT_SEND_CHANGE);
+		return NULL;
+	}
+
+	err = otrl_message_fragment_and_send(
+		&otr_ops,
+		ircctx,
+		co,
+		newmessage,
+		OTRL_FRAGMENT_SEND_ALL,
+		NULL);
+
+	if (err != 0) {
+		otr_notice(ircctx, to, TXT_SEND_FRAGMENT, msg);
+	} else
+		otr_debug(ircctx, to, TXT_SEND_CONVERTED, newmessage);
+#endif
+
+	return NULL;
+}
+
+struct ctxlist_ *otr_contexts(IOUSTATE *ioustate)
+{
+	ConnContext *context;
+	Fingerprint *fprint;
+	struct ctxlist_ *ctxlist = NULL, *ctxhead = NULL;
+	struct fplist_ *fplist, *fphead;
+	char fp[41];
+	char *trust;
+	int i;
+
+	for (context = ioustate->otr_state->context_root; context;
+	     context = context->next) {
+		if (!ctxlist)
+			ctxhead = ctxlist = g_malloc0(sizeof(struct ctxlist_));
+		else
+			ctxlist = ctxlist->next = g_malloc0(sizeof(struct
+								   ctxlist_));
+		switch (context->msgstate) {
+		case OTRL_MSGSTATE_PLAINTEXT: ctxlist->state = STUNENCRYPTED;
+			break;
+		case OTRL_MSGSTATE_ENCRYPTED: ctxlist->state = STENCRYPTED;
+			break;
+		case OTRL_MSGSTATE_FINISHED: ctxlist->state = STFINISHED;
+			break;
+		default: ctxlist->state = STUNKNOWN; break;
+		}
+		ctxlist->username = context->username;
+		ctxlist->accountname = context->accountname;
+
+		fplist = fphead = NULL;
+		for (fprint = context->fingerprint_root.next; fprint;
+		     fprint = fprint->next) {
+			if (!fplist)
+				fphead = fplist = g_malloc0(sizeof(struct
+								   fplist_));
+			else
+				fplist = fplist->next =
+						 g_malloc0(sizeof(struct
+								  fplist_));
+			trust = fprint->trust ? : "";
+			for (i = 0; i < 20; ++i)
+				sprintf(fp + i * 2, "%02x",
+					fprint->fingerprint[i]);
+			fplist->fp = g_strdup(fp);
+			if (*trust == '\0')
+				fplist->authby = NOAUTH;
+			else if (strcmp(trust, "smp") == 0)
+				fplist->authby = AUTHSMP;
+			else
+				fplist->authby = AUTHMAN;
+		}
+
+		ctxlist->fplist = fphead;
+	}
+	return ctxhead;
+}
+
+/*
+ * Get the OTR status of this conversation.
+ */
+int otr_getstatus(IRC_CTX *ircctx, const char *nick)
+{
+	ConnContext *co;
+	char accname[128];
+	struct co_info *coi;
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) {
+		return IO_ST_PLAINTEXT;
+	}
+
+	coi = co->app_data;
+
+	switch (co->msgstate) {
+	case OTRL_MSGSTATE_PLAINTEXT:
+		return IO_ST_PLAINTEXT;
+	case OTRL_MSGSTATE_ENCRYPTED: {
+		char *trust = co->active_fingerprint->trust;
+		int ex = co->smstate->nextExpected;
+		int code = 0;
+
+		switch (ex) {
+		case OTRL_SMP_EXPECT1:
+			if (coi->received_smp_init)
+				code = IO_ST_SMP_INCOMING;
+			break;
+		case OTRL_SMP_EXPECT2:
+			code = IO_ST_SMP_OUTGOING;
+			break;
+		case OTRL_SMP_EXPECT3:
+		case OTRL_SMP_EXPECT4:
+			code = IO_ST_SMP_FINALIZE;
+			break;
+		default:
+			otr_logst(
+				MSGLEVEL_CRAP,
+				"Encountered unknown SMP state in libotr, please let maintainers know");
+			return IO_ST_UNKNOWN;
+		}
+
+		if (trust && (*trust != '\0'))
+			code |= strcmp(trust, "smp") == 0 ? IO_ST_TRUST_SMP :
+				IO_ST_TRUST_MANUAL;
+		else
+			code |= IO_ST_UNTRUSTED;
+
+		return code;
+	}
+	case OTRL_MSGSTATE_FINISHED:
+		return IO_ST_FINISHED;
+	default:
+		otr_logst(
+			MSGLEVEL_CRAP,
+			"BUG Found! Please write us a mail and describe how you got here");
+		return IO_ST_UNKNOWN;
+	}
+}
+
+/*
+ * Finish the conversation.
+ */
+void otr_finish(IRC_CTX *ircctx, char *nick, const char *peername,
+		int inquery)
+{
+	ConnContext *co;
+	char accname[128];
+	struct co_info *coi;
+	char nickbuf[128];
+
+	if (peername) {
+		nick = nickbuf;
+		ircctx = ircctx_by_peername(peername, nick);
+		if (!ircctx)
+			return;
+	}
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) {
+		if (inquery)
+			otr_noticest(TXT_CTX_NOT_FOUND,
+				     accname, nick);
+		return;
+	}
+
+	otrl_message_disconnect(IRCCTX_IO_US(ircctx)->otr_state,
+				&otr_ops, ircctx, accname,
+				PROTOCOLID,
+#ifdef LIBOTR3
+				nick);
+#else
+				nick, co->their_instance);
+#endif
+
+	otr_status_change(ircctx, nick, IO_STC_FINISHED);
+
+	if (inquery) {
+		otr_info(ircctx, nick, TXT_CMD_FINISH, nick,
+			 IRCCTX_ADDR(ircctx));
+	} else {
+		otr_infost(TXT_CMD_FINISH, nick, IRCCTX_ADDR(ircctx));
+	}
+
+	coi = co->app_data;
+
+	/* finish if /otr finish has been issued. Reset if
+	 * we're called cause the query window has been closed. */
+	if (coi)
+		coi->finished = inquery;
+
+#ifndef LIBOTR3
+	// write the finished into the master as well
+	co = otrl_context_find(
+		IRCCTX_IO_US(ircctx)->otr_state,
+		nick,
+		accname,
+		PROTOCOLID,
+		OTRL_INSTAG_MASTER,
+		FALSE,
+		NULL,
+		NULL,
+		NULL);
+	coi = co->app_data;
+	if (coi)
+		coi->finished = inquery;
+#endif
+}
+
+void otr_finishall(IOUSTATE *ioustate)
+{
+	ConnContext *context;
+	int finished = 0;
+
+	for (context = ioustate->otr_state->context_root; context;
+	     context = context->next) {
+		struct co_info *coi = context->app_data;
+
+		if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED)
+			continue;
+
+		otrl_message_disconnect(ioustate->otr_state, &otr_ops,
+					coi->ircctx,
+					context->accountname,
+					PROTOCOLID,
+#ifdef LIBOTR3
+					context->username);
+#else
+					context->username,
+					context->their_instance);
+#endif
+		otr_status_change(coi->ircctx, context->username,
+				  IO_STC_FINISHED);
+
+		otr_infost(TXT_CMD_FINISH, context->username,
+			   IRCCTX_ADDR(coi->ircctx));
+		finished++;
+	}
+
+	if (!finished)
+		otr_infost(TXT_CMD_FINISHALL_NONE);
+}
+
+/*
+ * Trust our peer.
+ */
+void otr_trust(IRC_CTX *ircctx, char *nick, const char *peername)
+{
+	ConnContext *co;
+	char accname[128];
+	struct co_info *coi;
+	char nickbuf[128];
+
+	if (peername) {
+		nick = nickbuf;
+		ircctx = ircctx_by_peername(peername, nick);
+		if (!ircctx)
+			return;
+	}
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) {
+		otr_noticest(TXT_CTX_NOT_FOUND,
+			     accname, nick);
+		return;
+	}
+
+	otrl_context_set_trust(co->active_fingerprint, "manual");
+	otr_status_change(ircctx, nick, IO_STC_TRUST_MANUAL);
+
+	coi = co->app_data;
+	coi->smp_failed = FALSE;
+
+	otr_notice(ircctx, nick, TXT_FP_TRUST, nick);
+}
+
+/*
+ * Abort any ongoing SMP authentication.
+ */
+void otr_abort_auth(ConnContext *co, IRC_CTX *ircctx, const char *nick)
+{
+	struct co_info *coi;
+
+	coi = co->app_data;
+
+	coi->received_smp_init = FALSE;
+
+	otr_notice(ircctx, nick,
+		   co->smstate->nextExpected != OTRL_SMP_EXPECT1 ?
+		   TXT_AUTH_ABORTED_ONGOING :
+		   TXT_AUTH_ABORTED);
+
+	otrl_message_abort_smp(IRCCTX_IO_US(
+				       ircctx)->otr_state, &otr_ops, ircctx,
+			       co);
+	otr_status_change(ircctx, nick, IO_STC_SMP_ABORT);
+}
+
+/*
+ * implements /otr authabort
+ */
+void otr_authabort(IRC_CTX *ircctx, char *nick, const char *peername)
+{
+	ConnContext *co;
+	char accname[128];
+	char nickbuf[128];
+
+	if (peername) {
+		nick = nickbuf;
+		ircctx = ircctx_by_peername(peername, nick);
+		if (!ircctx)
+			return;
+	}
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) {
+		otr_noticest(TXT_CTX_NOT_FOUND,
+			     accname, nick);
+		return;
+	}
+
+	otr_abort_auth(co, ircctx, nick);
+}
+
+/*
+ * Initiate or respond to SMP authentication.
+ */
+void otr_auth(IRC_CTX *ircctx, char *nick, const char *peername,
+	      const char *question, const char *secret)
+{
+	ConnContext *co;
+	char accname[128];
+	struct co_info *coi;
+	char nickbuf[128];
+
+	if (peername) {
+		nick = nickbuf;
+		ircctx = ircctx_by_peername(peername, nick);
+		if (!ircctx)
+			return;
+	}
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, nick, FALSE, ircctx))) {
+		otr_noticest(TXT_CTX_NOT_FOUND,
+			     accname, nick);
+		return;
+	}
+
+	if (co->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
+		otr_notice(ircctx, nick, TXT_AUTH_NEEDENC);
+		return;
+	}
+
+	coi = co->app_data;
+
+	/* Aborting an ongoing auth */
+	if (co->smstate->nextExpected != OTRL_SMP_EXPECT1)
+		otr_abort_auth(co, ircctx, nick);
+
+	coi->smp_failed = FALSE;
+
+	/* reset trust level */
+	if (co->active_fingerprint) {
+		char *trust = co->active_fingerprint->trust;
+		if (trust && (*trust != '\0')) {
+			otrl_context_set_trust(co->active_fingerprint, "");
+			otr_writefps(IRCCTX_IO_US(ircctx));
+		}
+	}
+
+	if (!coi->received_smp_init) {
+#ifndef LIBOTR3
+		if (question)
+			otrl_message_initiate_smp_q(
+				IRCCTX_IO_US(ircctx)->otr_state,
+				&otr_ops,
+				ircctx,
+				co,
+				question,
+				(unsigned char*)secret,
+				strlen(secret));
+		else
+#endif
+		otrl_message_initiate_smp(
+			IRCCTX_IO_US(ircctx)->otr_state,
+			&otr_ops,
+			ircctx,
+			co,
+			(unsigned char*)secret,
+			strlen(secret));
+
+		otr_status_change(ircctx, nick, IO_STC_SMP_STARTED);
+	} else {
+		otrl_message_respond_smp(
+			IRCCTX_IO_US(ircctx)->otr_state,
+			&otr_ops,
+			ircctx,
+			co,
+			(unsigned char*)secret,
+			strlen(secret));
+		otr_status_change(ircctx, nick, IO_STC_SMP_RESPONDED);
+	}
+
+	otr_notice(ircctx, nick,
+		   coi->received_smp_init ?
+		   TXT_AUTH_RESPONDING :
+		   TXT_AUTH_INITIATED);
+}
+
+/*
+ * Handles incoming TLVs of the SMP authentication type. We're not only updating
+ * our own state but also giving libotr a leg up so it gets through the auth.
+ */
+void otr_handle_tlvs(OtrlTLV *tlvs, ConnContext *co,
+		     struct co_info *coi,
+		     IRC_CTX *ircctx, const char *from)
+{
+	int abort = FALSE;
+
+	OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+	if (tlv) {
+		if (co->smstate->nextExpected != OTRL_SMP_EXPECT1) {
+			otr_notice(ircctx, from, TXT_AUTH_HAVE_OLD,
+				   from);
+			abort = TRUE;
+		} else {
+			otr_notice(ircctx, from, TXT_AUTH_PEER,
+				   from);
+			coi->received_smp_init = TRUE;
+			otr_status_change(ircctx, from, IO_STC_SMP_INCOMING);
+		}
+	}
+
+	tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+	if (tlv) {
+		if (co->smstate->nextExpected != OTRL_SMP_EXPECT2) {
+			otr_notice(ircctx, from,
+				   TXT_AUTH_PEER_REPLY_WRONG,
+				   from);
+			abort = TRUE;
+		} else {
+			otr_notice(ircctx, from,
+				   TXT_AUTH_PEER_REPLIED,
+				   from);
+			co->smstate->nextExpected = OTRL_SMP_EXPECT4;
+			otr_status_change(ircctx, from, IO_STC_SMP_FINALIZE);
+		}
+	}
+
+	tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+	if (tlv) {
+		if (co->smstate->nextExpected != OTRL_SMP_EXPECT3) {
+			otr_notice(ircctx, from,
+				   TXT_AUTH_PEER_WRONG_SMP3,
+				   from);
+			abort = TRUE;
+		} else {
+			char *trust = co->active_fingerprint->trust;
+			if (trust && (*trust != '\0')) {
+				otr_notice(ircctx, from,
+					   TXT_AUTH_SUCCESSFUL);
+				otr_status_change(ircctx, from,
+						  IO_STC_SMP_SUCCESS);
+			} else {
+				otr_notice(ircctx, from,
+					   TXT_AUTH_FAILED);
+				coi->smp_failed = TRUE;
+				otr_status_change(ircctx, from,
+						  IO_STC_SMP_FAILED);
+			}
+			co->smstate->nextExpected = OTRL_SMP_EXPECT1;
+			coi->received_smp_init = FALSE;
+		}
+	}
+
+	tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+	if (tlv) {
+		if (co->smstate->nextExpected != OTRL_SMP_EXPECT4) {
+			otr_notice(ircctx, from,
+				   TXT_AUTH_PEER_WRONG_SMP4,
+				   from);
+			abort = TRUE;
+		} else {
+			char *trust = co->active_fingerprint->trust;
+			if (trust && (*trust != '\0')) {
+				otr_notice(ircctx, from,
+					   TXT_AUTH_SUCCESSFUL);
+				otr_status_change(ircctx, from,
+						  IO_STC_SMP_SUCCESS);
+			} else {
+				/* unreachable since 4 is never sent out on
+				 * error */
+				otr_notice(ircctx, from,
+					   TXT_AUTH_FAILED);
+				coi->smp_failed = TRUE;
+				otr_status_change(ircctx, from,
+						  IO_STC_SMP_FAILED);
+			}
+			co->smstate->nextExpected = OTRL_SMP_EXPECT1;
+			coi->received_smp_init = FALSE;
+		}
+	}
+	if (abort) {
+		otr_abort_auth(co, ircctx, from);
+		otr_status_change(ircctx, from, IO_STC_SMP_ABORTED);
+	}
+
+	tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+	if (tlv) {
+		otr_status_change(ircctx, from, IO_STC_PEER_FINISHED);
+		otr_notice(ircctx, from, TXT_PEER_FINISHED, from);
+	}
+}
+
+/*
+ * Hand the given message to OTR.
+ * Returns NULL if its an OTR protocol message and
+ * the (possibly) decrypted message otherwise.
+ */
+char *otr_receive(IRC_CTX *ircctx, const char *msg, const char *from)
+{
+	int ignore_message;
+	char *newmessage = NULL;
+	char accname[256];
+	char *lastmsg;
+	ConnContext *co;
+	struct co_info *coi;
+	OtrlTLV *tlvs;
+
+	IRCCTX_ACCNAME(accname, ircctx);
+
+	if (!(co = otr_getcontext(accname, from, TRUE, ircctx))) {
+		otr_noticest(TXT_CTX_NOT_CREATE,
+			     accname, from);
+		return NULL;
+	}
+
+	coi = co->app_data;
+
+	/* Really lame but I don't see how you could do this in a generic
+	 * way unless the IRC server would somehow mark continuation messages.
+	 */
+	if ((strcmp(msg, coi->better_msg_two) == 0) ||
+	    (strcmp(msg, formats[TXT_OTR_BETTER_THREE].def) == 0)) {
+		otr_debug(ircctx, from, TXT_RECEIVE_IGNORE_QUERY);
+		return NULL;
+	}
+
+	/* The server might have split lines that were too long
+	 * (bitlbee does that). The heuristic is simple: If we can find ?OTR:
+	 * in the message but it doesn't end with a ".", queue it and wait
+	 * for the rest.
+	 */
+	lastmsg = co->app_data;
+
+	if (coi->msgqueue) { /* already something in the queue */
+		strcpy(coi->msgqueue + strlen(coi->msgqueue), msg);
+
+		/* wait for more? */
+		if ((strlen(msg) > OTR_MAX_MSG_SIZE) &&
+		    (msg[strlen(msg) - 1] != '.') &&
+		    (msg[strlen(msg) - 1] != ','))
+			return NULL;
+
+		otr_debug(ircctx, from, TXT_RECEIVE_DEQUEUED,
+			  strlen(coi->msgqueue));
+
+		msg = coi->msgqueue;
+		coi->msgqueue = NULL;
+
+		/* this is freed thru our caller by otrl_message_free.
+		 * Currently ok since that just uses free().
+		 */
+	} else if (strstr(msg, "?OTR:") &&
+		   (strlen(msg) > OTR_MAX_MSG_SIZE) &&
+		   (msg[strlen(msg) - 1] != '.') &&
+		   (msg[strlen(msg) - 1] != ',')) {
+		coi->msgqueue = malloc(4096 * sizeof(char));
+		strcpy(coi->msgqueue, msg);
+		otr_debug(ircctx, from, TXT_RECEIVE_QUEUED, strlen(msg));
+		return NULL;
+	}
+
+	otr_logst(MSGLEVEL_CRAP, "%d: receive...", time(NULL));
+	ignore_message = otrl_message_receiving(
+		IRCCTX_IO_US(ircctx)->otr_state,
+		&otr_ops,
+		ircctx,
+		accname,
+		PROTOCOLID,
+		from,
+		msg,
+		&newmessage,
+		&tlvs,
+#ifdef LIBOTR3
+		NULL,
+		NULL);
+#else
+		&co,
+		context_add_app_info,
+		ircctx);
+#endif
+	otr_logst(MSGLEVEL_CRAP, "%d: received", time(NULL));
+
+	if (tlvs) {
+#ifdef LIBOTR3
+		otr_handle_tlvs(tlvs, co, coi, ircctx, from);
+#else
+		OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+		if (tlv) {
+			otr_status_change(ircctx, from, IO_STC_PEER_FINISHED);
+			otr_notice(ircctx, from, TXT_PEER_FINISHED, from);
+		}
+#endif
+	}
+
+	if (ignore_message) {
+		otr_debug(ircctx, from,
+			  TXT_RECEIVE_IGNORE, strlen(msg), accname, from, msg);
+		return NULL;
+	}
+
+	if (newmessage)
+		otr_debug(ircctx, from, TXT_RECEIVE_CONVERTED);
+
+	return newmessage ? : (char*)msg;
+}
+
+void otr_setpolicies(IOUSTATE *ioustate, const char *policies, int known)
+{
+#ifdef HAVE_GREGEX_H
+	GMatchInfo *match_info;
+	GSList *plist = known ? ioustate->plistknown : ioustate->plistunknown;
+
+	if (plist) {
+		GSList *p = plist;
+		do {
+			struct plistentry *ple = p->data;
+			g_pattern_spec_free(ple->namepat);
+			g_free(p->data);
+		} while ((p = g_slist_next(p)));
+
+		g_slist_free(plist);
+		plist = NULL;
+	}
+
+	g_regex_match(regex_policies, policies, 0, &match_info);
+
+	while (g_match_info_matches(match_info)) {
+		struct plistentry *ple =
+			(struct plistentry*)g_malloc0(sizeof(struct
+							     plistentry));
+		char *pol = g_match_info_fetch(match_info, 2);
+
+		ple->namepat =
+			g_pattern_spec_new(g_match_info_fetch(match_info, 1));
+
+		switch (*pol) {
+		case 'n':
+			ple->policy = OTRL_POLICY_NEVER;
+			break;
+		case 'm':
+			ple->policy = OTRL_POLICY_MANUAL;
+			break;
+		case 'h':
+			ple->policy = OTRL_POLICY_MANUAL |
+				      OTRL_POLICY_WHITESPACE_START_AKE;
+			break;
+		case 'o':
+			ple->policy = OTRL_POLICY_OPPORTUNISTIC;
+			break;
+		case 'a':
+			ple->policy = OTRL_POLICY_ALWAYS;
+			break;
+		}
+
+		plist = g_slist_append(plist, ple);
+
+		g_free(pol);
+
+		g_match_info_next(match_info, NULL);
+	}
+
+	g_match_info_free(match_info);
+
+	if (known)
+		ioustate->plistknown = plist;
+	else
+		ioustate->plistunknown = plist;
+#endif
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/irssi-plugin-otr.git



More information about the Pkg-privacy-commits mailing list