kmail

vacation.cpp

00001 /*  -*- c++ -*-
00002     vacation.cpp
00003 
00004     KMail, the KDE mail client.
00005     Copyright (c) 2002 Marc Mutz <mutz@kde.org>
00006 
00007     This program is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU General Public License,
00009     version 2.0, as published by the Free Software Foundation.
00010     You should have received a copy of the GNU General Public License
00011     along with this program; if not, write to the Free Software Foundation,
00012     Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
00013 */
00014 
00015 #ifdef HAVE_CONFIG_H
00016 #include <config.h>
00017 #endif
00018 
00019 #include "vacation.h"
00020 #include <limits.h>
00021 
00022 #include "vacationdialog.h"
00023 #include "sievejob.h"
00024 using KMail::SieveJob;
00025 #include "kmkernel.h"
00026 #include "kmmainwidget.h"
00027 #include "accountmanager.h"
00028 using KMail::AccountManager;
00029 #include "kmacctimap.h"
00030 #include "kmmessage.h"
00031 #include "globalsettings.h"
00032 #include <libkpimidentities/identitymanager.h>
00033 #include <libkpimidentities/identity.h>
00034 
00035 #include <kmime_header_parsing.h>
00036 using KMime::Types::AddrSpecList;
00037 
00038 #include <ksieve/parser.h>
00039 #include <ksieve/scriptbuilder.h>
00040 #include <ksieve/error.h>
00041 
00042 #include <klocale.h>
00043 #include <kmessagebox.h>
00044 #include <kdebug.h>
00045 
00046 #include <qdatetime.h>
00047 
00048 #include <cassert>
00049 #include <vector>
00050 #include <map>
00051 #include <set>
00052 
00053 namespace KSieveExt {
00054 
00055   class MultiScriptBuilder : public KSieve::ScriptBuilder {
00056     std::vector<KSieve::ScriptBuilder*> mBuilders;
00057   public:
00058     MultiScriptBuilder() : KSieve::ScriptBuilder() {}
00059     MultiScriptBuilder( KSieve::ScriptBuilder * sb1 )
00060       : KSieve::ScriptBuilder(), mBuilders( 1 )
00061     {
00062       mBuilders[0] = sb1;
00063       assert( sb1 );
00064     }
00065     MultiScriptBuilder( KSieve::ScriptBuilder * sb1,
00066                         KSieve::ScriptBuilder * sb2 )
00067       : KSieve::ScriptBuilder(), mBuilders( 2 )
00068     {
00069       mBuilders[0] = sb1;
00070       mBuilders[1] = sb2;
00071       assert( sb1 ); assert( sb2 );
00072     }
00073     MultiScriptBuilder( KSieve::ScriptBuilder * sb1,
00074                         KSieve::ScriptBuilder * sb2,
00075                         KSieve::ScriptBuilder * sb3 )
00076       : KSieve::ScriptBuilder(), mBuilders( 3 )
00077     {
00078       mBuilders[0] = sb1;
00079       mBuilders[1] = sb2;
00080       mBuilders[2] = sb3;
00081       assert( sb1 ); assert( sb2 ); assert( sb3 );
00082     }
00083     ~MultiScriptBuilder() {}
00084   private:
00085 #ifdef FOREACH
00086 #undef FOREACH
00087 #endif
00088 #define FOREACH for ( std::vector<KSieve::ScriptBuilder*>::const_iterator it = mBuilders.begin(), end = mBuilders.end() ; it != end ; ++it ) (*it)->
00089     void commandStart( const QString & identifier ) { FOREACH commandStart( identifier ); }
00090     void commandEnd() { FOREACH commandEnd(); }
00091     void testStart( const QString & identifier ) { FOREACH testStart( identifier ); }
00092     void testEnd() { FOREACH testEnd(); }
00093     void testListStart() { FOREACH testListStart(); }
00094     void testListEnd() { FOREACH testListEnd(); }
00095     void blockStart() { FOREACH blockStart(); }
00096     void blockEnd() { FOREACH blockEnd(); }
00097     void hashComment( const QString & comment ) { FOREACH hashComment( comment ); }
00098     void bracketComment( const QString & comment ) { FOREACH bracketComment( comment ); }
00099     void lineFeed() { FOREACH lineFeed(); }
00100     void error( const KSieve::Error & e ) { FOREACH error( e ); }
00101     void finished() { FOREACH finished(); }
00102     void taggedArgument( const QString & tag ) { FOREACH taggedArgument( tag ); }
00103     void stringArgument( const QString & string, bool multiline, const QString & fixme ) { FOREACH stringArgument( string, multiline, fixme ); }
00104     void numberArgument( unsigned long number, char quantifier ) { FOREACH numberArgument( number, quantifier ); }
00105     void stringListArgumentStart() { FOREACH stringListArgumentStart(); }
00106     void stringListEntry( const QString & string, bool multiline, const QString & fixme) { FOREACH stringListEntry( string, multiline, fixme ); }
00107     void stringListArgumentEnd() { FOREACH stringListArgumentEnd(); }
00108 #undef FOREACH
00109   };
00110 
00111 }
00112 
00113 namespace {
00114 
00115   class GenericInformationExtractor : public KSieve::ScriptBuilder {
00116   public:
00117     enum BuilderMethod {
00118       Any,
00119       TaggedArgument,
00120       StringArgument,
00121       NumberArgument,
00122       CommandStart,
00123       CommandEnd,
00124       TestStart,
00125       TestEnd,
00126       TestListStart,
00127       TestListEnd,
00128       BlockStart,
00129       BlockEnd,
00130       StringListArgumentStart,
00131       StringListEntry,
00132       StringListArgumentEnd
00133     };
00134 
00135     struct StateNode {
00136       // expectation:
00137       int depth;
00138       BuilderMethod method;
00139       const char * string;
00140       // actions:
00141       int if_found;
00142       int if_not_found;
00143       const char * save_tag;
00144     };
00145 
00146     const std::vector<StateNode> mNodes;
00147     std::map<QString,QString> mResults;
00148     std::set<unsigned int> mRecursionGuard;
00149     unsigned int mState;
00150     int mNestingDepth;
00151 
00152   public:
00153     GenericInformationExtractor( const std::vector<StateNode> & nodes )
00154       : KSieve::ScriptBuilder(), mNodes( nodes ), mState( 0 ), mNestingDepth( 0 ) {}
00155 
00156     const std::map<QString,QString> & results() const { return mResults; }
00157 
00158   private:
00159     void process( BuilderMethod method, const QString & string=QString::null ) {
00160       doProcess( method, string );
00161       mRecursionGuard.clear();
00162     }
00163     void doProcess( BuilderMethod method, const QString & string ) {
00164       mRecursionGuard.insert( mState );
00165       bool found = true;
00166       const StateNode & expected = mNodes[mState];
00167       if ( expected.depth != -1 && mNestingDepth != expected.depth )
00168         found = false;
00169       if ( expected.method != Any && method != expected.method )
00170         found = false;
00171       if ( const char * str = expected.string )
00172         if ( string.lower() != QString::fromUtf8( str ).lower() )
00173           found = false;
00174       kdDebug(5006) << ( found ? "found:     " : "not found: " )
00175                     << mState << " -> "
00176                     << ( found ? expected.if_found : expected.if_not_found ) << endl;
00177       mState = found ? expected.if_found : expected.if_not_found ;
00178       assert( mState < mNodes.size() );
00179       if ( found )
00180         if ( const char * save_tag = expected.save_tag )
00181           mResults[save_tag] = string;
00182       if ( !found && !mRecursionGuard.count( mState ) ) {
00183         doProcess( method, string );
00184       }
00185     }
00186     void commandStart( const QString & identifier ) { kdDebug(5006) << k_funcinfo << endl; process( CommandStart, identifier ); }
00187     void commandEnd() { kdDebug(5006) << k_funcinfo << endl; process( CommandEnd ); }
00188     void testStart( const QString & identifier ) { kdDebug(5006) << k_funcinfo << endl; process( TestStart, identifier ); }
00189     void testEnd() { kdDebug(5006) << k_funcinfo << endl; process( TestEnd ); }
00190     void testListStart() { kdDebug(5006) << k_funcinfo << endl; process( TestListStart ); }
00191     void testListEnd() { kdDebug(5006) << k_funcinfo << endl; process( TestListEnd ); }
00192     void blockStart() { kdDebug(5006) << k_funcinfo << endl; process( BlockStart ); ++mNestingDepth; }
00193     void blockEnd() { kdDebug(5006) << k_funcinfo << endl; --mNestingDepth; process( BlockEnd ); }
00194     void hashComment( const QString & ) { kdDebug(5006) << k_funcinfo << endl; }
00195     void bracketComment( const QString & ) { kdDebug(5006) << k_funcinfo << endl; }
00196     void lineFeed() { kdDebug(5006) << k_funcinfo << endl; }
00197     void error( const KSieve::Error & ) {
00198       kdDebug(5006) << k_funcinfo << endl;
00199       mState = 0;
00200     }
00201     void finished() { kdDebug(5006) << k_funcinfo << endl; }
00202 
00203     void taggedArgument( const QString & tag ) { kdDebug(5006) << k_funcinfo << endl; process( TaggedArgument, tag ); }
00204     void stringArgument( const QString & string, bool, const QString & ) { kdDebug(5006) << k_funcinfo << endl; process( StringArgument, string ); }
00205     void numberArgument( unsigned long number, char ) { kdDebug(5006) << k_funcinfo << endl; process( NumberArgument, QString::number( number ) ); }
00206     void stringListArgumentStart() { kdDebug(5006) << k_funcinfo << endl; process( StringListArgumentStart ); }
00207     void stringListEntry( const QString & string, bool, const QString & ) { kdDebug(5006) << k_funcinfo << endl; process( StringListEntry, string ); }
00208     void stringListArgumentEnd() { kdDebug(5006) << k_funcinfo << endl; process( StringListArgumentEnd ); }
00209   };
00210 
00211   typedef GenericInformationExtractor GIE;
00212   static const GenericInformationExtractor::StateNode spamNodes[] = {
00213     { 0, GIE::CommandStart, "if",  1, 0, 0 },              // 0
00214     { 0,   GIE::TestStart, "header", 2, 0, 0 },            // 1
00215     { 0,     GIE::TaggedArgument, "contains", 3, 0, 0 },   // 2
00216 
00217     // accept both string and string-list:
00218     { 0,     GIE::StringArgument, "x-spam-flag", 9, 4, "x-spam-flag" },    // 3
00219     { 0,     GIE::StringListArgumentStart, 0, 5, 0, 0 },                   // 4
00220     { 0,       GIE::StringListEntry, "x-spam-flag", 6, 7, "x-spam-flag" }, // 5
00221     { 0,       GIE::StringListEntry, 0, 6, 8, 0 },                         // 6
00222     { 0,     GIE::StringListArgumentEnd, 0, 0, 5, 0 },                     // 7
00223     { 0,     GIE::StringListArgumentEnd, 0, 9, 0, 0 },                     // 8
00224 
00225     // accept both string and string-list:
00226     { 0,     GIE::StringArgument, "yes", 15, 10, "spam-flag-yes" },    // 9
00227     { 0,     GIE::StringListArgumentStart, 0, 11, 0, 0 },              // 10
00228     { 0,       GIE::StringListEntry, "yes", 12, 13, "spam-flag-yes" }, // 11
00229     { 0,       GIE::StringListEntry, 0, 12, 14, 0 },                   // 12
00230     { 0,     GIE::StringListArgumentEnd, 0, 0, 11, 0 },                // 13
00231     { 0,     GIE::StringListArgumentEnd, 0, 15, 0, 0 },                // 14
00232 
00233     { 0,   GIE::TestEnd, 0, 16, 0, 0 }, // 15
00234 
00235     // block of command, find "stop", take nested if's into account:
00236     { 0,   GIE::BlockStart, 0, 17, 0, 0 },                // 16
00237     { 1,     GIE::CommandStart, "stop", 20, 19, "stop" }, // 17
00238     { -1,    GIE::Any, 0, 17, 0, 0 },                     // 18
00239     { 0,   GIE::BlockEnd, 0, 0, 18, 0 },                  // 19
00240 
00241     { -1, GIE::Any, 0, 20, 20, 0 }, // 20 end state
00242   };
00243   static const unsigned int numSpamNodes = sizeof spamNodes / sizeof *spamNodes ;
00244 
00245   class SpamDataExtractor : public GenericInformationExtractor {
00246   public:
00247     SpamDataExtractor()
00248       : GenericInformationExtractor( std::vector<StateNode>( spamNodes, spamNodes + numSpamNodes ) )
00249     {
00250 
00251     }
00252 
00253     bool found() const {
00254       return mResults.count( "x-spam-flag" ) &&
00255         mResults.count( "spam-flag-yes" ) &&
00256         mResults.count( "stop" ) ;
00257     }
00258   };
00259 
00260   // to understand this table, study the output of
00261   // libksieve/tests/parsertest
00262   //   'if not address :domain :contains ["from"] ["mydomain.org"] { keep; stop; }'
00263   static const GenericInformationExtractor::StateNode domainNodes[] = {
00264     { 0, GIE::CommandStart, "if", 1, 0, 0 },       // 0
00265     { 0,   GIE::TestStart, "not", 2, 0, 0, },      // 1
00266     { 0,     GIE::TestStart, "address", 3, 0, 0 }, // 2
00267 
00268     // :domain and :contains in arbitrary order:
00269     { 0,       GIE::TaggedArgument, "domain", 4, 5, 0 },     // 3
00270     { 0,       GIE::TaggedArgument, "contains", 7, 0, 0 },   // 4
00271     { 0,       GIE::TaggedArgument, "contains", 6, 0, 0 },   // 5
00272     { 0,       GIE::TaggedArgument, "domain", 7, 0, 0 },     // 6
00273 
00274     // accept both string and string-list:
00275     { 0,       GIE::StringArgument, "from", 13, 8, "from" },     // 7
00276     { 0,       GIE::StringListArgumentStart, 0, 9, 0, 0 },       // 8
00277     { 0,         GIE::StringListEntry, "from", 10, 11, "from" }, // 9
00278     { 0,         GIE::StringListEntry, 0, 10, 12, 0 },           // 10
00279     { 0,       GIE::StringListArgumentEnd, 0, 0, 9, 0 },         // 11
00280     { 0,       GIE::StringListArgumentEnd, 0, 13, 0, 0 },        // 12
00281 
00282     // string: save, string-list: save last
00283     { 0,       GIE::StringArgument, 0, 17, 14, "domainName" },    // 13
00284     { 0,       GIE::StringListArgumentStart, 0, 15, 0, 0 },       // 14
00285     { 0,         GIE::StringListEntry, 0, 15, 16, "domainName" }, // 15
00286     { 0,       GIE::StringListArgumentEnd, 0, 17, 0, 0 },         // 16
00287 
00288     { 0,     GIE::TestEnd, 0, 18, 0, 0 },  // 17
00289     { 0,   GIE::TestEnd, 0, 19, 0, 0 },    // 18
00290 
00291     // block of commands, find "stop", take nested if's into account:
00292     { 0,   GIE::BlockStart, 0, 20, 0, 0 },                 // 19
00293     { 1,     GIE::CommandStart, "stop", 23, 22, "stop" },  // 20
00294     { -1,    GIE::Any, 0, 20, 0, 0 },                      // 21
00295     { 0,   GIE::BlockEnd, 0, 0, 21, 0 },                   // 22
00296 
00297     { -1, GIE::Any, 0, 23, 23, 0 }  // 23 end state
00298   };
00299   static const unsigned int numDomainNodes = sizeof domainNodes / sizeof *domainNodes ;
00300 
00301   class DomainRestrictionDataExtractor : public GenericInformationExtractor {
00302   public:
00303     DomainRestrictionDataExtractor()
00304       : GenericInformationExtractor( std::vector<StateNode>( domainNodes, domainNodes+numDomainNodes ) )
00305     {
00306 
00307     }
00308 
00309     QString domainName() /*not const, since map::op[] isn't const*/ {
00310       return mResults.count( "stop" ) && mResults.count( "from" )
00311         ? mResults["domainName"] : QString::null ;
00312     }
00313   };
00314 
00315   class VacationDataExtractor : public KSieve::ScriptBuilder {
00316     enum Context {
00317       None = 0,
00318       // command itself:
00319       VacationCommand,
00320       // tagged args:
00321       Days, Addresses
00322     };
00323   public:
00324     VacationDataExtractor()
00325       : KSieve::ScriptBuilder(),
00326     mContext( None ), mNotificationInterval( 0 )
00327     {
00328       kdDebug(5006) << "VacationDataExtractor instantiated" << endl;
00329     }
00330     virtual ~VacationDataExtractor() {}
00331 
00332     int notificationInterval() const { return mNotificationInterval; }
00333     const QString & messageText() const { return mMessageText; }
00334     const QStringList & aliases() const { return mAliases; }
00335 
00336   private:
00337     void commandStart( const QString & identifier ) {
00338       kdDebug( 5006 ) << "VacationDataExtractor::commandStart( \"" << identifier << "\" )" << endl;
00339       if ( identifier != "vacation" )
00340     return;
00341       reset();
00342       mContext = VacationCommand;
00343     }
00344 
00345     void commandEnd() {
00346       kdDebug( 5006 ) << "VacationDataExtractor::commandEnd()" << endl;
00347       mContext = None;
00348     }
00349 
00350     void testStart( const QString & ) {}
00351     void testEnd() {}
00352     void testListStart() {}
00353     void testListEnd() {}
00354     void blockStart() {}
00355     void blockEnd() {}
00356     void hashComment( const QString & ) {}
00357     void bracketComment( const QString & ) {}
00358     void lineFeed() {}
00359     void error( const KSieve::Error & e ) {
00360       kdDebug( 5006 ) << "VacationDataExtractor::error() ### "
00361               << e.asString() << " @ " << e.line() << "," << e.column()
00362               << endl;
00363     }
00364     void finished() {}
00365 
00366     void taggedArgument( const QString & tag ) {
00367       kdDebug( 5006 ) << "VacationDataExtractor::taggedArgument( \"" << tag << "\" )" << endl;
00368       if ( mContext != VacationCommand )
00369     return;
00370       if ( tag == "days" )
00371     mContext = Days;
00372       else if ( tag == "addresses" )
00373     mContext = Addresses;
00374     }
00375 
00376     void stringArgument( const QString & string, bool, const QString & ) {
00377       kdDebug( 5006 ) << "VacationDataExtractor::stringArgument( \"" << string << "\" )" << endl;
00378       if ( mContext == Addresses ) {
00379     mAliases.push_back( string );
00380     mContext = VacationCommand;
00381       } else if ( mContext == VacationCommand ) {
00382     mMessageText = string;
00383     mContext = VacationCommand;
00384       }
00385     }
00386 
00387     void numberArgument( unsigned long number, char ) {
00388       kdDebug( 5006 ) << "VacationDataExtractor::numberArgument( \"" << number << "\" )" << endl;
00389       if ( mContext != Days )
00390     return;
00391       if ( number > INT_MAX )
00392     mNotificationInterval = INT_MAX;
00393       else
00394     mNotificationInterval = number;
00395       mContext = VacationCommand;
00396     }
00397 
00398     void stringListArgumentStart() {}
00399     void stringListEntry( const QString & string, bool, const QString & ) {
00400       kdDebug( 5006 ) << "VacationDataExtractor::stringListEntry( \"" << string << "\" )" << endl;
00401       if ( mContext != Addresses )
00402     return;
00403       mAliases.push_back( string );
00404     }
00405     void stringListArgumentEnd() {
00406       kdDebug( 5006 ) << "VacationDataExtractor::stringListArgumentEnd()" << endl;
00407       if ( mContext != Addresses )
00408     return;
00409       mContext = VacationCommand;
00410     }
00411 
00412   private:
00413     Context mContext;
00414     int mNotificationInterval;
00415     QString mMessageText;
00416     QStringList mAliases;
00417 
00418     void reset() {
00419       kdDebug(5006) << "VacationDataExtractor::reset()" << endl;
00420       mContext = None;
00421       mNotificationInterval = 0;
00422       mAliases.clear();
00423       mMessageText = QString::null;
00424     }
00425   };
00426 
00427 }
00428 
00429 namespace KMail {
00430 
00431   Vacation::Vacation( QObject * parent, bool checkOnly, const char * name )
00432     : QObject( parent, name ), mSieveJob( 0 ), mDialog( 0 ), mWasActive( false ), mCheckOnly( checkOnly )
00433   {
00434     mUrl = findURL();
00435     kdDebug(5006) << "Vacation: found url \"" << mUrl.prettyURL() << "\"" << endl;
00436     if ( mUrl.isEmpty() ) // nothing to do...
00437       return;
00438     mSieveJob = SieveJob::get( mUrl, !checkOnly );
00439     connect( mSieveJob, SIGNAL(gotScript(KMail::SieveJob*,bool,const QString&,bool)),
00440          SLOT(slotGetResult(KMail::SieveJob*,bool,const QString&,bool)) );
00441   }
00442 
00443   Vacation::~Vacation() {
00444     if ( mSieveJob ) mSieveJob->kill(); mSieveJob = 0;
00445     delete mDialog; mDialog = 0;
00446     kdDebug(5006) << "~Vacation()" << endl;
00447   }
00448 
00449   static inline QString dotstuff( QString s ) {
00450     if ( s.startsWith( "." ) )
00451       return '.' + s.replace( "\n.", "\n.." );
00452     else
00453       return s.replace( "\n.", "\n.." );
00454   }
00455 
00456   QString Vacation::composeScript( const QString & messageText,
00457                    int notificationInterval,
00458                    const AddrSpecList & addrSpecs,
00459                                    bool sendForSpam, const QString & domain )
00460   {
00461     QString addressesArgument;
00462     QStringList aliases;
00463     if ( !addrSpecs.empty() ) {
00464       addressesArgument += ":addresses [ ";
00465       QStringList sl;
00466       for ( AddrSpecList::const_iterator it = addrSpecs.begin() ; it != addrSpecs.end() ; ++it ) {
00467     sl.push_back( '"' + (*it).asString().replace( '\\', "\\\\" ).replace( '"', "\\\"" ) + '"' );
00468     aliases.push_back( (*it).asString() );
00469       }
00470       addressesArgument += sl.join( ", " ) + " ] ";
00471     }
00472     QString script = QString::fromLatin1("require \"vacation\";\n\n" );
00473     if ( !sendForSpam )
00474       script += QString::fromLatin1( "if header :contains \"X-Spam-Flag\" \"YES\""
00475                                      " { keep; stop; }\n" ); // FIXME?
00476 
00477     if ( !domain.isEmpty() ) // FIXME
00478       script += QString::fromLatin1( "if not address :domain :contains \"from\" \"%1\" { keep; stop; }\n" ).arg( domain );
00479 
00480     script += "vacation ";
00481     script += addressesArgument;
00482     if ( notificationInterval > 0 )
00483       script += QString::fromLatin1(":days %1 ").arg( notificationInterval );
00484     script += QString::fromLatin1("text:\n");
00485     script += dotstuff( messageText.isEmpty() ? defaultMessageText() : messageText );
00486     script += QString::fromLatin1( "\n.\n;\n" );
00487     return script;
00488   }
00489 
00490   static KURL findUrlForAccount( const KMail::ImapAccountBase * a ) {
00491     assert( a );
00492     const SieveConfig sieve = a->sieveConfig();
00493     if ( !sieve.managesieveSupported() )
00494       return KURL();
00495     if ( sieve.reuseConfig() ) {
00496       // assemble Sieve url from the settings of the account:
00497       KURL u;
00498       u.setProtocol( "sieve" );
00499       u.setHost( a->host() );
00500       u.setUser( a->login() );
00501       u.setPass( a->passwd() );
00502       u.setPort( sieve.port() );
00503       u.addQueryItem( "x-mech", a->auth() == "*" ? "PLAIN" : a->auth() ); //translate IMAP LOGIN to PLAIN
00504       if ( !a->useSSL() && !a->useTLS() )
00505           u.addQueryItem( "x-allow-unencrypted", "true" );
00506       u.setFileName( sieve.vacationFileName() );
00507       return u;
00508     } else {
00509       KURL u = sieve.alternateURL();
00510       if ( u.protocol().lower() == "sieve" && !a->useSSL() && !a->useTLS() && u.queryItem("x-allow-unencrypted").isEmpty() )
00511           u.addQueryItem( "x-allow-unencrypted", "true" );
00512       u.setFileName( sieve.vacationFileName() );
00513       return u;
00514     }
00515   }
00516 
00517   KURL Vacation::findURL() const {
00518     AccountManager * am = kmkernel->acctMgr();
00519     assert( am );
00520     for ( KMAccount * a = am->first() ; a ; a = am->next() )
00521       if ( KMail::ImapAccountBase * iab = dynamic_cast<KMail::ImapAccountBase*>( a ) ) {
00522         KURL u = findUrlForAccount( iab );
00523     if ( !u.isEmpty() )
00524       return u;
00525       }
00526     return KURL();
00527   }
00528 
00529   bool Vacation::parseScript( const QString & script, QString & messageText,
00530                   int & notificationInterval, QStringList & aliases,
00531                               bool & sendForSpam, QString & domainName ) {
00532     if ( script.stripWhiteSpace().isEmpty() ) {
00533       messageText = defaultMessageText();
00534       notificationInterval = defaultNotificationInterval();
00535       aliases = defaultMailAliases();
00536       sendForSpam = defaultSendForSpam();
00537       domainName = defaultDomainName();
00538       return true;
00539     }
00540 
00541     // The stripWhiteSpace() call below prevents parsing errors. The
00542     // slave somehow omits the last \n, which results in a lone \r at
00543     // the end, leading to a parse error.
00544     const QCString scriptUTF8 = script.stripWhiteSpace().utf8();
00545     kdDebug(5006) << "scriptUtf8 = \"" + scriptUTF8 + "\"" << endl;
00546     KSieve::Parser parser( scriptUTF8.begin(),
00547                scriptUTF8.begin() + scriptUTF8.length() );
00548     VacationDataExtractor vdx;
00549     SpamDataExtractor sdx;
00550     DomainRestrictionDataExtractor drdx;
00551     KSieveExt::MultiScriptBuilder tsb( &vdx, &sdx, &drdx );
00552     parser.setScriptBuilder( &tsb );
00553     if ( !parser.parse() )
00554       return false;
00555     messageText = vdx.messageText().stripWhiteSpace();
00556     notificationInterval = vdx.notificationInterval();
00557     aliases = vdx.aliases();
00558     if ( !GlobalSettings::allowOutOfOfficeUploadButNoSettings() ) {
00559       sendForSpam = !sdx.found();
00560       domainName = drdx.domainName();
00561     }
00562     return true;
00563   }
00564 
00565   QString Vacation::defaultMessageText() {
00566     return i18n("I am out of office till %1.\n"
00567         "\n"
00568         "In urgent cases, please contact Mrs. <vacation replacement>\n"
00569         "\n"
00570         "email: <email address of vacation replacement>\n"
00571         "phone: +49 711 1111 11\n"
00572         "fax.:  +49 711 1111 12\n"
00573         "\n"
00574         "Yours sincerely,\n"
00575         "-- <enter your name and email address here>\n")
00576       .arg( KGlobal::locale()->formatDate( QDate::currentDate().addDays( 1 ) ) );
00577   }
00578 
00579   int Vacation::defaultNotificationInterval() {
00580     return 7; // days
00581   }
00582 
00583   QStringList Vacation::defaultMailAliases() {
00584     QStringList sl;
00585     for ( KPIM::IdentityManager::ConstIterator it = kmkernel->identityManager()->begin() ;
00586       it != kmkernel->identityManager()->end() ; ++it )
00587       if ( !(*it).emailAddr().isEmpty() )
00588     sl.push_back( (*it).emailAddr() );
00589     return sl;
00590   }
00591 
00592   bool Vacation::defaultSendForSpam() {
00593     return GlobalSettings::outOfOfficeReactToSpam();
00594   }
00595 
00596   QString Vacation::defaultDomainName() {
00597     return GlobalSettings::outOfOfficeDomain();
00598   }
00599 
00600   void Vacation::slotGetResult( SieveJob * job, bool success,
00601                 const QString & script, bool active ) {
00602     kdDebug(5006) << "Vacation::slotGetResult( ??, " << success
00603           << ", ?, " << active << " )" << endl
00604           << "script:" << endl
00605           << script << endl;
00606     mSieveJob = 0; // job deletes itself after returning from this slot!
00607 
00608     if ( !mCheckOnly && mUrl.protocol() == "sieve" && !job->sieveCapabilities().isEmpty() &&
00609      !job->sieveCapabilities().contains("vacation") ) {
00610       KMessageBox::sorry( 0, i18n("Your server did not list \"vacation\" in "
00611                   "its list of supported Sieve extensions;\n"
00612                   "without it, KMail cannot install out-of-"
00613                   "office replies for you.\n"
00614                   "Please contact you system administrator.") );
00615       emit result( false );
00616       return;
00617     }
00618 
00619     if ( !mDialog && !mCheckOnly )
00620       mDialog = new VacationDialog( i18n("Configure \"Out of Office\" Replies"), 0, 0, false );
00621 
00622     QString messageText = defaultMessageText();
00623     int notificationInterval = defaultNotificationInterval();
00624     QStringList aliases = defaultMailAliases();
00625     bool sendForSpam = defaultSendForSpam();
00626     QString domainName = defaultDomainName();
00627     if ( !success ) active = false; // default to inactive
00628 
00629     if ( !mCheckOnly && ( !success || !parseScript( script, messageText, notificationInterval, aliases, sendForSpam, domainName ) ) )
00630       KMessageBox::information( 0, i18n("Someone (probably you) changed the "
00631                     "vacation script on the server.\n"
00632                     "KMail is no longer able to determine "
00633                     "the parameters for the autoreplies.\n"
00634                     "Default values will be used." ) );
00635 
00636     mWasActive = active;
00637     if ( mDialog ) {
00638       mDialog->setActivateVacation( active );
00639       mDialog->setMessageText( messageText );
00640       mDialog->setNotificationInterval( notificationInterval );
00641       mDialog->setMailAliases( aliases.join(", ") );
00642       mDialog->setSendForSpam( sendForSpam );
00643       mDialog->setDomainName( domainName );
00644       mDialog->enableDomainAndSendForSpam( !GlobalSettings::allowOutOfOfficeUploadButNoSettings() );
00645 
00646       connect( mDialog, SIGNAL(okClicked()), SLOT(slotDialogOk()) );
00647       connect( mDialog, SIGNAL(cancelClicked()), SLOT(slotDialogCancel()) );
00648       connect( mDialog, SIGNAL(defaultClicked()), SLOT(slotDialogDefaults()) );
00649 
00650       mDialog->show();
00651     }
00652 
00653     emit scriptActive( mWasActive );
00654     if ( mCheckOnly && mWasActive ) {
00655       if ( KMessageBox::questionYesNo( 0, i18n( "There is still an active out-of-office reply configured.\n"
00656                                         "Do you want to edit it?"), i18n("Out-of-office reply still active"),
00657                                         KGuiItem( i18n( "Edit"), "edit" ), KGuiItem( i18n("Ignore"), "button_cancel" ) )
00658            == KMessageBox::Yes ) {
00659         kmkernel->getKMMainWidget()->slotEditVacation();
00660       }
00661     }
00662   }
00663 
00664   void Vacation::slotDialogDefaults() {
00665     if ( !mDialog )
00666       return;
00667     mDialog->setActivateVacation( true );
00668     mDialog->setMessageText( defaultMessageText() );
00669     mDialog->setNotificationInterval( defaultNotificationInterval() );
00670     mDialog->setMailAliases( defaultMailAliases().join(", ") );
00671     mDialog->setSendForSpam( defaultSendForSpam() );
00672     mDialog->setDomainName( defaultDomainName() );
00673   }
00674 
00675   void Vacation::slotDialogOk() {
00676     kdDebug(5006) << "Vacation::slotDialogOk()" << endl;
00677     // compose a new script:
00678     const QString script = composeScript( mDialog->messageText(),
00679                     mDialog->notificationInterval(),
00680                     mDialog->mailAliases(),
00681                                     mDialog->sendForSpam(),
00682                                     mDialog->domainName() );
00683     const bool active = mDialog->activateVacation();
00684     emit scriptActive( active );
00685 
00686     kdDebug(5006) << "script:" << endl << script << endl;
00687 
00688     // and commit the dialog's settings to the server:
00689     mSieveJob = SieveJob::put( mUrl, script, active, mWasActive );
00690     connect( mSieveJob, SIGNAL(gotScript(KMail::SieveJob*,bool,const QString&,bool)),
00691          active
00692          ? SLOT(slotPutActiveResult(KMail::SieveJob*,bool))
00693          : SLOT(slotPutInactiveResult(KMail::SieveJob*,bool)) );
00694 
00695     // destroy the dialog:
00696     mDialog->delayedDestruct();
00697     mDialog = 0;
00698   }
00699 
00700   void Vacation::slotDialogCancel() {
00701     kdDebug(5006) << "Vacation::slotDialogCancel()" << endl;
00702     mDialog->delayedDestruct();
00703     mDialog = 0;
00704     emit result( false );
00705   }
00706 
00707   void Vacation::slotPutActiveResult( SieveJob * job, bool success ) {
00708     handlePutResult( job, success, true );
00709   }
00710 
00711   void Vacation::slotPutInactiveResult( SieveJob * job, bool success ) {
00712     handlePutResult( job, success, false );
00713   }
00714 
00715   void Vacation::handlePutResult( SieveJob *, bool success, bool activated ) {
00716     if ( success )
00717       KMessageBox::information( 0, activated
00718                 ? i18n("Sieve script installed successfully on the server.\n"
00719                        "Out of Office reply is now active.")
00720                 : i18n("Sieve script installed successfully on the server.\n"
00721                        "Out of Office reply has been deactivated.") );
00722 
00723     kdDebug(5006) << "Vacation::handlePutResult( ???, " << success << ", ? )"
00724           << endl;
00725     mSieveJob = 0; // job deletes itself after returning from this slot!
00726     emit result( success );
00727     emit scriptActive( activated );
00728   }
00729 
00730 
00731 } // namespace KMail
00732 
00733 #include "vacation.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys