# HG changeset patch
# Parent 1b31bdb72d83850b146a0e1e135eba35bad73d29
Switch to libotr v4 API

diff -r 1b31bdb72d83 mcabber/configure.ac
--- a/mcabber/configure.ac	Sun Jan 13 04:35:32 2013 +0200
+++ b/mcabber/configure.ac	Fri Jan 18 11:23:47 2013 +0200
@@ -183,16 +183,26 @@
 
 # Check for otr
 AC_ARG_ENABLE(otr,
-              AC_HELP_STRING([--enable-otr],
-                             [enable OTR (Off-the-Record) messaging support]),
-              enable_otr=$enableval, otr="")
+    AC_HELP_STRING([--enable-otr],
+                   [enable OTR (Off-the-Record) messaging support]),
+    enable_otr=$enableval,
+    otr="")
 if test "x$enable_otr" = "xyes"; then
-  # Look for libgcrypt and libotr
-  AM_PATH_LIBGCRYPT(1.2.2, [
-          AM_PATH_LIBOTR(3.1.0, ,
-                         AC_MSG_ERROR(libotr 3.1.0 or newer is required.))
-      ], AC_MSG_ERROR(libgcrypt 1.2.2 or newer is required.)
-  )
+    # Look for libgcrypt and libotr
+    AM_PATH_LIBGCRYPT(1.2.2, [
+        AM_PATH_LIBOTR(4.0.0, [
+            AC_DEFINE([HAVE_LIBOTR], 1, [Define if you use libotr])
+        ], [
+            AM_PATH_LIBOTR(3.1.0, [
+                AC_DEFINE([HAVE_LIBOTR], 1, [Define if you use libotr])
+                AC_DEFINE([HAVE_LIBOTR3], 1, [Define if you use libotr v3])
+            ], [
+                AC_MSG_ERROR(libotr 3.1.0 or newer is required.)
+            ])
+        ])
+    ], [
+        AC_MSG_ERROR(libgcrypt 1.2.2 or newer is required.)
+    ])
 fi
 
 # Check for Enchant stuff
@@ -289,3 +299,4 @@
                  mcabber.pc
                  Makefile])
 AC_OUTPUT
+dnl vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users...
diff -r 1b31bdb72d83 mcabber/mcabber/otr.c
--- a/mcabber/mcabber/otr.c	Sun Jan 13 04:35:32 2013 +0200
+++ b/mcabber/mcabber/otr.c	Fri Jan 18 11:23:47 2013 +0200
@@ -56,6 +56,20 @@
                                          const char *protocol,
                                          const char *recipient,
                                          const char *message);
+static void       cb_update_context_list(void *opdata);
+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 int        cb_max_message_size   (void *opdata, ConnContext *context);
+
+#ifdef HAVE_LIBOTR3
 static void       cb_notify             (void *opdata,
                                          OtrlNotifyLevel level,
                                          const char *accountname,
@@ -69,22 +83,26 @@
                                          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 void       otr_handle_smp_tlvs   (OtrlTLV *tlvs, ConnContext *ctx);
+#else /* HAVE_LIBOTR3 */
+static char *tagfile = NULL;
+static guint otr_timer_source = 0;
+
+static void       cb_handle_smp_event   (void *opdata, OtrlSMPEvent event,
+                                         ConnContext *context, unsigned short percent,
+                                         char *question);
+static void       cb_handle_msg_event   (void *opdata, OtrlMessageEvent event,
+                                         ConnContext *context, const char *message,
+                                         gcry_error_t err);
+static void       cb_create_instag      (void *opdata, const char *accountname,
+                                         const char *protocol);
+static void       cb_timer_control      (void *opdata, unsigned int interval);
+#endif /* HAVE_LIBOTR3 */
 
 static OtrlMessageAppOps ops =
 {
@@ -92,26 +110,44 @@
   cb_create_privkey,
   cb_is_logged_in,
   cb_inject_message,
+#ifdef HAVE_LIBOTR3
   cb_notify,
   cb_display_otr_message,
+#endif
   cb_update_context_list,
+#ifdef HAVE_LIBOTR3
   cb_protocol_name,
   cb_protocol_name_free,
+#endif
   cb_new_fingerprint,
   cb_write_fingerprints,
   cb_gone_secure,
   cb_gone_insecure,
   cb_still_secure,
+#ifdef HAVE_LIBOTR3
   cb_log_message,
+#endif
   cb_max_message_size,
-  NULL, /*account_name*/
-  NULL  /*account_name_free*/
+  NULL, /* account_name */
+  NULL, /* account_name_free */
+#ifndef HAVE_LIBOTR3
+  NULL, /* received_symkey */
+  NULL, /* otr_error_message */
+  NULL, /* otr_error_message_free */
+  NULL, /* resent_msg_prefix */
+  NULL, /* resent_msg_prefix_free */
+  cb_handle_smp_event,
+  cb_handle_msg_event,
+  cb_create_instag,
+  NULL, /* convert_msg */
+  NULL, /* convert_free */
+  cb_timer_control,
+#endif
 };
 
 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);
 
 static char *otr_get_dir(void);
 
