You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
13 KiB
340 lines
13 KiB
/***************************************************************************
|
|
* 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. *
|
|
***************************************************************************/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <tqstylesheet.h>
|
|
#include <tqtimer.h>
|
|
#include <tqregexp.h>
|
|
#include <tqfile.h>
|
|
#include <tqcolor.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdeaction.h>
|
|
#include <tdeactionclasses.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <tdeconfig.h>
|
|
#include <kgenericfactory.h>
|
|
#include <tdeversion.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdemessagebox.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
|
|
*/
|
|
|
|
|
|
OTRPlugin::OTRPlugin( TDEInstance *instance, TQObject *parent, const char *name, const TQStringList & /* args */ )
|
|
: Kopete::Plugin( 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<Kopete::ChatSession*> sessions =
|
|
Kopete::ChatSessionManager::self()->sessions();
|
|
TQValueListIterator<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 ){
|
|
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 <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 );
|
|
}
|
|
|
|
TQMap<TQString, TQString> OTRPlugin::getMessageCache(){
|
|
return messageCache;
|
|
}
|
|
|
|
void OtrMessageHandler::handleMessage( Kopete::MessageEvent *event ){
|
|
Kopete::Message msg = event->message();
|
|
Kopete::ChatSession *session = msg.manager();
|
|
TQMap<TQString, TQString> 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"
|