/* kmgroupware.cpp This file is part of KMail. Copyright (c) 2003 - 2004 Bo Thorsen Copyright (c) 2002 Karl-Heinz Zimmer Copyright (c) 2003 Steffen Hansen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "kmgroupware.h" #include "kmmainwin.h" #include "kmmainwidget.h" #include "kmfoldertree.h" #include "kmcomposewin.h" #include "kmidentity.h" #include "identitymanager.h" #include "kmkernel.h" #include #include #include #include #include #include #include #include #include #include #include #include "korganizeriface_stub.h" // Handle KOrganizer connection static bool connectToKOrganizer(); static KOrganizerIface_stub* mKOrganizerIfaceStub; //----------------------------------------------------------------------------- KMGroupware::KMGroupware( QObject* parent, const char* name ) : QObject( parent, name ), mUseGroupware( false ) { // Make the connection to KOrganizer ready mKOrganizerIfaceStub = 0; kapp->dcopClient()->setNotifications( true ); connect( kapp->dcopClient(), SIGNAL( applicationRemoved( const QCString& ) ), this, SLOT( unregisteredFromDCOP( const QCString& ) ) ); // Listen to config changes connect( kmkernel, SIGNAL( configChanged() ), this, SLOT( readConfig() ) ); } //----------------------------------------------------------------------------- KMGroupware::~KMGroupware() { kapp->dcopClient()->setNotifications( false ); delete mKOrganizerIfaceStub; } void KMGroupware::readConfig() { KConfigGroup options( KMKernel::config(), "Groupware" ); mUseGroupware = options.readBoolEntry( "Enabled", false ); } bool KMGroupware::vPartFoundAndDecoded( KMMessage* msg, QString& s ) { assert( msg ); if( ( DwMime::kTypeText == msg->type() && ( DwMime::kSubtypeVCal == msg->subtype() || DwMime::kSubtypeXVCard == msg->subtype() ) ) || ( DwMime::kTypeApplication == msg->type() && DwMime::kSubtypeOctetStream == msg->subtype() ) ) { s = QString::fromUtf8( msg->bodyDecoded() ); return true; } else if( DwMime::kTypeMultipart == msg->type() && (DwMime::kSubtypeMixed == msg->subtype() ) || (DwMime::kSubtypeAlternative == msg->subtype() )) { // kdDebug(5006) << "KMGroupware looking for TNEF data" << endl; DwBodyPart* dwPart = msg->findDwBodyPart( DwMime::kTypeApplication, DwMime::kSubtypeMsTNEF ); if( !dwPart ) dwPart = msg->findDwBodyPart( DwMime::kTypeApplication, DwMime::kSubtypeOctetStream ); if( dwPart ){ // kdDebug(5006) << "KMGroupware analyzing TNEF data" << endl; KMMessagePart msgPart; KMMessage::bodyPart(dwPart, &msgPart); if ( !connectToKOrganizer() ) kdError() << "DCOP error during KMGroupware::vPartFoundAndDecoded()\n"; else { s = mKOrganizerIfaceStub->msTNEFToVPart( msgPart.bodyDecodedBinary() ); return !s.isEmpty(); } } else { dwPart = msg->findDwBodyPart( DwMime::kTypeText, DwMime::kSubtypeVCal ); if (dwPart) { KMMessagePart msgPart; KMMessage::bodyPart(dwPart, &msgPart); s = msgPart.body(); return true; } } }else if( DwMime::kTypeMultipart == msg->type() && DwMime::kSubtypeMixed == msg->subtype() ) { // TODO: Something? } return false; } /* * Message part formatting */ QString KMGroupware::vPartToHTML( const QString& iCal ) { QString html; if( mUseGroupware ) { // Call KOrganizer and make that format the mail if ( !connectToKOrganizer() ) { kdError() << "DCOP error during KMGroupware::processVCalRequest()\n"; } else { html = mKOrganizerIfaceStub->formatICal( iCal ); kdDebug(5006) << "KOrganizer call succeeded, html = " << html << endl; } } return html; } QString KMGroupware::msTNEFToHTML( const QByteArray& tnef ) { QString html; if( mUseGroupware ) { // Call KOrganizer and make that format the mail if ( !connectToKOrganizer() ) { kdError() << "DCOP error during KMGroupware::processVCalRequest()\n"; } else { html = mKOrganizerIfaceStub->formatTNEF( tnef ); kdDebug(5006) << "KOrganizer call succeeded, html = " << html << endl; } } return html; } /* * Groupware URL handling */ static void iCalRequest( const QString& receiver, const QString& iCal, const QString& choice ) { #if 0 // FIXME: Reinstate Outlook workaround // If we are in legacy mode, and there is more than one receiver, we // need to ask the user which address to use KMMessage* msgOld = mMainWidget->headers()->currentMsg(); KConfigGroup options( KMKernel::config(), "Groupware" ); QString fromAddress; // this variable is only used in legacy mode if( options.readBoolEntry( "LegacyMangleFromToHeaders", false ) ) { QStringList toAddresses = KMMessage::splitEmailAddrList( msgOld->to() ); if( toAddresses.count() <= 1 ) // only one address: no problem, we can spare the user the dialog // and just take the from address fromAddress = msgOld->to(); else { // We have more than one To: address and are in legacy mode. Next // try is to search the identities for one of the email addresses // in the toAddresses list. for( QStringList::Iterator sit = toAddresses.begin(); sit != toAddresses.end(); ++sit ) { if( KMMessage::getEmailAddr( *sit ) == kmkernel->identityManager()->defaultIdentity().emailAddr().local8Bit() ) { // our default identity was contained in the To: list, // copy that from To: to From: fromAddress = *sit; break; // We are done } } // If we still haven't found anything, we have to ask the user // what to do. if( fromAddress.isEmpty() ) { bool bOk; fromAddress = KInputDialog::getItem( i18n( "Select Address" ), i18n( "In order to let Outlook(tm) recognize you as the receiver, you need to indicate which one of the following addresses is your email address:" ), toAddresses, 0, false, &bOk, kmkernel->mainWin() ); if( !bOk ) // If the user didn't select anything, just take the // first one so that we have something at all. fromAddress = toAddresses.first(); } } } #endif if ( !connectToKOrganizer() ) kdError() << "DCOP error during KMGroupware::processVCalRequest()\n"; else { bool rc = mKOrganizerIfaceStub->eventRequest( choice, receiver, iCal ); kdDebug(5006) << "KOrganizer call succeeded, rc = " << rc << endl; #if 0 // TODO(bo): We need to delete the msg somehow if( rc && mMainWidget ) mMainWidget->slotTrashMsg(); #endif } } static void iCalReply( const QString& iCal ) { bool event; if( iCal.find( QRegExp( "BEGIN:\\s*VEVENT" ) ) != -1 ) event = true; else if( iCal.find( QRegExp( "BEGIN:\\s*VTODO" ) ) != -1 ) event = false; else { kdDebug(5006) << "processVCalReply called with something that is not a iCal\n"; return; } // Step 1: call Organizer if ( !connectToKOrganizer() ) kdError() << "DCOP error during KMGroupware::processVCalReply()\n"; else { bool rc = mKOrganizerIfaceStub->eventReply( iCal ); kdDebug(5006) << "KOrganizer call succeeded, rc = " << rc << endl; // step 2: inform user that Organizer was updated KMessageBox::information( kmkernel->mainWin(), (event ? i18n("The answer was registered in your calendar.") : i18n("The answer was registered in your task list.")), QString::null, "groupwareBox"); #if 0 // An answer was saved, so trash the message if( mMainWidget ) mMainWidget->slotTrashMsg(); #endif } } static void iCalCancel( const QString& iCal ) { if ( !connectToKOrganizer() ) kdError() << "DCOP error during KMGroupware::iCalCancel()\n"; else { bool rc = mKOrganizerIfaceStub->cancelEvent( iCal ); kdDebug(5006) << "KOrganizer call succeeded, rc = " << rc << endl; #if 0 // An answer was saved, so trash the message if( mMainWidget ) mMainWidget->slotTrashMsg(); #endif } } // Return the part up until the first '_' or '#', or all of it // Chop off the used part of the string static QString urlPart( QString& aUrl ) { QString result; int i = aUrl.find( '_' ); if( i < 0 ) i = aUrl.find( '#' ); if( i < 0 ) { // Just take the remaining part result = aUrl; aUrl.truncate( 0 ); } else { result = aUrl.left( i ); aUrl = aUrl.mid( i + 1 ); } return result; } bool KMGroupware::handleLink( const KURL &url, KMMessage* msg ) { QString aUrl = url.path(); if( urlPart( aUrl ) != "groupware" ) return false; QString gwAction = urlPart( aUrl ); // Find the part of the message with the iCal QString iCal; if( !vPartFoundAndDecoded( msg, iCal ) ) { kdDebug(5006) << "Could not find the iCal for this link\n"; return false; } if( gwAction == "request" ) { // Find the receiver if we can QString receiver; if( msg ) { KMIdentity ident = kmkernel->identityManager()->identityForAddress( msg->to() ); if( ident != KMIdentity::null ) { receiver = ident.emailAddr(); } else { QStringList addrs = KMMessage::splitEmailAddrList( msg->to() ); if( addrs.count() == 1 ) // Don't ask the user to choose between 1 items receiver = addrs[0]; else { bool ok; receiver = KInputDialog:: getItem( i18n("Select Address"), i18n("None of your identities match the receiver " "of this message,
please choose which of " "the following addresses is yours:"), addrs, 0, FALSE, &ok, kmkernel->mainWin() ); if( !ok ) return false; } } } iCalRequest( receiver, iCal, urlPart( aUrl ) ); } else if( gwAction == "reply" ) iCalReply( iCal ); else if( gwAction == "cancel" ) iCalCancel( iCal ); else { // What what what? kdDebug(5006) << "Unknown groupware action " << gwAction << endl; return false; } return true; } /* * Automatic resource handling */ bool KMGroupware::incomingResourceMessage( KMAccount* /*acct*/, KMMessage* /*msg*/ ) { #if 0 // TODO: Reimplement with DCOP if( !mUseGroupware) return false; QString vCalIn; if( vPartFoundAndDecoded( msg, vCalIn ) ) return false; bool vCalInOK, vCalOutOK, isFree; QString vCalOut; QDateTime start, end; emit( signalResourceRequest( acct->intervals(), KMMessage::getEmailAddr( msg->to() ), vCalIn, vCalInOK, vCalOut, vCalOutOK, isFree, start, end ) ); if( !vCalInOK || !vCalOutOK ) return false; // parsing or generation error somewhere // Check whether we are supposed to answer automatically at all KConfigGroup options( KMKernel::config(), "Groupware" ); if( isFree && options.readBoolEntry( "AutoAccept", false ) ) return false; if( !isFree && options.readBoolEntry( "AutoDeclConflict", false ) ) return false; // Everything went fine so far, now attach the answer KMMessage* msgNew = 0; if( msg ){ msgNew = msg->createReply( KMail::ReplyAuthor, vCalOut, false, true, TRUE ); msgNew->setType( DwMime::kTypeText ); msgNew->setSubtype( DwMime::kSubtypeVCal ); msgNew->setHeaderField("Content-Type", "text/calendar; method=REPLY; charset=\"utf-8\""); internal_directlySendMessage( msgNew ); } // And also record in the account. acct->addInterval( qMakePair( start, end ) ); #endif return true; } /* * Handle connection to KOrganizer. */ static const QCString dcopObjectId = "KOrganizerIface"; static bool connectToKOrganizer() { if ( !mKOrganizerIfaceStub ) { QString error; QCString dcopService; int result = KDCOPServiceStarter::self()-> findServiceFor( "DCOP/Organizer", QString::null, QString::null, &error, &dcopService ); if ( result != 0 ) { kdDebug(5800) << "Couldn't connect to KOrganizer\n"; // TODO: You might want to show "error" (if not empty) here, // using e.g. KMessageBox return false; } QCString dummy; // OK, so korganizer (or kontact) is running. Now ensure the object we want is available. if ( !kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dcopService, dummy ) ) { KDCOPServiceStarter::self()->startServiceFor( "DCOP/Organizer", QString::null, QString::null, &error, &dcopService ); assert( kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dcopService, dummy ) ); } mKOrganizerIfaceStub = new KOrganizerIface_stub( kapp->dcopClient(), dcopService, dcopObjectId ); } return ( mKOrganizerIfaceStub != 0 ); } void KMGroupware::unregisteredFromDCOP( const QCString& appId ) { if ( mKOrganizerIfaceStub && mKOrganizerIfaceStub->app() == appId ) { // Delete the stub so that the next time we need the organizer, // we'll know that we need to start a new one. delete mKOrganizerIfaceStub; mKOrganizerIfaceStub = 0; } } #include "kmgroupware.moc"