Reimplement filter app.

Patch by Marc Mutz <Marc.Mutz@uni-bielefeld.de>

svn path=/trunk/kdenetwork/kmail/; revision=95487
wilder-work
Michael Haeckel 25 years ago
parent 09c4848c5c
commit aa6f266407
  1. 239
      kmfilteraction.cpp
  2. 21
      kmfilteraction.h
  3. 7
      kmfilterdlg.cpp

@ -22,9 +22,12 @@
#include <ktempfile.h>
#include <kdebug.h>
#include <klocale.h>
#include <kprocess.h>
#include <qcombobox.h>
#include <qlineedit.h>
#include <qvaluelist.h>
#include <qtl.h> // QT Template Library, needed for qHeapSort
#include <signal.h>
#include <stdlib.h>
@ -306,7 +309,55 @@ void KMFilterActionWithCommand::clearParamWidget( QWidget* paramWidget ) const
KMFilterActionWithString::clearParamWidget( paramWidget );
}
QString KMFilterActionWithCommand::substituteCommandLineArgsFor( KMMessage *aMsg, QList<KTempFile> & aTempFileList ) const
{
QString result = mParameter;
QValueList<int> argList;
QRegExp r( "%[0-9]+" );
int start=-1, len=0;
bool OK = FALSE;
// search for '%n'
while ( ( start = r.match( result, start + 1, &len ) ) > 0 ) {
// and save the encountered 'n' in a list.
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 = -1;
QString tempFileName;
KMMessagePart msgPart;
QValueList<int>::Iterator it;
for ( 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 ) {
delete tf;
kdDebug() << "KMFilterActionWithCommand: Could not create temp file!" << endl;
return QString::null;
}
tf->setAutoDelete(TRUE);
aTempFileList.append( tf );
tempFileName = tf->name();
aMsg->bodyPart( (*it), &msgPart );
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.
result = result.arg( tempFileName );
}
return result;
}
//=============================================================================
@ -562,72 +613,54 @@ KMFilterAction::ReturnCode KMFilterActionRedirect::process(KMMessage* aMsg, bool
class KMFilterActionExec : public KMFilterActionWithCommand
{
public:
KMFilterActionExec(const char* aName, const QString aLabel );
KMFilterActionExec();
virtual ReturnCode process(KMMessage* msg, bool& stopIt) const;
static KMFilterAction* newAction(void);
static void dummySigHandler(int);
};
KMFilterAction* KMFilterActionExec::newAction(void)
{
return (new KMFilterActionExec(0,0)); // ###
}
KMFilterActionExec::KMFilterActionExec(const char* aName, const QString aLabel )
: KMFilterActionWithCommand( aName ? aName : "execute" , aLabel ? aLabel : i18n("execute command") )
{
return (new KMFilterActionExec());
}
void KMFilterActionExec::dummySigHandler(int)
KMFilterActionExec::KMFilterActionExec()
: KMFilterActionWithCommand( "execute", i18n("execute command") )
{
}
KMFilterAction::ReturnCode KMFilterActionExec::process(KMMessage *aMsg, bool& /*stop*/) const
{
// should use K{,Shell}Process....
void (*oldSigHandler)(int);
int rc;
if (mParameter.isEmpty()) return ErrorButGoOn;
QString fullCmd = mParameter + " ";
if ( mParameter.isEmpty() )
return ErrorButGoOn;
QList<KTempFile> atmList;
KMMessagePart msgPart;
int i, nr;
while (fullCmd.contains("%"))
{
// this code seems broken for commands that
// specify e.g. %2 before %1. (mmutz)
i = fullCmd.find("%") + 1;
nr = fullCmd.mid(i, fullCmd.find(" ", i) - i).toInt();
aMsg->bodyPart(nr, &msgPart);
KTempFile *atmTempFile = new KTempFile();
atmList.append( atmTempFile );
atmTempFile->setAutoDelete( true );
kByteArrayToFile(msgPart.bodyDecodedBinary(), atmTempFile->name(),
false, false, false);
fullCmd = fullCmd.arg( atmTempFile->name() );
}
oldSigHandler = signal(SIGALRM, &KMFilterActionExec::dummySigHandler);
alarm(30);
rc = system(fullCmd.left(fullCmd.length() - 1 ));
alarm(0);
signal(SIGALRM, oldSigHandler);
if (rc & 255)
atmList.setAutoDelete(TRUE);
assert( aMsg );
QString commandLine = substituteCommandLineArgsFor( aMsg, atmList );
if ( commandLine.isEmpty() )
return ErrorButGoOn;
else
return GoOn;
}
// support suspended until proted to KProcess.
#define KMFILTERACTIONEXTFILTER_IS_BROKEN
KShellProcess shProc;
shProc << commandLine;
if ( !shProc.start( KProcess::Block, KProcess::NoCommunication ) )
return ErrorButGoOn;
#ifndef KMFILTERACTIONEXTFILTER_IS_BROKEN
if ( shProc.normalExit() && shProc.exitStatus() == 0 )
return GoOn;
else
return ErrorButGoOn;
}
//=============================================================================
// KMFilterActionExtFilter - use external filter app
// External message filter: executes a shell command with message
// on stdin; altered message is expected on stdout.
//=============================================================================
class KMFilterActionExtFilter: public KMFilterActionExec
class KMFilterActionExtFilter: public KMFilterActionWithCommand
{
public:
KMFilterActionExtFilter();
@ -641,67 +674,87 @@ KMFilterAction* KMFilterActionExtFilter::newAction(void)
}
KMFilterActionExtFilter::KMFilterActionExtFilter()
: KMFilterActionExec( "filter app", i18n("use external filter app") )
: KMFilterActionWithCommand( "filter app", i18n("pipe through") )
{
}
KMFilterAction::ReturnCode KMFilterActionExtFilter::process(KMMessage* aMsg, bool& stop) const
KMFilterAction::ReturnCode KMFilterActionExtFilter::process(KMMessage* aMsg, bool& ) const
{
int len;
ReturnCode rc=Ok;
QString msgText, origCmd;
char buf[8192];
FILE *fh;
bool ok = TRUE;
KTempFile inFile(locateLocal("tmp", "kmail-filter"), "in");
KTempFile outFile(locateLocal("tmp", "kmail-filter"), "out");
if ( mParameter.isEmpty() )
return ErrorButGoOn;
///////////////
KTempFile * inFile = new KTempFile;
KTempFile * outFile = new KTempFile;
inFile->setAutoDelete(TRUE);
outFile->setAutoDelete(TRUE);
outFile->close();
QList<KTempFile> atmList;
atmList.setAutoDelete(TRUE);
atmList.append( inFile );
atmList.append( outFile );
assert( aMsg );
QString commandLine = substituteCommandLineArgsFor( aMsg , atmList );
if ( commandLine.isEmpty() )
return ErrorButGoOn;
if (mParameter.isEmpty()) return ErrorButGoOn;
// The parentheses force the creation of a subshell
// in which the user-specifed 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 = QString( "(%1) <%2 >%3" ).arg( commandLine ).arg( inFile->name() ).arg( outFile->name() );
// write message to file
fh = inFile.fstream();
if (fh)
{
QString msgText;
FILE *fh;
bool ok = TRUE;
fh = inFile->fstream();
if (fh) {
msgText = aMsg->asString();
if (!fwrite(msgText, msgText.length(), 1, fh)) ok = FALSE;
inFile.close();
}
else ok = FALSE;
outFile.close();
if (ok)
{
// execute filter
origCmd = mParameter;
mParameter += " <" + inFile.name() + " >" + outFile.name();
rc = KMFilterActionExec::process(aMsg, stop);
mParameter = origCmd;
// read altered message
fh = fopen(outFile.name(), "r");
if (fh)
{
msgText = "";
while (1)
{
len = fread(buf, 1, 1023, fh);
if (len <= 0) break;
buf[len] = 0;
msgText += buf;
}
outFile.close();
if (!msgText.isEmpty()) aMsg->fromString(msgText);
}
else ok = FALSE;
}
inFile->close();
} else ok = FALSE;
inFile.unlink();
outFile.unlink();
if ( !ok )
return ErrorButGoOn;
return rc;
}
KShellProcess shProc;
shProc << commandLine;
#endif
if ( !shProc.start( KProcess::Block, KProcess::NoCommunication ) )
return ErrorButGoOn;
if ( !shProc.normalExit() || shProc.exitStatus() != 0 )
return ErrorButGoOn;
// read altered message
int len;
char buf[8192];
fh = fopen(outFile->name(), "r");
if (fh) {
msgText = "";
while (1) {
len = fread(buf, 1, 1023, fh);
if (len <= 0) break;
buf[len] = 0;
msgText += buf;
}
outFile->close();
if ( !msgText.isEmpty() )
aMsg->fromString( msgText );
} else
return ErrorButGoOn;
return GoOn;
}
//=============================================================================
@ -721,9 +774,7 @@ void KMFilterActionDict::init(void)
insert( KMFilterActionBounce::newAction );
#endif
insert( KMFilterActionExec::newAction );
#ifndef KMFILTERACTIONEXTFILTER_IS_BROKEN
insert( KMFilterActionExtFilter::newAction );
#endif
// Register custom filter actions below this line.
}
// The int in the QDict constructor (23) must be a prime

