Bug 48938

Summary: [PATCH] Filters for outgoing messages should be applied before mails are sent
Product: kmail Reporter: Andrew Kohlsmith <akohlsmith-kde>
Component: filteringAssignee: Bruno Bigras <bigras.bruno>
Status: RESOLVED FIXED    
Severity: wishlist CC: bigras.bruno, cobaco, cshobe, danielb, jannis.fath, jf, max, mfranz, mgd, pentek.imre, sven.burmeister
Priority: NOR    
Version: 1.4.7   
Target Milestone: ---   
Platform: Compiled Sources   
OS: Linux   
Latest Commit: Version Fixed In:
Attachments: kmail.diff

Description Andrew Kohlsmith 2002-10-09 20:08:19 UTC
Version:           1.4.7 (using KDE 3.0.8 (KDE 3.1 beta2))
Installed from:    compiled sources
Compiler:          gcc version 3.1
OS:          Linux (i686) release 2.4.18

When I am creating a "sent mail" filter, I would like to have that message mangled BEFORE it is sent, not after.

e.g. many mailing lists won't accept mail from nonsubscribers.  I don't like signing up with my "real" email address, so I use sign up using a dash-alias: -something.  (e.g. akohlsmith-kde@mixdown.ca, akohlsmith-slashdot@mixdown.ca, akohlsmith-spammylist@mixdown.ca).

Currently in kmail I need separate identities for each of these, or I need to manually edit the From: when composing/replying to a message.

I'd like to be able to set up a "sent mail" filter that changes the From: to whatever I want and have the outgoing message altered.  Currently when I set up a filter it sends the message BEFORE applying the filter.  The end result is that the message to the list bounces but my sent mail folder shows that the From is correct.

example:
I am signed up for the acpi-devel mailing list as akohlsmtih-acpi@mixdown.ca.  I don't want to create another identity just to facilitate sending email to this list, so I set up a filter:

Match any of the following:
<recipients> contains acpi-devel@lists.sourceforge.net

Filter actions:
rewrite header From replace akohlsmith with akohlsmith-acpi

Advanced Options:
apply this filter  [x] to sent messages 

Now when I send a message to acpi-devel@lists.sourceforge.net the mail that gets sent is from akohlsmith@mixdown.ca, but my sent mail shows it is from akohlsmith-acpi@mixdown.ca.  This is what I mean by the "to sent mail" checkbox is too literal; it only applies the filter to mail in the "sent mail" folder.  It should (in my opinion) be applied to any outgoing mail.

Now I also use sent mail filters to send all mail going to my company to a special sent mail folder, so the action is "move to mixdown-sent" -- this should still be possible.
Comment 1 Ingo Klöcker 2002-10-09 22:24:52 UTC
This is no bug but a wish. Furthermore I'm pretty sure that it's a duplicate. 
Comment 2 Andrew Kohlsmith 2002-10-09 22:31:56 UTC
Subject: Re:  filter for sent mail is too literal

> This is no bug but a wish. Furthermore I'm pretty sure that it's a
> duplicate.

My apologies, I mis-clicked.  I didn't notice anything like it in the similar 
bugs/wishlist screen that the bug tracker brings up though.

Regards,
Andrew

Comment 3 Ingo Klöcker 2003-11-05 13:51:56 UTC
*** Bug 67298 has been marked as a duplicate of this bug. ***
Comment 4 Andrew Kohlsmith 2004-05-11 16:21:04 UTC
Still would like to keep this as a feature request for 3.2.2+
Comment 5 Ingo Klöcker 2004-09-06 14:59:25 UTC
*** Bug 78816 has been marked as a duplicate of this bug. ***
Comment 6 Ingo Klöcker 2004-11-08 22:07:51 UTC
*** Bug 92225 has been marked as a duplicate of this bug. ***
Comment 7 Ingo Klöcker 2004-11-08 22:16:44 UTC
*** Bug 61592 has been marked as a duplicate of this bug. ***
Comment 8 _ 2005-01-11 11:35:38 UTC
This is also useful for outgoing mail virus scanning.
Comment 9 Andreas Gungl 2005-01-18 20:32:01 UTC
*** Bug 97243 has been marked as a duplicate of this bug. ***
Comment 10 Andrew Kohlsmith 2005-01-18 20:58:31 UTC
Since the functionality is now in the code but not working should this not be considered a bug now?

i.e. what use is setting the sending identity or rewriting headers if they only apply *after* the message leaves?
Comment 11 Andreas Gungl 2005-02-12 20:41:28 UTC
*** Bug 99194 has been marked as a duplicate of this bug. ***
Comment 12 Christian Schaarschmidt 2006-12-28 01:00:06 UTC
Created attachment 19051 [details]
kmail.diff

I have added a checkbox to the Advanced-Tab
"Apply this filter before sending messages"

Filters are processed before sending, at this point in time the mail is already
signed/encrypted. In other words the body can only be seen/manipulated in
filters if the message is not encrypted.

before I continue I'd like to get some feedback if this is required.

some options
a) as is, body in preSend filter is not available
b) pass unencrypted message to filter. Manipulation is still not possible, but
usage in filter criteria should work (to be verified)
c) process filter before encryption. If mail is deleted from outbox it goes
through the filter but is never sent. Moving to different folder will prevent
mail from sending, only mails in outbox are sent. (check: is FilterMgr
accessible at this stage?)

comments?
Comment 13 Andrew Kohlsmith 2006-12-28 02:34:54 UTC
At this point in time I can't see a real *use* for mangling the body of messages before sending, but I do see the possibility of using a filter to run the entire message through a script or something external (virus scan?) before sending.

Personally I like (c) best, but (b) is also pretty nifty...  with (b) you can always pass back a "bzzzt, try again" type of error and this bypasses the need to manipulate encrypted messages.

I need to check out kdesvn again, as I've upgraded my system and am running 3.5.5 on Slackware.
Comment 14 Michael Gerdau 2006-12-31 12:01:58 UTC
I'm not sure what is the difference between "mangling the body of messages before sending" and "run the entire message through a script".

However I'd like to see a hook to change the body of messages before sending simply to allow adding functionality (formatting/changing/replacing/whatever) that KMail does not (yet) provide through the use of scripts.

That said I think (c) does exactly that AFAICT.

Best,
Michael
Comment 15 Christian Schaarschmidt 2007-01-12 19:43:06 UTC
SVN commit 622728 by schaarsc:

Add "Apply this filter before sending messages" 

Note: Quick win. Works for headers and clear text mails.

CCBUG: 48938


 M  +8 -0      branches/work/kdepim-3.5.5+/kmail/kmfilter.cpp  
 M  +13 -0     branches/work/kdepim-3.5.5+/kmail/kmfilter.h  
 M  +20 -9     branches/work/kdepim-3.5.5+/kmail/kmfilterdlg.cpp  
 M  +1 -1      branches/work/kdepim-3.5.5+/kmail/kmfilterdlg.h  
 M  +1 -0      branches/work/kdepim-3.5.5+/kmail/kmfiltermgr.cpp  
 M  +1 -1      branches/work/kdepim-3.5.5+/kmail/kmfiltermgr.h  
 M  +16 -0     branches/work/kdepim-3.5.5+/kmail/kmsender.cpp  


