-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jon Keating wrote: > On Fri, Feb 24, 2006 at 11:33:36AM +0100, Martin Garbe wrote: > > > Anyways, I applied the patch and recompiled all of my plugins and here are my > short results. > > I run 2 licqs with OTRMode = 2 on both. > I message a user through the server and my licq crashes. Apparently the patch > causes messages from new users to crash Licq. I add the user to my list and > then am able to receive the events properly (kinda).
I found the bug and fixed it. > > Then I get some OTR packets: > > 07:16:21: [OTR] Received message: > "?OTR:AAIKAAAAwLwOm7HOYiundmsTeguS9njBDx1YofMlNiiDv1yLjXdIg2qRZtUmKPIPbC6Pp7Q3tTfiANvC/qk4OL5X+nxiQnfxAfr37cyEKOIUxzcjPAqeJddl5ZXNHv/EcDjikxyQrCINp22cPujoSrYCtOiXqwYuwoo9lr+DdgQ5fSEI7ZiadiiutwxbkJ1CzohjL5c4uPFhW2UVQAFZmIk838T3izhEwj/4RkpbZKMvlx9yzrhCHXlb4VaCpPzJe4/cuEDC9g==." > 07:16:21: [OTR] policyNameToValue: 2 > 07:16:21: [OTR] Create private key for (account:16325723, protocol:ICQ) > > Good, a private key has been made for myself. Then I reply: The key is beeing created... I changed the log message. In later versions at this point a notification message to gui should be send. > > 07:17:06: [PKT] Packet (SRVv0, 88 bytes) sent: > (192.168.0.100:57857 -> 205.188.1.240:80) > 0000: 2A 02 78 BB 00 52 00 04 00 06 00 00 00 00 00 37 *.x».R.........7 > 0010: 00 00 00 00 00 00 00 00 00 01 09 32 34 39 37 30 ...........24970 > 0020: 34 39 38 35 00 02 00 30 05 01 00 01 01 01 01 00 4985...0........ > 0030: 27 00 00 00 00 6F 6B 21 20 09 20 20 09 09 09 09 '....ok! . .... > 0040: 20 09 20 09 20 09 20 20 20 09 20 09 20 20 09 20 . . . . . . > 0050: 20 20 09 09 20 20 09 20 .. . > > This is marked as encrypted when clearly it isn't... I guess I have to wait > for the key with the current user to be made? I fixed it. > > Then I get this 25 seconds later: > > 07:17:31: [TCP] ProtoOtrDisplaySystemMessage - message: Private key created. > username: (null) The key generation lasts some seconds on fast maschine and maybe some minutes on slow maschine. But if it's done you won't need to generate one again. > > My impression is that it takes some time to create an OTR tunnel. Is there > anyway that this can be made to go faster and smoother The key generation needs some time. The rest ist fast. If OTR is included in the GUI the user should do the following. With activating OTR the OTR key will be generated automatically. So he can see the time the generation lasts and the finderprint of his key. The fingerprint is needed if he wants to check it with his communication partners. I attached a new version of the patch with the fixes mentioned above. Below I'm explaining the communication at the beginning of the OTR session a little bit. I took 2 licq + OTR-patch with OTRMode=2. Otr was activated the first time. You can see that the key generation is the point where we lost some time. The rest goes fast. LICQ 1 LICQ 2 - --------- ----------- OTR Query Message ----> <----- D-H Commit Message D-H Key Message ----> <----- Reveal Signature Message Signature Message -----> - --- LICQ 1 --- 10:13:51: [OTR] policyNameToValue: 2 10:13:51: [OTR] Encrypted message: "test1 " 10:13:51: [TCP] Replace unencrypted message with encrypted one. 10:13:51: [SRV] Sending message through server (#8900). - --- LICQ 2 --- 10:13:51: [OTR] Received message: "test1 " 10:13:51: [OTR] policyNameToValue: 2 10:13:51: [OTR] inject_message: (recipientname: 289692335, protocol: ICQ) 10:13:51: [SRV] Sending message through server (#24023). - --- LICQ 1 --- 10:13:52: [OTR] Received message: "?OTR:AAICAAAAxM2WkPkCMT5QMJd5l3GegWFCnlW9XXjHXRGAqMsSRJ7UOZJdyyjV+I0o3yN1YoYK7KTeNWpMUzFR9OkgVxFB0FAS6CAl/u2SQufsYvXRTdNLTtWyiQJsJUGnliGJqdD1eLWpvtmzf6ZAEyKlfrIQ2Nsyzmd4aYEDxw9HsWHXmO1KS3fFu9V9hcqZ+6kEtrCtAs8fIKavZV8nFZrbNkA7k4r3jZj/o9mr5s0vHyaWj20Sf7nyxQdHEc7qaJqdl19MczAlu7AAAAAg6PqZgyVpCMv3NG0io/mrkekw+vsqa7rskqWL5fPIzMA=." 10:13:52: [OTR] policyNameToValue: 2 10:13:52: [OTR] inject_message: (recipientname: 269961261, protocol: ICQ) 10:13:52: [SRV] Sending message through server (#8901). - --- LICQ 2 --- 10:13:52: [OTR] Received message: "?OTR:AAIKAAAAwM9Ew/OCn01wQdu1MCczOcjmurSBSswt6xIsHnVRajskFtMf4rGouEcciw6F1CnJ6goIedHna/d1H0rNvNxmwGLP97DvKVHT25zefE7bL3NiCXq1BbjaB9A/+4FFDgZpWBpCssxqxY6dxINjHV2nkxVQhiaYBDSY5SE06TVSAZy3Q06KjevIRiDS7QsMo2WNh1cDoTH+R1MgpJfy0o2ZRaTfBS0QTjrKot9CgEdrC3SWK6vgrln96zRjSJ0Gn+mUTw==." 10:13:52: [OTR] policyNameToValue: 2 10:13:52: [OTR] Creating private key for (account:269961261, protocol:ICQ) ... 10:14:16: [TCP] ProtoOtrDisplaySystemMessage - message: Private key created. username: (null) 10:14:16: [OTR] inject_message: (recipientname: 289692335, protocol: ICQ) 10:14:16: [SRV] Sending message through server (#24025). - --- LICQ 1 --- 10:14:17: [OTR] Received message: "?OTR:AAIRAAAAEMc5JA6/wVBMqw4ZoWJSWr4AAAHSZNcILSYEr38pU887DeAhnPwB1Fc1ALO94gr48QXFdi+u0nbSNBHNuS5kkDgx6P6mKawPWbEgk2wXrgoVI6yoRmRma3u7PTOpogpWRJQtICURv9UNgPqfvkF+XXAy7p2//PQvTyHhjnRunAoJe+uNcdTDw1lQk0hsYu62HnXHNMT2TW7Nv1/yT/sUU0mKjtoCbsiT4yOlVMIZUqMiq89BzJPS3phOUr+H0NQB9CBvWALuF7SRaVdEXyVdh7FtLFhFhJjP1ZgaOxWswSm9kpXpehR5mNcstyvda6zJG39HDddmX5PrRFJ/bvNDmqe9W5cCV/ZS5+0pqAmsY2mqeXaS2gcwhdCZsIOIrDm8l2jZV3/zMjEP6OnvdxrHkeZGZXXf8hYcP+quoE4iiOsQiMThJXK7pRzhdplmCDUBdUy6RhEczkoz8j2tIU2pkAZ1TMYX38987YzjYSwuPtzc4YId6hGJivLTgFJDqDochDBXPZ/tcWmbOeTA2xVrksTyb4s9FzWrQkBB0Bfcu6VhE1U9K/4TnWa1m7sLzTZK2AqBaowjEgf8wDkmp2w95iSrI2yFQW8jqaqKRlZ8uEpmM1UT0dzrS9QyrkntQTzMDeVFru2DazXqpfj/sokjyjR1vyufOniA9X5n." 10:14:17: [OTR] policyNameToValue: 2 10:14:17: [OTR] Creating private key for (account:289692335, protocol:ICQ) ... 10:14:28: [TCP] ProtoOtrDisplaySystemMessage - message: Private key created. username: (null) 10:14:28: [TCP] ProtoOtrDisplaySystemMessage - message: New fingerprint received: FBCDC2DE 6EF08FB0 073DFDD2 E0FE95D4 DCB346F7 username: 269961261 10:14:28: [OTR] New fingerprint received: (FBCDC2DE 6EF08FB0 073DFDD2 E0FE95D4 DCB346F7) 10:14:28: [OTR] Write fingerprints. 10:14:29: [TCP] ProtoOtrDisplaySystemMessage - message: Connection entered secure state. username: 269961261 10:14:29: [OTR] inject_message: (recipientname: 269961261, protocol: ICQ) 10:14:29: [SRV] Sending message through server (#8902). - --- LICQ 2 --- 10:14:29: [OTR] Received message: "?OTR:AAISAAAB0g7z+xjmaSRn7G8IXwn/nnDatLcHjsn8RkKDO01lg5vWUydcEp3E7CAPD8dyYkBDTf8AYju481gF+zKYaesNY/vVOxRT4XYFus5UNG/CU1PD1NGJHgPQUE9R8tAfF1hnShmacOQtWQEouZJDW32hLGy+gFR4Gi4ofbzm5sPN8ii6KchzYRK5J27h5nHEWlCHvFxIrZeLvMEiNpc4uJZmSF0GIIhQqdbtDbu/jyko9LKjXHRZ8ip+Rli7GkhtVre63i1eB3/0nW54wiWj2X70gdnYHEwWUY2cOoXPnfo6IxeNdl44mV/PsTxJZrwlJSdgvSQBDrXQ07A3EmY5uMFcW4D0LWChsX82J1hhl6uP1CqLFa0hVbqWvrwMgFZwOkD4+kBcIYhebrpPxmWHJKoW2HooLhXKfQPJ4+KSsmqdew4hvh6lSf5PVoqqdp39bYchnvNYqzAoE4n4RbKcxUqgs3MfCwjqBRzmrryasEwtGKA1EANIIalOTv9FTCtBZbKjzmyDn4/s7dpBgToChffiGYdZU0gqSvH0HWDbrASDuakPxsoV77JpCngozC26/T+A+aOutzbkHSKKrdDl81D4ATcBXmiZW0v6dI9LUxOl5TrR3m3l5aq9wVUUNiOgzufjEKKdMh0djg==." 10:14:29: [OTR] policyNameToValue: 2 10:14:29: [TCP] ProtoOtrDisplaySystemMessage - message: New fingerprint received: 4CFC4067 F5C271A8 774F8E34 5C06D476 F220C3A4 username: 289692335 10:14:29: [OTR] New fingerprint received: (4CFC4067 F5C271A8 774F8E34 5C06D476 F220C3A4) 10:14:29: [OTR] Write fingerprints. 10:14:29: [TCP] ProtoOtrDisplaySystemMessage - message: Connection entered secure state. username: 289692335 10:14:29: [OTR] It's an internal OTR message. Don't show it to user. - --------------- here the connection is secured ---------------- regards, rsLeo -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (GNU/Linux) Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org iD8DBQFEEWYmuFcJvHsEu9gRAq6dAJkBIAu3j2BpBjzen/y4PGTE2XkWlwCePf3B /H6hqH28K51jYw8QW0LBAIs= =USkj -----END PGP SIGNATURE-----
--- licq_orig/admin/acinclude.m4.in 2006-03-10 12:16:33.127011528 +0100 +++ licq_patched/admin/acinclude.m4.in 2006-03-10 12:15:31.849327152 +0100 @@ -267,6 +267,93 @@ fi ]) +AC_DEFUN([LICQ_CHECK_OTR], +[ + 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( + otr, + [ --disable-otr disable OTR support], + WITH_OTR="$enableval", WITH_OTR="yes") + + AC_ARG_WITH( + otr-inc, + [ --with-otr-inc=PATH include path for libotr headers], + otr_incdir="$withval", otr_incdir="no") + + AC_ARG_WITH( + otr-lib, + [ --with-otr-lib=PATH library path for libotr], + otr_libdir="$withval", otr_libdir="no") + + if test "$WITH_OTR" = "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 "$otr_incdir" != "no"; then + CPPFLAGS="$CPPFLAGS -I$otr_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_OTR="no"] + ) + AC_LANG_RESTORE + + if test "$WITH_OTR" = "yes"; then + dnl + dnl check for library + dnl + if test "$otr_libdir" != "no"; then + LIBS="$LIBS -L$otr_libdir" + fi + AC_CHECK_LIB(otr, otrl_message_sending, WITH_OTR="yes", WITH_OTR="no") + if test "$WITH_OTR" = "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) @@ -335,4 +422,3 @@ fi fi ]) - --- licq_orig/configure.in 2006-03-10 12:16:34.756763768 +0100 +++ licq_patched/configure.in 2006-03-10 12:15:32.997152656 +0100 @@ -150,6 +150,7 @@ AC_CHECK_SOCKS5 LICQ_CHECK_GPGME LICQ_CHECK_OPENSSL +LICQ_CHECK_OTR 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" @@ -235,4 +240,3 @@ echo "--- Now type \"gmake\" to build Licq ---" echo "--- If gmake is not working, try \"make\" ---" echo "" - --- licq_orig/src/Makefile.am 2006-03-10 12:16:32.715074152 +0100 +++ licq_patched/src/Makefile.am 2006-03-10 12:15:31.747342656 +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@ --- licq_orig/include/licq_user.h 2006-03-10 12:16:35.099711632 +0100 +++ licq_patched/include/licq_user.h 2006-03-10 12:15:33.288108424 +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) { m_szOTRMode = c; } 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.policyNumToOtrValue + // 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; }; --- licq_orig/include/licq_message.h 2006-03-10 12:16:35.004726072 +0100 +++ licq_patched/include/licq_message.h 2006-03-10 12:15:33.285108880 +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 --- licq_orig/include/licq_icqd.h 2006-03-10 12:16:35.081714368 +0100 +++ licq_patched/include/licq_icqd.h 2006-03-10 12:15:33.287108576 +0100 @@ -296,6 +296,11 @@ 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(const char *szId, unsigned long nPPID); + void ProtoOtrCloseSecureChannel(const char *szId, unsigned long nPPID); + void ProtoOtrDisplaySystemMessage(const char *szId, const char *szMsg, + const unsigned char nType); // TCP (user) functions // Message @@ -866,4 +871,3 @@ #endif - --- licq_orig/include/licq_icq.h 2006-03-10 12:16:34.920738840 +0100 +++ licq_patched/include/licq_icq.h 2006-03-10 12:15:33.224118152 +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; --- /dev/null 2005-08-09 09:06:25.000000000 +0200 +++ licq_patched/include/licq_otr.h 2006-03-10 12:15:33.224118152 +0100 @@ -0,0 +1,99 @@ +/* + 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 *szId, unsigned long nPPID, const char *szMsg, + char **pszNewMsg); + int Encrypt(const char *szId, unsigned long nPPID, const char *szPlainMsg, + char **pszNewMsg); + void FreeNewMessage(char **pszNewMsg); + + void GenerateKey(const char *szOwnerId, unsigned long nPPID); + void StartPrivateConversation(const char *szId, unsigned long nPPID, + const char *szOwnerId); + void EndPrivateConversation(const char *szId, unsigned long nPPID, + const char *szOwnerId); + static bool IsOtrSysMsg(const char *szMsg); + static bool IsOtrTaggedMsg(const char *szMsg); + + //internal helper functions +#ifdef HAVE_LIBOTR + static const char* protocolIDToName(unsigned long protoID) ; + static unsigned long protocolNameToID(const char *protoName); + static OtrlPolicy policyNumToOtrValue(char policyNum); + + //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_ --- licq_orig/src/icqd-srv.cpp 2006-03-10 12:16:32.588093456 +0100 +++ licq_patched/src/icqd-srv.cpp 2006-03-10 12:15:31.743343264 +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) @@ -3037,9 +3040,37 @@ else szMsg = gTranslator.RNToN(szMessage); delete [] szMessage; + unsigned long nflags = 0; +#ifdef HAVE_LIBOTR + char *szNewMsg = NULL; + int ret = gOTRHelper.Decrypt(szId, LICQ_PPID, szMsg, &szNewMsg); + if (ret == 1) + { + //internal message. don't show to user. + break; + } + if (szNewMsg != NULL) + { + if (gOTRHelper.IsOtrTaggedMsg(szMsg)) + { + gLog.Info("%sUntag OTR tagged message.\n", L_SRVxSTR); + } + else + { + gLog.Info("%sReplace original message with decrypted.\n", L_SRVxSTR); + nflags |= E_ENCRYPTED; + } + delete [] szMsg; + szMsg = new char[strlen(szNewMsg)+1]; + strcpy(szMsg, szNewMsg); + gOTRHelper.FreeNewMessage(&szNewMsg); + } +#endif + // now send the message to the user - CEventMsg *e = CEventMsg::Parse(szMsg, ICQ_CMDxRCV_SYSxMSGxONLINE, nTimeSent, 0); + + CEventMsg *e = CEventMsg::Parse(szMsg, ICQ_CMDxRCV_SYSxMSGxONLINE, nTimeSent, nflags); delete [] szMsg; // Lock the user to add the message to their queue @@ -6205,4 +6236,3 @@ return nId; } - --- licq_orig/src/icqd-tcp.cpp 2006-03-10 12:16:32.452114128 +0100 +++ licq_patched/src/icqd-tcp.cpp 2006-03-10 12:15:31.740343720 +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,43 @@ 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 + char *szNewMsg = NULL; + //only encrypt if the message is not already an internal OTR message + if (otrl_proto_message_type(mDos) == OTRL_MSGTYPE_NOTOTR) + { + int ret = gOTRHelper.Encrypt(szId, LICQ_PPID, mDos, &szNewMsg); + if (ret == 1) + { + gLog.Error("%sFailed to encrypt message. DON'T SEND IT UNENCRYPTED!", + L_TCPxSTR); + return 0; + } + if (szNewMsg != NULL) + { + if (gOTRHelper.IsOtrTaggedMsg(szNewMsg)) + { + gLog.Info("%sTagged plaintext message with OTR.\n", L_TCPxSTR); + delete [] mDos; + mDos = new char[strlen(szNewMsg)+1]; + strcpy(mDos, szNewMsg); + } + else + { + gLog.Info("%sReplace unencrypted message with encrypted one.\n", + L_TCPxSTR); + cipher = (char*) malloc(strlen(szNewMsg)+1); + strcpy(cipher, szNewMsg); + } + gOTRHelper.FreeNewMessage(&szNewMsg); + } + } +#endif gUserManager.DropUser(u); if (cipher) f |= E_ENCRYPTED; @@ -1461,6 +1503,52 @@ 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(const char *szId, unsigned long nPPID) +{ + ICQOwner* o = gUserManager.FetchOwner(nPPID, LOCK_N); + gOTRHelper.StartPrivateConversation(o->IdString(), nPPID, szId); + gUserManager.DropOwner(nPPID); +} + +void CICQDaemon::ProtoOtrCloseSecureChannel(const char *szId, unsigned long nPPID) +{ + ICQOwner* o = gUserManager.FetchOwner(nPPID, LOCK_N); + gOTRHelper.EndPrivateConversation(o->IdString(), nPPID, szId); + gUserManager.DropOwner(nPPID); +} + +void CICQDaemon::ProtoOtrDisplaySystemMessage(const char *szId, const char *szMsg, + const unsigned char nType) +{ + gLog.Info("%sProtoOtrDisplaySystemMessage - message: %s\nusername: %s\n", + L_TCPxSTR, 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 @@ -4246,5 +4334,3 @@ CFileTransferManager *ftman = new CFileTransferManager(NULL, 0); delete ftman; } - - --- licq_orig/src/user.cpp 2006-03-10 12:16:32.933041016 +0100 +++ licq_patched/src/user.cpp 2006-03-10 12:15:31.792335816 +0100 @@ -2039,6 +2039,7 @@ m_fConf.ReadBool("UseGPG", m_bUseGPG, false ); m_fConf.ReadStr("GPGKey", szTemp, "" ); SetString( &m_szGPGKey, szTemp ); + m_fConf.ReadNum("OTRMode", m_szOTRMode, 0); m_fConf.ReadNum("PPFieldCount", nPPFieldCount, 0); for (int i = 0; i < nPPFieldCount; i++) { @@ -2294,6 +2295,8 @@ // GPG key m_szGPGKey = strdup(""); + + m_szOTRMode = 0; if (_szId) m_szId = strdup(_szId); @@ -3440,6 +3443,7 @@ m_fConf.WriteNum("SharedFilesStatus", m_nSharedFilesStatus); m_fConf.WriteBool("UseGPG", m_bUseGPG ); m_fConf.WriteStr("GPGKey", m_szGPGKey ); + m_fConf.WriteNum("OTRMode", m_szOTRMode ); m_fConf.WriteNum("PPFieldCount", (unsigned short)m_mPPFields.size()); std::map<string,string>::iterator iter; @@ -3745,6 +3749,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 +3822,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 +3859,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 +3916,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()); @@ -3999,4 +4008,3 @@ close(dest); } } - --- licq_orig/src/message.cpp 2006-03-10 12:16:32.226148480 +0100 +++ licq_patched/src/message.cpp 2006-03-10 12:15:31.733344784 +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, --- licq_orig/src/icqd.cpp 2006-03-10 12:16:32.655083272 +0100 +++ licq_patched/src/icqd.cpp 2006-03-10 12:15:31.744343112 +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); --- /dev/null 2005-08-09 09:06:25.000000000 +0200 +++ licq_patched/src/otr.cpp 2006-03-10 12:15:31.793335664 +0100 @@ -0,0 +1,605 @@ +/* + 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 *szProtoName) +{ + if (strcmp(szProtoName, szICQ_NAME) == 0 ) return szICQ_PID; + if (strcmp(szProtoName, szMSN_NAME) == 0 ) return szMSN_PID; + gLog.Error("%sUnknown protocol name: \"%s\".\n",L_OTRxSTR, szProtoName); + return 0; +} + +/** Convert a policy number to the OtrlPolicy value. + Possible policy numbers are: + - 3 - ALWAYS, always secured connection + - 2 - OPPORTUNISTIC, mark outgoing messages so, that others know you offer OTR + if others offer OTR then secure connection automatically + - 1 - MANUAL, no automatically secured connection, only manual + - 0 - NEVER, never secure connection == deactivated +*/ +OtrlPolicy COTRHelper::policyNumToOtrValue(char policyNum) +{ + gLog.Info("%spolicyNameToValue: %d\n",L_OTRxSTR, policyNum); + if (policyNum == 3) + return OTRL_POLICY_ALWAYS; + if (policyNum == 2) + return OTRL_POLICY_OPPORTUNISTIC; + if (policyNum == 1) + return OTRL_POLICY_MANUAL; + if (policyNum == 0) + return OTRL_POLICY_NEVER; + + return OTRL_POLICY_NEVER; +} +#endif + +bool COTRHelper::IsOtrSysMsg(const char *szMsg) +{ +#ifdef HAVE_LIBOTR + OtrlMessageType mt; + mt = otrl_proto_message_type(szMsg); + 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 +} + +bool COTRHelper::IsOtrTaggedMsg(const char *szMsg) +{ +#ifdef HAVE_LIBOTR + OtrlMessageType mt; + mt = otrl_proto_message_type(szMsg); + if (mt == OTRL_MSGTYPE_TAGGEDPLAINTEXT) return true; + return false; +#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 = policyNumToOtrValue(u->OTRMode()); + gUserManager.DropUser(u); + } + else + { + //use global settings + policy = policyNumToOtrValue(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("%sCreating 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) +{ + //don't do anything +} + + +/* 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 szRecID The User ID of the communication partner + * \param nPPID Protocol ID + * \param szMsg encrypted message + * \param pszNewMsg the new unencrypted message + * \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 *szRecId, unsigned long nPPID, + const char *szMsg, char **pszNewMsg) +{ +#ifdef HAVE_LIBOTR + OtrlTLV *tlvs = NULL; + int ret = 0; + *pszNewMsg = NULL; + + //only handle encrypted messages if the user is on the contact list + ICQUser *u = gUserManager.FetchUser(szRecId, nPPID, LOCK_N); + if (u == NULL) + { + gLog.Info("%sUser not know. Handle message as unencrypted.\n",L_OTRxSTR); + gUserManager.DropUser(u); + return 0; + } + gUserManager.DropUser(u); + + ICQOwner *o = gUserManager.FetchOwner(nPPID, LOCK_N); + gLog.Info("%sReceived message: \"%s\"\n", L_OTRxSTR, szMsg); + ret = otrl_message_receiving(userstate, &ui_ops, NULL, o->IdString(), + protocolIDToName(nPPID), szRecId, szMsg, pszNewMsg, &tlvs, NULL, NULL); + gUserManager.DropOwner(nPPID); + + 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 ((*pszNewMsg) != NULL) + //replace message + gLog.Info("%sDecoded/Untagged message: \"%s\"\n",L_OTRxSTR, *pszNewMsg); + else + gLog.Info("%sDecoded message is same as undecoded one.\n",L_OTRxSTR); + return 0; + } +#endif + return 0; +} + + +void COTRHelper::FreeNewMessage(char **pszNewMsg) +{ +#ifdef HAVE_LIBOTR + otrl_message_free(*pszNewMsg); +#endif +} + + +/** Encrypt a message. + * \param szRecID The User ID of the communication partner + * \param nPPID Protocol ID + * \param szPlainMsg plaintext message + * \param pszNewMsg the new encrypted message + * \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 *szRecId, unsigned long nPPID, + const char *szPlainMsg, char **pszNewMsg) +{ +#ifdef HAVE_LIBOTR + gcry_error_t err; + + ICQOwner *o = gUserManager.FetchOwner(nPPID, LOCK_N); + err = otrl_message_sending(userstate, &ui_ops, NULL, o->IdString(), protocolIDToName(nPPID), + szRecId, szPlainMsg, NULL, pszNewMsg, NULL, NULL); + gUserManager.DropOwner(nPPID); + if (err) + { + /* Be *sure* not to send out plaintext */ + gLog.Error("%sCould not encrypt message. Don't send message unencrypted!\n",L_OTRxSTR); + if (*pszNewMsg != NULL) otrl_message_free(*pszNewMsg); + return 1; + } + if (*pszNewMsg) + { + gLog.Info("%sEncrypted/Tagged message:\n\"%s\"\n",L_OTRxSTR,*pszNewMsg); + return 0; + } +#endif + return 0; +} + + +/** + Creates the key for the user/protocol pair if it wasn't created before. + With this function the key can be generated manually. It's also + possible to generate the key automatically. 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 *szOwnerId, 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(), szOwnerId, 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 user/protocol. + If the conversation is already initiated it is newly initiated. +*/ +void COTRHelper::StartPrivateConversation(const char *szId, unsigned long nPPID, + const char *szOwnerId) +{ +#ifdef HAVE_LIBOTR + char *qmsg = NULL; + qmsg = otrl_proto_default_query_msg(szId, OTRL_POLICY_ALWAYS); + if (qmsg != NULL) + { + inject_message_cb(NULL, szOwnerId, protocolIDToName(nPPID), szId, 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 *szId, unsigned long nPPID, + const char *szOwnerId) +{ +#ifdef HAVE_LIBOTR + gLog.Info("%sEnd private conversation with %s\n", L_OTRxSTR, szId); + otrl_message_disconnect(userstate, &ui_ops, NULL, szOwnerId, + protocolIDToName(nPPID), szId); +#endif +}