@ -19,7 +19,7 @@ class KMMessage;
class QWidget;
class KMFolder;
class QStringList;
class KTempFile;
//=========================================================
@ -401,17 +401,22 @@ public:
/** Abstract base class for KMail's filter actions that need a command
line as parameter, e.g. 'forward to'. Can create a @ref QListBox
(are there better widgtes in the depth of the kdelibs?) as
line as parameter, e.g. 'forward to'. Can create a @ref QLineEdit
(are there better widgets in the depths of the kdelibs?) as
parameter widget. A subclass of this must provide at least
implementations for the following methods:
@li virtual @ref KMFilterAction::ReturnCodes @ref KMFilterAction::process
@li static @ref KMFilterAction::newAction
The implementation of @ref KMFilterAction::process should take the
command line specified in mParameter, make all required
modifications and stream the resulting command line into @p
mProcess. Then you can start the command with @p mProcess.start().
@short Abstract base class for filter actions with a command line as parameter.
@author Marc Mutz <Marc@Mutz.com>, based upon work by Stefan Taferner <taferner@kde.org>
@see KMFilterActionWithString KMFilterAction KMFilter
@see KMFilterActionWithString KMFilterAction KMFilter KShellProcess
*/
class KMFilterActionWithCommand : public KMFilterActionWithString
@ -437,6 +442,14 @@ public:
/** The filter action shall clear it's parameter widget's
contents. */
virtual void clearParamWidget(QWidget* paramWidget) const;
/** Substitutes various placeholders for data from the message
resp. for filenames containing that data. Currently, only %n is
supported, where n in an integer >= 0. %n gets substituted for
the name of a tempfile holding the n'th message part, with n=0
meaning the body of the message. */
virtual QString substituteCommandLineArgsFor( KMMessage *aMsg, QList<KTempFile> & aTempFileList ) const;
};

@ -205,14 +205,13 @@ void KMFilterListBox::slotUpdateFilterName()
void KMFilterListBox::slotApplyFilterChanges()
{
setEnabled( FALSE );
// unselect all filters:
mListBox->selectAll( FALSE );
// maybe QListBox doesn't emit selected(-1) on unselect,
// so we make sure the edit widgets receive an equivalent:
emit resetWidgets();
mIdxSelItem = -1;
enableControls();
// by now all edit widgets should have written back
// their widget's data into our filter list.
@ -235,10 +234,8 @@ void KMFilterListBox::slotApplyFilterChanges()
delete f;
}
// allow usage of the filters agin.
// allow usage of the filters again.
fm->endUpdate();
setEnabled( TRUE );
fm->writeConfig();
}

Loading…
Cancel
Save