--- branches/work/kdepim-3.5.5+/kmail/kmfilter.cpp #622727:622728
@@ -51,6 +51,7 @@
     mAction = Down;
   else {
     bApplyOnInbound = true;
+    bApplyBeforeOutbound = false;
     bApplyOnOutbound = false;
     bApplyOnExplicit = true;
     bStopProcessingHere = true;
@@ -75,6 +76,7 @@
     mAction = aFilter.mAction;
   } else {
     bApplyOnInbound = aFilter.applyOnInbound();
+    bApplyBeforeOutbound = aFilter.applyBeforeOutbound();
     bApplyOnOutbound = aFilter.applyOnOutbound();
     bApplyOnExplicit = aFilter.applyOnExplicit();
     bStopProcessingHere = aFilter.stopProcessingHere();
@@ -231,12 +233,14 @@
   else {
     QStringList sets = config->readListEntry("apply-on");
     if ( sets.isEmpty() && !config->hasKey("apply-on") ) {
+      bApplyBeforeOutbound = false;
       bApplyOnOutbound = false;
       bApplyOnInbound = true;
       bApplyOnExplicit = true;
       mApplicability = ButImap;
     } else {
       bApplyOnInbound = bool(sets.contains("check-mail"));
+      bApplyBeforeOutbound = bool(sets.contains("before-send-mail"));
       bApplyOnOutbound = bool(sets.contains("send-mail"));
       bApplyOnExplicit = bool(sets.contains("manual-filtering"));
       mApplicability = (AccountType)config->readNumEntry( "Applicability", ButImap );
@@ -317,6 +321,8 @@
     QStringList sets;
     if ( bApplyOnInbound )
       sets.append( "check-mail" );
+    if ( bApplyBeforeOutbound )
+      sets.append( "before-send-mail" );
     if ( bApplyOnOutbound )
       sets.append( "send-mail" );
     if ( bApplyOnExplicit )
@@ -403,6 +409,8 @@
     result += "This filter belongs to the following sets:";
     if ( bApplyOnInbound )
       result += " Inbound";
+    if ( bApplyBeforeOutbound )
+      result += " before-Outbound";
     if ( bApplyOnOutbound )
       result += " Outbound";
     if ( bApplyOnExplicit )
--- branches/work/kdepim-3.5.5+/kmail/kmfilter.h #622727:622728
@@ -145,12 +145,24 @@
   */
   void setApplyOnOutbound( bool aApply=TRUE ) { bApplyOnOutbound = aApply; }
 
+  /** Set whether this filter should be applied on
+      outbound messages before sending (@p aApply == TRUE) or not.
+      See applyOnOutbound applyOnInbound setApplyOnInbound
+  */
+  void setApplyBeforeOutbound( bool aApply=TRUE ) { bApplyBeforeOutbound = aApply; }
+
   /** @return TRUE if this filter should be applied on
       outbound messages, FALSE otherwise.
       @see setApplyOnOutbound applyOnInbound setApplyOnInbound
   */
   bool applyOnOutbound() const { return bApplyOnOutbound; }
 
+  /** @return TRUE if this filter should be applied on
+      outbound messages before they are sent, FALSE otherwise.
+      @see setApplyOnOutbound applyOnInbound setApplyOnInbound
+  */
+  bool applyBeforeOutbound() const { return bApplyBeforeOutbound; }
+
   /** Set whether this filter should be applied on
       inbound messages (@p aApply == TRUE) or not.
       @see setApplyOnOutbound applyOnInbound applyOnOutbound
@@ -300,6 +312,7 @@
   KShortcut mShortcut;
   bool bPopFilter : 1;
   bool bApplyOnInbound : 1;
+  bool bApplyBeforeOutbound : 1;
   bool bApplyOnOutbound : 1;
   bool bApplyOnExplicit : 1;
   bool bStopProcessingHere : 1;
--- branches/work/kdepim-3.5.5+/kmail/kmfilterdlg.cpp #622727:622728
@@ -181,7 +181,7 @@
 				    i18n("Advanced Options"), page2);
     {
       QWidget *adv_w = new QWidget( mAdvOptsGroup );
-      QGridLayout *gl = new QGridLayout( adv_w, 8 /*rows*/, 3 /*cols*/,
+      QGridLayout *gl = new QGridLayout( adv_w, 10 /*rows*/, 3 /*cols*/,
       				         0 /*border*/, spacingHint() );
 
       QVBoxLayout *vbl3 = new QVBoxLayout( gl, spacingHint(), "vbl3" );
@@ -209,26 +209,29 @@
       mAccountList->setSorting( -1 );
       gl->addMultiCellWidget( mAccountList, 0, 3, 1, 3 );
 
+      mApplyBeforeOut = new QCheckBox( i18n("Apply this filter before sending messages"), adv_w );
+      gl->addMultiCellWidget( mApplyBeforeOut, 4, 4, 0, 3 );
+
       mApplyOnOut = new QCheckBox( i18n("Apply this filter to &sent messages"), adv_w );
-      gl->addMultiCellWidget( mApplyOnOut, 4, 4, 0, 3 );
+      gl->addMultiCellWidget( mApplyOnOut, 5, 5, 0, 3 );
 
       mApplyOnCtrlJ = new QCheckBox( i18n("Apply this filter on manual &filtering"), adv_w );
-      gl->addMultiCellWidget( mApplyOnCtrlJ, 5, 5, 0, 3 );
+      gl->addMultiCellWidget( mApplyOnCtrlJ, 6, 6, 0, 3 );
 
       mStopProcessingHere = new QCheckBox( i18n("If this filter &matches, stop processing here"), adv_w );
       gl->addMultiCellWidget( mStopProcessingHere,
-			      6, 6, /*from to row*/
+			      7, 7, /*from to row*/
   			      0, 3 /*from to col*/ );
       mConfigureShortcut = new QCheckBox( i18n("Add this filter to the Apply Filter menu"), adv_w );
-      gl->addMultiCellWidget( mConfigureShortcut, 7, 7, 0, 1 );
+      gl->addMultiCellWidget( mConfigureShortcut, 8, 8, 0, 1 );
       QLabel *keyButtonLabel = new QLabel( i18n( "Shortcut:" ), adv_w );
       keyButtonLabel->setAlignment( AlignVCenter | AlignRight );
-      gl->addMultiCellWidget( keyButtonLabel, 7, 7, 2, 2 );
+      gl->addMultiCellWidget( keyButtonLabel, 8, 8, 2, 2 );
       mKeyButton = new KKeyButton( adv_w, "FilterShortcutSelector" );
-      gl->addMultiCellWidget( mKeyButton, 7, 7, 3, 3 );
+      gl->addMultiCellWidget( mKeyButton, 8, 8, 3, 3 );
       mKeyButton->setEnabled( false );
       mConfigureToolbar = new QCheckBox( i18n("Additionally add this filter to the toolbar"), adv_w );
-      gl->addMultiCellWidget( mConfigureToolbar, 8, 8, 0, 3 );
+      gl->addMultiCellWidget( mConfigureToolbar, 9, 9, 0, 3 );
       mConfigureToolbar->setEnabled( false );
 
       QHBox *hbox = new QHBox( adv_w );
@@ -243,7 +246,7 @@
       mFilterActionIconButton->setIcon( "gear" );
       mFilterActionIconButton->setEnabled( false );
 
-      gl->addMultiCellWidget( hbox, 9, 9, 0, 3 );
+      gl->addMultiCellWidget( hbox, 10, 10, 0, 3 );
     }
     vbl2->addWidget( mAdvOptsGroup, 0, Qt::AlignTop );
   }
@@ -273,6 +276,8 @@
   	     this, SLOT(slotApplicabilityChanged()) );
     connect( mApplyOnForChecked, SIGNAL(clicked()),
   	     this, SLOT(slotApplicabilityChanged()) );
