diff options
Diffstat (limited to 'kopete/plugins/otr/otrlchatinterface.cpp')
-rw-r--r-- | kopete/plugins/otr/otrlchatinterface.cpp | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/kopete/plugins/otr/otrlchatinterface.cpp b/kopete/plugins/otr/otrlchatinterface.cpp new file mode 100644 index 00000000..0d216cce --- /dev/null +++ b/kopete/plugins/otr/otrlchatinterface.cpp @@ -0,0 +1,1076 @@ +/*************************************************************************** + * Copyright (C) 2007 by Michael Zanetti * + * Copyright (C) 2014 by Timothy Pearson <kb9vqf@pearsoncomputing.net> * + * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + + +/** + * @author Michael Zanetti + */ + +#ifdef HAVE_CONFIG_H +#include "config-kopete.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <kopetechatsession.h> +#include <kopeteaccount.h> +#include <kopeteaccountmanager.h> +#include <kopetemessageevent.h> +#include <kopetecontactlist.h> +#include <kopetemetacontact.h> +#include <kopeteview.h> +#include <kopeteprotocol.h> + +#include <kdebug.h> +#include <tdemessagebox.h> +#include <kstandarddirs.h> +#include <tdelocale.h> +#include <kprogress.h> +#include <kpassivepopup.h> +#include <kanimwidget.h> +#include <kpushbutton.h> + +#include <tqvbox.h> +#include <tqlabel.h> +#include <tqnamespace.h> +#include <tqeventloop.h> +#include <tqapplication.h> +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqptrlist.h> + +#include "otrlchatinterface.h" +#include "otrguiclient.h" +#include "otrplugin.h" +#include "privkeypopup.h" +#include "smppopup.h" + +OtrlChatInterface *OtrlChatInterface::mSelf = 0; +static OtrlUserState userstate; +static OtrlPolicy confPolicy; +static void *updateContextList = 0; + +/***************************** Gui_UI_Ops for libotr **********************************/ +static OtrlPolicy policy(void *opdata, ConnContext *context){ + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + bool noerr; + + // Disable OTR for IRC + if( session->protocol()->pluginId() == "IRCProtocol" ){ + kdDebug() << "Disabling OTR for: " << session->protocol()->pluginId() << endl; + return OTRL_POLICY_NEVER; + } + TQString policy = session->members().getFirst()->metaContact()->pluginData( OTRPlugin::plugin(), "otr_policy" ); + switch( policy.toInt( &noerr, 10 ) ){ + case 1: + return OTRL_POLICY_ALWAYS; + case 2: + return OTRL_POLICY_OPPORTUNISTIC; + case 3: + return OTRL_POLICY_MANUAL; + case 4: + return OTRL_POLICY_NEVER; + default: + return confPolicy; + } +} + +static void create_privkey(void *opdata, const char *accountname, const char *protocol){ + + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + + PrivKeyPopup *popup = new PrivKeyPopup( session->view()->mainWidget(), i18n("Generating private key").utf8(), TQt::WType_Dialog | TQt::WStyle_StaysOnTop ); + KAnimWidget *anim = new KAnimWidget( "kde", 72, popup->animFrame, "kopete" ); + anim->start(); + anim->show(); + + popup->setCloseLock( true ); + popup->show(); + KeyGenThread *keyGenThread = new KeyGenThread( accountname, protocol ); + keyGenThread->start(); + while( !keyGenThread->wait(100) ){ + tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput | TQEventLoop::ExcludeSocketNotifiers, 100); + } + popup->setCloseLock( false ); + popup->close(); +} + +static int is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient){ + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::ContactPtrList list = session->members(); + for ( TQPtrListIterator<Kopete::Contact> it( list ); Kopete::Contact *contact = it.current(); ++it ){ + if( contact->contactId().compare( recipient ) == 0 ){ + Kopete::OnlineStatus status = session->contactOnlineStatus( contact ); + if( status == Kopete::OnlineStatus::Unknown){ + return -1; + } else if( status == Kopete::OnlineStatus::Offline ){ + return 0; + } else { + return 1; + } + } + } + return -1; +} + +static void inject_message( void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message ){ + //KMessageBox::information( NULL, TQString(accountname) + ":" + TQString(protocol) + ":" + TQString(recipient) + ":" + TQString(message) ); + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::ContactPtrList list = session->members(); + for ( TQPtrListIterator<Kopete::Contact> it( list ); Kopete::Contact *contact = it.current(); ++it ){ + if( contact->contactId().compare( recipient ) == 0 ){ + Kopete::Message msg( session->account()->myself(), contact, TQString( message ), Kopete::Message::Outbound ); + session->sendMessage( msg ); + return; + } + } +} + +#ifndef HAVE_LIBOTR_0400 +static void notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary){ + KMessageBox::information(NULL, TQString( primary ) + TQString( secondary ), TQString( title ) ); +} +#endif // HAVE_LIBOTR_0400 + +#ifndef HAVE_LIBOTR_0400 +static int display_otr_message( void *opdata, const char *accountname, const char *protocol, const char *username, const char *message ){ + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::ContactPtrList list = session->members(); + for ( TQPtrListIterator<Kopete::Contact> it( list ); Kopete::Contact *contact = it.current(); ++it ){ + if( contact->contactId().compare( username ) == 0 ){ + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), TQString( message ), Kopete::Message::Internal ); + msg.setBody( TQString( message ), Kopete::Message::RichText ); + session->appendMessage( msg ); + return 0; + } + } + return 1; +} +#endif // HAVE_LIBOTR_0400 + +static void update_context_list(void *opdata){ +//Not used... +} + +#ifndef HAVE_LIBOTR_0400 +static const char *protocol_name(void *opdata, const char *protocol){ +//Never seen... + kdDebug() << "protocol_name called" << endl; +} +#endif // HAVE_LIBOTR_0400 + +#ifndef HAVE_LIBOTR_0400 +static void protocol_name_free(void *opdata, const char *protocol_name){ +//Never seen... + kdDebug() << "protocol_name_free called" << endl; +} +#endif // HAVE_LIBOTR_0400 + +static void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]){ + kdDebug() << "Received a new Fingerprint" << endl; + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Received a new fingerprint from <a>%1</a>. You should authenticate this contact.</b>").arg( session->members().getFirst()->contactId() ), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); +} + +static void write_fingerprints(void *opdata){ + kdDebug() << "Writing fingerprints" << endl; + otrl_privkey_write_fingerprints( userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("fingerprints").local8Bit() ); +} + +static void gone_secure(void *opdata, ConnContext *context){ + kdDebug() << "gone secure" << endl; + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + + if( context->active_fingerprint->trust && context->active_fingerprint->trust[0] ){ + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Private OTR session started.</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( ((Kopete::ChatSession*)opdata), 2 ); + } else { + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Unverified OTR session started.</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( ((Kopete::ChatSession*)opdata), 1 ); + } + +#ifdef HAVE_LIBOTR_0400 + session->setProperty("otr-instag", QString::number(context->their_instance)); +#endif // HAVE_LIBOTR_0400 +} + +/* Actually I've never seen this event but its implemented in case someone should receive it + kopete, gaim and miranda send a heartbeat message at disconnect. See log_message. + Searching libotr I could not find any call of gone_insecure. */ +static void gone_insecure(void *opdata, ConnContext *context){ + kdDebug() << "gone insecure" << endl; + OTRPlugin::plugin()->emitGoneSecure(((Kopete::ChatSession*)opdata), 0); + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>OTR Session ended. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); +} + +static void still_secure(void *opdata, ConnContext *context, int is_reply){ + kdDebug() << "still secure" << endl; + Kopete::ChatSession *session= ((Kopete::ChatSession*)opdata); + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>OTR connection refreshed successfully.</b>") , Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); + + if( context->active_fingerprint->trust && context->active_fingerprint->trust[0] ){ + OTRPlugin::plugin()->emitGoneSecure( session, 2); + } else { + OTRPlugin::plugin()->emitGoneSecure( session, 1); + } +} + +#ifndef HAVE_LIBOTR_0400 +static void log_message(void *opdata, const char *message){ + kdDebug() << "libotr: "<< message << endl; +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +static void received_symkey(void *opdata, ConnContext *context, unsigned int use, const unsigned char *usedata, size_t usedatalen, const unsigned char *symkey){ + // Not used +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +static const char *otr_error_message(void *opdata, ConnContext *context, OtrlErrorCode err_code){ + Q_UNUSED(opdata) + + char *err_msg = 0; + switch (err_code) + { + case OTRL_ERRCODE_NONE : + break; + case OTRL_ERRCODE_ENCRYPTION_ERROR : { + TQString message = i18n("Error occurred encrypting message."); + err_msg = (char*)malloc(message.length() + 1); + memset(err_msg, 0, message.length() + 1); + memcpy(err_msg, message.utf8().data(), message.length()); + break; + } + case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE : + if (context) { + TQString message = i18n("You sent encrypted data to %s, who wasn't expecting it.").arg(context->accountname); + err_msg = (char*)malloc(message.length() + 1); + memset(err_msg, 0, message.length() + 1); + memcpy(err_msg, message.utf8().data(), message.length()); + } + break; + case OTRL_ERRCODE_MSG_UNREADABLE : { + TQString message = i18n("You transmitted an unreadable encrypted message."); + err_msg = (char*)malloc(message.length() + 1); + memset(err_msg, 0, message.length() + 1); + memcpy(err_msg, message.utf8().data(), message.length()); + break; + } + case OTRL_ERRCODE_MSG_MALFORMED : { + TQString message = i18n("You transmitted a malformed data message."); + err_msg = (char*)malloc(message.length() + 1); + memset(err_msg, 0, message.length() + 1); + memcpy(err_msg, message.utf8().data(), message.length()); + break; + } + } + return err_msg; +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void otr_error_message_free(void *opdata, const char *err_msg){ + Q_UNUSED(opdata) + + if (err_msg) { + free((char*)err_msg); + } +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +const char *resent_msg_prefix(void *opdata, ConnContext *context){ + Q_UNUSED(opdata) + Q_UNUSED(context) + + TQString message = i18n("[resent]"); + char *msg_prefix = (char*)malloc(message.length() + 1); + memset(msg_prefix, 0, message.length() + 1); + memcpy(msg_prefix, message.utf8().data(), message.length()); + return msg_prefix; +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void resent_msg_prefix_free(void *opdata, const char *prefix){ + Q_UNUSED(opdata) + + if (prefix) { + free((char*)prefix); + } +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void handle_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question){ + Q_UNUSED(progress_percent) + + Kopete::ChatSession *chatSession = (Kopete::ChatSession*)opdata; + + if (!context) { + return; + } + + switch (smp_event) { + case OTRL_SMPEVENT_NONE: + break; + case OTRL_SMPEVENT_ASK_FOR_SECRET: { + SMPPopup *popup = new SMPPopup( chatSession->view()->mainWidget(), i18n("Enter authentication secret").utf8(), TQString::null, TQt::WType_Dialog | TQt::WStyle_StaysOnTop, context, chatSession, false ); + popup->show(); + break; + } + case OTRL_SMPEVENT_ASK_FOR_ANSWER: { + SMPPopup *popup = new SMPPopup( chatSession->view()->mainWidget(), question, question, TQt::WType_Dialog | TQt::WStyle_StaysOnTop, context, chatSession, false ); + popup->show(); + break; + } + case OTRL_SMPEVENT_IN_PROGRESS: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authenticating contact...</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_SMPEVENT_SUCCESS: { + if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication successful. The conversation is now secure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 2 ); + } + else { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication failed. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 1 ); + } + break; + } + case OTRL_SMPEVENT_FAILURE: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication failed. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 1 ); + break; + } + case OTRL_SMPEVENT_ABORT: + case OTRL_SMPEVENT_CHEATED: + case OTRL_SMPEVENT_ERROR: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication error!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OtrlChatInterface::self()->abortSMP( context, chatSession ); + break; + } + } +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err){ + Kopete::ChatSession *chatSession= ((Kopete::ChatSession*)opdata); + Kopete::ContactPtrList list = chatSession->members(); + + switch (msg_event) + { + case OTRL_MSGEVENT_NONE: + break; + case OTRL_MSGEVENT_ENCRYPTION_REQUIRED: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("You attempted to send an unencrypted message to <b>%1</b>").arg(context->username) , Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_ENCRYPTION_ERROR: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("An error occurred when encrypting your message. The message was not sent."), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_CONNECTION_ENDED: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>%1</b> has ended the OTR session. You should do the same.").arg(context->username) , Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_SETUP_ERROR: { + if (!err) { + err = GPG_ERR_INV_VALUE; + } + switch(gcry_err_code(err)) { + case GPG_ERR_INV_VALUE: + kdDebug() << "Error setting up private conversation: Malformed message received"; + default: + kdDebug() << "Error setting up private conversation:" << err; + } + + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("OTR error"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_MSG_REFLECTED: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("We are receiving our own OTR messages. You are either trying to talk to yourself, or someone is reflecting your messages back at you."), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_MSG_RESENT: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("The last message to <b>%1</b> was resent.").arg(context->username) , Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("The encrypted message received from <b>%1</b> is unreadable, as you are not currently communicating privately.").arg(context->username) , Kopete::Message::Inbound, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + //OtrlChatInterface::self()->m_blacklistIds.append(msg.id()); + break; + } + case OTRL_MSGEVENT_RCVDMSG_UNREADABLE: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("We received an unreadable encrypted message from <b>%1</b>."), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_RCVDMSG_MALFORMED: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("We received a malformed data message from <b>%1</b>."), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD: { + kdDebug() << "Heartbeat received from" << context->username; + return; + } + case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT: { + kdDebug() << "Heartbeat sent to" << context->username; + break; + } + case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), message, Kopete::Message::Inbound, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + break; + } + case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>The following message received from <b>%1</b> was <i>not</i> encrypted: [</b>%2<b>]</b>").arg(context->username).arg(message), Kopete::Message::Inbound, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + //OtrlChatInterface::self()->m_blacklistIds.append(msg.id()); + break; + } + case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED: { + kdDebug() << "Unrecognized OTR message received from" << context->username; + break; + } + case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE: { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>%1</b> has sent an encrypted message intended for a different session. If you are logged in multiple times, another session may have received the message.").arg(context->username), Kopete::Message::Inbound, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + //OtrlChatInterface::self()->m_blacklistIds.append(msg.id()); + break; + } + } +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void create_instag(void *opdata, const char *accountname, const char *protocol){ + Q_UNUSED(opdata) + + otrl_instag_generate(OtrlChatInterface::self()->getUserstate(), TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("instags").local8Bit(), accountname, protocol); +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void convert_msg(void *opdata, ConnContext *context, OtrlConvertType convert_type, char ** dest, const char *src){ + // Not used +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void convert_free(void *opdata, ConnContext *context, char *dest){ + // Not used +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +void timer_control(void *opdata, unsigned int interval){ + kdDebug() << "timer_control called" << endl; + + Q_UNUSED(opdata) + if (interval > 0) { + OtrlChatInterface::self()->forwardSecrecyTimerStart(interval); + } + else { + OtrlChatInterface::self()->forwardSecrecyTimerStop(); + } +} +#endif // HAVE_LIBOTR_0400 + +#ifdef HAVE_LIBOTR_0400 +static OtrlMessageAppOps ui_ops = { + policy, + create_privkey, + is_logged_in, + inject_message, + update_context_list, + new_fingerprint, + write_fingerprints, + gone_secure, + gone_insecure, + still_secure, + NULL, // max_message_size, + NULL, // account_name, + NULL, // account_name_free, + received_symkey, + otr_error_message, + otr_error_message_free, + resent_msg_prefix, + resent_msg_prefix_free, + handle_smp_event, + handle_msg_event, + create_instag, + NULL, // convert_msg, + NULL, // convert_free, + timer_control +}; +#else // HAVE_LIBOTR_0400 +static OtrlMessageAppOps ui_ops = { + policy, + create_privkey, + is_logged_in, + inject_message, + notify, + display_otr_message, + update_context_list, + protocol_name, + protocol_name_free, + new_fingerprint, + write_fingerprints, + gone_secure, + gone_insecure, + still_secure, + log_message +}; +#endif // HAVE_LIBOTR_0400 + +/*********************** Gui_UI_Ops finished *************************/ + + +/*********************** Constructor/Destructor **********************/ + +OtrlChatInterface::OtrlChatInterface(){ + kdDebug() << "Creating OtrlChatInterface" << endl; + mSelf = this; + OTRL_INIT; + + userstate = otrl_userstate_create(); + + otrl_privkey_read( userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("privkeys").local8Bit() ); + + otrl_privkey_read_fingerprints(userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("fingerprints").local8Bit(), NULL, NULL); + +#ifdef HAVE_LIBOTR_0400 + otrl_instag_read(userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("instags").local8Bit()); + + unsigned int interval = otrl_message_poll_get_default_interval(userstate); + forwardSecrecyTimerStart(interval); + connect(&m_forwardSecrecyTimer, SIGNAL(timeout()), this, SLOT(otrlMessagePoll())); +#endif // HAVE_LIBOTR_0400 + +} + +OtrlChatInterface::~ OtrlChatInterface(){ + otrl_userstate_free(userstate); +} + + +OtrlChatInterface *OtrlChatInterface::self(){ + if( !mSelf ){ + new OtrlChatInterface(); + } + return mSelf; +} + +void OtrlChatInterface::forwardSecrecyTimerStart(int interval){ + m_forwardSecrecyTimer.start(interval * 1000); +} + +void OtrlChatInterface::forwardSecrecyTimerStop(){ + m_forwardSecrecyTimer.stop(); +} + +void OtrlChatInterface::otrlMessagePoll(){ +#ifdef HAVE_LIBOTR_0400 + otrl_message_poll(userstate, 0, 0); +#endif // HAVE_LIBOTR_0400 +} + +/********************* Chat section ***************************/ + +OtrlUserState OtrlChatInterface::getUserstate(){ + return userstate; +} + + +int OtrlChatInterface::decryptMessage( TQString *msg, TQString accountId, + TQString protocol, TQString contactId , Kopete::ChatSession *chatSession){ + + int ignoremessage; + char *newMessage = NULL; + OtrlTLV *tlvs = NULL; + OtrlTLV *tlv = NULL; + ConnContext *context; + NextExpectedSMP nextMsg; + +#ifdef HAVE_LIBOTR_0400 + ignoremessage = otrl_message_receiving( userstate, &ui_ops, chatSession, accountId.latin1(), protocol.latin1(), contactId.latin1(), msg->latin1(), &newMessage, &tlvs, NULL, NULL, NULL ); +#else // HAVE_LIBOTR_0400 + ignoremessage = otrl_message_receiving( userstate, &ui_ops, chatSession, accountId.latin1(), protocol.latin1(), contactId.latin1(), msg->latin1(), &newMessage, &tlvs, NULL, NULL ); +#endif // HAVE_LIBOTR_0400 + + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); + if( tlv ){ + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>%1</b> has ended the OTR session. You should do the same.").arg(chatSession->members().getFirst()->contactId()) , Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + + OTRPlugin::plugin()->emitGoneSecure( chatSession, 3 ); + } + +#ifdef HAVE_LIBOTR_0400 + context = otrl_context_find( userstate, contactId.latin1(), accountId.latin1(), protocol.latin1(), 0, 0, NULL, NULL, NULL); +#else // HAVE_LIBOTR_0400 + context = otrl_context_find( userstate, contactId.latin1(), accountId.latin1(), protocol.latin1(), 0, NULL, NULL, NULL); +#endif // HAVE_LIBOTR_0400 + if (context) { + nextMsg = context->smstate->nextExpected; + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT1){ + abortSMP( context, chatSession ); + } else { + SMPPopup *popup = new SMPPopup( chatSession->view()->mainWidget(), i18n("Enter authentication secret").utf8(), TQString::null, TQt::WType_Dialog | TQt::WStyle_StaysOnTop, context, chatSession, false ); + popup->show(); + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT2) + abortSMP( context, chatSession ); + else { + kdDebug() << "Update SMP state: 2 -> 3" << endl; + context->smstate->nextExpected = OTRL_SMP_EXPECT4; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT3) + abortSMP( context, chatSession ); + else { + if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication successful. The conversation is now secure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 2 ); + } else { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication failed. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 1 ); + } + + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT4) + abortSMP( context, chatSession ); + else { + if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication successful. The conversation is now secure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 2 ); + } else { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication failed. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + OTRPlugin::plugin()->emitGoneSecure( chatSession, 1 ); + } + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT); + if (tlv) { + Kopete::Message msg( chatSession->members().getFirst(), chatSession->account()->myself(), i18n("<b>Authentication error!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + + otrl_tlv_free(tlvs); + } + + + // message is now decrypted or is a Plaintext message and ready to deliver + if( !ignoremessage ){ + // message is decrypted + if( newMessage != NULL ){ + *msg = TQString::fromUtf8(newMessage); + otrl_message_free( newMessage ); + msg->replace( TQString("\n"), TQString("<br>"), false ); + } + } + return ignoremessage; +} + +TQString OtrlChatInterface::encryptMessage( TQString msg, TQString accountId, + TQString protocol, TQString contactId , Kopete::ChatSession *chatSession ){ + int err; + char * newMessage; + if( otrl_proto_message_type( msg.latin1() ) == OTRL_MSGTYPE_NOTOTR ){ + msg.replace( TQString("<"), TQString("<"), false ); +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = chatSession->property("otr-instag").toUInt(); + err = otrl_message_sending( userstate, &ui_ops, chatSession, accountId.latin1(), protocol.latin1(), contactId.latin1(), instance, msg.utf8(), NULL, &newMessage, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, NULL, NULL, NULL ); +#else // HAVE_LIBOTR_0400 + err = otrl_message_sending( userstate, &ui_ops, chatSession, accountId.latin1(), protocol.latin1(), contactId.latin1(), msg.utf8(), NULL, &newMessage, NULL, NULL ); +#endif // HAVE_LIBOTR_0400 + + if( err != 0 ){ + msg = i18n("Encryption error"); + } else { + if( newMessage != NULL ){ + msg = TQString::fromUtf8( newMessage ); + otrl_message_free( newMessage ); + } + } + } + OtrlMessageType type = otrl_proto_message_type( msg.latin1() ); + if( type == OTRL_MSGTYPE_NOTOTR | type == OTRL_MSGTYPE_TAGGEDPLAINTEXT ){ + msg.replace( "<", "<", false ); + } + return msg; +} + +TQString OtrlChatInterface::getDefaultQuery( TQString accountId ){ + char *message; + message = otrl_proto_default_query_msg( accountId.latin1(), OTRL_POLICY_ALLOW_V2 ); + TQString msg( message ); + otrl_message_free( message ); + return msg; +} + +void OtrlChatInterface::disconnectSession( Kopete::ChatSession *chatSession ){ +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = chatSession->property("otr-instag").toUInt(); + otrl_message_disconnect( userstate, &ui_ops, chatSession, chatSession->account()->accountId().latin1(), chatSession->account()->protocol()->displayName().latin1(), chatSession->members().getFirst()->contactId().latin1(), instance ); +#else // HAVE_LIBOTR_0400 + otrl_message_disconnect( userstate, &ui_ops, chatSession, chatSession->account()->accountId().latin1(), chatSession->account()->protocol()->displayName().latin1(), chatSession->members().getFirst()->contactId().latin1() ); +#endif // HAVE_LIBOTR_0400 + OTRPlugin::plugin()->emitGoneSecure( chatSession, false ); + + Kopete::Message msg( chatSession->account()->myself(), chatSession->members().getFirst(), i18n("Terminating OTR session."), Kopete::Message::Internal ); +// msg.setBody( TQString( message ), Kopete::Message::RichText ); + chatSession->appendMessage( msg ); + +} + +bool OtrlChatInterface::shouldDiscard( TQString message ){ + if( !message.isEmpty() && !message.isNull() ){ + switch( otrl_proto_message_type( message.latin1() ) ){ + case OTRL_MSGTYPE_TAGGEDPLAINTEXT: + case OTRL_MSGTYPE_UNKNOWN: + case OTRL_MSGTYPE_NOTOTR: + return false; + default: + return true; + } + } else { + return false; + } +} + + +void OtrlChatInterface::setPolicy( OtrlPolicy policy ){ + confPolicy = policy; +} + + +int OtrlChatInterface::privState( Kopete::ChatSession *session ){ + ConnContext *context; + +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = session->property("otr-instag").toUInt(); + context = otrl_context_find(userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->account()->protocol()->displayName().latin1(), instance, 0, NULL, NULL, NULL); +#else // HAVE_LIBOTR_0400 + context = otrl_context_find(userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->account()->protocol()->displayName().latin1(), 0, NULL, NULL, NULL); +#endif // HAVE_LIBOTR_0400 + + if( context ){ + switch( context->msgstate ){ + case OTRL_MSGSTATE_PLAINTEXT: + return 0; + case OTRL_MSGSTATE_ENCRYPTED: + if( context->active_fingerprint->trust && context->active_fingerprint->trust[0] != '\0' ) + return 2; + else + return 1; + case OTRL_MSGSTATE_FINISHED: + return 3; + } + } + return 0; +} + +TQString OtrlChatInterface::formatContact(TQString contactId){ + + Kopete::MetaContact *metaContact = Kopete::ContactList::self()->findMetaContactByContactId(contactId); + if( metaContact ){ + TQString displayName = metaContact->displayName(); + if((displayName != contactId) && !displayName.isNull()){ + return displayName + " (" + contactId+")"; + } + } + return contactId; +} + +void OtrlChatInterface::verifyFingerprint( Kopete::ChatSession *session ){ + ConnContext *context; + +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = session->property("otr-instag").toUInt(); + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), instance, 0, NULL, NULL, NULL); +#else // HAVE_LIBOTR_0400 + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), 0, NULL, NULL, NULL); +#endif // HAVE_LIBOTR_0400 + + SMPPopup *popup = new SMPPopup( session->view()->mainWidget(), i18n("Enter authentication secret").utf8(), TQString::null, TQt::WType_Dialog | TQt::WStyle_StaysOnTop, context, session, true ); + popup->show(); +} + +void OtrlChatInterface::setTrust( Kopete::ChatSession *session, bool trust ){ + Fingerprint *fingerprint; + + fingerprint = findFingerprint( session->members().getFirst()->contactId() ); + if( fingerprint != 0 ){ + if( trust ){ + otrl_context_set_trust( fingerprint, "verified" ); + } else { + otrl_context_set_trust( fingerprint, NULL ); + } + kdDebug() << "Writing fingerprints" << endl; + otrl_privkey_write_fingerprints( userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("fingerprints").local8Bit() ); + OTRPlugin::plugin()->emitGoneSecure( session, privState( session ) ); + } else { + kdDebug() << "could not find fingerprint" << endl; + } +} + +Fingerprint *OtrlChatInterface::findFingerprint( TQString account ){ + ConnContext *context; + + for( context = userstate->context_root; context != NULL; context = context->next ){ + kdDebug() << context->username << endl; + if (context->username == account) + { + kdDebug() << "found Context" << endl; + return context->active_fingerprint ? context->active_fingerprint : NULL; + } + } + return NULL; +} + +TQString OtrlChatInterface::findActiveFingerprint( Kopete::ChatSession *session ){ + ConnContext *context; + char hash[45]; + + for( context = userstate->context_root; context != NULL; context = context->next ){ + kdDebug() << context->username << endl; + if (context->username == session->members().getFirst()->contactId()) + { +// otrl_privkey_hash_to_human( hash, context->fingerprint_root.next->fingerprint ); + otrl_privkey_hash_to_human( hash, context->active_fingerprint->fingerprint ); + return hash; + } + } + return NULL; +} + +bool OtrlChatInterface::isVerified( Kopete::ChatSession *session ){ + kdDebug() << "checking for trust" << endl; + Fingerprint *fingerprint = findFingerprint( session->members().getFirst()->contactId() ); + + if( fingerprint->trust && fingerprint->trust[0] != '\0' ){ + kdDebug() << "verified" << endl; + return true; + } else { + kdDebug() << "not verified" << endl; + return false; + } +} + +void OtrlChatInterface::updateKeyfile( Kopete::Account *account ){ +// Updating private keys from <=0.3 + kdDebug() << "updating keys" << endl; + TQFile keyfile( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys" ); + TQString line; + TQString file; + + if( keyfile.open( IO_ReadWrite ) ){ + kdDebug() << "file open" << endl; + while( keyfile.readLine( line, 200 ) != -1){ + if( line.find( "protocol" ) != -1 ){ + if( line.find( account->accountLabel() ) != -1 ){ + line.replace( account->accountLabel(), account->protocol()->displayName() ); + kdDebug() << "Successfully updated keyfile for account " << account->accountId() << endl; + } + } + file.append( line ); + } + } + keyfile.remove(); + keyfile.open( IO_ReadWrite ); + keyfile.writeBlock( file.latin1(), file.length() ); + keyfile.close(); + otrl_privkey_forget_all( userstate ); + otrl_privkey_read( userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("privkeys").local8Bit() ); + + file = ""; + line = ""; +// Updating fingerprints from <=0.3 + kdDebug() << "updating fingerprints" << endl; + TQFile fingerprintfile( TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("fingerprints").local8Bit() ); + + if( fingerprintfile.open( IO_ReadWrite ) ){ + kdDebug() << "file open" << endl; + while( fingerprintfile.readLine( line, 200 ) != -1){ + int pos = line.findRev( account->accountLabel() ); + if( pos != -1 ){ + line.replace( pos, account->accountLabel().length(), account->protocol()->displayName() ); + kdDebug() << "Successfully updated fingerprint for account " << account->accountId() << endl; + } + file.append( line ); + } + } + fingerprintfile.remove(); + fingerprintfile.open( IO_ReadWrite ); + fingerprintfile.writeBlock( file.latin1(), file.length() ); + fingerprintfile.close(); + otrl_context_forget_all( userstate ); + otrl_privkey_read_fingerprints(userstate, TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("fingerprints").local8Bit(), NULL, NULL); + +} + +void OtrlChatInterface::checkFilePermissions( TQString file ){ + if( TQFile::exists( file ) ){ + TQFile privkeys( file ); + TQFileInfo privkeysInfo( privkeys ); + if( !privkeysInfo.permission( TQFileInfo::ReadOwner | TQFileInfo::WriteOwner ) | + privkeysInfo.permission( TQFileInfo::ReadGroup ) | + privkeysInfo.permission( TQFileInfo::WriteGroup ) | + privkeysInfo.permission( TQFileInfo::ExeGroup ) | + privkeysInfo.permission( TQFileInfo::ReadOther ) | + privkeysInfo.permission( TQFileInfo::WriteOther ) | + privkeysInfo.permission( TQFileInfo::ExeOther ) ){ + kdDebug() << "Permissions of OTR storage file are wrong! Correcting..." << endl; + chmod( file.local8Bit(), 0600); + } + } + +} + +/*bool OtrlChatInterface::verifyQuestion( Kopete::ChatSession *session, TQString fingerprint ){ + kdDebug() << "searching for Fingerprint" << endl; + + if( fingerprint != NULL ){ + int doVerify = KMessageBox::questionYesNo( + NULL, + i18n("Please contact %1 via another secure way and verify that the following Fingerprint is correct:").arg( formatContact(session->members().getFirst()->contactId())) + "\n\n" + fingerprint + "\n\n" + i18n("Are you sure you want to trust this fingerprint?"), + i18n("Verify fingerprint") ); + if( doVerify == KMessageBox::Yes ){ + return true; + } else { + return false; + verifyFingerprint( session, false ); + } + } else { + KMessageBox::error( NULL, i18n( "No fingerprint yet received from this contact." ), i18n( "No fingerprint found" ) ); + } + return false; +} +*/ + +/****************** SMP implementations ****************/ + +void OtrlChatInterface::abortSMP( ConnContext *context, Kopete::ChatSession *session ){ + otrl_message_abort_smp( userstate, &ui_ops, session, context); + if (context->active_fingerprint->trust && !context->active_fingerprint->trust[0]) { + OTRPlugin::plugin()->emitGoneSecure( session, 1 ); + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Authentication aborded. The conversation is now insecure!</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); + } +} + +void OtrlChatInterface::respondSMP( ConnContext *context, Kopete::ChatSession *session, TQString secret, bool initiate ){ + if( initiate ){ +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = session->property("otr-instag").toUInt(); + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), instance, 0, NULL, NULL, NULL); +#else // HAVE_LIBOTR_0400 + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), 0, NULL, NULL, NULL); +#endif // HAVE_LIBOTR_0400 + otrl_message_initiate_smp( userstate, &ui_ops, session, context, (unsigned char*)secret.latin1(), secret.length() ); + } + else { + otrl_message_respond_smp( userstate, &ui_ops, session, context, (unsigned char*)secret.latin1(), secret.length()); + } + + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Authenticating contact...</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); +} + +void OtrlChatInterface::respondSMPQ( ConnContext *context, Kopete::ChatSession *session, TQString question, TQString secret, bool initiate ){ + if( initiate ){ +#ifdef HAVE_LIBOTR_0400 + otrl_instag_t instance = session->property("otr-instag").toUInt(); + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), instance, 0, NULL, NULL, NULL); +#else // HAVE_LIBOTR_0400 + context = otrl_context_find( userstate, session->members().getFirst()->contactId().latin1(), session->account()->accountId().latin1(), session->protocol()->displayName().latin1(), 0, NULL, NULL, NULL); +#endif // HAVE_LIBOTR_0400 + otrl_message_initiate_smp_q( userstate, &ui_ops, session, context, (const char*)question.latin1(), (unsigned char*)secret.latin1(), secret.length() ); + } + else { + otrl_message_respond_smp( userstate, &ui_ops, session, context, (unsigned char*)secret.latin1(), secret.length()); + } + + Kopete::Message msg( session->members().getFirst(), session->account()->myself(), i18n("<b>Authenticating contact...</b>"), Kopete::Message::Internal, Kopete::Message::RichText ); + session->appendMessage( msg ); +} + +/****************** KeyGenThread *******************/ + +KeyGenThread::KeyGenThread( TQString accountname, TQString protocol ){ + this->accountname = accountname; + this->protocol = protocol; +} + + +void KeyGenThread::run() +{ + kdDebug() << "Creating private key... Storing to: " + TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true)) + "privkeys" << endl; + otrl_privkey_generate(OtrlChatInterface::self()->getUserstate(), TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("privkeys").local8Bit(), accountname.latin1(), protocol.latin1()); + OtrlChatInterface::self()->checkFilePermissions( TQString("%1%2").arg(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )).arg("privkeys").local8Bit() ); +} + +#include "otrlchatinterface.moc" |