Hi all

Here the mail comes again but this time with the attachment as an
attachment.

I want to submit/discuss a patch for OTR encryption.
At first I must apologize for that big patch ;-)

This patch integrates OTR encryption support
(http://www.cypherpunks.ca/otr/) in latest Licq CVS.

What is OTR?
Off-the-Record (OTR) Messaging allows you to have private conversations
over instant messaging by providing:
Encryption
 No one else can read your instant messages.
Authentication
 You are assured the correspondent is who you think it is.
Deniability
 The messages you send do not have digital signatures that are checkable
 by a third party. Anyone can forge messages after a conversation to
make them look like they came from you. However, during a conversation,
your correspondent is assured the messages he sees are authentic and
unmodified.
Perfect forward secrecy
 If you lose control of your private keys, no previous conversation is
compromised.
For more Information on OTR go to their homepage.

The patch integrates OTR into the core. I did not made changes to the
User Interface.
The following files where changes: (short discription of change)

admin/acinclude.m4.in
- check for otr library version >= 3.0.0

configure.in
- call check of acinclude.m4.in

src/Makefile.am
- include otr.cpp

include/licq_otr.h
src/otr.cpp
- define a new class COTRHelper with Encrypt(), Decrypt() ...

src/icqd.cpp
- call gOTRHelper.Start() and read/load otr config files

src/icqd-srv.cpp
- in ProcessDoneEvent() check whether the message is an OTR System
Message. If so, don't log it in history and don't play sound...
- in ProcessMessageFam() call Decrypt(). Call it here and not in
CEventMsg() like GPG because we only need to decrypt messages coming
from outside.

include/licq_icqd.h
src/icqd-tcp.cpp
- set flag E_OTRxSYSxMSG if message is an Otr System Message. See
ProcessDoneEvent()
- call Encrypt() in ProtoSendMessage()
- define ProtoOtr...() functions. These functions are the interface to
the UI. The UI can call these functions to open, close secure
connections and generate a private key for the user.
ProtoOtrDisplaySystemMessage() is used by COTRHelper to send Messages to
the UI.

include/licq_message.h
src/message.cpp
- define E_OTRxSYSxMSG used in ProcessDoneEvent()
- class CEventOtrSysMsg() is used to send message from the OTR to the UI.

include/licq_user.h
- ICQUser has a variable OTRMode. Possible values are
  * "OPPORTUNISTIC" - mark outgoing messages so, that others know you
offer OTR and if others offer OTR to you then make secure connection
automatically
  * "MANUAL" - no automatically secured connection, only manual
  * "ALWAYS" - no unsecured connection possible
  * "NEVER" - never secure connection == deactivate
- ICQOwner has a variable bool UseOTRUserSpecific. If it's false use the
Owners OTRMode for all connection. If it's false look at the users
OTRModes to distinguish the Otr policy.

include/licq_icq.h
- define ICQ_CMDxSUB_OTRxMSG that are send from OTR to UI
- ICQ_OTR_... If the UI receives a ICQ_CMDxSUB_OTRxMSG it can decide
with the help of the ICQ_OTR_... type what to do with the message.

src/user.cpp
- look for OTRMode in config file


Configuration
-------------
As shown above you can set different policies for different users. If
you only want to set one policy for all user set
UseOTRUserSpecific=false. By default the policiy is opportunistic. That
means if two people have licq with this patch they don't even need an
encryption button. Why? In opportunistic mode some space characters are
added to the message. If the other side notices that and has has also
opportunistic configured the connection will automatically be secured.

User Interface
--------------
The following OTR messages are send from OTR to the UI
nSubCommand = ICQ_CMDxSUB_OTRxMSG
nCommand=ICQ_OTR_CREATING_PRIVKEY
         ICQ_OTR_NOTIFY_MSG
         ICQ_OTR_MSG
         ICQ_OTR_CONFIRM_FINGERPRINT
         ICQ_OTR_CONN_SECURE
         ICQ_OTR_CONN_INSECURE
         ICQ_OTR_CONN_STILL_SECURE
I think it's a good idea to write messages like "connection secured",
"connection insecured" and so on directly into the conversation window
of the user. So the user knows exactly at which point a connection gets
secured and no extra windows needs to pop up. These system messages
should have a different color than these of the users.
The UI should have a button to create and close secure connections.
The user should also be able to configure the policies for all users,
generate his private key and look at the fingerprint of himself and the
other users.
All the UI stuff needs to be done.


I hope I could make my work a little bit understandable. If there are
any questions then mail me, the mailinglist or look for me (rsLeo) at #Licq.

At the end I want to thank you all for the great work.


cu
rsLeo




diff -urN licq_orig/admin/acinclude.m4.in licq_patched/admin/acinclude.m4.in
--- licq_orig/admin/acinclude.m4.in     2006-02-02 19:59:46.055999440 +0100
+++ licq_patched/admin/acinclude.m4.in  2006-02-02 19:57:22.939756416 +0100
@@ -267,6 +267,93 @@
   fi
 ])
 