+    connect( mApplyBeforeOut, SIGNAL(clicked()),
+  	     this, SLOT(slotApplicabilityChanged()) );
     connect( mApplyOnOut, SIGNAL(clicked()),
   	     this, SLOT(slotApplicabilityChanged()) );
     connect( mApplyOnCtrlJ, SIGNAL(clicked()),
@@ -376,6 +381,8 @@
 		  << aFilter->applyOnInbound() << endl;
     kdDebug(5006) << "apply on outbound == "
 		  << aFilter->applyOnOutbound() << endl;
+    kdDebug(5006) << "apply before outbound == "
+		  << aFilter->applyBeforeOutbound() << endl;
     kdDebug(5006) << "apply on explicit == "
 		  << aFilter->applyOnExplicit() << endl;
 
@@ -385,6 +392,7 @@
     const bool applyOnIn = aFilter->applyOnInbound();
     const bool applyOnForAll = aFilter->applicability() == KMFilter::All;
     const bool applyOnTraditional = aFilter->applicability() == KMFilter::ButImap;
+    const bool applyBeforeOut = aFilter->applyBeforeOutbound();
     const bool applyOnOut = aFilter->applyOnOutbound();
     const bool applyOnExplicit = aFilter->applyOnExplicit();
     const bool stopHere = aFilter->stopProcessingHere();
@@ -402,6 +410,7 @@
     mApplyOnForChecked->setChecked( !applyOnForAll && !applyOnTraditional );
     mAccountList->setEnabled( mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked() );
     slotUpdateAccountList();
+    mApplyBeforeOut->setChecked( applyBeforeOut );
     mApplyOnOut->setChecked( applyOnOut );
     mApplyOnCtrlJ->setChecked( applyOnExplicit );
     mStopProcessingHere->setChecked( stopHere );
@@ -439,6 +448,7 @@
 {
   if ( mFilter ) {
     mFilter->setApplyOnInbound( mApplyOnIn->isChecked() );
+    mFilter->setApplyBeforeOutbound( mApplyBeforeOut->isChecked() );
     mFilter->setApplyOnOutbound( mApplyOnOut->isChecked() );
     mFilter->setApplyOnExplicit( mApplyOnCtrlJ->isChecked() );
     if ( mApplyOnForAll->isChecked() )
@@ -467,6 +477,7 @@
     kdDebug(5006) << "KMFilterDlg: setting filter to be applied at "
                   << ( mFilter->applyOnInbound() ? "incoming " : "" )
                   << ( mFilter->applyOnOutbound() ? "outgoing " : "" )
+                  << ( mFilter->applyBeforeOutbound() ? "before_outgoing " : "" )
                   << ( mFilter->applyOnExplicit() ? "explicit CTRL-J" : "" )
                   << endl;
   }
--- branches/work/kdepim-3.5.5+/kmail/kmfilterdlg.h #622727:622728
@@ -376,7 +376,7 @@
   KMPopFilterActionWidget *mActionGroup;
   /** Lets the user select whether to apply this filter on
       inbound/outbound messages, both, or only on explicit CTRL-J. */
-  QCheckBox *mApplyOnIn, *mApplyOnOut, *mApplyOnCtrlJ;
+  QCheckBox *mApplyOnIn, *mApplyOnOut, *mApplyBeforeOut, *mApplyOnCtrlJ;
   /** For a filter applied to inbound messages selects whether to apply
       this filter to all accounts or to selected accounts only. */
   QRadioButton *mApplyOnForAll, *mApplyOnForTraditional, *mApplyOnForChecked;
--- branches/work/kdepim-3.5.5+/kmail/kmfiltermgr.cpp #622727:622728
@@ -301,6 +301,7 @@
 	   ( !account ||
 	     ( account && (*it)->applyOnAccount( accountId ) ) ) ) ||
          ( (set&Outbound)  && (*it)->applyOnOutbound() ) ||
+         ( (set&BeforeOutbound)  && (*it)->applyBeforeOutbound() ) ||
          ( (set&Explicit) && (*it)->applyOnExplicit() ) ) {
         // filter is applicable
 
--- branches/work/kdepim-3.5.5+/kmail/kmfiltermgr.h #622727:622728
@@ -43,7 +43,7 @@
   void clear();
 
   enum FilterSet { NoSet = 0x0, Inbound = 0x1, Outbound = 0x2, Explicit = 0x4,
-		   All = Inbound|Outbound|Explicit };
+       BeforeOutbound = 0x8, All = Inbound|BeforeOutbound|Outbound|Explicit };
 
   /** Reload filter rules from config file. */
   void readConfig(void);
