/*************************************************************************** * Copyright (C) 2007 by Michael Zanetti * * * * * * 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. * ***************************************************************************/ #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 "otrplugin.h" #include "otrguiclient.h" #include "otrlchatinterface.h" #include "kopete_otr.h" /** * @author Michael Zanetti */ typedef KGenericFactory OTRPluginFactory; static const TDEAboutData aboutdata("kopete_otr", I18N_NOOP("OTR") , "0.7" ); K_EXPORT_COMPONENT_FACTORY( kopete_otr, OTRPluginFactory( &aboutdata ) ) OTRPlugin::OTRPlugin( TQObject *parent, const char *name, const TQStringList & /* args */ ) : Kopete::Plugin( OTRPluginFactory::instance(), parent, name ) { if( !pluginStatic_ ) pluginStatic_=this; m_inboundHandler = new OtrMessageHandlerFactory(this); connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( aboutToSend( Kopete::Message & ) ), TQT_SLOT( slotOutgoingMessage( Kopete::Message & ) ) ); // connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( aboutToDisplay( Kopete::Message & ) ), // this, TQT_SLOT( slotIncomingMessage( Kopete::Message & ) ) ); connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ), this, TQT_SLOT( slotNewChatSessionWindow( Kopete::ChatSession * ) ) ); TQObject::connect( this, TQT_SIGNAL( settingsChanged() ), this, TQT_SLOT( slotSettingsChanged() ) ); //initialize the otrlib and create the interface object otrlChatInterface = OtrlChatInterface::self(); //update key files when an account is ready to use if( TQFile::exists( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkey" ) && !TQFile::exists( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkeys" ) ){ kdDebug() << "Detected old format keyfile. Doing updates!" << endl; kdDebug() << "Reading old keyfile..." << endl; TQFile fpold( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkey" ); TQString line; TQString file; if( fpold.open( IO_ReadWrite ) ){ while( fpold.readLine( line, 100 ) != -1){ file.append( line ); } } kdDebug() << "Writing new keyfile" << endl; TQFile fpnew( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkeys" ); fpnew.open( IO_ReadWrite ); fpnew.writeBlock( file.latin1(), file.length() ); fpnew.close(); kdDebug() << "Writing backup for old keyfile" << endl; TQFile fpbup( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkey.old" ); fpbup.open( IO_ReadWrite ); fpbup.writeBlock( file.latin1(), file.length() ); fpbup.close(); kdDebug() << "Deleting old keyfile" << endl; fpold.remove(); kdDebug() << "Reading old fingerprintsfile..." << endl; TQFile fpfingerprintsold( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints" ); line = ""; file = ""; if( fpfingerprintsold.open( IO_ReadWrite ) ){ while( fpfingerprintsold.readLine( line, 100 ) != -1){ file.append( line ); } } kdDebug() << "Writing backup for old fingerprintsfile" << endl; TQFile fpfingerprintsbup( TQString(TDEGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "fingerprints.old" ); fpfingerprintsbup.open( IO_ReadWrite ); fpfingerprintsbup.writeBlock( file.latin1(), file.length() ); fpfingerprintsbup.close(); kdDebug() << "Waiting for accounts to update keyfile format" << endl; connect( Kopete::AccountManager::self(), TQT_SIGNAL( accountRegistered( Kopete::Account * ) ), this, TQT_SLOT( accountReady( Kopete::Account * ) ) ); } // Checking file Permissions OtrlChatInterface::self()->checkFilePermissions( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkeys" ); OtrlChatInterface::self()->checkFilePermissions( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "fingerprints" ); // Check also file permissions for eventuallly old beckup files OtrlChatInterface::self()->checkFilePermissions( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkey.old" ); OtrlChatInterface::self()->checkFilePermissions( TQString( TDEGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "fingerprints.old" ); //setting the policy slotSettingsChanged(); //adding menu to contaclists menubar and contacts popup menu TQStringList policies; policies << i18n("&Default") << i18n("Al&ways") << i18n("&Opportunistic") << i18n("&Manual") << i18n("Ne&ver"); otrPolicyMenu = new TDESelectAction( i18n( "&OTR Policy" ), TQString::fromLatin1("kopete_otr"), 0, actionCollection(), "otr_policy" ); otrPolicyMenu->setItems( policies ); otrPolicyMenu->popupMenu()->insertSeparator( 1 ); otrPolicyMenu->setEnabled( false ); connect( otrPolicyMenu, TQT_SIGNAL( activated() ), this, TQT_SLOT( slotSetPolicy() ) ); connect( Kopete::ContactList::self(), TQT_SIGNAL( metaContactSelected( bool ) ), this, TQT_SLOT( slotSelectionChanged( bool ) ) ); setXMLFile( "otrui.rc" ); //Add GUI action to all already existing kmm // (if the plugin is launched when kopete already runing) TQValueList sessions = Kopete::ChatSessionManager::self()->sessions(); TQValueListIterator it; for (it= sessions.begin(); it!=sessions.end() ; ++it){ slotNewChatSessionWindow( *it ); } } OTRPlugin::~OTRPlugin() { delete m_inboundHandler; pluginStatic_ = 0L; kdDebug() << "Exiting plugin" << endl; } OTRPlugin* OTRPlugin::plugin() { return pluginStatic_ ; } OTRPlugin* OTRPlugin::pluginStatic_ = 0L; void OTRPlugin::slotNewChatSessionWindow( Kopete::ChatSession *KMM ) { //Check if there is another user in the session. //If not it could be a Jabber-MUC //If there are more then one members it is a MUC // Also don't add the Button on an IRC window! if( KMM->members().count() == 1 && (KMM->protocol()) && ( KMM->protocol()->pluginId() != "IRCProtocol" ) ){ new OtrGUIClient( KMM ); } } void OTRPlugin::slotOutgoingMessage( Kopete::Message& msg ) { if( msg.direction() == Kopete::Message::Outbound ){ TQString plainBody = msg.plainBody(); TQString accountId = msg.manager()->account()->accountId(); Kopete::Contact *contact = msg.to().first(); TQString encBody = otrlChatInterface->encryptMessage( plainBody, accountId, msg.manager()->account()->protocol()->displayName(), contact->contactId(), msg.manager() ); msg.setBody( encBody, Kopete::Message::Crypted ); if( !msg.plainBody().isEmpty() ){ messageCache.insert( encBody, plainBody ); } } } void OTRPlugin::slotEnableOtr( Kopete::ChatSession *session, bool enable ){ if( enable ){ TQString policy = session->members().getFirst()->metaContact()->pluginData( OTRPlugin::plugin(), "otr_policy" ); bool noerr; KopeteOtrKcfg::self()->readConfig(); if( policy.toInt( &noerr, 10 ) == 4 || ( policy.toInt( &noerr, 10 ) == 0 && KopeteOtrKcfg::self()->rbNever() ) ){ Kopete::Message msg( session->account()->myself(), session->members(), i18n( "Your policy settings do not allow encrypted sessions to this contact." ), Kopete::Message::Internal, Kopete::Message::RichText ); session->appendMessage( msg ); } else { TQString body = otrlChatInterface->getDefaultQuery( session->account()->accountId() ); Kopete::Message msg1( session->account()->myself(), session->members().getFirst(), TQString( body ), Kopete::Message::Outbound ); if( otrlChatInterface->privState( session ) > 0 ){ body = i18n("Attempting to refresh the OTR session with %1...").arg( otrlChatInterface->formatContact( session->members().getFirst()->contactId() ) ); } else { body = i18n("Attempting to start a private OTR session with %1...").arg( otrlChatInterface->formatContact( session->members().getFirst()->contactId() ) ); } Kopete::Message msg2( session->account()->myself(), session->members().getFirst(), body, Kopete::Message::Internal, Kopete::Message::RichText ); session->sendMessage( msg1 ); session->appendMessage( msg2 ); } } else { otrlChatInterface->disconnectSession( session ); } } void OTRPlugin::slotVerifyFingerprint( Kopete::ChatSession *session ){ otrlChatInterface->verifyFingerprint( session ); } void OTRPlugin::slotSettingsChanged(){ KopeteOtrKcfg::self()->readConfig(); if( KopeteOtrKcfg::self()->rbAlways() ){ otrlChatInterface->setPolicy( OTRL_POLICY_ALWAYS ); } else if( KopeteOtrKcfg::self()->rbOpportunistic() ){ otrlChatInterface->setPolicy( OTRL_POLICY_OPPORTUNISTIC ); } else if( KopeteOtrKcfg::self()->rbManual() ){ otrlChatInterface->setPolicy( OTRL_POLICY_MANUAL ); } else if( KopeteOtrKcfg::self()->rbNever() ){ otrlChatInterface->setPolicy( OTRL_POLICY_NEVER ); } else { otrlChatInterface->setPolicy( OTRL_POLICY_DEFAULT ); } } void OTRPlugin::emitGoneSecure( Kopete::ChatSession *session, int status){ emit goneSecure( session, status ); } TQMap OTRPlugin::getMessageCache(){ return messageCache; } void OtrMessageHandler::handleMessage( Kopete::MessageEvent *event ){ Kopete::Message msg = event->message(); Kopete::ChatSession *session = msg.manager(); TQMap messageCache = OTRPlugin::plugin()->getMessageCache(); if( msg.direction() == Kopete::Message::Inbound ){ TQString body = msg.parsedBody(); kdDebug() << "Received Message: " << msg.parsedBody() << endl; TQString accountId = msg.manager()->account()->accountId(); TQString contactId = msg.from()->contactId(); int ignoremessage = OtrlChatInterface::self()->decryptMessage( &body, accountId, msg.manager()->account()->protocol()->displayName(), contactId, msg.manager() ); msg.setBody( body, Kopete::Message::RichText ); if( ignoremessage | OtrlChatInterface::self()->shouldDiscard( msg.plainBody() ) ){ event->discard(); return; } } else if( msg.direction() == Kopete::Message::Outbound ){ if( messageCache.contains( msg.plainBody() ) ){ msg.setBody( messageCache[msg.plainBody()] ); messageCache.remove( messageCache[msg.plainBody()] ); if(messageCache.count() > 5) messageCache.clear(); } // Check if Message is an OTR message. Should it be discarded or shown? if( OtrlChatInterface::self()->shouldDiscard( msg.plainBody() ) ){ event->discard(); kdDebug() << "discarding" << endl; return; } // If the message is sent while a Finished state libotr deletes the messagetext. // This prevents the empty message from beeing shown in out chatwindow if( msg.plainBody().isEmpty() ){ event->discard(); return; } } event->setMessage( msg ); MessageHandler::handleMessage( event ); } void OTRPlugin::slotSelectionChanged( bool single){ otrPolicyMenu->setEnabled( single ); if ( !single ) return; Kopete::MetaContact *metaContact = Kopete::ContactList::self()->selectedMetaContacts().first(); TQString policy = metaContact->pluginData( this, "otr_policy" ); bool noerr; if ( !policy.isEmpty() && policy != "null" ) otrPolicyMenu->setCurrentItem( policy.toInt( &noerr, 10 )); else otrPolicyMenu->setCurrentItem( 0 ); } void OTRPlugin::slotSetPolicy(){ kdDebug() << "Setting contact policy" << endl; Kopete::MetaContact *metaContact = Kopete::ContactList::self()->selectedMetaContacts().first(); if( metaContact ){ metaContact->setPluginData( this, "otr_policy", TQString::number( otrPolicyMenu->currentItem() ) ); } } void OTRPlugin::accountReady( Kopete::Account *account ){ kdDebug() << "Account " << account->accountId() << " ready. Calling update function."<< endl; otrlChatInterface->updateKeyfile( account ); } #include "otrplugin.moc" // vim: set noet ts=4 sts=4 sw=4: