/*************************************************************************** * Copyright (C) 2007 by Michael Zanetti * * Copyright (C) 2014 by Timothy Pearson * * * * * * 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "otrlchatinterface.h" #include "otrguiclient.h" #include "otrplugin.h" #include "privkeypopup.h" #include "smppopup.h" #include "config.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"), TQt::WStyle_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 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 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 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("Received a new fingerprint from %1. You should authenticate this contact.").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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints" ); } 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("Private OTR session started."), 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("Unverified OTR session started."), 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("OTR Session ended. The conversation is now insecure!"), 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("OTR connection refreshed successfully.") , 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"), TQString::null, TQt::WStyle_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::WStyle_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("Authenticating contact..."), 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("Authentication successful. The conversation is now secure!"), 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("Authentication failed. The conversation is now insecure!"), 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("Authentication failed. The conversation is now insecure!"), 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("Authentication error!"), 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 %1").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("%1 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 %1 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 %1 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 %1."), 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 %1."), 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("The following message received from %1 was not encrypted: [%2]").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("%1 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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "instags", 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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys" ); otrl_privkey_read_fingerprints(userstate, TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints", NULL, NULL); #ifdef HAVE_LIBOTR_0400 otrl_instag_read(userstate, TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "instags"); 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("%1 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"), TQString::null, TQt::WStyle_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("Authentication successful. The conversation is now secure!"), 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("Authentication failed. The conversation is now insecure!"), 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("Authentication successful. The conversation is now secure!"), 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("Authentication failed. The conversation is now insecure!"), 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("Authentication error!"), 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("
"), 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 ) == 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 ); 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(), 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() ); #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(), session->account()->accountId(), session->account()->protocol()->displayName(), instance, 0, NULL, NULL, NULL); #else // HAVE_LIBOTR_0400 context = otrl_context_find(userstate, session->members().getFirst()->contactId(), session->account()->accountId(), session->account()->protocol()->displayName(), 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"), TQString::null, TQt::WStyle_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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints" ); 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( strcmp( context->username, account ) == 0 ){ 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( strcmp( context->username, session->members().getFirst()->contactId() ) == 0 ){ // 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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys" ); file = ""; line = ""; // Updating fingerprints from <=0.3 kdDebug() << "updating fingerprints" << endl; TQFile fingerprintfile( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints" ); 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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints", 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, 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("Authentication aborded. The conversation is now insecure!"), 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("Authenticating contact..."), 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("Authenticating contact..."), 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(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys", accountname, protocol); OtrlChatInterface::self()->checkFilePermissions( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys" ); }