--- branches/work/kdepim-3.5.5+/kmail/kmsender.cpp #622727:622728
@@ -462,6 +462,22 @@
   }
   mCurrentMsg->setTransferInProgress( TRUE );
 
+  // apply filters before sending message
+  // TODO: to support encrypted/signed messages this sould be moved to messagecomposer.cpp
+  // Disable the emitting of msgAdded signal, because the message is taken out of the
+  // current folder (outbox) and re-added, to make filter actions changing the message
+  // work. We don't want that to screw up message counts.
+  if ( kmkernel->filterMgr() ) {
+    if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
+    const int processResult = kmkernel->filterMgr()->process( mCurrentMsg, KMFilterMgr::BeforeOutbound );
+    if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
+    if ( processResult == 2 /* critical error */ ) {
+      perror("Critical error: Unable to execute filters before sending message(out of space?)");
+      KMessageBox::information(0, i18n("Critical error: "
+            "during filtering before sending message (out of space?)"));
+    }
+  }
+
   // start the sender process or initialize communication
   if (!mSendInProgress)
   {
Comment 16 Thomas McGuire 2007-07-10 18:57:48 UTC
*** Bug 147723 has been marked as a duplicate of this bug. ***
Comment 17 tony 2007-10-08 17:29:38 UTC
Added my vote for this, as it's potentially very useful for me and it had me fooled when trying to use it.
I usually use sendmail as my mail transport but AOL rejects emails from hosts it doesn't recognise so it would be great if a filter could automatically use my ISP's SMTP server if the email's going to an AOL user without me having to notice who their ISP is.
Comment 18 S. Burmeister 2007-12-02 23:23:56 UTC
This is very important for users' privacy, e.g. for adding a x-no-archive: yes header.
Comment 19 Imre Péntek 2009-06-30 15:31:37 UTC
This bug is still present in KMail 1.11.2
Comment 20 Bruno Bigras 2009-07-25 20:53:48 UTC
SVN commit 1002356 by bbigras:

Add support to filter outgoing message before they are sent
based on a commit from Christian Schaarschmidt on the kdepim-3.5.5+
branch
BUG: 48938
CCMAIL: exit3219@gmail.com

 M  +8 -0      kmfilter.cpp  
 M  +13 -0     kmfilter.h  
 M  +21 -7     kmfilterdlg.cpp  
 M  +1 -1      kmfilterdlg.h  
 M  +1 -0      kmfiltermgr.cpp  
 M  +1 -1      kmfiltermgr.h  
 M  +16 -0     kmsender.cpp  


WebSVN link: http://websvn.kde.org/?view=rev&revision=1002356