diff options
Diffstat (limited to 'kmail/kmfilteraction.cpp')
-rw-r--r-- | kmail/kmfilteraction.cpp | 1930 |
1 files changed, 1930 insertions, 0 deletions
diff --git a/kmail/kmfilteraction.cpp b/kmail/kmfilteraction.cpp new file mode 100644 index 000000000..ea77e14b4 --- /dev/null +++ b/kmail/kmfilteraction.cpp @@ -0,0 +1,1930 @@ +// kmfilteraction.cpp +// The process methods really should use an enum instead of an int +// -1 -> status unchanged, 0 -> success, 1 -> failure, 2-> critical failure +// (GoOn), (Ok), (ErrorButGoOn), (CriticalError) + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "kmfilteraction.h" + +#include "kmcommands.h" +#include "kmmsgpart.h" +#include "kmfiltermgr.h" +#include "kmfolderindex.h" +#include "kmfoldermgr.h" +#include "messagesender.h" +#include "kmmainwidget.h" +#include <libkpimidentities/identity.h> +#include <libkpimidentities/identitymanager.h> +#include <libkpimidentities/identitycombo.h> +#include <libkdepim/kfileio.h> +#include <libkdepim/collectingprocess.h> +using KPIM::CollectingProcess; +#include <mimelib/message.h> +#include "kmfawidgets.h" +#include "folderrequester.h" +using KMail::FolderRequester; +#include "kmmsgbase.h" +#include "templateparser.h" +#include "messageproperty.h" +#include "actionscheduler.h" +using KMail::MessageProperty; +using KMail::ActionScheduler; +#include "regexplineedit.h" +using KMail::RegExpLineEdit; +#include <kregexp3.h> +#include <ktempfile.h> +#include <kdebug.h> +#include <klocale.h> +#include <kprocess.h> +#include <kaudioplayer.h> +#include <kurlrequester.h> + +#include <qlabel.h> +#include <qlayout.h> +#include <qtextcodec.h> +#include <qtimer.h> +#include <qobject.h> +#include <qstylesheet.h> +#include <assert.h> + + +//============================================================================= +// +// KMFilterAction +// +//============================================================================= + +KMFilterAction::KMFilterAction( const char* aName, const QString aLabel ) +{ + mName = aName; + mLabel = aLabel; +} + +KMFilterAction::~KMFilterAction() +{ +} + +void KMFilterAction::processAsync(KMMessage* msg) const +{ + ActionScheduler *handler = MessageProperty::filterHandler( msg ); + ReturnCode result = process( msg ); + if (handler) + handler->actionMessage( result ); +} + +bool KMFilterAction::requiresBody(KMMsgBase*) const +{ + return true; +} + +KMFilterAction* KMFilterAction::newAction() +{ + return 0; +} + +QWidget* KMFilterAction::createParamWidget(QWidget* parent) const +{ + return new QWidget(parent); +} + +void KMFilterAction::applyParamWidgetValue(QWidget*) +{ +} + +void KMFilterAction::setParamWidgetValue( QWidget * ) const +{ +} + +void KMFilterAction::clearParamWidget( QWidget * ) const +{ +} + +bool KMFilterAction::folderRemoved(KMFolder*, KMFolder*) +{ + return false; +} + +int KMFilterAction::tempOpenFolder(KMFolder* aFolder) +{ + return kmkernel->filterMgr()->tempOpenFolder(aFolder); +} + +void KMFilterAction::sendMDN( KMMessage * msg, KMime::MDN::DispositionType d, + const QValueList<KMime::MDN::DispositionModifier> & m ) { + if ( !msg ) return; + + /* createMDN requires Return-Path and Disposition-Notification-To + * if it is not set in the message we assume that the notification should go to the + * sender + */ + const QString returnPath = msg->headerField( "Return-Path" ); + const QString dispNoteTo = msg->headerField( "Disposition-Notification-To" ); + if ( returnPath.isEmpty() ) + msg->setHeaderField( "Return-Path", msg->from() ); + if ( dispNoteTo.isEmpty() ) + msg->setHeaderField( "Disposition-Notification-To", msg->from() ); + + KMMessage * mdn = msg->createMDN( KMime::MDN::AutomaticAction, d, false, m ); + if ( mdn && !kmkernel->msgSender()->send( mdn, KMail::MessageSender::SendLater ) ) { + kdDebug(5006) << "KMFilterAction::sendMDN(): sending failed." << endl; + //delete mdn; + } + + //restore orignial header + if ( returnPath.isEmpty() ) + msg->removeHeaderField( "Return-Path" ); + if ( dispNoteTo.isEmpty() ) + msg->removeHeaderField( "Disposition-Notification-To" ); +} + + +//============================================================================= +// +// KMFilterActionWithNone +// +//============================================================================= + +KMFilterActionWithNone::KMFilterActionWithNone( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ) +{ +} + +const QString KMFilterActionWithNone::displayString() const +{ + return label(); +} + + +//============================================================================= +// +// KMFilterActionWithUOID +// +//============================================================================= + +KMFilterActionWithUOID::KMFilterActionWithUOID( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ), mParameter( 0 ) +{ +} + +void KMFilterActionWithUOID::argsFromString( const QString argsStr ) +{ + mParameter = argsStr.stripWhiteSpace().toUInt(); +} + +const QString KMFilterActionWithUOID::argsAsString() const +{ + return QString::number( mParameter ); +} + +const QString KMFilterActionWithUOID::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + + +//============================================================================= +// +// KMFilterActionWithString +// +//============================================================================= + +KMFilterActionWithString::KMFilterActionWithString( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ) +{ +} + +QWidget* KMFilterActionWithString::createParamWidget( QWidget* parent ) const +{ + QLineEdit *le = new KLineEdit(parent); + le->setText( mParameter ); + return le; +} + +void KMFilterActionWithString::applyParamWidgetValue( QWidget* paramWidget ) +{ + mParameter = ((QLineEdit*)paramWidget)->text(); +} + +void KMFilterActionWithString::setParamWidgetValue( QWidget* paramWidget ) const +{ + ((QLineEdit*)paramWidget)->setText( mParameter ); +} + +void KMFilterActionWithString::clearParamWidget( QWidget* paramWidget ) const +{ + ((QLineEdit*)paramWidget)->clear(); +} + +void KMFilterActionWithString::argsFromString( const QString argsStr ) +{ + mParameter = argsStr; +} + +const QString KMFilterActionWithString::argsAsString() const +{ + return mParameter; +} + +const QString KMFilterActionWithString::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + +//============================================================================= +// +// class KMFilterActionWithStringList +// +//============================================================================= + +KMFilterActionWithStringList::KMFilterActionWithStringList( const char* aName, const QString aLabel ) + : KMFilterActionWithString( aName, aLabel ) +{ +} + +QWidget* KMFilterActionWithStringList::createParamWidget( QWidget* parent ) const +{ + QComboBox *cb = new QComboBox( false, parent ); + cb->insertStringList( mParameterList ); + setParamWidgetValue( cb ); + return cb; +} + +void KMFilterActionWithStringList::applyParamWidgetValue( QWidget* paramWidget ) +{ + mParameter = ((QComboBox*)paramWidget)->currentText(); +} + +void KMFilterActionWithStringList::setParamWidgetValue( QWidget* paramWidget ) const +{ + int idx = mParameterList.findIndex( mParameter ); + ((QComboBox*)paramWidget)->setCurrentItem( idx >= 0 ? idx : 0 ); +} + +void KMFilterActionWithStringList::clearParamWidget( QWidget* paramWidget ) const +{ + ((QComboBox*)paramWidget)->setCurrentItem(0); +} + +void KMFilterActionWithStringList::argsFromString( const QString argsStr ) +{ + int idx = mParameterList.findIndex( argsStr ); + if ( idx < 0 ) { + mParameterList.append( argsStr ); + idx = mParameterList.count() - 1; + } + mParameter = *mParameterList.at( idx ); +} + + +//============================================================================= +// +// class KMFilterActionWithFolder +// +//============================================================================= + +KMFilterActionWithFolder::KMFilterActionWithFolder( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ) +{ + mFolder = 0; +} + +QWidget* KMFilterActionWithFolder::createParamWidget( QWidget* parent ) const +{ + FolderRequester *req = new FolderRequester( parent, + kmkernel->getKMMainWidget()->folderTree() ); + setParamWidgetValue( req ); + return req; +} + +void KMFilterActionWithFolder::applyParamWidgetValue( QWidget* paramWidget ) +{ + mFolder = ((FolderRequester *)paramWidget)->folder(); + mFolderName = ((FolderRequester *)paramWidget)->folderId(); +} + +void KMFilterActionWithFolder::setParamWidgetValue( QWidget* paramWidget ) const +{ + if ( mFolder ) + ((FolderRequester *)paramWidget)->setFolder( mFolder ); + else + ((FolderRequester *)paramWidget)->setFolder( mFolderName ); +} + +void KMFilterActionWithFolder::clearParamWidget( QWidget* paramWidget ) const +{ + ((FolderRequester *)paramWidget)->setFolder( kmkernel->draftsFolder() ); +} + +void KMFilterActionWithFolder::argsFromString( const QString argsStr ) +{ + mFolder = kmkernel->folderMgr()->findIdString( argsStr ); + if (!mFolder) + mFolder = kmkernel->dimapFolderMgr()->findIdString( argsStr ); + if (!mFolder) + mFolder = kmkernel->imapFolderMgr()->findIdString( argsStr ); + if (mFolder) + mFolderName = mFolder->idString(); + else + mFolderName = argsStr; +} + +const QString KMFilterActionWithFolder::argsAsString() const +{ + QString result; + if ( mFolder ) + result = mFolder->idString(); + else + result = mFolderName; + return result; +} + +const QString KMFilterActionWithFolder::displayString() const +{ + QString result; + if ( mFolder ) + result = mFolder->prettyURL(); + else + result = mFolderName; + return label() + " \"" + QStyleSheet::escape( result ) + "\""; +} + +bool KMFilterActionWithFolder::folderRemoved( KMFolder* aFolder, KMFolder* aNewFolder ) +{ + if ( aFolder == mFolder ) { + mFolder = aNewFolder; + if ( aNewFolder ) + mFolderName = mFolder->idString(); + return true; + } else + return false; +} + +//============================================================================= +// +// class KMFilterActionWithAddress +// +//============================================================================= + +KMFilterActionWithAddress::KMFilterActionWithAddress( const char* aName, const QString aLabel ) + : KMFilterActionWithString( aName, aLabel ) +{ +} + +QWidget* KMFilterActionWithAddress::createParamWidget( QWidget* parent ) const +{ + KMFilterActionWithAddressWidget *w = new KMFilterActionWithAddressWidget(parent); + w->setText( mParameter ); + return w; +} + +void KMFilterActionWithAddress::applyParamWidgetValue( QWidget* paramWidget ) +{ + mParameter = ((KMFilterActionWithAddressWidget*)paramWidget)->text(); +} + +void KMFilterActionWithAddress::setParamWidgetValue( QWidget* paramWidget ) const +{ + ((KMFilterActionWithAddressWidget*)paramWidget)->setText( mParameter ); +} + +void KMFilterActionWithAddress::clearParamWidget( QWidget* paramWidget ) const +{ + ((KMFilterActionWithAddressWidget*)paramWidget)->clear(); +} + +//============================================================================= +// +// class KMFilterActionWithCommand +// +//============================================================================= + +KMFilterActionWithCommand::KMFilterActionWithCommand( const char* aName, const QString aLabel ) + : KMFilterActionWithUrl( aName, aLabel ) +{ +} + +QWidget* KMFilterActionWithCommand::createParamWidget( QWidget* parent ) const +{ + return KMFilterActionWithUrl::createParamWidget( parent ); +} + +void KMFilterActionWithCommand::applyParamWidgetValue( QWidget* paramWidget ) +{ + KMFilterActionWithUrl::applyParamWidgetValue( paramWidget ); +} + +void KMFilterActionWithCommand::setParamWidgetValue( QWidget* paramWidget ) const +{ + KMFilterActionWithUrl::setParamWidgetValue( paramWidget ); +} + +void KMFilterActionWithCommand::clearParamWidget( QWidget* paramWidget ) const +{ + KMFilterActionWithUrl::clearParamWidget( paramWidget ); +} + +QString KMFilterActionWithCommand::substituteCommandLineArgsFor( KMMessage *aMsg, QPtrList<KTempFile> & aTempFileList ) const +{ + QString result = mParameter; + QValueList<int> argList; + QRegExp r( "%[0-9-]+" ); + + // search for '%n' + int start = -1; + while ( ( start = r.search( result, start + 1 ) ) > 0 ) { + int len = r.matchedLength(); + // and save the encountered 'n' in a list. + bool OK = false; + int n = result.mid( start + 1, len - 1 ).toInt( &OK ); + if ( OK ) + argList.append( n ); + } + + // sort the list of n's + qHeapSort( argList ); + + // and use QString::arg to substitute filenames for the %n's. + int lastSeen = -2; + QString tempFileName; + for ( QValueList<int>::Iterator it = argList.begin() ; it != argList.end() ; ++it ) { + // setup temp files with check for duplicate %n's + if ( (*it) != lastSeen ) { + KTempFile *tf = new KTempFile(); + if ( tf->status() != 0 ) { + tf->close(); + delete tf; + kdDebug(5006) << "KMFilterActionWithCommand: Could not create temp file!" << endl; + return QString::null; + } + tf->setAutoDelete(true); + aTempFileList.append( tf ); + tempFileName = tf->name(); + if ((*it) == -1) + KPIM::kCStringToFile( aMsg->asString(), tempFileName, //### + false, false, false ); + else if (aMsg->numBodyParts() == 0) + KPIM::kByteArrayToFile( aMsg->bodyDecodedBinary(), tempFileName, + false, false, false ); + else { + KMMessagePart msgPart; + aMsg->bodyPart( (*it), &msgPart ); + KPIM::kByteArrayToFile( msgPart.bodyDecodedBinary(), tempFileName, + false, false, false ); + } + tf->close(); + } + // QString( "%0 and %1 and %1" ).arg( 0 ).arg( 1 ) + // returns "0 and 1 and %1", so we must call .arg as + // many times as there are %n's, regardless of their multiplicity. + if ((*it) == -1) result.replace( "%-1", tempFileName ); + else result = result.arg( tempFileName ); + } + + // And finally, replace the %{foo} with the content of the foo + // header field: + QRegExp header_rx( "%\\{([a-z0-9-]+)\\}", false ); + int idx = 0; + while ( ( idx = header_rx.search( result, idx ) ) != -1 ) { + QString replacement = KProcess::quote( aMsg->headerField( header_rx.cap(1).latin1() ) ); + result.replace( idx, header_rx.matchedLength(), replacement ); + idx += replacement.length(); + } + + return result; +} + + +KMFilterAction::ReturnCode KMFilterActionWithCommand::genericProcess(KMMessage* aMsg, bool withOutput) const +{ + Q_ASSERT( aMsg ); + + if ( mParameter.isEmpty() ) + return ErrorButGoOn; + + // KProcess doesn't support a QProcess::launch() equivalent, so + // we must use a temp file :-( + KTempFile * inFile = new KTempFile; + inFile->setAutoDelete(true); + + QPtrList<KTempFile> atmList; + atmList.setAutoDelete(true); + atmList.append( inFile ); + + QString commandLine = substituteCommandLineArgsFor( aMsg , atmList ); + if ( commandLine.isEmpty() ) + return ErrorButGoOn; + + // The parentheses force the creation of a subshell + // in which the user-specified command is executed. + // This is to really catch all output of the command as well + // as to avoid clashes of our redirection with the ones + // the user may have specified. In the long run, we + // shouldn't be using tempfiles at all for this class, due + // to security aspects. (mmutz) + commandLine = "(" + commandLine + ") <" + inFile->name(); + + // write message to file + QString tempFileName = inFile->name(); + KPIM::kCStringToFile( aMsg->asString(), tempFileName, //### + false, false, false ); + inFile->close(); + + CollectingProcess shProc; + shProc.setUseShell(true); + shProc << commandLine; + + // run process: + if ( !shProc.start( KProcess::Block, + withOutput ? KProcess::Stdout + : KProcess::NoCommunication ) ) + return ErrorButGoOn; + + if ( !shProc.normalExit() || shProc.exitStatus() != 0 ) { + return ErrorButGoOn; + } + + if ( withOutput ) { + // read altered message: + QByteArray msgText = shProc.collectedStdout(); + + if ( !msgText.isEmpty() ) { + /* If the pipe through alters the message, it could very well + happen that it no longer has a X-UID header afterwards. That is + unfortunate, as we need to removed the original from the folder + using that, and look it up in the message. When the (new) message + is uploaded, the header is stripped anyhow. */ + QString uid = aMsg->headerField("X-UID"); + aMsg->fromByteArray( msgText ); + aMsg->setHeaderField("X-UID",uid); + } + else + return ErrorButGoOn; + } + return GoOn; +} + + +//============================================================================= +// +// Specific Filter Actions +// +//============================================================================= + +//============================================================================= +// KMFilterActionSendReceipt - send receipt +// Return delivery receipt. +//============================================================================= +class KMFilterActionSendReceipt : public KMFilterActionWithNone +{ +public: + KMFilterActionSendReceipt(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionSendReceipt::newAction(void) +{ + return (new KMFilterActionSendReceipt); +} + +KMFilterActionSendReceipt::KMFilterActionSendReceipt() + : KMFilterActionWithNone( "confirm delivery", i18n("Confirm Delivery") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionSendReceipt::process(KMMessage* msg) const +{ + KMMessage *receipt = msg->createDeliveryReceipt(); + if ( !receipt ) return ErrorButGoOn; + + // Queue message. This is a) so that the user can check + // the receipt before sending and b) for speed reasons. + kmkernel->msgSender()->send( receipt, KMail::MessageSender::SendLater ); + + return GoOn; +} + + + +//============================================================================= +// KMFilterActionSetTransport - set transport to... +// Specify mail transport (smtp server) to be used when replying to a message +//============================================================================= +class KMFilterActionTransport: public KMFilterActionWithString +{ +public: + KMFilterActionTransport(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionTransport::newAction(void) +{ + return (new KMFilterActionTransport); +} + +KMFilterActionTransport::KMFilterActionTransport() + : KMFilterActionWithString( "set transport", i18n("Set Transport To") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionTransport::process(KMMessage* msg) const +{ + if ( mParameter.isEmpty() ) + return ErrorButGoOn; + msg->setHeaderField( "X-KMail-Transport", mParameter ); + return GoOn; +} + + +//============================================================================= +// KMFilterActionReplyTo - set Reply-To to +// Set the Reply-to header in a message +//============================================================================= +class KMFilterActionReplyTo: public KMFilterActionWithString +{ +public: + KMFilterActionReplyTo(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionReplyTo::newAction(void) +{ + return (new KMFilterActionReplyTo); +} + +KMFilterActionReplyTo::KMFilterActionReplyTo() + : KMFilterActionWithString( "set Reply-To", i18n("Set Reply-To To") ) +{ + mParameter = ""; +} + +KMFilterAction::ReturnCode KMFilterActionReplyTo::process(KMMessage* msg) const +{ + msg->setHeaderField( "Reply-To", mParameter ); + return GoOn; +} + + + +//============================================================================= +// KMFilterActionIdentity - set identity to +// Specify Identity to be used when replying to a message +//============================================================================= +class KMFilterActionIdentity: public KMFilterActionWithUOID +{ +public: + KMFilterActionIdentity(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(); + + QWidget * createParamWidget( QWidget * parent ) const; + void applyParamWidgetValue( QWidget * parent ); + void setParamWidgetValue( QWidget * parent ) const; + void clearParamWidget( QWidget * param ) const; +}; + +KMFilterAction* KMFilterActionIdentity::newAction() +{ + return (new KMFilterActionIdentity); +} + +KMFilterActionIdentity::KMFilterActionIdentity() + : KMFilterActionWithUOID( "set identity", i18n("Set Identity To") ) +{ + mParameter = kmkernel->identityManager()->defaultIdentity().uoid(); +} + +KMFilterAction::ReturnCode KMFilterActionIdentity::process(KMMessage* msg) const +{ + msg->setHeaderField( "X-KMail-Identity", QString::number( mParameter ) ); + return GoOn; +} + +QWidget * KMFilterActionIdentity::createParamWidget( QWidget * parent ) const +{ + KPIM::IdentityCombo * ic = new KPIM::IdentityCombo( kmkernel->identityManager(), parent ); + ic->setCurrentIdentity( mParameter ); + return ic; +} + +void KMFilterActionIdentity::applyParamWidgetValue( QWidget * paramWidget ) +{ + KPIM::IdentityCombo * ic = dynamic_cast<KPIM::IdentityCombo*>( paramWidget ); + assert( ic ); + mParameter = ic->currentIdentity(); +} + +void KMFilterActionIdentity::clearParamWidget( QWidget * paramWidget ) const +{ + KPIM::IdentityCombo * ic = dynamic_cast<KPIM::IdentityCombo*>( paramWidget ); + assert( ic ); + ic->setCurrentItem( 0 ); + //ic->setCurrentIdentity( kmkernel->identityManager()->defaultIdentity() ); +} + +void KMFilterActionIdentity::setParamWidgetValue( QWidget * paramWidget ) const +{ + KPIM::IdentityCombo * ic = dynamic_cast<KPIM::IdentityCombo*>( paramWidget ); + assert( ic ); + ic->setCurrentIdentity( mParameter ); +} + +//============================================================================= +// KMFilterActionSetStatus - set status to +// Set the status of messages +//============================================================================= +class KMFilterActionSetStatus: public KMFilterActionWithStringList +{ +public: + KMFilterActionSetStatus(); + virtual ReturnCode process(KMMessage* msg) const; + virtual bool requiresBody(KMMsgBase*) const; + + static KMFilterAction* newAction(); + + virtual bool isEmpty() const { return false; } + + virtual void argsFromString( const QString argsStr ); + virtual const QString argsAsString() const; + virtual const QString displayString() const; +}; + + +static const KMMsgStatus stati[] = +{ + KMMsgStatusFlag, + KMMsgStatusRead, + KMMsgStatusUnread, + KMMsgStatusReplied, + KMMsgStatusForwarded, + KMMsgStatusOld, + KMMsgStatusNew, + KMMsgStatusWatched, + KMMsgStatusIgnored, + KMMsgStatusSpam, + KMMsgStatusHam +}; +static const int StatiCount = sizeof( stati ) / sizeof( KMMsgStatus ); + +KMFilterAction* KMFilterActionSetStatus::newAction() +{ + return (new KMFilterActionSetStatus); +} + +KMFilterActionSetStatus::KMFilterActionSetStatus() + : KMFilterActionWithStringList( "set status", i18n("Mark As") ) +{ + // if you change this list, also update + // KMFilterActionSetStatus::stati above + mParameterList.append( "" ); + mParameterList.append( i18n("msg status","Important") ); + mParameterList.append( i18n("msg status","Read") ); + mParameterList.append( i18n("msg status","Unread") ); + mParameterList.append( i18n("msg status","Replied") ); + mParameterList.append( i18n("msg status","Forwarded") ); + mParameterList.append( i18n("msg status","Old") ); + mParameterList.append( i18n("msg status","New") ); + mParameterList.append( i18n("msg status","Watched") ); + mParameterList.append( i18n("msg status","Ignored") ); + mParameterList.append( i18n("msg status","Spam") ); + mParameterList.append( i18n("msg status","Ham") ); + + mParameter = *mParameterList.at(0); +} + +KMFilterAction::ReturnCode KMFilterActionSetStatus::process(KMMessage* msg) const +{ + int idx = mParameterList.findIndex( mParameter ); + if ( idx < 1 ) return ErrorButGoOn; + + KMMsgStatus status = stati[idx-1] ; + msg->setStatus( status ); + return GoOn; +} + +bool KMFilterActionSetStatus::requiresBody(KMMsgBase*) const +{ + return false; +} + +void KMFilterActionSetStatus::argsFromString( const QString argsStr ) +{ + if ( argsStr.length() == 1 ) { + for ( int i = 0 ; i < StatiCount ; i++ ) + if ( KMMsgBase::statusToStr(stati[i])[0] == argsStr[0] ) { + mParameter = *mParameterList.at(i+1); + return; + } + } + mParameter = *mParameterList.at(0); +} + +const QString KMFilterActionSetStatus::argsAsString() const +{ + int idx = mParameterList.findIndex( mParameter ); + if ( idx < 1 ) return QString::null; + + KMMsgStatus status = stati[idx-1]; + return KMMsgBase::statusToStr(status); +} + +const QString KMFilterActionSetStatus::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + +//============================================================================= +// KMFilterActionFakeDisposition - send fake MDN +// Sends a fake MDN or forces an ignore. +//============================================================================= +class KMFilterActionFakeDisposition: public KMFilterActionWithStringList +{ +public: + KMFilterActionFakeDisposition(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction() { + return (new KMFilterActionFakeDisposition); + } + + virtual bool isEmpty() const { return false; } + + virtual void argsFromString( const QString argsStr ); + virtual const QString argsAsString() const; + virtual const QString displayString() const; +}; + + +// if you change this list, also update +// the count in argsFromString +static const KMime::MDN::DispositionType mdns[] = +{ + KMime::MDN::Displayed, + KMime::MDN::Deleted, + KMime::MDN::Dispatched, + KMime::MDN::Processed, + KMime::MDN::Denied, + KMime::MDN::Failed, +}; +static const int numMDNs = sizeof mdns / sizeof *mdns; + + +KMFilterActionFakeDisposition::KMFilterActionFakeDisposition() + : KMFilterActionWithStringList( "fake mdn", i18n("Send Fake MDN") ) +{ + // if you change this list, also update + // mdns above + mParameterList.append( "" ); + mParameterList.append( i18n("MDN type","Ignore") ); + mParameterList.append( i18n("MDN type","Displayed") ); + mParameterList.append( i18n("MDN type","Deleted") ); + mParameterList.append( i18n("MDN type","Dispatched") ); + mParameterList.append( i18n("MDN type","Processed") ); + mParameterList.append( i18n("MDN type","Denied") ); + mParameterList.append( i18n("MDN type","Failed") ); + + mParameter = *mParameterList.at(0); +} + +KMFilterAction::ReturnCode KMFilterActionFakeDisposition::process(KMMessage* msg) const +{ + int idx = mParameterList.findIndex( mParameter ); + if ( idx < 1 ) return ErrorButGoOn; + + if ( idx == 1 ) // ignore + msg->setMDNSentState( KMMsgMDNIgnore ); + else // send + sendMDN( msg, mdns[idx-2] ); // skip first two entries: "" and "ignore" + return GoOn; +} + +void KMFilterActionFakeDisposition::argsFromString( const QString argsStr ) +{ + if ( argsStr.length() == 1 ) { + if ( argsStr[0] == 'I' ) { // ignore + mParameter = *mParameterList.at(1); + return; + } + for ( int i = 0 ; i < numMDNs ; i++ ) + if ( char(mdns[i]) == argsStr[0] ) { // send + mParameter = *mParameterList.at(i+2); + return; + } + } + mParameter = *mParameterList.at(0); +} + +const QString KMFilterActionFakeDisposition::argsAsString() const +{ + int idx = mParameterList.findIndex( mParameter ); + if ( idx < 1 ) return QString::null; + + return QString( QChar( idx < 2 ? 'I' : char(mdns[idx-2]) ) ); +} + +const QString KMFilterActionFakeDisposition::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + +//============================================================================= +// KMFilterActionRemoveHeader - remove header +// Remove all instances of the given header field. +//============================================================================= +class KMFilterActionRemoveHeader: public KMFilterActionWithStringList +{ +public: + KMFilterActionRemoveHeader(); + virtual ReturnCode process(KMMessage* msg) const; + virtual QWidget* createParamWidget( QWidget* parent ) const; + virtual void setParamWidgetValue( QWidget* paramWidget ) const; + + static KMFilterAction* newAction(); +}; + +KMFilterAction* KMFilterActionRemoveHeader::newAction() +{ + return (new KMFilterActionRemoveHeader); +} + +KMFilterActionRemoveHeader::KMFilterActionRemoveHeader() + : KMFilterActionWithStringList( "remove header", i18n("Remove Header") ) +{ + mParameterList << "" + << "Reply-To" + << "Delivered-To" + << "X-KDE-PR-Message" + << "X-KDE-PR-Package" + << "X-KDE-PR-Keywords"; + mParameter = *mParameterList.at(0); +} + +QWidget* KMFilterActionRemoveHeader::createParamWidget( QWidget* parent ) const +{ + QComboBox *cb = new QComboBox( true/*editable*/, parent ); + cb->setInsertionPolicy( QComboBox::AtBottom ); + setParamWidgetValue( cb ); + return cb; +} + +KMFilterAction::ReturnCode KMFilterActionRemoveHeader::process(KMMessage* msg) const +{ + if ( mParameter.isEmpty() ) return ErrorButGoOn; + + while ( !msg->headerField( mParameter.latin1() ).isEmpty() ) + msg->removeHeaderField( mParameter.latin1() ); + return GoOn; +} + +void KMFilterActionRemoveHeader::setParamWidgetValue( QWidget* paramWidget ) const +{ + QComboBox * cb = dynamic_cast<QComboBox*>(paramWidget); + Q_ASSERT( cb ); + + int idx = mParameterList.findIndex( mParameter ); + cb->clear(); + cb->insertStringList( mParameterList ); + if ( idx < 0 ) { + cb->insertItem( mParameter ); + cb->setCurrentItem( cb->count() - 1 ); + } else { + cb->setCurrentItem( idx ); + } +} + + +//============================================================================= +// KMFilterActionAddHeader - add header +// Add a header with the given value. +//============================================================================= +class KMFilterActionAddHeader: public KMFilterActionWithStringList +{ +public: + KMFilterActionAddHeader(); + virtual ReturnCode process(KMMessage* msg) const; + virtual QWidget* createParamWidget( QWidget* parent ) const; + virtual void setParamWidgetValue( QWidget* paramWidget ) const; + virtual void applyParamWidgetValue( QWidget* paramWidget ); + virtual void clearParamWidget( QWidget* paramWidget ) const; + + virtual const QString argsAsString() const; + virtual void argsFromString( const QString argsStr ); + + virtual const QString displayString() const; + + static KMFilterAction* newAction() + { + return (new KMFilterActionAddHeader); + } +private: + QString mValue; +}; + +KMFilterActionAddHeader::KMFilterActionAddHeader() + : KMFilterActionWithStringList( "add header", i18n("Add Header") ) +{ + mParameterList << "" + << "Reply-To" + << "Delivered-To" + << "X-KDE-PR-Message" + << "X-KDE-PR-Package" + << "X-KDE-PR-Keywords"; + mParameter = *mParameterList.at(0); +} + +KMFilterAction::ReturnCode KMFilterActionAddHeader::process(KMMessage* msg) const +{ + if ( mParameter.isEmpty() ) return ErrorButGoOn; + + msg->setHeaderField( mParameter.latin1(), mValue ); + return GoOn; +} + +QWidget* KMFilterActionAddHeader::createParamWidget( QWidget* parent ) const +{ + QWidget *w = new QWidget( parent ); + QHBoxLayout *hbl = new QHBoxLayout( w ); + hbl->setSpacing( 4 ); + QComboBox *cb = new QComboBox( true, w, "combo" ); + cb->setInsertionPolicy( QComboBox::AtBottom ); + hbl->addWidget( cb, 0 /* stretch */ ); + QLabel *l = new QLabel( i18n("With value:"), w ); + l->setFixedWidth( l->sizeHint().width() ); + hbl->addWidget( l, 0 ); + QLineEdit *le = new KLineEdit( w, "ledit" ); + hbl->addWidget( le, 1 ); + setParamWidgetValue( w ); + return w; +} + +void KMFilterActionAddHeader::setParamWidgetValue( QWidget* paramWidget ) const +{ + int idx = mParameterList.findIndex( mParameter ); + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + cb->clear(); + cb->insertStringList( mParameterList ); + if ( idx < 0 ) { + cb->insertItem( mParameter ); + cb->setCurrentItem( cb->count() - 1 ); + } else { + cb->setCurrentItem( idx ); + } + QLineEdit *le = (QLineEdit*)paramWidget->child("ledit"); + Q_ASSERT( le ); + le->setText( mValue ); +} + +void KMFilterActionAddHeader::applyParamWidgetValue( QWidget* paramWidget ) +{ + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + mParameter = cb->currentText(); + + QLineEdit *le = (QLineEdit*)paramWidget->child("ledit"); + Q_ASSERT( le ); + mValue = le->text(); +} + +void KMFilterActionAddHeader::clearParamWidget( QWidget* paramWidget ) const +{ + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + cb->setCurrentItem(0); + QLineEdit *le = (QLineEdit*)paramWidget->child("ledit"); + Q_ASSERT( le ); + le->clear(); +} + +const QString KMFilterActionAddHeader::argsAsString() const +{ + QString result = mParameter; + result += '\t'; + result += mValue; + + return result; +} + +const QString KMFilterActionAddHeader::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + +void KMFilterActionAddHeader::argsFromString( const QString argsStr ) +{ + QStringList l = QStringList::split( '\t', argsStr, true /*allow empty entries*/ ); + QString s; + if ( l.count() < 2 ) { + s = l[0]; + mValue = ""; + } else { + s = l[0]; + mValue = l[1]; + } + + int idx = mParameterList.findIndex( s ); + if ( idx < 0 ) { + mParameterList.append( s ); + idx = mParameterList.count() - 1; + } + mParameter = *mParameterList.at( idx ); +} + + +//============================================================================= +// KMFilterActionRewriteHeader - rewrite header +// Rewrite a header using a regexp. +//============================================================================= +class KMFilterActionRewriteHeader: public KMFilterActionWithStringList +{ +public: + KMFilterActionRewriteHeader(); + virtual ReturnCode process(KMMessage* msg) const; + virtual QWidget* createParamWidget( QWidget* parent ) const; + virtual void setParamWidgetValue( QWidget* paramWidget ) const; + virtual void applyParamWidgetValue( QWidget* paramWidget ); + virtual void clearParamWidget( QWidget* paramWidget ) const; + + virtual const QString argsAsString() const; + virtual void argsFromString( const QString argsStr ); + + virtual const QString displayString() const; + + static KMFilterAction* newAction() + { + return (new KMFilterActionRewriteHeader); + } +private: + KRegExp3 mRegExp; + QString mReplacementString; +}; + +KMFilterActionRewriteHeader::KMFilterActionRewriteHeader() + : KMFilterActionWithStringList( "rewrite header", i18n("Rewrite Header") ) +{ + mParameterList << "" + << "Subject" + << "Reply-To" + << "Delivered-To" + << "X-KDE-PR-Message" + << "X-KDE-PR-Package" + << "X-KDE-PR-Keywords"; + mParameter = *mParameterList.at(0); +} + +KMFilterAction::ReturnCode KMFilterActionRewriteHeader::process(KMMessage* msg) const +{ + if ( mParameter.isEmpty() || !mRegExp.isValid() ) + return ErrorButGoOn; + + KRegExp3 rx = mRegExp; // KRegExp3::replace is not const. + + QString newValue = rx.replace( msg->headerField( mParameter.latin1() ), + mReplacementString ); + + msg->setHeaderField( mParameter.latin1(), newValue ); + return GoOn; +} + +QWidget* KMFilterActionRewriteHeader::createParamWidget( QWidget* parent ) const +{ + QWidget *w = new QWidget( parent ); + QHBoxLayout *hbl = new QHBoxLayout( w ); + hbl->setSpacing( 4 ); + + QComboBox *cb = new QComboBox( true, w, "combo" ); + cb->setInsertionPolicy( QComboBox::AtBottom ); + hbl->addWidget( cb, 0 /* stretch */ ); + + QLabel *l = new QLabel( i18n("Replace:"), w ); + l->setFixedWidth( l->sizeHint().width() ); + hbl->addWidget( l, 0 ); + + RegExpLineEdit *rele = new RegExpLineEdit( w, "search" ); + hbl->addWidget( rele, 1 ); + + l = new QLabel( i18n("With:"), w ); + l->setFixedWidth( l->sizeHint().width() ); + hbl->addWidget( l, 0 ); + + QLineEdit *le = new KLineEdit( w, "replace" ); + hbl->addWidget( le, 1 ); + + setParamWidgetValue( w ); + return w; +} + +void KMFilterActionRewriteHeader::setParamWidgetValue( QWidget* paramWidget ) const +{ + int idx = mParameterList.findIndex( mParameter ); + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + + cb->clear(); + cb->insertStringList( mParameterList ); + if ( idx < 0 ) { + cb->insertItem( mParameter ); + cb->setCurrentItem( cb->count() - 1 ); + } else { + cb->setCurrentItem( idx ); + } + + RegExpLineEdit *rele = (RegExpLineEdit*)paramWidget->child("search"); + Q_ASSERT( rele ); + rele->setText( mRegExp.pattern() ); + + QLineEdit *le = (QLineEdit*)paramWidget->child("replace"); + Q_ASSERT( le ); + le->setText( mReplacementString ); +} + +void KMFilterActionRewriteHeader::applyParamWidgetValue( QWidget* paramWidget ) +{ + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + mParameter = cb->currentText(); + + RegExpLineEdit *rele = (RegExpLineEdit*)paramWidget->child("search"); + Q_ASSERT( rele ); + mRegExp.setPattern( rele->text() ); + + QLineEdit *le = (QLineEdit*)paramWidget->child("replace"); + Q_ASSERT( le ); + mReplacementString = le->text(); +} + +void KMFilterActionRewriteHeader::clearParamWidget( QWidget* paramWidget ) const +{ + QComboBox *cb = (QComboBox*)paramWidget->child("combo"); + Q_ASSERT( cb ); + cb->setCurrentItem(0); + + RegExpLineEdit *rele = (RegExpLineEdit*)paramWidget->child("search"); + Q_ASSERT( rele ); + rele->clear(); + + QLineEdit *le = (QLineEdit*)paramWidget->child("replace"); + Q_ASSERT( le ); + le->clear(); +} + +const QString KMFilterActionRewriteHeader::argsAsString() const +{ + QString result = mParameter; + result += '\t'; + result += mRegExp.pattern(); + result += '\t'; + result += mReplacementString; + + return result; +} + +const QString KMFilterActionRewriteHeader::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + +void KMFilterActionRewriteHeader::argsFromString( const QString argsStr ) +{ + QStringList l = QStringList::split( '\t', argsStr, true /*allow empty entries*/ ); + QString s; + + s = l[0]; + mRegExp.setPattern( l[1] ); + mReplacementString = l[2]; + + int idx = mParameterList.findIndex( s ); + if ( idx < 0 ) { + mParameterList.append( s ); + idx = mParameterList.count() - 1; + } + mParameter = *mParameterList.at( idx ); +} + + +//============================================================================= +// KMFilterActionMove - move into folder +// File message into another mail folder +//============================================================================= +class KMFilterActionMove: public KMFilterActionWithFolder +{ +public: + KMFilterActionMove(); + virtual ReturnCode process(KMMessage* msg) const; + virtual bool requiresBody(KMMsgBase*) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionMove::newAction(void) +{ + return (new KMFilterActionMove); +} + +KMFilterActionMove::KMFilterActionMove() + : KMFilterActionWithFolder( "transfer", i18n("Move Into Folder") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionMove::process(KMMessage* msg) const +{ + if ( !mFolder ) + return ErrorButGoOn; + + ActionScheduler *handler = MessageProperty::filterHandler( msg ); + if (handler) { + MessageProperty::setFilterFolder( msg, mFolder ); + } else { + // The old filtering system does not support online imap targets. + // Skip online imap targets when using the old system. + KMFolder *check; + check = kmkernel->imapFolderMgr()->findIdString( argsAsString() ); + if (mFolder && (check != mFolder)) { + MessageProperty::setFilterFolder( msg, mFolder ); + } + } + return GoOn; +} + +bool KMFilterActionMove::requiresBody(KMMsgBase*) const +{ + return false; //iff mFolder->folderMgr == msgBase->parent()->folderMgr; +} + + +//============================================================================= +// KMFilterActionCopy - copy into folder +// Copy message into another mail folder +//============================================================================= +class KMFilterActionCopy: public KMFilterActionWithFolder +{ +public: + KMFilterActionCopy(); + virtual ReturnCode process(KMMessage* msg) const; + virtual void processAsync(KMMessage* msg) const; + virtual bool requiresBody(KMMsgBase*) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionCopy::newAction(void) +{ + return (new KMFilterActionCopy); +} + +KMFilterActionCopy::KMFilterActionCopy() + : KMFilterActionWithFolder( "copy", i18n("Copy Into Folder") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionCopy::process(KMMessage* msg) const +{ + // TODO opening and closing the folder is a trade off. + // Perhaps Copy is a seldomly used action for now, + // but I gonna look at improvements ASAP. + if ( !mFolder ) + return ErrorButGoOn; + if ( mFolder && mFolder->open( "filtercopy" ) != 0 ) + return ErrorButGoOn; + + // copy the message 1:1 + KMMessage* msgCopy = new KMMessage( new DwMessage( *msg->asDwMessage() ) ); + + int index; + int rc = mFolder->addMsg(msgCopy, &index); + if (rc == 0 && index != -1) + mFolder->unGetMsg( index ); + mFolder->close("filtercopy"); + + return GoOn; +} + +void KMFilterActionCopy::processAsync(KMMessage* msg) const +{ + // FIXME remove the debug output + kdDebug(5006) << "##### KMFilterActionCopy::processAsync(KMMessage* msg)" << endl; + ActionScheduler *handler = MessageProperty::filterHandler( msg ); + + KMCommand *cmd = new KMCopyCommand( mFolder, msg ); + QObject::connect( cmd, SIGNAL( completed( KMCommand * ) ), + handler, SLOT( copyMessageFinished( KMCommand * ) ) ); + cmd->start(); +} + +bool KMFilterActionCopy::requiresBody(KMMsgBase*) const +{ + return true; +} + + +//============================================================================= +// KMFilterActionForward - forward to +// Forward message to another user +//============================================================================= +class KMFilterActionForward: public KMFilterActionWithAddress +{ +public: + KMFilterActionForward(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionForward::newAction(void) +{ + return (new KMFilterActionForward); +} + +KMFilterActionForward::KMFilterActionForward() + : KMFilterActionWithAddress( "forward", i18n("Forward To") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionForward::process(KMMessage* aMsg) const +{ + if ( mParameter.isEmpty() ) + return ErrorButGoOn; + + // avoid endless loops when this action is used in a filter + // which applies to sent messages + if ( KMMessage::addressIsInAddressList( mParameter, aMsg->to() ) ) + return ErrorButGoOn; + + // Create the forwarded message by hand to make forwarding of messages with + // attachments work. + // Note: This duplicates a lot of code from KMMessage::createForward() and + // KMComposeWin::applyChanges(). + // ### FIXME: Remove the code duplication again. + + KMMessage* msg = new KMMessage; + + msg->initFromMessage( aMsg ); + + // QString st = QString::fromUtf8( aMsg->createForwardBody() ); + + TemplateParser parser( msg, TemplateParser::Forward, + aMsg->body(), false, false, false, false); + parser.process( aMsg ); + + QCString + encoding = KMMsgBase::autoDetectCharset( aMsg->charset(), + KMMessage::preferredCharsets(), + msg->body() ); + if( encoding.isEmpty() ) + encoding = "utf-8"; + QCString str = KMMsgBase::codecForName( encoding )->fromUnicode( msg->body() ); + + msg->setCharset( encoding ); + msg->setTo( mParameter ); + msg->setSubject( "Fwd: " + aMsg->subject() ); + + bool isQP = kmkernel->msgSender()->sendQuotedPrintable(); + + if( aMsg->numBodyParts() == 0 ) + { + msg->setAutomaticFields( true ); + msg->setHeaderField( "Content-Type", "text/plain" ); + // msg->setCteStr( isQP ? "quoted-printable": "8bit" ); + QValueList<int> dummy; + msg->setBodyAndGuessCte(str, dummy, !isQP); + msg->setCharset( encoding ); + if( isQP ) + msg->setBodyEncoded( str ); + else + msg->setBody( str ); + } + else + { + KMMessagePart bodyPart, msgPart; + + msg->removeHeaderField( "Content-Type" ); + msg->removeHeaderField( "Content-Transfer-Encoding" ); + msg->setAutomaticFields( true ); + msg->setBody( "This message is in MIME format.\n\n" ); + + bodyPart.setTypeStr( "text" ); + bodyPart.setSubtypeStr( "plain" ); + // bodyPart.setCteStr( isQP ? "quoted-printable": "8bit" ); + QValueList<int> dummy; + bodyPart.setBodyAndGuessCte(str, dummy, !isQP); + bodyPart.setCharset( encoding ); + bodyPart.setBodyEncoded( str ); + msg->addBodyPart( &bodyPart ); + + for( int i = 0; i < aMsg->numBodyParts(); i++ ) + { + aMsg->bodyPart( i, &msgPart ); + if( i > 0 || qstricmp( msgPart.typeStr(), "text" ) != 0 ) + msg->addBodyPart( &msgPart ); + } + } + msg->cleanupHeader(); + msg->link( aMsg, KMMsgStatusForwarded ); + + sendMDN( aMsg, KMime::MDN::Dispatched ); + + if ( !kmkernel->msgSender()->send( msg, KMail::MessageSender::SendLater ) ) { + kdDebug(5006) << "KMFilterAction: could not forward message (sending failed)" << endl; + return ErrorButGoOn; // error: couldn't send + } + return GoOn; +} + + +//============================================================================= +// KMFilterActionRedirect - redirect to +// Redirect message to another user +//============================================================================= +class KMFilterActionRedirect: public KMFilterActionWithAddress +{ +public: + KMFilterActionRedirect(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionRedirect::newAction(void) +{ + return (new KMFilterActionRedirect); +} + +KMFilterActionRedirect::KMFilterActionRedirect() + : KMFilterActionWithAddress( "redirect", i18n("Redirect To") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionRedirect::process(KMMessage* aMsg) const +{ + KMMessage* msg; + if ( mParameter.isEmpty() ) + return ErrorButGoOn; + + msg = aMsg->createRedirect( mParameter ); + + sendMDN( aMsg, KMime::MDN::Dispatched ); + + if ( !kmkernel->msgSender()->send( msg, KMail::MessageSender::SendLater ) ) { + kdDebug(5006) << "KMFilterAction: could not redirect message (sending failed)" << endl; + return ErrorButGoOn; // error: couldn't send + } + return GoOn; +} + + +//============================================================================= +// KMFilterActionExec - execute command +// Execute a shell command +//============================================================================= +class KMFilterActionExec : public KMFilterActionWithCommand +{ +public: + KMFilterActionExec(); + virtual ReturnCode process(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionExec::newAction(void) +{ + return (new KMFilterActionExec()); +} + +KMFilterActionExec::KMFilterActionExec() + : KMFilterActionWithCommand( "execute", i18n("Execute Command") ) +{ +} + +KMFilterAction::ReturnCode KMFilterActionExec::process(KMMessage *aMsg) const +{ + return KMFilterActionWithCommand::genericProcess( aMsg, false ); // ignore output +} + +//============================================================================= +// KMFilterActionExtFilter - use external filter app +// External message filter: executes a shell command with message +// on stdin; altered message is expected on stdout. +//============================================================================= + +#include <weaver.h> +class PipeJob : public KPIM::ThreadWeaver::Job +{ + public: + PipeJob(QObject* parent = 0 , const char* name = 0, KMMessage* aMsg = 0, QString cmd = 0, QString tempFileName = 0 ) + : Job (parent, name), + mTempFileName(tempFileName), + mCmd(cmd), + mMsg( aMsg ) + { + } + + ~PipeJob() {} + virtual void processEvent( KPIM::ThreadWeaver::Event *ev ) + { + KPIM::ThreadWeaver::Job::processEvent( ev ); + if ( ev->action() == KPIM::ThreadWeaver::Event::JobFinished ) + deleteLater( ); + } + protected: + void run() + { + KPIM::ThreadWeaver::debug (1, "PipeJob::run: doing it .\n"); + FILE *p; + QByteArray ba; + + // backup the serial number in case the header gets lost + QString origSerNum = mMsg->headerField( "X-KMail-Filtered" ); + + p = popen(QFile::encodeName(mCmd), "r"); + int len =100; + char buffer[100]; + // append data to ba: + while (true) { + if (! fgets( buffer, len, p ) ) break; + int oldsize = ba.size(); + ba.resize( oldsize + strlen(buffer) ); + qmemmove( ba.begin() + oldsize, buffer, strlen(buffer) ); + } + pclose(p); + if ( !ba.isEmpty() ) { + KPIM::ThreadWeaver::debug (1, "PipeJob::run: %s", QString(ba).latin1() ); + KMFolder *filterFolder = mMsg->parent(); + ActionScheduler *handler = MessageProperty::filterHandler( mMsg->getMsgSerNum() ); + + mMsg->fromByteArray( ba ); + if ( !origSerNum.isEmpty() ) + mMsg->setHeaderField( "X-KMail-Filtered", origSerNum ); + if ( filterFolder && handler ) { + bool oldStatus = handler->ignoreChanges( true ); + filterFolder->take( filterFolder->find( mMsg ) ); + filterFolder->addMsg( mMsg ); + handler->ignoreChanges( oldStatus ); + } else { + kdDebug(5006) << "Warning: Cannot refresh the message from the external filter." << endl; + } + } + + KPIM::ThreadWeaver::debug (1, "PipeJob::run: done.\n" ); + // unlink the tempFile + QFile::remove(mTempFileName); + } + QString mTempFileName; + QString mCmd; + KMMessage *mMsg; +}; + +class KMFilterActionExtFilter: public KMFilterActionWithCommand +{ +public: + KMFilterActionExtFilter(); + virtual ReturnCode process(KMMessage* msg) const; + virtual void processAsync(KMMessage* msg) const; + static KMFilterAction* newAction(void); +}; + +KMFilterAction* KMFilterActionExtFilter::newAction(void) +{ + return (new KMFilterActionExtFilter); +} + +KMFilterActionExtFilter::KMFilterActionExtFilter() + : KMFilterActionWithCommand( "filter app", i18n("Pipe Through") ) +{ +} +KMFilterAction::ReturnCode KMFilterActionExtFilter::process(KMMessage* aMsg) const +{ + return KMFilterActionWithCommand::genericProcess( aMsg, true ); // use output +} + +void KMFilterActionExtFilter::processAsync(KMMessage* aMsg) const +{ + + ActionScheduler *handler = MessageProperty::filterHandler( aMsg->getMsgSerNum() ); + KTempFile * inFile = new KTempFile; + inFile->setAutoDelete(false); + + QPtrList<KTempFile> atmList; + atmList.setAutoDelete(true); + atmList.append( inFile ); + + QString commandLine = substituteCommandLineArgsFor( aMsg , atmList ); + if ( commandLine.isEmpty() ) + handler->actionMessage( ErrorButGoOn ); + + // The parentheses force the creation of a subshell + // in which the user-specified command is executed. + // This is to really catch all output of the command as well + // as to avoid clashes of our redirection with the ones + // the user may have specified. In the long run, we + // shouldn't be using tempfiles at all for this class, due + // to security aspects. (mmutz) + commandLine = "(" + commandLine + ") <" + inFile->name(); + + // write message to file + QString tempFileName = inFile->name(); + KPIM::kCStringToFile( aMsg->asString(), tempFileName, //### + false, false, false ); + inFile->close(); + + PipeJob *job = new PipeJob(0, 0, aMsg, commandLine, tempFileName); + QObject::connect ( job, SIGNAL( done() ), handler, SLOT( actionMessage() ) ); + kmkernel->weaver()->enqueue(job); +} + +//============================================================================= +// KMFilterActionExecSound - execute command +// Execute a sound +//============================================================================= +class KMFilterActionExecSound : public KMFilterActionWithTest +{ +public: + KMFilterActionExecSound(); + virtual ReturnCode process(KMMessage* msg) const; + virtual bool requiresBody(KMMsgBase*) const; + static KMFilterAction* newAction(void); +}; + +KMFilterActionWithTest::KMFilterActionWithTest( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ) +{ +} + +KMFilterActionWithTest::~KMFilterActionWithTest() +{ +} + +QWidget* KMFilterActionWithTest::createParamWidget( QWidget* parent ) const +{ + KMSoundTestWidget *le = new KMSoundTestWidget(parent); + le->setUrl( mParameter ); + return le; +} + + +void KMFilterActionWithTest::applyParamWidgetValue( QWidget* paramWidget ) +{ + mParameter = ((KMSoundTestWidget*)paramWidget)->url(); +} + +void KMFilterActionWithTest::setParamWidgetValue( QWidget* paramWidget ) const +{ + ((KMSoundTestWidget*)paramWidget)->setUrl( mParameter ); +} + +void KMFilterActionWithTest::clearParamWidget( QWidget* paramWidget ) const +{ + ((KMSoundTestWidget*)paramWidget)->clear(); +} + +void KMFilterActionWithTest::argsFromString( const QString argsStr ) +{ + mParameter = argsStr; +} + +const QString KMFilterActionWithTest::argsAsString() const +{ + return mParameter; +} + +const QString KMFilterActionWithTest::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + + +KMFilterActionExecSound::KMFilterActionExecSound() + : KMFilterActionWithTest( "play sound", i18n("Play Sound") ) +{ +} + +KMFilterAction* KMFilterActionExecSound::newAction(void) +{ + return (new KMFilterActionExecSound()); +} + +KMFilterAction::ReturnCode KMFilterActionExecSound::process(KMMessage*) const +{ + if ( mParameter.isEmpty() ) + return ErrorButGoOn; + QString play = mParameter; + QString file = QString::fromLatin1("file:"); + if (mParameter.startsWith(file)) + play = mParameter.mid(file.length()); + KAudioPlayer::play(QFile::encodeName(play)); + return GoOn; +} + +bool KMFilterActionExecSound::requiresBody(KMMsgBase*) const +{ + return false; +} + +KMFilterActionWithUrl::KMFilterActionWithUrl( const char* aName, const QString aLabel ) + : KMFilterAction( aName, aLabel ) +{ +} + +KMFilterActionWithUrl::~KMFilterActionWithUrl() +{ +} + +QWidget* KMFilterActionWithUrl::createParamWidget( QWidget* parent ) const +{ + KURLRequester *le = new KURLRequester(parent); + le->setURL( mParameter ); + return le; +} + + +void KMFilterActionWithUrl::applyParamWidgetValue( QWidget* paramWidget ) +{ + mParameter = ((KURLRequester*)paramWidget)->url(); +} + +void KMFilterActionWithUrl::setParamWidgetValue( QWidget* paramWidget ) const +{ + ((KURLRequester*)paramWidget)->setURL( mParameter ); +} + +void KMFilterActionWithUrl::clearParamWidget( QWidget* paramWidget ) const +{ + ((KURLRequester*)paramWidget)->clear(); +} + +void KMFilterActionWithUrl::argsFromString( const QString argsStr ) +{ + mParameter = argsStr; +} + +const QString KMFilterActionWithUrl::argsAsString() const +{ + return mParameter; +} + +const QString KMFilterActionWithUrl::displayString() const +{ + // FIXME after string freeze: + // return i18n("").arg( ); + return label() + " \"" + QStyleSheet::escape( argsAsString() ) + "\""; +} + + +//============================================================================= +// +// Filter Action Dictionary +// +//============================================================================= +void KMFilterActionDict::init(void) +{ + insert( KMFilterActionMove::newAction ); + insert( KMFilterActionCopy::newAction ); + insert( KMFilterActionIdentity::newAction ); + insert( KMFilterActionSetStatus::newAction ); + insert( KMFilterActionFakeDisposition::newAction ); + insert( KMFilterActionTransport::newAction ); + insert( KMFilterActionReplyTo::newAction ); + insert( KMFilterActionForward::newAction ); + insert( KMFilterActionRedirect::newAction ); + insert( KMFilterActionSendReceipt::newAction ); + insert( KMFilterActionExec::newAction ); + insert( KMFilterActionExtFilter::newAction ); + insert( KMFilterActionRemoveHeader::newAction ); + insert( KMFilterActionAddHeader::newAction ); + insert( KMFilterActionRewriteHeader::newAction ); + insert( KMFilterActionExecSound::newAction ); + // Register custom filter actions below this line. +} +// The int in the QDict constructor (41) must be a prime +// and should be greater than the double number of KMFilterAction types +KMFilterActionDict::KMFilterActionDict() + : QDict<KMFilterActionDesc>(41) +{ + mList.setAutoDelete(true); + init(); +} + +void KMFilterActionDict::insert( KMFilterActionNewFunc aNewFunc ) +{ + KMFilterAction *action = aNewFunc(); + KMFilterActionDesc* desc = new KMFilterActionDesc; + desc->name = action->name(); + desc->label = action->label(); + desc->create = aNewFunc; + QDict<KMFilterActionDesc>::insert( desc->name, desc ); + QDict<KMFilterActionDesc>::insert( desc->label, desc ); + mList.append( desc ); + delete action; +} |