changeset 1299:3b338a5c01fc

OTR support
author Frank Zschockelt <mcabber_otr[at]freakysoft.de>
date Thu, 30 Aug 2007 00:52:51 +0200
parents af035a304bec
children 0d4a1699accb
files mcabber/doc/help/en/hlp_otr.txt mcabber/doc/help/en/hlp_otrpolicy.txt mcabber/src/Makefile.am mcabber/src/commands.c mcabber/src/compl.h mcabber/src/jabglue.c mcabber/src/main.c mcabber/src/otr.c mcabber/src/otr.h mcabber/src/settings.c mcabber/src/settings.h
diffstat 11 files changed, 1127 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/doc/help/en/hlp_otr.txt	Thu Aug 30 00:52:51 2007 +0200
@@ -0,0 +1,26 @@
+
+ /otr key
+ /otr start|stop|info [jid]
+ /otr fingerprint [jid [fpr]]
+ /otr smpq|smpr [jid] secret
+ /otr smpa [jid]
+ 
+You can use the shortcut-jid "." for the currently selected contact.
+
+/otr key
+ Print the fingerprint of your private key to the Status Buffer
+/otr start [jid]
+ Open an OTR channel to the specified jid (or the currently selected contact)
+/otr stop [jid]
+ Close the OTR channel to the specified jid (or the currently selected contact)
+/otr info [jid]
+ Show current OTR status for the specified jid (or the currently selected contact)
+/otr fingerprint [jid [fpr]]
+ Show the active fingerprint of an OTR channel.
+ If the fingerprint is provided instead of "fpr", the fingerprint will become trusted. If you replace "fpr" by some bogus string the fingerprint will loose the trusted status.
+/otr smpq [jid] secret
+ Initiate the Socialist Millionaires Protocol with the secret and the buddy.  
+/otr smpr [jid] secret
+ Respond to the Initiation of the jid with the secret.
+/otr smpa [jid]
+ Abort the running Socialist Millionaires Protocol.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/doc/help/en/hlp_otrpolicy.txt	Thu Aug 30 00:52:51 2007 +0200
@@ -0,0 +1,19 @@
+
+ /otrpolicy
+ /otrpolicy (default|jid) (plain|manual|opportunistic|always)
+ 
+You can use the shortcut-jid "." for the currently selected contact.
+
+/otrpolicy
+ Prints all otr policies to the status buffer
+
+/otrpolicy (default|jid) (plain|manual|opportunistic|always)
+ Sets either the default policy or the policy for the given jid.
+ 
+ The plain policy should never be used, because you won't be able to receive or send any otr encrypted messages. 
+ 
+ If you set the policy to manual, you or your chat partner have to start the otr encryption by hand (e.g. with /otr start). 
+ 
+ The policy "opportunistic" does that itself by sending a special whitespace-sequence at the end of unencrypted messages. So the other otr-enabled chat client knows, that you want to use otr. Note, that the first message will always be unencryted, if you use this policy.
+
+ With the policy "always" no message will be sent in plain text. If you try to sent the first message unencrypted, mcabber will try to establish an otr channel. Please resend your message, when you get the information that the channel was established. If someone sends you plaintext messages while the policy is set to "always", you'll be able to read the message but it won't be saved to the history.
--- a/mcabber/src/Makefile.am	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/Makefile.am	Thu Aug 30 00:52:51 2007 +0200
@@ -5,12 +5,12 @@
 		  hbuf.c hbuf.h screen.c screen.h logprint.h \
 		  settings.c settings.h hooks.c hooks.h utf8.c utf8.h \
 		  histolog.c histolog.h utils.c utils.h pgp.c pgp.h \
-		  help.c help.h
+		  otr.c otr.h help.c help.h
 
-LDADD = $(GLIB_LIBS) $(GPGME_LIBS) \
+LDADD = $(GLIB_LIBS) $(GPGME_LIBS) $(LIBOTR_LIBS) \
 	../libjabber/liblibjabber.a ../connwrap/libconnwrap.a
 
-AM_CPPFLAGS = $(GLIB_CFLAGS) $(GPGME_CFLAGS)
+AM_CPPFLAGS = $(GLIB_CFLAGS) $(GPGME_CFLAGS) 
 
 CLEANFILES = hgcset.h
 
--- a/mcabber/src/commands.c	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/commands.c	Thu Aug 30 00:52:51 2007 +0200
@@ -32,6 +32,7 @@
 #include "utils.h"
 #include "settings.h"
 #include "events.h"
+#include "otr.h"
 
 #define IMSTATUS_AWAY           "away"
 #define IMSTATUS_ONLINE         "online"
@@ -78,6 +79,8 @@
 static void do_chat_disable(char *arg);
 static void do_source(char *arg);
 static void do_color(char *arg);
+static void do_otr(char *arg);
+static void do_otrpolicy(char *arg);
 
 // Global variable for the commands list
 static GSList *Commands;
@@ -129,6 +132,8 @@
   cmd_add("msay", "Send a multi-lines message to the selected buddy",
           COMPL_MULTILINE, 0, &do_msay);
   cmd_add("pgp", "Manage PGP settings", COMPL_PGP, COMPL_JID, &do_pgp);
+  cmd_add("otr", "Manage OTR settings", COMPL_OTR, COMPL_JID, &do_otr);
+  cmd_add("otrpolicy", "Manage OTR policies", COMPL_JID, COMPL_OTRPOLICY, &do_otrpolicy);
   cmd_add("quit", "Exit the software", 0, 0, NULL);
   cmd_add("rawxml", "Send a raw XML string", 0, 0, &do_rawxml);
   cmd_add("rename", "Rename the current buddy", 0, 0, &do_rename);
@@ -255,6 +260,22 @@
   compl_add_category_word(COMPL_PGP, "info");
   compl_add_category_word(COMPL_PGP, "setkey");
 
