kmail Library API Documentation

vacation.cpp

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