@@ -135,7 +171,6 @@
   account = jidtodisp(fjid);
   keyfile = g_strdup_printf("%s%s.key", root, account);
   fprfile = g_strdup_printf("%s%s.fpr", root, account);
-  g_free(root);
 
   if (otrl_privkey_read(userstate, keyfile)){
     scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR key from %s", keyfile);
@@ -145,6 +180,14 @@
     scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR fingerprints from %s",
                  fprfile);
   }
+#ifndef HAVE_LIBOTR3
+  tagfile = g_strdup_printf("%s%s.tag", root, account);
+  if (otrl_instag_read(userstate, tagfile)) {
+    scr_LogPrint(LPRINT_LOGNORM, "Could not read OTR instance tag from %s", tagfile);
+    cb_create_instag(NULL, account, OTR_PROTOCOL_NAME);
+  }
+#endif
+  g_free(root);
 }
 
 void otr_terminate(void)
@@ -154,6 +197,13 @@
   if (!otr_is_enabled)
     return;
 
+#ifndef HAVE_LIBOTR3
+  if (otr_timer_source > 0) {
+    g_source_remove (otr_timer_source);
+    otr_timer_source = 0;
+  }
+#endif
+
   for (ctx = userstate->context_root; ctx; ctx = ctx->next)
     if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
       otr_message_disconnect(ctx);
@@ -175,6 +225,12 @@
   userstate = NULL;
   g_free(keyfile);
   keyfile = NULL;
+  g_free(fprfile);
+  fprfile = NULL;
+#ifndef HAVE_LIBOTR3
+  g_free(tagfile);
+  tagfile = NULL;
+#endif
 }
 
 static char *otr_get_dir(void)
@@ -206,7 +262,12 @@
 
   mc_strtolower(lowcasebuddy);
   ctx = otrl_context_find(userstate, lowcasebuddy, account, OTR_PROTOCOL_NAME,
+#ifdef HAVE_LIBOTR3
                           1, &null, NULL, NULL);
+#else
+                          // INSTAG XXX
+                          OTRL_INSTAG_BEST, 1, &null, NULL, NULL);
+#endif
   g_free(lowcasebuddy);
   return ctx;
 }
@@ -216,7 +277,12 @@
   if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED)
     cb_gone_insecure(NULL, ctx);
   otrl_message_disconnect(userstate, &ops, NULL, ctx->accountname,
+#ifdef HAVE_LIBOTR3
                           ctx->protocol, ctx->username);
+#else
+                          // INSTAG XXX
+                          ctx->protocol, ctx->username, OTRL_INSTAG_BEST);
+#endif
 }
 
 static void otr_startstop(const char *buddy, int start)
@@ -283,6 +349,8 @@
   cb_write_fingerprints(NULL);
 }
 
+#ifdef HAVE_LIBOTR3
+
 static void otr_handle_smp_tlvs(OtrlTLV *tlvs, ConnContext *ctx)
 {
   OtrlTLV *tlv = NULL;
@@ -354,6 +422,130 @@
   }
 }
 
+#else /* HAVE_LIBOTR3 */
+
+static void cb_handle_smp_event(void *opdata, OtrlSMPEvent event,
+                                ConnContext *context, unsigned short percent,
+                                char *question)
+{
+  const char *msg = NULL;
+  char *freeme = NULL;
+  switch (event) {
+    case OTRL_SMPEVENT_ASK_FOR_SECRET:
+      msg = freeme = g_strdup_printf("OTR: Socialist Millionaires' Protocol: "
+                                     "Received SMP Initiation.\n"
+                                     "Answer with /otr smpr %s $secret",
+                                     context->username);
+      break;
+    case OTRL_SMPEVENT_ASK_FOR_ANSWER:
+      msg = freeme = g_strdup_printf("OTR: Socialist Millionaires' Protocol: "
+                                     "Received SMP Initiation.\n"
+                                     "Answer with /otr smpr %s $secret\n"
+                                     "Question: %s", context->username,
+                                     question);
+      break;
+    case OTRL_SMPEVENT_CHEATED:
+      msg = "OTR: Socialist Millionaires' Protocol: Correspondent cancelled negotiation!";
+      otrl_message_abort_smp(userstate, &ops, opdata, context);
+      break;
+    case OTRL_SMPEVENT_IN_PROGRESS:
+      scr_log_print(LPRINT_DEBUG, "OTR: Socialist Millionaires' Protocol: "
+                                  "Negotiation is in pogress...");
+      break;
+    case OTRL_SMPEVENT_SUCCESS:
+      msg = "OTR: Socialist Millionaires' Protocol: Success!";
+      break;
+    case OTRL_SMPEVENT_FAILURE:
+      msg = "OTR: Socialist Millionaires' Protocol: Failure.";
+      break;
+    case OTRL_SMPEVENT_ABORT:
+      msg = "OTR: Socialist Millionaires' Protocol: Aborted.";
+      break;
+    case OTRL_SMPEVENT_ERROR:
+      msg = "OTR: Socialist Millionaires' Protocol: Error occured, aborting negotiations!";
+      otrl_message_abort_smp(userstate, &ops, opdata, context);
+      break;
+    default:
+      break;
+  }
+
+  if (msg) {
+    scr_WriteIncomingMessage(context->username, msg, 0, HBB_PREFIX_INFO, 0);
+    g_free(freeme);
+  }
+}
+
+static void cb_handle_msg_event(void *opdata, OtrlMessageEvent event,
+                                ConnContext *context, const char *message,
+                                gcry_error_t err)
+{
+  const char *msg = NULL;
+  char *freeme = NULL;
+  switch (event) {
+    case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
+      msg = "OTR: Policy requires encryption on message!";
+      break;
+    case OTRL_MSGEVENT_ENCRYPTION_ERROR:
+      msg = "OTR: Encryption error! Message not sent.";
+      break;
+    case OTRL_MSGEVENT_CONNECTION_ENDED:
+      msg = "OTR: Connection closed by remote end, message lost. "
+            "Close or refresh connection.";
+      break;
+    case OTRL_MSGEVENT_SETUP_ERROR:
+      // FIXME
+      msg = freeme = g_strdup_printf("OTR: Error setting up private conversation: %u",
+                                     err);
+      break;
+    case OTRL_MSGEVENT_MSG_REFLECTED:
+      msg = "OTR: Received own OTR message!";
+      break;
+    case OTRL_MSGEVENT_MSG_RESENT:
+      msg = "OTR: Previous message was resent.";
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
+      msg = "OTR: Received encrypted message, but connection is not established " \
+            "yet! Message lost.";
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
+      msg = "OTR: Unable to read incoming message!";
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
+      msg = "OTR: Malformed incoming message!";
+      break;
+    case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
+      scr_log_print(LPRINT_DEBUG, "OTR: Received heartbeat.");
+      break;
+    case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
+      scr_log_print(LPRINT_DEBUG, "OTR: Sent heartbeat.");
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
+      msg = freeme = g_strdup_printf("OTR: Received general otr error: %s",
+                                     message);
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
+      msg = freeme = g_strdup_printf("OTR: Received unencrypted message: %s",
+                                     message);
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
+      msg = "OTR: Unable to determine type of received OTR message!";
+      break;
+    case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
+      // XXX
+      scr_log_print(LPRINT_DEBUG, "OTR: Received message for other instance.");
+      break;
+    default:
+      break;
+  }
+
+  if (msg) {
+    scr_WriteIncomingMessage(context->username, msg, 0, HBB_PREFIX_INFO, 0);
+    g_free(freeme);
+  }
+}
+
+#endif /* HAVE_LIBOTR3 */
+
 /*
  * returns whether a otr_message was received
  * sets *otr_data to NULL, when it was an internal otr message
@@ -362,8 +554,10 @@
 {
   int ignore_message;
   char *newmessage = NULL;
+#ifdef HAVE_LIBOTR3
   OtrlTLV *tlvs = NULL;
   OtrlTLV *tlv = NULL;
+#endif
   ConnContext *ctx;
 
   ctx = otr_get_context(buddy);
@@ -371,8 +565,8 @@
   ignore_message = otrl_message_receiving(userstate, &ops, NULL,
                                           ctx->accountname, ctx->protocol,
                                           ctx->username, *otr_data,
-                                          &newmessage, &tlvs,NULL, NULL);
-
+#ifdef HAVE_LIBOTR3
+                                          &newmessage, &tlvs, NULL, NULL);
 
   tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
   if (tlv) {
@@ -387,6 +581,9 @@
 
   if (tlvs != NULL)
     otrl_tlv_free(tlvs);
+#else
+                                          &newmessage, NULL, NULL, NULL, NULL);
+#endif
 
   if (ignore_message)
     *otr_data = NULL;
@@ -410,13 +607,27 @@
 
   if (ctx->msgstate == OTRL_MSGSTATE_PLAINTEXT)
     err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
+#ifdef HAVE_LIBOTR3
                                ctx->protocol, ctx->username, *msg, NULL,
                                &newmessage, NULL, NULL);
+#else
+                               // INSTAG XXX
+                               ctx->protocol, ctx->username, OTRL_INSTAG_BEST,
+                               *msg, NULL, &newmessage, OTRL_FRAGMENT_SEND_SKIP,
+                               NULL, NULL, NULL);
+#endif
   else {
     htmlmsg = html_escape(*msg);
     err = otrl_message_sending(userstate, &ops, NULL, ctx->accountname,
+#ifdef HAVE_LIBOTR3
                                ctx->protocol, ctx->username, htmlmsg, NULL,
                                &newmessage, NULL, NULL);
+#else
+                               // INSTAG XXX
+                               ctx->protocol, ctx->username, OTRL_INSTAG_BEST,
+                               htmlmsg, NULL, &newmessage, OTRL_FRAGMENT_SEND_SKIP,
+                               NULL, NULL, NULL);
+#endif
     g_free(htmlmsg);
   }
 
@@ -648,42 +859,6 @@
                   LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
 }
 
-/* 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;
-    default:                  type = "unknown";
-  }
-  sbuf = g_strdup_printf("OTR %s:%s\n%s\n%s",type,title, primary, secondary);
-  scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
-  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)
-{
-  char *strippedmsg = html_strip(msg);
-  scr_WriteIncomingMessage(username, strippedmsg, 0, HBB_PREFIX_INFO, 0);
-  g_free(strippedmsg);
-  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)
@@ -692,19 +867,6 @@
    * 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,
@@ -748,12 +910,92 @@
                            HBB_PREFIX_INFO, 0);
 }
 
+#ifdef HAVE_LIBOTR3
+
+/* 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;
+    default:                  type = "unknown";
+  }
+  sbuf = g_strdup_printf("OTR %s:%s\n%s\n%s",type,title, primary, secondary);
+  scr_WriteIncomingMessage(username, sbuf, 0, HBB_PREFIX_INFO, 0);
+  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)
+{
+  char *strippedmsg = html_strip(msg);
+  scr_WriteIncomingMessage(username, strippedmsg, 0, HBB_PREFIX_INFO, 0);
+  g_free(strippedmsg);
+  return 0;
+}
+
+/* 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 */
+}
+
 /* 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);
 }
 
+#else /* HAVE_LIBOTR3 */
+
+/* Generate unique instance tag for account. */
+static void cb_create_instag(void *opdata, const char *accountname,
+                             const char *protocol)
+{
+  if (otrl_instag_generate(userstate, tagfile, accountname, protocol)) {
+    scr_LogPrint(LPRINT_LOGNORM, "OTR instance tag generation failed!");
+  }
+}
+
+static gboolean otr_timer_cb(gpointer userdata)
+{
+  otrl_message_poll(userstate, &ops, userdata);
+  return TRUE;
+}
+
+static void cb_timer_control(void *opdata, unsigned int interval)
+{
+  if (otr_timer_source > 0) {
+    g_source_remove(otr_timer_source);
+    otr_timer_source = 0;
+  }
+  if (interval > 0)
+    otr_timer_source = g_timeout_add_seconds(interval, otr_timer_cb, opdata);
+}
+
+#endif /* HAVE_LIBOTR3 */
+
 /* Find the maximum message size supported by this protocol. */
 static int cb_max_message_size(void *opdata, ConnContext *context)
 {
diff -r 1b31bdb72d83 mcabber/mcabber/otr.h
--- a/mcabber/mcabber/otr.h	Sun Jan 13 04:35:32 2013 +0200
+++ b/mcabber/mcabber/otr.h	Fri Jan 18 11:23:47 2013 +0200
@@ -5,6 +5,9 @@
 
 #ifdef HAVE_LIBOTR
 
+#ifndef HAVE_LIBOTR3
+# include <libotr/instag.h>
+#endif
 #include <libotr/proto.h>
 #include <libotr/message.h>
 #include <libotr/privkey.h>
