diff options
Diffstat (limited to 'src/otrplugin.cpp')
-rw-r--r-- | src/otrplugin.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/src/otrplugin.cpp b/src/otrplugin.cpp new file mode 100644 index 0000000..873c800 --- /dev/null +++ b/src/otrplugin.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** + * 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 <qstylesheet.h> +#include <qtimer.h> +#include <qregexp.h> +#include <qfile.h> +#include <qcolor.h> + +#include <kdebug.h> +#include <kaction.h> +#include <kactionclasses.h> +#include <kpopupmenu.h> +#include <kconfig.h> +#include <kgenericfactory.h> +#include <kdeversion.h> +#include <kaboutdata.h> +#include <kstandarddirs.h> +#include <kmessagebox.h> + +#include <kopetemetacontact.h> +#include <kopetecontactlist.h> +#include <kopetechatsessionmanager.h> +#include <kopetesimplemessagehandler.h> +#include <kopeteuiglobal.h> +#include <kopetecontact.h> +#include <kopetemessage.h> +#include <kopeteaccount.h> +#include <kopeteaccountmanager.h> +#include <kopetemessageevent.h> +#include <kopeteprotocol.h> + +#include "otrplugin.h" +#include "otrguiclient.h" +#include "otrlchatinterface.h" +#include "kopete_otr.h" + +/** + * @author Michael Zanetti + */ + + + +typedef KGenericFactory<OTRPlugin> OTRPluginFactory; +static const KAboutData aboutdata("kopete_otr", I18N_NOOP("OTR") , "0.7" ); +K_EXPORT_COMPONENT_FACTORY( kopete_otr, OTRPluginFactory( &aboutdata ) ) + +OTRPlugin::OTRPlugin( QObject *parent, const char *name, const QStringList & /* args */ ) +: Kopete::Plugin( OTRPluginFactory::instance(), parent, name ) +{ + if( !pluginStatic_ ) + pluginStatic_=this; + + m_inboundHandler = new OtrMessageHandlerFactory(this); + + connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToSend( Kopete::Message & ) ), + SLOT( slotOutgoingMessage( Kopete::Message & ) ) ); +// connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToDisplay( Kopete::Message & ) ), +// this, SLOT( slotIncomingMessage( Kopete::Message & ) ) ); + + connect( Kopete::ChatSessionManager::self(), SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ), + this, SLOT( slotNewChatSessionWindow( Kopete::ChatSession * ) ) ); + QObject::connect( this, SIGNAL( settingsChanged() ), this, SLOT( slotSettingsChanged() ) ); + + + + //initialize the otrlib and create the interface object + otrlChatInterface = OtrlChatInterface::self(); + + //update key files when an account is ready to use + if( QFile::exists( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkey" ) && + !QFile::exists( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkeys" ) ){ + kdDebug() << "Detected old format keyfile. Doing updates!" << endl; + kdDebug() << "Reading old keyfile..." << endl; + QFile fpold( QString(KGlobal::dirs()->saveLocation("data", "kopete_otr/", true )) + "privkey" ); + QString line; + QString file; + if( fpold.open( IO_ReadWrite ) ){ + while( fpold.readLine( line, 100 ) != -1){ + file.append( line ); + } + } + kdDebug() << "Writing new keyfile" << endl; + QFile fpnew( QString(KGlobal::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; + QFile fpbup( QString(KGlobal::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; + QFile fpfingerprintsold( QString(KGlobal::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; + QFile fpfingerprintsbup( QString(KGlobal::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(), SIGNAL( accountRegistered( Kopete::Account * ) ), + this, SLOT( accountReady( Kopete::Account * ) ) ); + } + + // Checking file Permissions + OtrlChatInterface::self()->checkFilePermissions( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkeys" ); + OtrlChatInterface::self()->checkFilePermissions( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "fingerprints" ); + // Check also file permissions for eventuallly old beckup files + OtrlChatInterface::self()->checkFilePermissions( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "privkey.old" ); + OtrlChatInterface::self()->checkFilePermissions( QString( KGlobal::dirs()->saveLocation( "data", "kopete_otr/", true ) ) + "fingerprints.old" ); + + //setting the policy + slotSettingsChanged(); + + //adding menu to contaclists menubar and contacts popup menu + QStringList policies; + policies << i18n("&Default") << i18n("Al&ways") << i18n("&Opportunistic") << i18n("&Manual") << i18n("Ne&ver"); + otrPolicyMenu = new KSelectAction( i18n( "&OTR Policy" ), QString::fromLatin1("kopete_otr"), 0, actionCollection(), "otr_policy" ); + otrPolicyMenu->setItems( policies ); + otrPolicyMenu->popupMenu()->insertSeparator( 1 ); + otrPolicyMenu->setEnabled( false ); + connect( otrPolicyMenu, SIGNAL( activated() ), this, SLOT( slotSetPolicy() ) ); + connect( Kopete::ContactList::self(), SIGNAL( metaContactSelected( bool ) ), this, SLOT( slotSelectionChanged( bool ) ) ); + + + setXMLFile( "otrui.rc" ); + + + + //Add GUI action to all already existing kmm + // (if the plugin is launched when kopete already runing) + QValueList<Kopete::ChatSession*> sessions = + Kopete::ChatSessionManager::self()->sessions(); + QValueListIterator<Kopete::ChatSession*> 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 ){ + QString plainBody = msg.plainBody(); + QString accountId = msg.manager()->account()->accountId(); + Kopete::Contact *contact = msg.to().first(); + + QString 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 ){ + QString 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 { + QString body = otrlChatInterface->getDefaultQuery( session->account()->accountId() ); + Kopete::Message msg1( session->account()->myself(), session->members().getFirst(), QString( body ), Kopete::Message::Outbound ); + if( otrlChatInterface->privState( session ) > 0 ){ + body = i18n("Attempting to refresh the OTR session with <b>%1</b>...").arg( otrlChatInterface->formatContact( session->members().getFirst()->contactId() ) ); + } else { + body = i18n("Attempting to start a private OTR session with <b>%1</b>...").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 ); +} + +QMap<QString, QString> OTRPlugin::getMessageCache(){ + return messageCache; +} + +void OtrMessageHandler::handleMessage( Kopete::MessageEvent *event ){ + Kopete::Message msg = event->message(); + Kopete::ChatSession *session = msg.manager(); + QMap<QString, QString> messageCache = OTRPlugin::plugin()->getMessageCache(); + + if( msg.direction() == Kopete::Message::Inbound ){ + QString body = msg.parsedBody(); +kdDebug() << "Received Message: " << msg.parsedBody() << endl; + QString accountId = msg.manager()->account()->accountId(); + QString 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(); + + QString 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", QString::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: + |