+  // OTR category
+  compl_add_category_word(COMPL_OTR, "start");
+  compl_add_category_word(COMPL_OTR, "stop");
+  compl_add_category_word(COMPL_OTR, "fingerprint");
+  compl_add_category_word(COMPL_OTR, "smpq");
+  compl_add_category_word(COMPL_OTR, "smpr");
+  compl_add_category_word(COMPL_OTR, "smpa");
+  compl_add_category_word(COMPL_OTR, "info");
+  compl_add_category_word(COMPL_OTR, "key");
+
+  // OTR Policy category
+  compl_add_category_word(COMPL_OTRPOLICY, "plain");
+  compl_add_category_word(COMPL_OTRPOLICY, "manual");
+  compl_add_category_word(COMPL_OTRPOLICY, "opportunistic");
+  compl_add_category_word(COMPL_OTRPOLICY, "always");
+
   // Color category
   compl_add_category_word(COMPL_COLOR, "roster");
   compl_add_category_word(COMPL_COLOR, "muc");
@@ -2883,6 +2904,223 @@
   free_arg_lst(paramlst);
 }
 
+static void do_otr(char *arg)
+{
+#ifdef HAVE_LIBOTR
+  char **paramlst;
+  char *fjid, *subcmd, *keyid;
+  enum {
+    otr_none,
+    otr_start,
+    otr_stop,
+    otr_fpr,
+    otr_smpq,
+    otr_smpr,
+    otr_smpa,
+    otr_k,
+    otr_info
+  } op = 0;
+
+  paramlst = split_arg(arg, 3, 0); // subcmd, jid, [key]
+  subcmd = *paramlst;
+  fjid = *(paramlst+1);
+  keyid = *(paramlst+2);
+
+  if (!subcmd)
+    fjid = NULL;
+  if (!fjid)
+    keyid = NULL;
+
+  if (subcmd) {
+    if (!strcasecmp(subcmd, "start"))
+      op = otr_start;
+    else if (!strcasecmp(subcmd, "stop"))
+      op = otr_stop;
+    else if (!strcasecmp(subcmd, "fingerprint"))
+      op = otr_fpr;
+    else if (!strcasecmp(subcmd, "smpq"))
+      op = otr_smpq;
+    else if (!strcasecmp(subcmd, "smpr"))
+      op = otr_smpr;
+    else if (!strcasecmp(subcmd, "smpa"))
+      op = otr_smpa;
+    else if (!strcasecmp(subcmd, "key"))
+      op = otr_k;
+    else if (!strcasecmp(subcmd, "info"))
+      op = otr_info;
+  }
+
+  if (!op) {
+    scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
+    free_arg_lst(paramlst);
+    return;
+  }
+
+  if(op == otr_k)
+    otr_key();
+  else {
+    // Allow special jid "" or "." (current buddy)
+    if (fjid && (!*fjid || !strcmp(fjid, ".")))
+      fjid = NULL;
+
+    if (fjid) {
+      // The JID has been specified.  Quick check...
+      if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
+        scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+                     "<%s> is not a valid Jabber ID.", fjid);
+        fjid = NULL;
+      } else {
+        // Convert jid to lowercase and strip resource
+        char *p;
+        for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+          *p = tolower(*p);
+        if (*p == JID_RESOURCE_SEPARATOR)
+          *p = '\0';
+      }
+    } else {
+      gpointer bud = NULL;
+      if (current_buddy)
+        bud = BUDDATA(current_buddy);
+      if (bud) {
+        guint type = buddy_gettype(bud);
+        if (type & ROSTER_TYPE_USER)  // Is it a user?
+          fjid = (char*)buddy_getjid(bud);
+        else
+          scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
+      }
+    }
+
+    if (fjid) { // fjid is actually a bare jid...
+      switch (op) {
+        case otr_start:
+          otr_establish(fjid);          break;
+        case otr_stop:
+          otr_disconnect(fjid);         break;
+        case otr_fpr:
+          otr_fingerprint(fjid, keyid); break;
+        case otr_smpq:
+          otr_smp_query(fjid, keyid);   break;
+        case otr_smpr:        
+          otr_smp_respond(fjid, keyid); break;
+        case otr_smpa:
+          otr_smp_abort(fjid);          break;
+        case otr_info:
+          otr_print_info(fjid);         break;
+        default:
+          break;
+      }
+    } else
+      scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+  }
+  free_arg_lst(paramlst);
+
+#else
+  scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
+#endif /* HAVE_LIBOTR */
+}
+
+#ifdef HAVE_LIBOTR
+static char * string_for_otrpolicy(enum otr_policy p)
+{
+  switch (p) {
+    case plain:         return "plain";
+    case opportunistic: return "opportunistic";
+    case manual:        return "manual";
+    case always:        return "always";
+    default:            return "unknown";
+  }
+}
+
+static void dump_otrpolicy(char * k, char * v, void * nothing)
+{
+  scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8, "otrpolicy for %s: %s", k,
+               string_for_otrpolicy(*(enum otr_policy*)v));
+}
+#endif
+
+static void do_otrpolicy(char *arg)
+{
+#ifdef HAVE_LIBOTR
+  char **paramlst;
+  char *fjid, *policy;
+  enum otr_policy p;
+
+  paramlst = split_arg(arg, 2, 0); // [jid|default] policy
+  fjid = *paramlst;
+  policy = *(paramlst+1);
+
+  if (!fjid && !policy) {
+    scr_LogPrint(LPRINT_NORMAL, "default otrpolicy: %s",
+                 string_for_otrpolicy(settings_otr_getpolicy(NULL)));
+    settings_foreach(SETTINGS_TYPE_OTR, &dump_otrpolicy, NULL);
+    return;
+  }
+
+  if (!policy) {
+    scr_LogPrint(LPRINT_NORMAL, "Unrecognized or missing parameter!");
+    free_arg_lst(paramlst);
+    return;
+  }
+
+  if (!strcasecmp(policy, "plain"))
+    p = plain;
+  else if (!strcasecmp(policy, "manual"))
+    p = manual;
+  else if (!strcasecmp(policy, "opportunistic"))
+    p = opportunistic;
+  else if (!strcasecmp(policy, "always"))
+    p = always;
+  else {
+    /* Fail, we don't know _this_ policy*/
+  }
+
+  if(!strcasecmp(fjid, "default")){
+    /*set default policy*/
+    settings_otr_setpolicy(NULL, p);
+    return;
+  }
+  // Allow special jid "" or "." (current buddy)
+  if (fjid && (!*fjid || !strcmp(fjid, ".")))
+    fjid = NULL;
+
+  if (fjid) {
+    // The JID has been specified.  Quick check...
+    if (check_jid_syntax(fjid) || !strchr(fjid, '@')) {
+      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+                   "<%s> is not a valid Jabber ID.", fjid);
+      fjid = NULL;
+    } else {
+      // Convert jid to lowercase and strip resource
+      char *p;
+      for (p = fjid; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+        *p = tolower(*p);
+      if (*p == JID_RESOURCE_SEPARATOR)
+        *p = '\0';
+    }
+  } else {
+    gpointer bud = NULL;
+    if (current_buddy)
+      bud = BUDDATA(current_buddy);
+    if (bud) {
+      guint type = buddy_gettype(bud);
+      if (type & ROSTER_TYPE_USER)  // Is it a user?
+        fjid = (char*)buddy_getjid(bud);
+      else
+        scr_LogPrint(LPRINT_NORMAL, "The selected item should be a user.");
+    }
+  }
+
+  if (fjid)
+    settings_otr_setpolicy(fjid, p);
+  else
+    scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+
+  free_arg_lst(paramlst);
+#else
+  scr_LogPrint(LPRINT_NORMAL, "Please recompile mcabber with libotr enabled.");
+#endif /* HAVE_LIBOTR */
+}
+
 /* !!!
   After changing the /iline arguments names here, you must change ones
   in init_bindings().
--- a/mcabber/src/compl.h	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/compl.h	Thu Aug 30 00:52:51 2007 +0200
@@ -22,6 +22,8 @@
 #define COMPL_EVENTSID    (1U<<16)
 #define COMPL_PGP         (1U<<17)
 #define COMPL_COLOR       (1U<<18)
+#define COMPL_OTR         (1U<<19)
+#define COMPL_OTRPOLICY   (1U<<20)
 
 void    compl_add_category_word(guint, const char *command);
 void    compl_del_category_word(guint categ, const char *word);
--- a/mcabber/src/jabglue.c	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/jabglue.c	Thu Aug 30 00:52:51 2007 +0200
@@ -33,6 +33,7 @@
 #include "histolog.h"
 #include "commands.h"
 #include "pgp.h"
+#include "otr.h"
 
 #define JABBERPORT      5222
 #define JABBERSSLPORT   5223
@@ -551,7 +552,7 @@
   return g_strdup_printf("%u%d", msg_idn, (int)(now%10L));
 }
 
-//  jb_send_msg(jid, test, type, subject, msgid, *encrypted)
+//  jb_send_msg(jid, text, type, subject, msgid, *encrypted)
 // When encrypted is not NULL, the function set *encrypted to 1 if the
 // message has been PGP-encrypted.  If encryption enforcement is set and
 // encryption fails, *encrypted is set to -1.
@@ -560,6 +561,7 @@
 {
   xmlnode x;
   gchar *strtype;
+  int otr_msg = 0;
 #if defined HAVE_GPGME || defined JEP0022 || defined JEP0085
   char *rname, *barejid;
   GSList *sl_buddy;
@@ -584,7 +586,7 @@
   else
     strtype = TMSG_CHAT;
 
-#if defined HAVE_GPGME || defined JEP0022 || defined JEP0085
+#if defined HAVE_GPGME || defined HAVE_LIBOTR || defined JEP0022 || defined JEP0085
   rname = strchr(fjid, JID_RESOURCE_SEPARATOR);
   barejid = jidtodisp(fjid);
   sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
@@ -594,6 +596,23 @@
   if (rname)
     rname++;
 
+#ifdef HAVE_LIBOTR
+  if (msgid && strcmp(msgid, "otrinject") == 0)
+    msgid = NULL;
+  else {
+    otr_msg = otr_send((char **)&text, barejid);
+    if (!text) {
+      g_free(barejid);
+      if (encrypted)
+        *encrypted = -1;
+      return;
+    }
+  }
+  if (otr_msg && encrypted) {
+    *encrypted = 1;
+  }
+#endif
+
 #ifdef HAVE_GPGME
   if (type == ROSTER_TYPE_USER && sl_buddy && gpg_enabled()) {
     if (!settings_pgp_getdisabled(barejid)) { // not disabled for this contact?
@@ -1669,6 +1688,7 @@
   char *bjid;
   const char *rname, *s;
   char *decrypted = NULL;
+  int otr_msg = 0, free_msg = 0;
 
   bjid = jidtodisp(from);
 
@@ -1687,6 +1707,14 @@
     check_signature(bjid, rname, xmldata_signed, decrypted);
 #endif
 
+#ifdef HAVE_LIBOTR
+  otr_msg = otr_receive((char **)&body, bjid, &free_msg);
+  if(!body){
+    g_free(bjid);
+    return;
+  }
+#endif
+
   // Check for unexpected groupchat messages
   // If we receive a groupchat message from a room we're not a member of,
   // this is probably a server issue and the best we can do is to send
@@ -1715,6 +1743,8 @@
 
     g_free(bjid);
     g_free(decrypted);
+    if(free_msg)
+      g_free((char *)body);
 
     buddylist_build();
     scr_DrawRoster();
@@ -1729,12 +1759,14 @@
       (type && strcmp(type, "chat")) ||
       ((s = settings_opt_get("server")) != NULL && !strcasecmp(bjid, s))) {
     hk_message_in(bjid, rname, timestamp, body, type,
-                  (decrypted ? TRUE : FALSE));
+                  ((decrypted || otr_msg) ? TRUE : FALSE));
   } else {
     scr_LogPrint(LPRINT_LOGNORM, "Blocked a message from <%s>", bjid);
   }
   g_free(bjid);
   g_free(decrypted);
+  if(free_msg)
+    g_free((char *)body);
 }
 
 static const char *defaulterrormsg(int code)
--- a/mcabber/src/main.c	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/main.c	Thu Aug 30 00:52:51 2007 +0200
@@ -41,6 +41,7 @@
 #include "hooks.h"
 #include "utils.h"
 #include "pgp.h"
+#include "otr.h"
 
 #ifdef ENABLE_HGCSET
 # include "hgcset.h"
@@ -151,6 +152,9 @@
   }
 
   bjid = compose_jid(username, servername, resource);
+#if defined(HAVE_LIBOTR)
+  otr_init(bjid);
+#endif
   jc = jb_connect(bjid, servername, port, ssl, password);
   g_free(bjid);
 
@@ -261,6 +265,9 @@
 #ifdef HAVE_GPGME
   puts("Compiled with GPG support.");
 #endif
+#ifdef HAVE_LIBOTR
+  puts("Compiled with OTR support.");
+#endif
 #ifdef WITH_ASPELL
   puts("Compiled with Aspell support.");
 #endif
@@ -464,6 +471,9 @@
     }
   }
 
+#ifdef HAVE_LIBOTR
+  otr_terminate();
+#endif
   jb_disconnect();
 #ifdef HAVE_GPGME
   gpg_terminate();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/otr.c	Thu Aug 30 00:52:51 2007 +0200
@@ -0,0 +1,676 @@
+/*
+ * otr.c      -- Off-The-Record Messaging for mcabber
+ *
+ * Copyright (C) 2007 Frank Zschockelt <mcabber_otr@freakysoft.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBOTR
+
+#include "otr.h"
+#include "logprint.h"
+#include "hbuf.h"
+#include "jab_priv.h"
+#include "roster.h"
+#include "utils.h"
+
+
+static OtrlUserState userstate = NULL;
+static char * account = NULL;
+static char * keyfile = NULL;
+static char * fprfile = NULL;
+static enum otr_policy policy = manual;
+
+
+static OtrlPolicy cb_policy             (void *opdata, ConnContext *ctx);
+static void       cb_create_privkey     (void *opdata,
+                                         const char *accountname,
+                                         const char *protocol);
+static int        cb_is_logged_in       (void *opdata,
+                                         const char *accountname,
+                                         const char *protocol,
+                                         const char *recipient);
+static void       cb_inject_message     (void *opdata,
+                                         const char *accountname,
+                                         const char *protocol,
+                                         const char *recipient,
+                                         const char *message);
+static void       cb_notify             (void *opdata,
+                                         OtrlNotifyLevel level,
+                                         const char *accountname,
+                                         const char *protocol,
+                                         const char *username,
+                                         const char *title,
+                                         const char *primary,
+                                         const char *secondary);
+static int        cb_display_otr_message(void *opdata,
+                                         const char *accountname,
+                                         const char *protocol,
+                                         const char *username,
+                                         const char *msg);
+static void       cb_update_context_list(void *opdata);
+static const char *cb_protocol_name     (void *opdata, const char *protocol);
+static void       cb_protocol_name_free (void *opdata,
+                                         const char *protocol_name);
+static void       cb_new_fingerprint    (void *opdata, OtrlUserState us,
+                                         const char *accountname,
+                                         const char *protocol,
+                                         const char *username,
+                                         unsigned char fingerprint[20]);
+static void       cb_write_fingerprints (void *opdata);
+static void       cb_gone_secure        (void *opdata, ConnContext *context);
+static void       cb_gone_insecure      (void *opdata, ConnContext *context);
+static void       cb_still_secure       (void *opdata, ConnContext *context,
+                                         int is_reply);
+static void       cb_log_message        (void *opdata, const char *message);
+static int        cb_max_message_size   (void *opdata, ConnContext *context);
+
+static OtrlMessageAppOps ops =
+{
+  policy:                 cb_policy,
+  create_privkey:         cb_create_privkey,
+  is_logged_in:           cb_is_logged_in,
+  inject_message:         cb_inject_message,
+  notify:                 cb_notify,
+  display_otr_message:    cb_display_otr_message,
+  update_context_list:    cb_update_context_list,
+  protocol_name:          cb_protocol_name,
+  protocol_name_free:     cb_protocol_name_free,
+  new_fingerprint:        cb_new_fingerprint,
+  write_fingerprints:     cb_write_fingerprints,
+  gone_secure:            cb_gone_secure,
+  gone_insecure:          cb_gone_insecure,
+  still_secure:           cb_still_secure,
+  log_message:            cb_log_message,
+  max_message_size:       cb_max_message_size
+};
+
+static void otr_message_disconnect(ConnContext *ctx);
+static ConnContext * otr_get_context(const char *buddy);
+static void otr_startstop(const char * buddy, int start);
+static void otr_handle_smp_tlvs(OtrlTLV * tlvs, ConnContext * ctx);
+
+
+int otr_init(const char *jid)
+{
+  char * root = expand_filename("~/.mcabber/otr/");
+  account = jidtodisp(jid);
+  keyfile = g_strdup_printf("%s%s.key", root, account);
+  fprfile = g_strdup_printf("%s%s.fpr", root, account);
+  g_free(root);
+
+  OTRL_INIT;
+
+  userstate = otrl_userstate_create ();
+  if (otrl_privkey_read(userstate, keyfile)){
+    scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR key from %s", keyfile);
+    cb_create_privkey(NULL, account, "jabber");
+  }
+  if (otrl_privkey_read_fingerprints(userstate, fprfile, NULL, NULL)){
+    scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR fingerprints from %s",
+                 fprfile);
+  }
+}
+
+void otr_terminate(void)
+{
+  ConnContext * ctx;
+
+  for(ctx = userstate->context_root; ctx; ctx = ctx->next)
+    if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+      otr_message_disconnect(ctx);
+
+  g_free(account);
+  account = NULL;
+  otrl_userstate_free(userstate);
+  userstate = NULL;
+  g_free(keyfile);
+  keyfile = NULL;
+}
+
+static ConnContext * otr_get_context(const char *buddy)
+{
+  int null = 0;
+  return otrl_context_find(userstate, buddy, account, "jabber", 1, &null,
+                           NULL, NULL);
+}
+
+static void otr_message_disconnect(ConnContext *ctx)
+{
+  if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+    cb_gone_insecure(NULL, ctx);
+  otrl_message_disconnect(userstate, &ops, NULL, ctx->accountname,
+                          ctx->protocol, ctx->username);
+}
+
+static void otr_startstop(const char * buddy, int start)
+{
+  char * msg = NULL;
+  ConnContext *ctx = otr_get_context(buddy);
+
+  if (!userstate || !ctx)
+    return;
+
+  if (start && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+    otr_message_disconnect(ctx);
+
+  if (start) {
+    OtrlPolicy policy = cb_policy(NULL, ctx);
+    msg = otrl_proto_default_query_msg(ctx->accountname, policy);
+    cb_inject_message(NULL, ctx->accountname, ctx->protocol, ctx->username,
+                      msg);
+    free (msg);
+  }
+  else
+    otr_message_disconnect(ctx);
+}
+
+void otr_establish(const char *buddy)
+{
+  otr_startstop(buddy, 1);
+}
+
+void otr_disconnect(const char * buddy)
+{
+  otr_startstop(buddy, 0);
+}
+
+void otr_fingerprint(const char * buddy, const char * trust)
+{
+  char fpr[45], *tr;
+  ConnContext *ctx = otr_get_context(buddy);
+  if (!userstate || !ctx)
+    return;
+
+  if (!ctx->active_fingerprint || !ctx->active_fingerprint->fingerprint) {
+    scr_LogPrint(LPRINT_LOGNORM,
+                 "No active fingerprint - start OTR for this buddy first.");
+    return;
+  }
+
+  otrl_privkey_hash_to_human(fpr, ctx->active_fingerprint->fingerprint);
+  if (trust) {
+    if (strcmp(fpr, trust) == 0)
+      otrl_context_set_trust(ctx->active_fingerprint, "trust");
+    else
+      otrl_context_set_trust(ctx->active_fingerprint, NULL);
+  }
+
+  tr = ctx->active_fingerprint->trust;
+  scr_LogPrint(LPRINT_LOGNORM, "%s [%44s]: %s", ctx->username, fpr,
+               tr && *tr ?  "trusted" : "untrusted");
+  cb_write_fingerprints(NULL);
+}
+
+static void otr_handle_smp_tlvs(OtrlTLV * tlvs, ConnContext * ctx)
+{
+  OtrlTLV *tlv = NULL;
+  char *sbuf = NULL;
+  NextExpectedSMP nextMsg = ctx->smstate->nextExpected;
+
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+  if (tlv) {
+    if (nextMsg != OTRL_SMP_EXPECT1)
+      otr_smp_abort(ctx->username);
+    else {
+      sbuf = g_strdup_printf("OTR: Received SMP Initiation. "
+                             "Answer with /otr smpr %s $secret",
+                             ctx->username);
+    }
+  }
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+  if (tlv) {
+    if (nextMsg != OTRL_SMP_EXPECT2)
+      otr_smp_abort(ctx->username);
+    else {
+      sbuf = g_strdup("OTR: Received SMP Response.");
+      /* If we received TLV2, we will send TLV3 and expect TLV4 */
+      ctx->smstate->nextExpected = OTRL_SMP_EXPECT4;
+    }
+  }
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+  if (tlv) {
+    if (nextMsg != OTRL_SMP_EXPECT3)
+      otr_smp_abort(ctx->username);
+    else {
+      /* If we received TLV3, we will send TLV4
+       * We will not expect more messages, so prepare for next SMP */
+      ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+      /* Report result to user */
+      if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
+         *ctx->active_fingerprint->trust != '\0')
+        sbuf = g_strdup("OTR: SMP succeeded");
+      else
+        sbuf = g_strdup("OTR: SMP failed");
+    }
+  }
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+  if (tlv) {
+    if (nextMsg != OTRL_SMP_EXPECT4)
+      otr_smp_abort(ctx->username);
+    else {
+      /* We will not expect more messages, so prepare for next SMP */
+      ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+      /* Report result to user */
+      if (ctx->active_fingerprint && ctx->active_fingerprint->trust &&
+         *ctx->active_fingerprint->trust != '\0')
+        sbuf = g_strdup("OTR: SMP succeeded");
+      else
+        sbuf = g_strdup("OTR: SMP failed");
+    }
+  }
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+  if (tlv) {
+    /* The message we are waiting for will not arrive, so reset
+     * and prepare for the next SMP */
+    sbuf = g_strdup("OTR: SMP aborted by your buddy");
+    ctx->smstate->nextExpected = OTRL_SMP_EXPECT1;
+  }
+
+  if (sbuf) {
+    scr_WriteIncomingMessage(ctx->username, sbuf, 0, HBB_PREFIX_INFO);
+    g_free(sbuf);
+  }
+}
+
+/*
+ * returns whether a otr_message was received
+ * sets *otr_data to NULL, when it was an internal otr message
+ */
+int otr_receive(char **otr_data, const char * buddy, int * free_msg)
+{
+  int ignore_message;
+  char *newmessage = NULL;
+  OtrlTLV *tlvs = NULL;
+  OtrlTLV *tlv = NULL;
+  ConnContext * ctx;
+
+  *free_msg = 0;
+  ignore_message = otrl_message_receiving(userstate, &ops, NULL, account,
+    "jabber", buddy, *otr_data, &newmessage, &tlvs, NULL, NULL);
+
+  ctx = otr_get_context(buddy);
+
+  tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+  if (tlv) {
+    /* Notify the user that the other side disconnected. */
+    if (ctx) {
+      cb_gone_insecure(NULL, ctx);
+      otr_disconnect(buddy);
+    }
+  }
+
+  otr_handle_smp_tlvs(tlvs, ctx);
+
+  if (tlvs != NULL)
+    otrl_tlv_free(tlvs);
+
+  if (ignore_message)
+    *otr_data = NULL;
+
+  if (!ignore_message && newmessage) {
+    *free_msg = 1;
+    *otr_data = g_strdup(newmessage);
+    otrl_message_free(newmessage);
+    if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+      return 1;
+  }
+  return 0;
+}
+
+int otr_send(char **msg, const char *buddy)
+{
+  gcry_error_t err;
+  char *newmessage = NULL;
+  ConnContext * ctx = otr_get_context(buddy);
+
+  err = otrl_message_sending(userstate, &ops, NULL, account, "jabber", buddy,
+    *msg, NULL, &newmessage, NULL, NULL);
+
+  if (err)
+    *msg = NULL; /*something went wrong, don't send the plain-message! */
+
+  if (!err && newmessage) {
+    *msg = g_strdup(newmessage);
+    otrl_message_free(newmessage);
+    if (cb_policy(NULL, ctx) & OTRL_POLICY_REQUIRE_ENCRYPTION ||
+        ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
+      return 1;
+  }
+  return 0;
+}
+
+/* Prints OTR connection state */
+void otr_print_info(const char * buddy)
+{
+  const char *state, *auth, *policy;
+  ConnContext * ctx = otr_get_context(buddy);
+  OtrlPolicy p = cb_policy (ctx->app_data, ctx);
+
+  if (!userstate || !ctx)
+    return;
+
+  switch (ctx->msgstate) {
+    case OTRL_MSGSTATE_PLAINTEXT: state = "plaintext"; break;
+    case OTRL_MSGSTATE_ENCRYPTED:
+     switch (ctx->protocol_version) {
+       case 1: state = "encrypted V1"; break;
+       case 2: state = "encrypted V2"; break;
+       default:state = "encrypted";
+     };
+     break;
+    case OTRL_MSGSTATE_FINISHED:  state = "finished";  break;
+    default:                      state = "unknown state";
+  }
+  switch (ctx->auth.authstate) {
+    case OTRL_AUTHSTATE_NONE:
+      switch (ctx->otr_offer) {
+        case OFFER_NOT:      auth = "no offer sent";  break;
+        case OFFER_SENT:     auth = "offer sent";     break;
+        case OFFER_ACCEPTED: auth = "offer accepted"; break;
+        case OFFER_REJECTED: auth = "offer rejected"; break;
+        default:             auth = "unknown auth";
+      }
+      break;
+    case OTRL_AUTHSTATE_AWAITING_DHKEY:
+      auth = "awaiting D-H key";          break;
+    case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
+      auth = "awaiting reveal signature"; break;
+    case OTRL_AUTHSTATE_AWAITING_SIG:
+      auth = "awaiting signature";        break;
+    case OTRL_AUTHSTATE_V1_SETUP:
+      auth = "v1 setup";                  break;
+    default:
+      auth = "unknown auth";
+  }
+  if (p == OTRL_POLICY_NEVER)
+    policy = "plain";
+  else if (p == OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1)
+    policy = "opportunistic";
+  else if (p == OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1)
+    policy = "manual";
+  else if (p == (OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1))
+    policy = "always";
+  else
+    policy = "unknown";
+
+  scr_LogPrint(LPRINT_LOGNORM, "%s: %s (%s) [%s]",
+               ctx->username, state, auth, policy);
+}
+
+static ConnContext * otr_context_encrypted(const char * buddy)
+{
+  ConnContext * ctx = otr_get_context(buddy);
+
+  if (!userstate || !ctx  || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED){
+    scr_LogPrint(LPRINT_LOGNORM,
+                 "You have to start an OTR channel with %s before you can "
+                 "use SMP.", buddy);
+    return NULL;
+  }
+
+  return ctx;
+}
+
+void otr_smp_query(const char * buddy, const char * secret)
+{
+  ConnContext * ctx = otr_context_encrypted(buddy);
+
+  if (!secret) {
+    scr_LogPrint(LPRINT_LOGNORM,
+                 "Using SMP without a secret isn't a good idea.");
+    return;
+  }
+
+  if (ctx) {
+    otrl_message_initiate_smp(userstate, &ops, NULL, ctx, secret,
+                              strlen(secret));
+    scr_WriteIncomingMessage(ctx->username,
+                             "OTR: Socialist Millionaires' Protocol "
+                             "initiated.", 0, HBB_PREFIX_INFO);
+  }
+}
+
+void otr_smp_respond(const char * buddy, const char * secret)
+{
+  ConnContext * ctx = otr_context_encrypted(buddy);
+
+  if (!secret) {
+    scr_LogPrint(LPRINT_LOGNORM,
+                 "Using SMP without a secret isn't a good idea.");
+    return;
+  }
+
+  if (ctx) {
+    if (!ctx->smstate->secret) {
+      scr_LogPrint(LPRINT_LOGNORM,
+                   "Don't call smpr before you haven't received an SMP "
+                   "Initiation!");
+      return;
+    }
+    otrl_message_respond_smp(userstate, &ops, NULL, ctx, secret,
+                             strlen(secret));
+    scr_WriteIncomingMessage(ctx->username,
+                             "OTR: Socialist Millionaires' Protocol: "
+                             "response sent", 0, HBB_PREFIX_INFO);
+  }
+}
+
+void otr_smp_abort(const char * buddy)
+{
+  ConnContext * ctx = otr_context_encrypted(buddy);
+
+  if (ctx) {
+    otrl_message_abort_smp(userstate, &ops, NULL, ctx);
+    scr_WriteIncomingMessage(ctx->username,
+                             "OTR: Socialist Millionaires' Protocol aborted.",
+                             0, HBB_PREFIX_INFO);
+  }
+}
+
+void otr_key(void)
+{
+  OtrlPrivKey * key;
+  char readable[45] = "";
+
+  if(!userstate)
+    return;
+  for (key = userstate->privkey_root; key; key = key->next) {
+    otrl_privkey_fingerprint(userstate, readable, key->accountname,
+                             key->protocol);
+    scr_LogPrint(LPRINT_LOGNORM, "%s: %s", key->accountname, readable);
+  }
+}
+
+/* Return the OTR policy for the given context. */
+static OtrlPolicy cb_policy(void *opdata, ConnContext *ctx)
+{
+  enum otr_policy p = settings_otr_getpolicy(NULL);
+
+  if(ctx)
+    if(settings_otr_getpolicy(ctx->username))
+      p = settings_otr_getpolicy(ctx->username);
+
+  switch (p) {
+    case plain:
+      return OTRL_POLICY_NEVER;
+    case opportunistic:
+      return OTRL_POLICY_OPPORTUNISTIC & ~OTRL_POLICY_ALLOW_V1;
+    case manual:
+      return OTRL_POLICY_MANUAL & ~OTRL_POLICY_ALLOW_V1;
+    case always:
+      return OTRL_POLICY_ALWAYS & ~OTRL_POLICY_ALLOW_V1;
+  }
+}
+
+/* Create a private key for the given accountname/protocol if
+ * desired. */
+static void cb_create_privkey(void *opdata, const char *accountname,
+                              const char *protocol)
+{
+  gcry_error_t e;
+
+  scr_LogPrint(LPRINT_LOGNORM,
+               "Generating new OTR key for %s. This may take a while...",
+               accountname);
+  scr_DoUpdate();
+
+  e = otrl_privkey_generate(userstate, keyfile, accountname, protocol);
+
+  if (e)
+    scr_LogPrint(LPRINT_LOGNORM, "OTR key generation failed!"
+                 " Please mkdir ~/.mcabber/otr/ and restart mcabber.");
+  else
+    scr_LogPrint(LPRINT_LOGNORM, "OTR key generated.");
+}
+
+/* Report whether you think the given user is online.  Return 1 if
+ * you think he is, 0 if you think he isn't, -1 if you're not sure.
+ * If you return 1, messages such as heartbeats or other
+ * notifications may be sent to the user, which could result in "not
+ * logged in" errors if you're wrong. */
+static int cb_is_logged_in(void *opdata, const char *accountname,
+                           const char *protocol, const char *recipient)
+{
+  int ret = (roster_getstatus((char *)recipient, NULL) != offline);
+  return ret;
+}
+
+/* Send the given IM to the given recipient from the given
+ * accountname/protocol. */
+static void cb_inject_message(void *opdata, const char *accountname,
+                              const char *protocol, const char *recipient,
+                              const char *message)
+{
+  char * id = g_strdup("otrinject");
+  jb_send_msg(recipient, message, ROSTER_TYPE_USER, "", id, NULL);
+  g_free(id);
+}
+
+/* Display a notification message for a particular
+ * accountname / protocol / username conversation. */
+static void cb_notify(void *opdata, OtrlNotifyLevel level,
+                      const char *accountname, const char *protocol,
+                      const char *username, const char *title,
+                      const char *primary, const char *secondary)
+{
+  char * type;
+  char *sbuf = NULL;
+  switch (level) {
+    case OTRL_NOTIFY_ERROR:   type = "error";   break;
+    case OTRL_NOTIFY_WARNING: type = "warning"; break;
+    case OTRL_NOTIFY_INFO:    type = "info";    break;
+  }
+  sbuf = g_strdup_printf("OTR %s:%s\n%s\n%s",type,title, primary, secondary);
+  scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO);
+  g_free(sbuf);
+}
+
+/* Display an OTR control message for a particular
+ * accountname / protocol / username conversation.  Return 0 if you are able
+ * to successfully display it.  If you return non-0 (or if this
+ * function is NULL), the control message will be displayed inline,
+ * as a received message, or else by using the above notify()
+ * callback. */
+static int cb_display_otr_message(void *opdata, const char *accountname,
+                                  const char *protocol, const char *username,
+                                  const char *msg)
+{
+  scr_WriteIncomingMessage(username, msg, 0, HBB_PREFIX_INFO);
+  return 0;
+}
+
+/* When the list of ConnContexts changes (including a change in
+ * state), this is called so the UI can be updated. */
+static void cb_update_context_list(void *opdata)
+{
+  /*maybe introduce new status characters for mcabber,
+   * then use this function (?!)*/
+}
+
+/* Return a newly allocated string containing a human-friendly name
+ * for the given protocol id */
+static const char *cb_protocol_name(void *opdata, const char *protocol)
+{
+  return protocol;
+}
+
+/* Deallocate a string allocated by protocol_name */
+static void cb_protocol_name_free (void *opdata, const char *protocol_name)
+{
+  /* We didn't allocated memory, so we don't have to free anything :p */
+}
+
+/* A new fingerprint for the given user has been received. */
+static void cb_new_fingerprint(void *opdata, OtrlUserState us,
+                               const char *accountname, const char *protocol,
+                               const char *username,
+                               unsigned char fingerprint[20])
+{
+  char *sbuf = NULL;
+  char readable[45];
+
+  otrl_privkey_hash_to_human(readable, fingerprint);
+  sbuf = g_strdup_printf("OTR: new fingerprint: %s", readable);
+  scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO);
+  g_free(sbuf);
+}
+
+/* The list of known fingerprints has changed.  Write them to disk. */
+static void cb_write_fingerprints(void *opdata)
+{
+  otrl_privkey_write_fingerprints(userstate, fprfile);
+}
+
+/* A ConnContext has entered a secure state. */
+static void cb_gone_secure(void *opdata, ConnContext *context)
+{
+  scr_WriteIncomingMessage(context->username, "OTR: channel established", 0,
+                           HBB_PREFIX_INFO);
+}
+
+/* A ConnContext has left a secure state. */
+static void cb_gone_insecure(void *opdata, ConnContext *context)
+{
+  scr_WriteIncomingMessage(context->username, "OTR: channel closed", 0,
+                           HBB_PREFIX_INFO);
+}
+
+/* We have completed an authentication, using the D-H keys we
+ * already knew.  is_reply indicates whether we initiated the AKE. */
+static void cb_still_secure(void *opdata, ConnContext *context, int is_reply)
+{
+  scr_WriteIncomingMessage(context->username, "OTR: channel reestablished", 0,
+                           HBB_PREFIX_INFO);
+}
+
+/* Log a message.  The passed message will end in "\n". */
+static void cb_log_message(void *opdata, const char *message)
+{
+  scr_LogPrint(LPRINT_DEBUG, "OTR: %s", message);
+}
+
+/* Find the maximum message size supported by this protocol. */
+static int cb_max_message_size(void *opdata, ConnContext *context)
+{
+  return 8192;
+}
+
+#endif /* HAVE_LIBOTR */
+/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/otr.h	Thu Aug 30 00:52:51 2007 +0200
@@ -0,0 +1,39 @@
+#ifndef __OTR_H__
+#define __OTR_H__ 1
+
+#ifdef HAVE_LIBOTR
+
+#include <libotr/proto.h>
+#include <libotr/message.h>
+
+enum otr_policy {
+  plain,
+  opportunistic,
+  manual,
+  always
+};
+
+int  otr_init(const char *jid);
+void otr_terminate(void);
+
+void otr_establish  (const char * buddy);
+void otr_disconnect (const char * buddy);
+void otr_fingerprint(const char * buddy, const char * trust);
+void otr_print_info (const char * buddy);
+
+void otr_smp_query  (const char * buddy, const char * secret);
+void otr_smp_respond(const char * buddy, const char * secret);
+void otr_smp_abort  (const char * buddy);
+
+void otr_key        (void);
+
+int  otr_receive    (char **otr_data, const char * buddy, int * free_msg);
+int  otr_send       (char **msg, const char *buddy);
+
+#endif /* HAVE_LIBOTR */
+
+int  otr_enabled    (void);
+
+#endif /* __OTR_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/settings.c	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/settings.c	Thu Aug 30 00:52:51 2007 +0200
@@ -27,8 +27,9 @@
 #include "commands.h"
 #include "utils.h"
 #include "logprint.h"
+#include "otr.h"
 
-// Maximum line length 
+// Maximum line length
 // (probably best to use the same value as INPUTLINE_LENGTH)
 #define CONFLINE_LENGTH  1024
 
@@ -46,11 +47,19 @@
 } T_pgpopt;
 #endif
 
+#ifdef HAVE_LIBOTR
+static GHashTable *otrpolicy;
+static enum otr_policy default_policy;
+#endif
+
 static inline GHashTable *get_hash(guint type)
 {
   if      (type == SETTINGS_TYPE_OPTION)  return option;
   else if (type == SETTINGS_TYPE_ALIAS)   return alias;
   else if (type == SETTINGS_TYPE_BINDING) return binding;
+#ifdef HAVE_LIBOTR
+  else if (type == SETTINGS_TYPE_OTR)     return otrpolicy;
+#endif
   return NULL;
 }
 
@@ -64,6 +73,9 @@
 #ifdef HAVE_GPGME
   pgpopt = g_hash_table_new(&g_str_hash, &g_str_equal);
 #endif
+#ifdef HAVE_LIBOTR
+  otrpolicy = g_hash_table_new(&g_str_hash, &g_str_equal);
+#endif
 }
 
 //  cfg_read_file(filename, mainfile)
@@ -157,17 +169,19 @@
     if ((*line == '\n') || (*line == '\0') || (*line == '#'))
       continue;
 