+AC_DEFUN([LICQ_CHECK_LIBOTR],
+[
+  libotr_save_LIBS="$LIBS"
+  libotr_save_CPPFLAGS="$CPPFLAGS"
+  
+  min_libotr_version="3.0.0"
+  libotr_min_major_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    libotr_min_minor_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    libotr_min_sub_version=`echo $min_libotr_version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+  AC_ARG_ENABLE(
+    libotr,
+    [  --disable-libotr        disable OTR support],
+    WITH_LIBOTR="$enableval", WITH_LIBOTR="yes")
+
+  AC_ARG_WITH(
+    libotr-inc,
+    [  --with-libotr-inc=PATH  include path for libotr headers],
+    libotr_incdir="$withval", libotr_incdir="no")
+ 
+  AC_ARG_WITH(
+    libotr-lib,
+    [  --with-libotr-lib=PATH  library path for libotr],
+    libotr_libdir="$withval", libotr_libdir="no")
+
+  if test "$WITH_LIBOTR" = "no"; then
+    AC_MSG_CHECKING(for libotr )
+    AC_MSG_RESULT(no)
+  else
+    dnl
+    dnl check for headers
+    dnl
+    AC_MSG_CHECKING(for libotr headers version $libotr_min_major_version.x >= 
$min_libotr_version)
+
+    if test "$libotr_incdir" != "no"; then
+      CPPFLAGS="$CPPFLAGS -I$libotr_incdir"
+    fi
+
+    AC_LANG_SAVE
+    AC_LANG_C
+    AC_COMPILE_IFELSE([
+      AC_LANG_PROGRAM([
+        #include <stdlib.h>
+        #include <libotr/version.h>
+        ],[
+          if(OTRL_VERSION_MAJOR != $libotr_min_major_version)
+            exit(1);
+          else
+            if(OTRL_VERSION_MINOR > $libotr_min_minor_version)
+               exit(0);
+            else
+            {
+              if(OTRL_VERSION_MINOR < $libotr_min_minor_version)
+                exit(1);
+              if(OTRL_VERSION_SUB < $libotr_min_sub_version)
+                exit(1);
+            }
+          exit(0);])
+      ],
+      [AC_MSG_RESULT(found.)],
+      [AC_MSG_RESULT(not present.)
+       WITH_LIBOTR="no"]
+    )
+    AC_LANG_RESTORE
+
+    if test "$WITH_LIBOTR" = "yes"; then
+      dnl
+      dnl check for library
+      dnl
+      if test "$libotr_libdir" != "no"; then
+        LIBS="$LIBS -L$libotr_libdir"
+      fi
+      AC_CHECK_LIB(otr, otrl_message_sending, WITH_LIBOTR="yes", 
WITH_LIBOTR="no")
+      if test "$WITH_LIBOTR" = "yes"; then
+       AC_DEFINE_UNQUOTED(HAVE_LIBOTR, 1, [Define to 1 if you have the 'otr' 
library (-otr)])
+       LIBS="-lotr $LIBS"
+      else
+        CPPFLAGS="$libotr_save_CPPFLAGS"
+       LIBS="$libotr_save_LIBS"
+      fi
+    fi
+  fi
+])
+
 AC_DEFUN([LICQ_CHECK_OPENSSL],
 [
   AC_MSG_CHECKING(if OpenSSL support is desired)
diff -urN licq_orig/configure.in licq_patched/configure.in
--- licq_orig/configure.in      2006-02-02 20:00:38.506025824 +0100
+++ licq_patched/configure.in   2006-02-02 19:57:23.006746232 +0100
@@ -150,6 +150,7 @@
 AC_CHECK_SOCKS5
 LICQ_CHECK_GPGME
 LICQ_CHECK_OPENSSL
+LICQ_CHECK_LIBOTR
 
 dnl Switch to C++ mode and check for needed C++ headers
 AC_LANG_SAVE
@@ -209,7 +210,11 @@
 else
   echo "GPGME support is enabled."
 fi
-
+if test "$WITH_OTR" = "no"; then
+  echo "OTR support is disabled."
+else
+  echo "OTR support is enabled."
+fi
 if test "$WITH_OPENSSL" = "no"; then
   echo ""
   echo "OpenSSL support is not available.  Licq will not be able to"
diff -urN licq_orig/include/licq_icqd.h licq_patched/include/licq_icqd.h
--- licq_orig/include/licq_icqd.h       2006-02-02 20:00:20.281796328 +0100
+++ licq_patched/include/licq_icqd.h    2006-02-02 19:57:23.034741976 +0100
@@ -296,6 +296,13 @@
   unsigned long ProtoCloseSecureChannel(const char *szId, unsigned long nPPID);
   void ProtoOpenSecureChannelCancel(const char *szId, unsigned long nPPID,
     unsigned long nSequence);
+  void ProtoOtrGenerateKey(unsigned long nPPID);
+  void ProtoOtrOpenSecureChannel(unsigned long nPPID, const char *szRecId);
+  void ProtoOtrOpenSecureChannel(unsigned long nPPID, unsigned long nRecId);
+  void ProtoOtrCloseSecureChannel(unsigned long nPPID, const char *szRecId);
+  void ProtoOtrCloseSecureChannel(unsigned long nPPID, unsigned long nRecId);
+  void ProtoOtrDisplaySystemMessage(const char *szId, const char *szMsg, 
+    const unsigned char nType);
     
   // TCP (user) functions
   // Message
diff -urN licq_orig/include/licq_icq.h licq_patched/include/licq_icq.h
--- licq_orig/include/licq_icq.h        2006-02-02 20:00:20.290794960 +0100
+++ licq_patched/include/licq_icq.h     2006-02-02 19:57:23.029742736 +0100
@@ -323,6 +323,7 @@
 const unsigned short ICQ_CMDxSUB_PICTURE           = 0x0019;
 const unsigned short ICQ_CMDxSUB_SMS               = 0x001A;
 const unsigned short ICQ_CMDxSUB_ICBM              = 0x001A;  // This one sucks
+const unsigned short ICQ_CMDxSUB_OTRxMSG           = 0x001B;
 const unsigned short ICQ_CMDxSUB_FxMULTIREC        = 0x8000;
 // These exist as ICBM plugins only and the number is unofficial
 const unsigned short ICQ_CMDxSUB_SENDxEICQ         = 0x00D7;
@@ -424,6 +425,15 @@
 const unsigned char ICQ_PRIVACY_BLOCK_FOLLOWING   = 4;
 const unsigned char ICQ_PRIVACY_ALLOW_ONLY_LIST   = 5;
 
+//OTR messages
+const unsigned char ICQ_OTR_CREATING_PRIVKEY      = 1;
+const unsigned char ICQ_OTR_NOTIFY_MSG            = 2;
+const unsigned char ICQ_OTR_MSG                   = 3;
+const unsigned char ICQ_OTR_CONFIRM_FINGERPRINT   = 4;
+const unsigned char ICQ_OTR_CONN_SECURE           = 5;
+const unsigned char ICQ_OTR_CONN_INSECURE         = 6;
+const unsigned char ICQ_OTR_CONN_STILL_SECURE     = 7;
+
 const unsigned char ICQ_PLUGIN_REQUEST            = 0;
 const unsigned char ICQ_PLUGIN_SUCCESS            = 1;
 const unsigned char ICQ_PLUGIN_STATUSxREPLY       = 2;
diff -urN licq_orig/include/licq_message.h licq_patched/include/licq_message.h
--- licq_orig/include/licq_message.h    2006-02-02 20:00:20.318790704 +0100
+++ licq_patched/include/licq_message.h 2006-02-02 19:57:23.032742280 +0100
@@ -20,6 +20,7 @@
 const unsigned long E_URGENT        = 0x00040000;
 const unsigned long E_CANCELLED     = 0x00080000;
 const unsigned long E_ENCRYPTED     = 0x00100000;
+const unsigned long E_OTRxSYSxMSG   = 0x00200000;
 const unsigned long E_UNKNOWN       = 0x80000000;
 
 class ICQUser;
@@ -51,6 +52,7 @@
    bool IsCancelled() { return m_nFlags & E_CANCELLED; }
    bool IsLicq()  { return LicqVersion() != 0; };
    bool IsEncrypted()  { return m_nFlags & E_ENCRYPTED; };
+   bool IsOtrSysMsg()  { return m_nFlags & E_OTRxSYSxMSG; };
    unsigned short LicqVersion()  { return m_nFlags & E_LICQxVER; }
    direction Direction()  {  return m_eDir; }
    CICQColor *Color() { return &m_sColor; }
@@ -559,6 +561,31 @@
   char *m_sz;
 };
 
+//-----CEventOtrSysMsg-------------------------------------------------------------
+class CEventOtrSysMsg : public CUserEvent
+{
+public:
+  CEventOtrSysMsg(const char *_szMessage, 
+                  unsigned short _nCommand,
+                  time_t _tTime, 
+                  const unsigned char _nType);
+  virtual ~CEventOtrSysMsg();
+  virtual CEventOtrSysMsg *Copy()
+    {
+      CEventOtrSysMsg *e = new CEventOtrSysMsg(m_szMessage, m_nCommand, 
+                                               m_tTime, m_nType);
+      e->CopyBase(this);
+      return e;
+    }
+  const char *Message()  { return m_szMessage; }
+  unsigned char Type() { return m_nType; }
+  virtual void AddToHistory(ICQUser *, unsigned long, direction);
+        
+protected:
+  void CreateDescription();
+  char *m_szMessage;
+  unsigned char m_nType;
+};
 
 //-----CEventUnknownSysMsg-----------------------------------------------------
 class CEventUnknownSysMsg : public CUserEvent
diff -urN licq_orig/include/licq_user.h licq_patched/include/licq_user.h
--- licq_orig/include/licq_user.h       2006-02-02 20:00:20.393779304 +0100
+++ licq_patched/include/licq_user.h    2006-02-02 19:57:23.035741824 +0100
@@ -547,6 +547,7 @@
   time_t IdleSince()                    { return m_nIdleSince; }
   bool UseGPG()                                { return m_bUseGPG; }
   char* GPGKey()                       { return m_szGPGKey; }
+  char* OTRMode()                      { return m_szOTRMode; }
   bool AutoChatAccept()                 { return m_nAutoAccept & 
AUTO_ACCEPT_CHAT; }
   bool AutoFileAccept()                 { return m_nAutoAccept & 
AUTO_ACCEPT_FILE; }
   bool AutoSecure()                     { return m_nAutoAccept & AUTO_SECURE; }
@@ -655,6 +656,7 @@
   void SetAcceptInDND(bool s)         { s ? m_nAutoAccept |= ACCEPT_IN_DND : 
m_nAutoAccept &= ~ACCEPT_IN_DND; SaveLicqInfo(); }
   void SetUseGPG(bool b)               { m_bUseGPG = b; SaveLicqInfo(); }
   void SetGPGKey(const char *c)                { SetString(&m_szGPGKey, c); 
SaveLicqInfo(); }
+  void SetOTRMode(const char *c)               { SetString(&m_szOTRMode, c); 
SaveLicqInfo(); }
   void SetStatusToUser(unsigned short s)    { m_nStatusToUser = s; 
SaveLicqInfo(); }
   void SetKeepAliasOnUpdate(bool b)   { m_bKeepAliasOnUpdate = b; }
   void SetCustomAutoResponse(const char *s) { 
SetString(&m_szCustomAutoResponse, s); SaveLicqInfo(); }
@@ -861,7 +863,10 @@
   // GPG data
   bool m_bUseGPG;
   char *m_szGPGKey;
-
+  
+  //OTR data
+  char *m_szOTRMode;  //see gOTRHelper.policyNameToValue
+       
   // General Info
   char *m_szAlias;
   char *m_szFirstName;
@@ -975,6 +980,8 @@
   bool SavePassword()         { return m_bSavePassword; }
   unsigned long RandomChatGroup() { return m_nRandomChatGroup; }
   unsigned long AddStatusFlags(unsigned long nStatus);
+  bool UseOTRUserSpecific()                            { return 
m_bUseOTRUserSpecific; }
+  void SetUseOTRUserSpecific(bool b)           { m_bUseOTRUserSpecific = b; 
SaveLicqInfo(); }  
 
   // Server Side List functions
   time_t GetSSTime()                  { return m_nSSTime; }
@@ -1001,6 +1008,7 @@
   unsigned short m_nSSCount;
   time_t unsigned long m_nSSTime;
   unsigned short m_nPDINFO;
+  bool m_bUseOTRUserSpecific;
 };
 
 
diff -urN licq_orig/src/icqd.cpp licq_patched/src/icqd.cpp
--- licq_orig/src/icqd.cpp      2006-02-02 20:00:54.824545032 +0100
+++ licq_patched/src/icqd.cpp   2006-02-02 19:57:22.913760368 +0100
@@ -39,6 +39,7 @@
 #include "licq_packets.h"
 #include "licq_plugind.h"
 #include "licq_gpg.h"    // ##
+#include "licq_otr.h"
 #include "licq.h"
 #include "support.h"
 
@@ -347,6 +348,9 @@
 
   // start GPG helper
   gGPGHelper.Start();
+       
+  //start OTR helper
+  gOTRHelper.Start();
 
   // Start up our threads
   pthread_mutex_init(&mutex_runningevents, NULL);
diff -urN licq_orig/src/icqd-srv.cpp licq_patched/src/icqd-srv.cpp
--- licq_orig/src/icqd-srv.cpp  2006-02-02 20:00:54.901533328 +0100
+++ licq_patched/src/icqd-srv.cpp       2006-02-02 19:57:22.911760672 +0100
@@ -39,6 +39,8 @@
 #include "licq_countrycodes.h"
 #include "licq_protoplugind.h"
 
+#include "licq_otr.h"
+
 void CICQDaemon::ProtoAddUser(const char *_szId, unsigned long _nPPID,
                               bool _bAuthRequired)
 {
@@ -1742,7 +1744,8 @@
   // Write the event to the history file if appropriate
   if (e->m_pUserEvent != NULL &&
       (e->m_eResult == EVENT_ACKED || e->m_eResult == EVENT_SUCCESS) &&
-      e->m_nSubResult != ICQ_TCPxACK_RETURN)
+      e->m_nSubResult != ICQ_TCPxACK_RETURN &&
+           e->m_pUserEvent->IsOtrSysMsg() == false)
   {
     ICQUser *u = gUserManager.FetchUser(e->m_szId, e->m_nPPID, LOCK_R);
     if (u != NULL)
@@ -3039,6 +3042,21 @@
       delete [] szMessage;
 
       // now send the message to the user
+                       
+#ifdef HAVE_LIBOTR                     
+      char *szNewMsg = NULL;
+      int ret = gOTRHelper.Decrypt(szMsg, &szNewMsg, LICQ_PPID, szId);
+      if (ret == 1) { //internal message. don't show to user.
+        break;
+       }
+       if (szNewMsg != NULL) { //replace original message with decrypted one
+         gLog.Info("replace original message\n");
+         delete [] szMsg;
+         szMsg = new char[strlen(szNewMsg)+1];
+         strcpy(szMsg, szNewMsg);
+         gOTRHelper.FreeNewMessage(&szNewMsg);
+       }                               
+#endif
       CEventMsg *e = CEventMsg::Parse(szMsg, ICQ_CMDxRCV_SYSxMSGxONLINE, 
nTimeSent, 0);
       delete [] szMsg;
 
diff -urN licq_orig/src/icqd-tcp.cpp licq_patched/src/icqd-tcp.cpp
--- licq_orig/src/icqd-tcp.cpp  2006-02-02 20:00:54.935528160 +0100
+++ licq_patched/src/icqd-tcp.cpp       2006-02-02 19:57:22.908761128 +0100
@@ -34,9 +34,16 @@
 #include "licq_chat.h"
 #include "licq_filetransfer.h"
 #include "licq_gpg.h"
+#include "licq_otr.h"
 #include "support.h"
 #include "licq_protoplugind.h"
 
+#ifdef HAVE_LIBOTR
+extern "C" {
+  #include <libotr/proto.h>
+};
+#endif
+
 //-----ICQ::sendMessage--------------------------------------------------------
 unsigned long CICQDaemon::ProtoSendMessage(const char *_szId, unsigned long 
_nPPID,
    const char *m, bool online, unsigned short nLevel, bool bMultipleRecipients,
@@ -79,8 +86,28 @@
   u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_R);
   if (u)
     bUserOffline = u->StatusOffline();
+  if (gOTRHelper.IsOtrSysMsg(mDos))
+    f |= E_OTRxSYSxMSG;                
   if (u && u->UseGPG() && !bUserOffline)
     cipher = gGPGHelper.Encrypt(mDos, szId, LICQ_PPID);
+
+#ifdef HAVE_LIBOTR
+  //encrypt message and replace the original one
+  char *szNewMsg = NULL;
+  if ( otrl_proto_message_type(mDos) == OTRL_MSGTYPE_NOTOTR ) { //only encrypt 
if the message is not already an internal OTR message
+    int ret = gOTRHelper.Encrypt(mDos, &szNewMsg, LICQ_PPID, szId);
+    if ( ret == 1 ) { //error encrypting. DON'T SEND IT UNENCRYPTED!
+      gLog.Error("Failed to encrypt message. Won't send message unencrypted.");
+      return 0;
+    }
+    if ( szNewMsg != NULL ) { //replace original message with encrypted one
+      gLog.Info("replace unencrypted message\n");
+      cipher = (char*) malloc(strlen(szNewMsg)+1);
+      strcpy(cipher, szNewMsg);
+      gOTRHelper.FreeNewMessage(&szNewMsg);
+    }
+  }
+#endif
   gUserManager.DropUser(u);
 
   if (cipher) f |= E_ENCRYPTED;
@@ -1461,6 +1488,64 @@
   return false;
 }
 
+/*------------------------------------------------------------------------------
+ * OTR encryption stuff
+ 
*----------------------------------------------------------------------------*/
+void CICQDaemon::ProtoOtrGenerateKey(unsigned long nPPID)
+{
+  ICQOwner* o = gUserManager.FetchOwner(nPPID, LOCK_N);
+  gOTRHelper.GenerateKey(o->IdString(), nPPID);
+  gUserManager.DropOwner(nPPID);
+}
+
+void CICQDaemon::ProtoOtrOpenSecureChannel(unsigned long nPPID,        const 
char *szRecId)
+{
+  ICQOwner* o = gUserManager.FetchOwner(nPPID, LOCK_N);
+  gOTRHelper.StartPrivateConversation(o->IdString(), nPPID, szRecId);
+  gUserManager.DropOwner(nPPID);
+}
+
+void CICQDaemon::ProtoOtrOpenSecureChannel(unsigned long nPPID,        
unsigned long nRecId)
+{
+  char szUin[24];
+  sprintf(szUin, "%lu", nRecId);
+  ProtoOtrOpenSecureChannel(nPPID, szUin);
+}
+
+void CICQDaemon::ProtoOtrCloseSecureChannel(unsigned long nPPID, const char 
*szRecId)
+{
+  ICQOwner* o = gUserManager.FetchOwner(nPPID, LOCK_N);
+  gOTRHelper.EndPrivateConversation(o->IdString(), nPPID, szRecId);
+  gUserManager.DropOwner(nPPID);
+}
+
+void CICQDaemon::ProtoOtrCloseSecureChannel(unsigned long nPPID, unsigned long 
nRecId)
+{
+  char szUin[24];
+  sprintf(szUin, "%lu", nRecId);
+  ProtoOtrCloseSecureChannel(nPPID, szUin);
+}
+
+void CICQDaemon::ProtoOtrDisplaySystemMessage(const char *szId, const char 
*szMsg, 
+  const unsigned char nType)
+{
+  gLog.Info("ProtoOtrDisplaySystemMessage - message: %s\nusername: %s\n", 
szMsg, szId);
+  
+  CEventOtrSysMsg *e = new CEventOtrSysMsg(szMsg, nType, 0, 0);
+  if (nType==ICQ_OTR_CREATING_PRIVKEY) {
+    ICQOwner *o = gUserManager.FetchOwner(LICQ_PPID, LOCK_W);
+    gLicqDaemon->AddUserEvent(o, e);  //e will be deleted here
+    gUserManager.DropOwner(LICQ_PPID);
+  }
+  else
+  {
+    ICQUser *u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W);
+    gLicqDaemon->AddUserEvent(u, e);  //e will be deleted here
+    gUserManager.DropUser(u);
+  }
+}
+
+
 
 
/*------------------------------------------------------------------------------
  * ConnectToUser
diff -urN licq_orig/src/Makefile.am licq_patched/src/Makefile.am
--- licq_orig/src/Makefile.am   2006-02-02 20:10:55.831178136 +0100
+++ licq_patched/src/Makefile.am        2006-02-02 19:57:22.916759912 +0100
@@ -19,7 +19,7 @@
         icqd-chat.cpp sighandler.c icqd-filetransfer.cpp \
         hebrev.c icqcolor.cpp fifo.cpp  protoplugind.cpp gpg.cpp \
          occupationcodes.c homepagecodes.c interestcodes.c \
-         organizationcodes.c backgroundcodes.c providers.c rtf.cc
+         organizationcodes.c backgroundcodes.c providers.c rtf.cc otr.cpp
 
 licq_LDADD = @SOCKS_LIBS@ 
 licq_LDFLAGS = -export-dynamic @SOCKS_LIBDIR@
diff -urN licq_orig/src/message.cpp licq_patched/src/message.cpp
--- licq_orig/src/message.cpp   2006-02-02 20:00:55.108501864 +0100
+++ licq_patched/src/message.cpp        2006-02-02 19:57:22.901762192 +0100
@@ -22,6 +22,7 @@
 #include "licq_translate.h"
 #include "licq_icqd.h"
 #include "licq_gpg.h"
+#include "licq_otr.h"
 #include "support.h"
 
 #ifdef USE_HEBREW
@@ -1018,6 +1019,39 @@
 }
 
 
+
+
+//=====CEventOtrSysMsg=====================================================
+CEventOtrSysMsg::CEventOtrSysMsg(const char *_szMessage, unsigned short 
_nCommand,
+    time_t _tTime, const unsigned char _nType)
+    : CUserEvent(ICQ_CMDxSUB_OTRxMSG, ICQ_CMDxRCV_SYSxMSGxONLINE, 0, _tTime, 0)
+{
+  m_szMessage = strdup(_szMessage == NULL ? "" : _szMessage);
+  m_nType = _nType;
+}
+
+CEventOtrSysMsg::~CEventOtrSysMsg()
+{
+  free(m_szMessage);
+}
+
+void CEventOtrSysMsg::CreateDescription()
+{
+  if (m_szText) delete [] m_szText;
+  char* tmp = "OTR System Message: ";
+  int tmplen = strlen(m_szMessage) + strlen(tmp) +2;
+  m_szText = new char[tmplen];
+  snprintf(m_szText, tmplen, "%s%s\n", tmp, m_szMessage);
+}
+
+void CEventOtrSysMsg::AddToHistory(ICQUser *u, unsigned long _nPPID, direction 
_nDir)
+{
+  //don't write anything to history
+}
+
+
+
+
 //=====CEventUnknownSysMsg=====================================================
 CEventUnknownSysMsg::CEventUnknownSysMsg(unsigned short _nSubCommand,
                              unsigned short _nCommand, unsigned long _nUin,
diff -urN licq_orig/src/user.cpp licq_patched/src/user.cpp
--- licq_orig/src/user.cpp      2006-02-02 20:00:55.265478000 +0100
+++ licq_patched/src/user.cpp   2006-02-02 19:57:22.929757936 +0100
@@ -2039,6 +2039,8 @@
   m_fConf.ReadBool("UseGPG", m_bUseGPG, false );
   m_fConf.ReadStr("GPGKey", szTemp, "" );
   SetString( &m_szGPGKey, szTemp );
+  m_fConf.ReadStr("OTRMode", szTemp, "OPPORTUNISTIC");
+  SetString(&m_szOTRMode, szTemp);
   m_fConf.ReadNum("PPFieldCount", nPPFieldCount, 0);
   for (int i = 0; i < nPPFieldCount; i++)
   {
@@ -2166,6 +2168,8 @@
       free( m_szId );
   if ( m_szGPGKey )
       free( m_szGPGKey );
+  if ( m_szOTRMode )
+      free( m_szOTRMode );
   
   delete m_Interests;
   delete m_Organizations;
@@ -2294,6 +2298,8 @@
 
   // GPG key
   m_szGPGKey = strdup("");
+       
+  m_szOTRMode = strdup("OPPORTUNISTIC");
 
   if (_szId)
     m_szId = strdup(_szId);
@@ -3440,6 +3446,7 @@
    m_fConf.WriteNum("SharedFilesStatus", m_nSharedFilesStatus);
    m_fConf.WriteBool("UseGPG", m_bUseGPG );
    m_fConf.WriteStr("GPGKey", m_szGPGKey );
+   m_fConf.WriteStr("OTRMode", m_szOTRMode );
    m_fConf.WriteNum("PPFieldCount", (unsigned short)m_mPPFields.size());
    
    std::map<string,string>::iterator iter;
@@ -3745,6 +3752,7 @@
   m_fConf.ReadNum("SSTime", m_nSSTime, 0L);
   m_fConf.ReadNum("SSCount", m_nSSCount, 0);
   m_fConf.ReadNum("PDINFO", m_nPDINFO, 0);
+  m_fConf.ReadBool("UseOTRUserSpecific", m_bUseOTRUserSpecific, false);
 
   SetAutoResponse(szTemp);
 
@@ -3817,6 +3825,7 @@
   m_fConf.ReadNum("SSTime", m_nSSTime, 0L);
   m_fConf.ReadNum("SSCount", m_nSSCount, 0);
   m_fConf.ReadNum("PDINFO", m_nPDINFO, 0);
+  m_fConf.ReadBool("UseOTRUserSpecific", m_bUseOTRUserSpecific, false);
   
   SetAutoResponse(szTemp);
 
@@ -3853,6 +3862,8 @@
   m_fConf.WriteNum("SSTime", m_nSSTime);
   m_fConf.WriteNum("SSCount", m_nSSCount);
   m_fConf.WriteNum("PDINFO", m_nPDINFO);
+  m_fConf.WriteBool("UseOTRUserSpecific", m_bUseOTRUserSpecific);
+
   if (!m_fConf.FlushFile())
   {
     gLog.Error("%sError opening '%s' for writing.\n%sSee log for details.\n",
@@ -3908,6 +3919,7 @@
   m_fConf.WriteNum("SSTime", m_nSSTime);
   m_fConf.WriteNum("SSCount", m_nSSCount);
   m_fConf.WriteNum("PDINFO", m_nPDINFO);
+  m_fConf.WriteBool("UseOTRUserSpecific", m_bUseOTRUserSpecific);
   
   if (m_bSavePassword)
     m_fConf.WriteStr("Password", Password());
--- /dev/null   2005-08-09 09:06:25.000000000 +0200
+++ licq_patched/include/licq_otr.h     2006-02-02 19:57:23.030742584 +0100
@@ -0,0 +1,98 @@
+/*
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+// written by Martin Garbe <[EMAIL PROTECTED]>
+
+#ifndef _LICQ_OTR_H_INCLUDED_
+#define _LICQ_OTR_H_INCLUDED_
+
+#include <pthread.h>
+
+//libotr headers
+#ifdef HAVE_LIBOTR
+extern "C" {
+  #include <libotr/proto.h>
+  #include <libotr/userstate.h>
+  #include <libotr/message.h>
+  #include <libotr/privkey.h>
+};
+
+#define PRIVKEYFILENAME "otr.private_key"
+#define FINGERPRINTFILENAME "otr.fingerprints"
+#define MAXPOLICYLENGTH 27     //values < 27 cut last letters from 
"OTRL_POLICY_OPPORTUNISTIC"
+
+const char L_OTRxSTR[]  = "[OTR] ";
+
+#endif
+
+class COTRHelper
+{
+public:
+  COTRHelper();
+  ~COTRHelper();
+
+  void Start(); 
+
+  int Decrypt(const char *szCipher, char **newmessage, unsigned long protoID, 
+    const char *recipientID);
+  int Encrypt(const char *szPlain, char **newmessage, unsigned long protoID, 
+    const char *recipientID);
+  void FreeNewMessage(char **newmessage); 
+
+  void GenerateKey(const char *accountname, unsigned long nPPID);
+  void StartPrivateConversation(const char *accountname, unsigned long nPPID, 
+  const char *recipientID);
+  void EndPrivateConversation(const char *accountname, unsigned long nPPID, 
+    const char *recipientID);
+  static bool IsOtrSysMsg(const char *msg); 
+
+  //internal helper functions
+#ifdef HAVE_LIBOTR
+  static const char* protocolIDToName(unsigned long protoID) ;
+  static unsigned long protocolNameToID(const char *protoName);
+  static OtrlPolicy policyNameToValue(char *policyName);
+
+  //OTR callback functions
+  static OtrlPolicy policy_cb(void *opdata, ConnContext *context);
+  static void create_privkey_cb(void *opdata, const char *accountID,
+    const char *protocol);
+  static int is_logged_in_cb(void *opdata, const char *accountID,
+    const char *protocol, const char *recipientID);
+  static void inject_message_cb(void *opdata, const char *accountID,
+    const char *protocol, const char *recipientID, const char *message);
+  static void notify_cb(void *opdata, OtrlNotifyLevel level,
+    const char *accountname, const char *protocol, const char *username,
+    const char *title, const char *primary, const char *secondary);
+  static int display_otr_message_cb(void *opdata, const char *accountname, 
+    const char *protocol, const char *username, const char *msg);
+  static void update_context_list_cb(void *opdata);
+  static const char *protocol_name_cb(void *opdata, const char *protocol);
+  static void protocol_name_free_cb(void *opdata, const char *protocol_name);
+  static void confirm_fingerprint_cb(void *opdata, OtrlUserState us, 
+    const char *accountname, const char *protocol, const char *username,
+    unsigned char fingerprint[20]);
+  static void write_fingerprints_cb(void *opdata);
+  static void gone_secure_cb(void *opdata, ConnContext *context);
+  static void gone_insecure_cb(void *opdata, ConnContext *context);
+  static void still_secure_cb(void *opdata, ConnContext *context, int 
is_reply);
+  static void log_message_cb(void *opdata, const char *message);  
+#endif
+};
+
+
+extern COTRHelper gOTRHelper;
+
+#endif //_LICQ_OTR_H_INCLUDED_
--- /dev/null   2005-08-09 09:06:25.000000000 +0200
+++ licq_patched/src/otr.cpp    2006-02-02 19:57:22.930757784 +0100
@@ -0,0 +1,570 @@
+/*
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+// written by Martin Garbe <[EMAIL PROTECTED]>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//other headers
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+//LICQ includes
+#include "licq_file.h"
+#include "licq_log.h"
+#include "licq_user.h"
+#include "licq_icqd.h"
+
+#include "licq_otr.h"
+
+using std::string;
+
+//list of known protocol names
+const char *szICQ_NAME = "ICQ";
+const char *szMSN_NAME = "MSN";
+//list of known protocol IDs
+unsigned long szICQ_PID = 0x4C696371;
+unsigned long szMSN_PID = 0x4D534E5F;
+
+COTRHelper gOTRHelper;
+
+#ifdef HAVE_LIBOTR
+OtrlUserState userstate;
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//    OTR helper functions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_LIBOTR
+const char* COTRHelper::protocolIDToName(unsigned long protoID) 
+{
+  if (protoID == szICQ_PID) return szICQ_NAME;
+  if (protoID == szMSN_PID) return szMSN_NAME;
+
+  gLog.Error("%sUnknown protocol ID: %ld\n", L_OTRxSTR, protoID);
+  return NULL;
+}
+
+unsigned long COTRHelper::protocolNameToID(const char *protoName)
+{
+  if (strcmp(protoName, szICQ_NAME) == 0 ) return szICQ_PID;
+  if (strcmp(protoName, szMSN_NAME) == 0 ) return szMSN_PID;
+  gLog.Error("%sUnknown protocol name: \"%s\".\n",L_OTRxSTR, protoName);
+  return 0;
+}
+
+/** Convert a policy name to the OtrlPolicy value.
+  Possible policy names are:
+  - "OPPORTUNISTIC" - mark outgoing messages so, that others know you offer OTR
+                    - if others offer OTR to you then make secure connection 
automatically
+  - "MANUAL" - no automatically secured connection, only manual
+  - "ALWAYS" - no unsecured connection possible
+  - "NEVER" - never secure connection == deactivate
+*/
+OtrlPolicy COTRHelper::policyNameToValue(char *policyName)
+{
+  gLog.Info("%spolicyNameToValue: %s\n",L_OTRxSTR, policyName);
+  if (strcmp(policyName, "OPPORTUNISTIC") == 0) 
+    return OTRL_POLICY_OPPORTUNISTIC;
+  if (strcmp(policyName, "MANUAL") == 0) 
+    return OTRL_POLICY_MANUAL;
+  if (strcmp(policyName, "ALWAYS") == 0) 
+    return OTRL_POLICY_ALWAYS;
+  if (strcmp(policyName, "NEVER") == 0)
+    return OTRL_POLICY_NEVER;
+  
+  return OTRL_POLICY_NEVER;
+}
+#endif
+
+bool COTRHelper::IsOtrSysMsg(const char *msg)
+{
+#ifdef HAVE_LIBOTR
+  OtrlMessageType mt;
+  mt = otrl_proto_message_type(msg);
+  switch (mt) {
+    case OTRL_MSGTYPE_TAGGEDPLAINTEXT:
+    case OTRL_MSGTYPE_DATA:
+    case OTRL_MSGTYPE_NOTOTR:
+    case OTRL_MSGTYPE_UNKNOWN:
+      return false;
+    case OTRL_MSGTYPE_QUERY:
+    case OTRL_MSGTYPE_DH_COMMIT:
+    case OTRL_MSGTYPE_DH_KEY:
+    case OTRL_MSGTYPE_REVEALSIG:
+    case OTRL_MSGTYPE_SIGNATURE:
+    case OTRL_MSGTYPE_V1_KEYEXCH:
+    case OTRL_MSGTYPE_ERROR:
+      //all messages listed here, so no warning on compiling
+      break;
+  } 
+  return true;
+#else
+  return false;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//    OTR Callback functions
+//
+////////////////////////////////////////////////////////////////////////////////
+#ifdef HAVE_LIBOTR
+
+/* Return the OTR policy for the given context. */
+OtrlPolicy COTRHelper::policy_cb(void *opdata, ConnContext *context)
+{
+  OtrlPolicy policy = OTRL_POLICY_NEVER;
+  ICQOwner* o = gUserManager.FetchOwner(protocolNameToID(context->protocol), 
LOCK_R);
+  if (o == NULL) {
+    return policy;
+  }
+  if (o->UseOTRUserSpecific()) 
+  { //use user specific settings
+    ICQUser *u = gUserManager.FetchUser(context->username, 
protocolNameToID(context->protocol), 
+      LOCK_R);
+    if (u == NULL)
+    {
+      gLog.Error("%sError: Recipient User not know.\n",L_OTRxSTR);
+      return policy;
+    }
+    policy = policyNameToValue(u->OTRMode());
+    gUserManager.DropUser(u);
+  }
+  else
+  { //use global settings
+    policy = policyNameToValue(o->OTRMode());
+  }
+  gUserManager.DropOwner(protocolNameToID(context->protocol));
+  return policy;
+}
+
+
+/* Create a private key for the given accountname/protocol if desired. */
+void COTRHelper::create_privkey_cb(void *opdata, const char *accountname, 
const char *protocol)
+{
+  string privkeyfile = string(BASE_DIR) + "/" + PRIVKEYFILENAME;
+
+  gLog.Info("%sCreate private key for (account:%s, protocol:%s)\n", L_OTRxSTR, 
+    accountname, protocol);
+  /* Generate the key */
+  otrl_privkey_generate(userstate, privkeyfile.c_str(), accountname, protocol);
+
+  gLicqDaemon->ProtoOtrDisplaySystemMessage(NULL, "Private key created.", 
+    ICQ_OTR_CREATING_PRIVKEY);
+}
+
+
+/* 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. */
+int COTRHelper::is_logged_in_cb(void *opdata, const char *accountname,
+  const char *protoName, const char *recipientname)
+{
+  int logged_in = 0;
+
+  ICQUser *u = gUserManager.FetchUser(recipientname, 
+    gOTRHelper.protocolNameToID(protoName), LOCK_R);
+  if (u == NULL)
+  {
+    gLog.Error("%sError: Recipient User not know.\n",L_OTRxSTR);
+    return 0;
+  }
+  
+  if (u->Status() != ICQ_STATUS_OFFLINE) logged_in = 1;
+  gUserManager.DropUser(u);
+  
+  return logged_in;
+}
+
+
+/* Send the given IM to the given recipient from the given 
accountname/protocol. */
+void COTRHelper::inject_message_cb(void *opdata, const char *accountname, 
+  const char *protoName, const char *recipientname, const char *message)
+{
+  gLog.Info("%sinject_message: (recipientname: %s, protocol: %s)\n", L_OTRxSTR,
+    recipientname, protoName);
+  bool isOnline = false;
+  unsigned long tag = gLicqDaemon->ProtoSendMessage(recipientname, 
+    protocolNameToID(protoName), message, isOnline, false); 
+  
+  if (tag == 0)
+  {
+    gLog.Warn("%sSending message to %s failed.\n", L_OTRxSTR, recipientname);
+  }
+  return;
+}
+
+
+/* Display a notification message for a particular accountname /
+ * protocol / username conversation. */
+void COTRHelper::notify_cb(void *opdata, OtrlNotifyLevel level,
+  const char *accountname, const char *protocol, const char *username,
+  const char *title, const char *primary, const char *secondary)
+{
+  string nstr = string(title) + "\n" + string(primary) + "\n" + 
string(secondary);
+  
+  gLog.Info("%sNotify: %s\n", L_OTRxSTR, nstr.c_str());
+  gLicqDaemon->ProtoOtrDisplaySystemMessage(username, nstr.c_str(), 
ICQ_OTR_NOTIFY_MSG);
+  
+  return;
+}
+
+
+/* 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. */
+int COTRHelper::display_otr_message_cb(void *opdata, const char *accountname, 
+  const char *protocol, const char *username, const char *msg)
+{
+  gLog.Info("%sDisplay OTR Msg: (%s,%s,%s) - %s\n", L_OTRxSTR, accountname, 
+    protocol, username, msg);
+  gLicqDaemon->ProtoOtrDisplaySystemMessage(username, msg, ICQ_OTR_MSG);
+  return 0;
+}
+
+
+/* When the list of ConnContexts changes (including a change in
+ * state), this is called so the UI can be updated. */
+void COTRHelper::update_context_list_cb(void *opdata)
+{
+  //
+}
+
+
+/* Return a newly-allocated string containing a human-friendly name
+ * for the given protocol id */
+const char *COTRHelper::protocol_name_cb(void *opdata, const char *protocol)
+{
+  unsigned long protoID = atol(protocol);
+  if (protoID == szICQ_PID) return szICQ_NAME;
+  if (protoID == szMSN_PID) return szMSN_NAME;
+
+  gLog.Error("%sDon't know protocol name: %s.\n",L_OTRxSTR, protocol);
+  return NULL;
+}
+
+
+/* Deallocate a string allocated by protocol_name */
+void COTRHelper::protocol_name_free_cb(void *opdata, const char *protocol)
+{
+  //no need to free something
+}
+
+
+/* A new fingerprint for the given user has been received. */
+void COTRHelper::confirm_fingerprint_cb( void *opdata,
+  OtrlUserState us, 
+  const char *accountname, 
+  const char *protocol, 
+  const char *username,
+  unsigned char fingerprint[20])
+{
+  char hhash[45];
+  otrl_privkey_hash_to_human(hhash, fingerprint);
+  string msg = string("New fingerprint received: ") + string(hhash);
+  gLicqDaemon->ProtoOtrDisplaySystemMessage( username, msg.c_str(), 
+    ICQ_OTR_CONFIRM_FINGERPRINT);
+  gLog.Info("%sNew fingerprint received: (%s)\n",L_OTRxSTR, hhash);
+  return;
+}
+
+
+/* The list of known fingerprints has changed.  Write them to disk. */
+void COTRHelper::write_fingerprints_cb(void *opdata)
+{
+  string fingerprintfilename = string(BASE_DIR) + "/" + 
string(FINGERPRINTFILENAME);
+    
+  gcry_error_t err = otrl_privkey_write_fingerprints(userstate, 
fingerprintfilename.c_str());
+  if (err != GPG_ERR_NO_ERROR) {
+    gLog.Error("%sError calling 
otrl_privkey_write_fingerprints().\n",L_OTRxSTR);
+    return;
+  }
+  gLog.Info("%sWrite fingerprints.\n",L_OTRxSTR);
+  
+  return;
+}
+
+
+/* A ConnContext has entered a secure state. */
+void COTRHelper::gone_secure_cb(void *opdata, ConnContext *context)
+{
+  gLicqDaemon->ProtoOtrDisplaySystemMessage(context->username, 
+    "Connection entered secure state.", ICQ_OTR_CONN_SECURE);
+  return;
+}
+
+
+/* A ConnContext has left a secure state. */
+void COTRHelper::gone_insecure_cb(void *opdata, ConnContext *context)
+{
+  gLicqDaemon->ProtoOtrDisplaySystemMessage(context->username, 
+    "Connection left secure state.", ICQ_OTR_CONN_INSECURE);
+  return;
+}
+
+
+/* We have completed an authentication, using the D-H keys we
+ * already knew.  is_reply indicates whether we initiated the AKE. */
+void COTRHelper::still_secure_cb(void *opdata, ConnContext *context, int 
is_reply)
+{
+  if (is_reply == 0) {
+    gLicqDaemon->ProtoOtrDisplaySystemMessage(context->username, 
+      "Secure connection refreshed.", ICQ_OTR_CONN_STILL_SECURE);
+  }
+  return;
+}
+
+
+/* Log a message.  The passed message will end in "\n". */
+void COTRHelper::log_message_cb(void *opdata, const char *message)
+{
+    gLog.Info("%slog message: %s",L_OTRxSTR, message);
+}
+
+static OtrlMessageAppOps ui_ops = {
+    gOTRHelper.policy_cb,
+    gOTRHelper.create_privkey_cb,
+    gOTRHelper.is_logged_in_cb,
+    gOTRHelper.inject_message_cb,
+    gOTRHelper.notify_cb,
+    gOTRHelper.display_otr_message_cb,
+    gOTRHelper.update_context_list_cb,
+    gOTRHelper.protocol_name_cb,
+    gOTRHelper.protocol_name_free_cb,
+    gOTRHelper.confirm_fingerprint_cb,
+    gOTRHelper.write_fingerprints_cb,
+    gOTRHelper.gone_secure_cb,
+    gOTRHelper.gone_insecure_cb,
+    gOTRHelper.still_secure_cb,
+    gOTRHelper.log_message_cb
+  };
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//    OTR main functions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+  
+COTRHelper::COTRHelper() {
+#ifdef HAVE_LIBOTR
+  OTRL_INIT;
+  userstate = otrl_userstate_create();
+#endif
+}
+  
+  
+COTRHelper::~COTRHelper()
+{
+#ifdef HAVE_LIBOTR  
+  otrl_userstate_free(userstate);
+#endif
+}
+  
+/**
+ * Process OTR enrypted message. It's save to pass all messages here.
+ * \param szCipher
+ * \param newmessage
+ * \param protoID
+ * \param recipientname
+ * \return If Decrypt returns 1, then the message you received
+ *         was an internal protocol message, and no message should be delivered
+ *         to the user.
+ *         If it returns 0, then check if *newmessage was set to non-NULL.  If
+ *         so, replace the received message with the contents of *messagep, and
+ *         deliver that to the user instead.  You must call
+ *         FreeNewMessage(*newmessage) when you're done with it.
+ *         If Decrypt returns 0 and *newmessage is NULL, then this
+ *         was an ordinary, non-OTR message, which should just be delivered to
+ *         the user without modification.
+ */
+int COTRHelper::Decrypt(const char *szCipher, char **newmessage, unsigned long 
protoID, 
+  const char *recipientname)
+{
+#ifdef HAVE_LIBOTR
+  OtrlTLV *tlvs = NULL;
+  int ret = 0;
+  *newmessage = NULL;
+  
+  ICQOwner *o = gUserManager.FetchOwner(protoID, LOCK_N);
+  gLog.Info("%sReceived message: \"%s\"\n", L_OTRxSTR, szCipher);
+  ret = otrl_message_receiving(userstate, &ui_ops, NULL,  o->IdString(), 
+    protocolIDToName(protoID), recipientname, szCipher, newmessage, &tlvs, 
NULL, NULL);
+  gUserManager.DropOwner(protoID);
+
+  if (tlvs != NULL ) otrl_tlv_free(tlvs);
+
+  if (ret == 1) {
+    gLog.Info("%sIt's an internal OTR message. Don't show it to 
user.\n",L_OTRxSTR);
+    return 1;
+  }
+  if (ret == 0) {
+    if ((*newmessage) != NULL)
+      //replace message 
+      gLog.Info("%sDecoded message: %s\n",L_OTRxSTR, *newmessage);
+    else
+      gLog.Info("%sDecoded message is same as undecoded one.\n",L_OTRxSTR);
+    return 0;
+  }
+#endif  
+  return 0;
+}
+
+
+void COTRHelper::FreeNewMessage(char **newmessage)
+{
+#ifdef HAVE_LIBOTR
+  otrl_message_free(*newmessage);
+#endif
+}
+
+
+/** Encrypt a message.
+  * \param msg plain text of the message
+  * \param newmessage
+  * \param protoID ID of the Protocol
+  * \param recipientname ID of the receiver
+  * \return The routine returns non-zero if there was an error in encryption. 
The
+  *         message should not be send unencrypted to the user.
+  *         If zero is returned check whether *newmessage is non-NULL. If so 
replace 
+  *         your message with the contents of *newmessage and when your're
+  *         done call FreeNewMessage(*newmessage).
+  */
+int COTRHelper::Encrypt(const char *msg, char **newmessage, unsigned long 
protoID, 
+  const char *recipientname)
+{
+#ifdef HAVE_LIBOTR
+  gcry_error_t err;
+
+  ICQOwner *o = gUserManager.FetchOwner(protoID, LOCK_N);
+  err = otrl_message_sending(userstate, &ui_ops, NULL, o->IdString(), 
protocolIDToName(protoID), 
+    recipientname, msg, NULL, newmessage, NULL, NULL);
+  gUserManager.DropOwner(protoID);
+  if (err) {  /* Be *sure* not to send out plaintext */
+    gLog.Error("%sCould not encrypt message. Don't send message 
unencrypted!\n",L_OTRxSTR);
+    if (*newmessage != NULL) otrl_message_free(*newmessage);
+    return 1;
+  } 
+  if (*newmessage) {
+    gLog.Info("%sEncrypted message:\n\"%s\"\n",L_OTRxSTR,*newmessage);
+    return 0;
+  }
+#endif
+  return 0;
+}
+  
+
+/**
+  Creates the key for the user/protocol if it wasn't created before.
+  With this function the key can be generated manually. It's also
+  possible to automatically generate the key. That's the case if we don't
+  have a key but the policy says we should use OTR so it is generated
+  automatically.
+*/
+void COTRHelper::GenerateKey(const char *accountname, unsigned long nPPID)
+{
+#ifdef HAVE_LIBOTR
+  string privkeyfile = string(BASE_DIR) + "/" + string(PRIVKEYFILENAME);
+
+  //test whether files exist
+  FILE *f1 = fopen(privkeyfile.c_str(),"r");
+  if ( f1 == NULL ) {
+    if ( f1 != NULL ) fclose(f1);
+    otrl_privkey_generate(userstate, privkeyfile.c_str(), accountname, 
protocolIDToName(nPPID));
+    gLog.Info("%sNew private key generated.\n",L_OTRxSTR);
+  }
+#endif
+  return;
+}
+
+
+/**
+  Initializing. 
+*/
+void COTRHelper::Start()
+{
+#ifdef HAVE_LIBOTR
+  //read privatekeyfile and fingerprintfile
+  string privkeyfile = string(BASE_DIR) + "/" +string(PRIVKEYFILENAME);
+  string fingerprintfile = string(BASE_DIR) + "/" + 
string(FINGERPRINTFILENAME);
+  
+  gcry_error_t err;
+  err = otrl_privkey_read(userstate, privkeyfile.c_str());
+  if (err != GPG_ERR_NO_ERROR) {
+    gLog.Error("%sError opening file with private key (%s).\n",L_OTRxSTR, 
+      privkeyfile.c_str());
+    return;
+  }
+  
+  err = otrl_privkey_read_fingerprints(userstate, fingerprintfile.c_str(), 
NULL, NULL);
+  if (err != GPG_ERR_NO_ERROR) {
+    gLog.Error("%sError opening file with fingerprints (%s).\n",L_OTRxSTR, 
+      fingerprintfile.c_str());
+    return;
+  }
+#endif
+  return;
+}
+
+
+/** Initiates a private encrypted conversation with another user under a given 
protocol.
+  * If the conversation is already initiated it is newly initiated.
+*/
+void COTRHelper::StartPrivateConversation(const char *accountname, unsigned 
long protoID, 
+  const char *recipientname)
+{
+#ifdef HAVE_LIBOTR
+  char *qmsg = NULL;
+  qmsg = otrl_proto_default_query_msg(recipientname, OTRL_POLICY_ALWAYS);
+  if (qmsg != NULL) 
+  {
+    inject_message_cb(NULL, accountname, protocolIDToName(protoID), 
recipientname, qmsg);
+    free(qmsg);
+  }
+  else
+    gLog.Error("%sCould not generate a default query message.\n",L_OTRxSTR);
+#endif
+  return;
+}
+
+
+/**
+ * Put a connection manually into the PLAINTEXT state.
+*/
+void COTRHelper::EndPrivateConversation(const char *accountname, unsigned long 
protoID,
+  const char *username)
+{
+#ifdef HAVE_LIBOTR
+  gLog.Info("%sEnd private conversation with %s\n", L_OTRxSTR, username);
+  otrl_message_disconnect(userstate, &ui_ops, NULL, accountname, 
+    protocolIDToName(protoID), username);
+#endif
+}

Reply via email to