-    // We only allow assignments line, except for commands "pgp", "source"
-    // and "color"
+    // We only allow assignments line, except for commands "pgp", "source",
+    // "color" and "otrpolicy"
     if ((strchr(line, '=') != NULL) ||
         startswith(line, "pgp ", FALSE) || startswith(line, "source ", FALSE) ||
-        startswith(line, "color ", FALSE)) {
+        startswith(line, "color ", FALSE) ||
+        startswith(line, "otrpolicy", FALSE)) {
       // Only accept the set, alias, bind, pgp and source commands
       if (!startswith(line, "set ", FALSE)   &&
           !startswith(line, "bind ", FALSE)  &&
           !startswith(line, "alias ", FALSE) &&
           !startswith(line, "pgp ", FALSE)   &&
           !startswith(line, "source ", FALSE) &&
+          !startswith(line, "otrpolicy ", FALSE) &&
           !startswith(line, "color ", FALSE)) {
         scr_LogPrint(LPRINT_LOGNORM,
                      "Error in configuration file (l. %d): bad command", ln);
@@ -501,6 +515,64 @@
   return NULL;
 }
 
+/* otr settings */
+
+#ifdef HAVE_LIBOTR
+static void remove_default_policies(char * k, char * policy, void * defaultp)
+{
+  if (*(enum otr_policy *)policy == *(enum otr_policy *)defaultp) {
+    g_free((enum otr_policy *) policy);
+    g_hash_table_remove(otrpolicy, k);
+  }
+}
+#endif
+
+void settings_otr_setpolicy(const char *bjid, guint value)
+{
+#ifdef HAVE_LIBOTR
+  enum otr_policy *otrdata;
+
+  if (!bjid) {
+    default_policy = value;
+    /* refresh hash */
+    settings_foreach(SETTINGS_TYPE_OTR, &remove_default_policies, &value);
+    return;
+  }
+
+  otrdata = g_hash_table_lookup(otrpolicy, bjid);
+
+  if (value == default_policy) {
+    if (otrdata) {
+      g_free(otrdata);
+      g_hash_table_remove(otrpolicy, bjid);
+    }
+  } else if (otrdata) {
+    *otrdata = value;
+  } else {
+    otrdata = g_new(enum otr_policy, 1);
+    *otrdata = value;
+    g_hash_table_insert(otrpolicy, g_strdup(bjid), otrdata);
+  }
+#endif
+}
+
+guint settings_otr_getpolicy(const char *bjid)
+{
+#ifdef HAVE_LIBOTR
+  enum otr_policy * otrdata;
+  if (!bjid)
+    return default_policy;
+
+  otrdata = g_hash_table_lookup(otrpolicy, bjid);
+  if (otrdata)
+    return *otrdata;
+  else
+    return default_policy;
+#else
+  return 0;
+#endif
+}
+
 guint get_max_history_blocks(void)
 {
   int max_num_of_blocks = settings_opt_get_int("max_history_blocks");
--- a/mcabber/src/settings.h	Tue Aug 28 18:56:50 2007 +0200
+++ b/mcabber/src/settings.h	Thu Aug 30 00:52:51 2007 +0200
@@ -14,6 +14,9 @@
 #define SETTINGS_TYPE_OPTION    1
 #define SETTINGS_TYPE_ALIAS     2
 #define SETTINGS_TYPE_BINDING   3
+#ifdef HAVE_LIBOTR
+#define SETTINGS_TYPE_OTR       4
+#endif
 
 #define COMMAND_CHAR    '/'
 #define COMMAND_CHARSTR "/"