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 <tdelocale.h> 00043 #include <tdemessagebox.h> 00044 #include <kdebug.h> 00045 00046 #include <tqdatetime.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 TQString & identifier ) { FOREACH commandStart( identifier ); } 00090 void commandEnd() { FOREACH commandEnd(); } 00091 void testStart( const TQString & 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 TQString & comment ) { FOREACH hashComment( comment ); } 00098 void bracketComment( const TQString & 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 TQString & tag ) { FOREACH taggedArgument( tag ); } 00103 void stringArgument( const TQString & string, bool multiline, const TQString & 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 TQString & string, bool multiline, const TQString & 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<TQString,TQString> 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<TQString,TQString> & results() const { return mResults; } 00157 00158 private: 00159 void process( BuilderMethod method, const TQString & string=TQString() ) { 00160 doProcess( method, string ); 00161 mRecursionGuard.clear(); 00162 } 00163 void doProcess( BuilderMethod method, const TQString & 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() != TQString::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 TQString & identifier ) { kdDebug(5006) << k_funcinfo << endl; process( CommandStart, identifier ); } 00187 void commandEnd() { kdDebug(5006) << k_funcinfo << endl; process( CommandEnd ); } 00188 void testStart( const TQString & 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 TQString & ) { kdDebug(5006) << k_funcinfo << endl; } 00195 void bracketComment( const TQString & ) { 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 TQString & tag ) { kdDebug(5006) << k_funcinfo << endl; process( TaggedArgument, tag ); } 00204 void stringArgument( const TQString & string, bool, const TQString & ) { kdDebug(5006) << k_funcinfo << endl; process( StringArgument, string ); } 00205 void numberArgument( unsigned long number, char ) { kdDebug(5006) << k_funcinfo << endl; process( NumberArgument, TQString::number( number ) ); } 00206 void stringListArgumentStart() { kdDebug(5006) << k_funcinfo << endl; process( StringListArgumentStart ); } 00207 void stringListEntry( const TQString & string, bool, const TQString & ) { 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 TQString domainName() /*not const, since map::op[] isn't const*/ { 00310 return mResults.count( "stop" ) && mResults.count( "from" ) 00311 ? mResults["domainName"] : TQString() ; 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 TQString & messageText() const { return mMessageText; } 00334 const TQStringList & aliases() const { return mAliases; } 00335 00336 private: 00337 void commandStart( const TQString & 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 TQString & ) {} 00351 void testEnd() {} 00352 void testListStart() {} 00353 void testListEnd() {} 00354 void blockStart() {} 00355 void blockEnd() {} 00356 void hashComment( const TQString & ) {} 00357 void bracketComment( const TQString & ) {} 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 TQString & 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 TQString & string, bool, const TQString & ) { 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 TQString & string, bool, const TQString & ) { 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 TQString mMessageText; 00416 TQStringList mAliases; 00417 00418 void reset() { 00419 kdDebug(5006) << "VacationDataExtractor::reset()" << endl; 00420 mContext = None; 00421 mNotificationInterval = 0; 00422 mAliases.clear(); 00423 mMessageText = TQString(); 00424 } 00425 }; 00426 00427 } 00428 00429 namespace KMail { 00430 00431 Vacation::Vacation( TQObject * parent, bool checkOnly, const char * name ) 00432 : TQObject( 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, TQT_SIGNAL(gotScript(KMail::SieveJob*,bool,const TQString&,bool)), 00440 TQT_SLOT(slotGetResult(KMail::SieveJob*,bool,const TQString&,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 TQString dotstuff( TQString s ) { 00450 if ( s.startsWith( "." ) ) 00451 return '.' + s.replace( "\n.", "\n.." ); 00452 else 00453 return s.replace( "\n.", "\n.." ); 00454 } 00455 00456 TQString Vacation::composeScript( const TQString & messageText, 00457 int notificationInterval, 00458 const AddrSpecList & addrSpecs, 00459 bool sendForSpam, const TQString & domain ) 00460 { 00461 TQString addressesArgument; 00462 TQStringList aliases; 00463 if ( !addrSpecs.empty() ) { 00464 addressesArgument += ":addresses [ "; 00465 TQStringList 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 TQString script = TQString::fromLatin1("require \"vacation\";\n\n" ); 00473 if ( !sendForSpam ) 00474 script += TQString::fromLatin1( "if header :contains \"X-Spam-Flag\" \"YES\"" 00475 " { keep; stop; }\n" ); // FIXME? 00476 00477 if ( !domain.isEmpty() ) // FIXME 00478 script += TQString::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 += TQString::fromLatin1(":days %1 ").arg( notificationInterval ); 00484 script += TQString::fromLatin1("text:\n"); 00485 script += dotstuff( messageText.isEmpty() ? defaultMessageText() : messageText ); 00486 script += TQString::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 TQString & script, TQString & messageText, 00530 int & notificationInterval, TQStringList & aliases, 00531 bool & sendForSpam, TQString & 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 TQCString 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 TQString 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( TDEGlobal::locale()->formatDate( TQDate::currentDate().addDays( 1 ) ) ); 00577 } 00578 00579 int Vacation::defaultNotificationInterval() { 00580 return 7; // days 00581 } 00582 00583 TQStringList Vacation::defaultMailAliases() { 00584 TQStringList sl; 00585 for ( KPIM::IdentityManager::ConstIterator it = kmkernel->identityManager()->begin() ; 00586 it != kmkernel->identityManager()->end() ; ++it ) { 00587 if ( !(*it).primaryEmailAddress().isEmpty() ) 00588 sl.push_back( (*it).primaryEmailAddress() ); 00589 sl += (*it).emailAliases(); 00590 } 00591 return sl; 00592 } 00593 00594 bool Vacation::defaultSendForSpam() { 00595 return GlobalSettings::outOfOfficeReactToSpam(); 00596 } 00597 00598 TQString Vacation::defaultDomainName() { 00599 return GlobalSettings::outOfOfficeDomain(); 00600 } 00601 00602 void Vacation::slotGetResult( SieveJob * job, bool success, 00603 const TQString & script, bool active ) { 00604 kdDebug(5006) << "Vacation::slotGetResult( ??, " << success 00605 << ", ?, " << active << " )" << endl 00606 << "script:" << endl 00607 << script << endl; 00608 mSieveJob = 0; // job deletes itself after returning from this slot! 00609 00610 if ( !mCheckOnly && mUrl.protocol() == "sieve" && !job->sieveCapabilities().isEmpty() && 00611 !job->sieveCapabilities().contains("vacation") ) { 00612 KMessageBox::sorry( 0, i18n("Your server did not list \"vacation\" in " 00613 "its list of supported Sieve extensions;\n" 00614 "without it, KMail cannot install out-of-" 00615 "office replies for you.\n" 00616 "Please contact you system administrator.") ); 00617 emit result( false ); 00618 return; 00619 } 00620 00621 if ( !mDialog && !mCheckOnly ) 00622 mDialog = new VacationDialog( i18n("Configure \"Out of Office\" Replies"), 0, 0, false ); 00623 00624 TQString messageText = defaultMessageText(); 00625 int notificationInterval = defaultNotificationInterval(); 00626 TQStringList aliases = defaultMailAliases(); 00627 bool sendForSpam = defaultSendForSpam(); 00628 TQString domainName = defaultDomainName(); 00629 if ( !success ) active = false; // default to inactive 00630 00631 if ( !mCheckOnly && ( !success || !parseScript( script, messageText, notificationInterval, aliases, sendForSpam, domainName ) ) ) 00632 KMessageBox::information( 0, i18n("Someone (probably you) changed the " 00633 "vacation script on the server.\n" 00634 "KMail is no longer able to determine " 00635 "the parameters for the autoreplies.\n" 00636 "Default values will be used." ) ); 00637 00638 mWasActive = active; 00639 if ( mDialog ) { 00640 mDialog->setActivateVacation( active ); 00641 mDialog->setMessageText( messageText ); 00642 mDialog->setNotificationInterval( notificationInterval ); 00643 mDialog->setMailAliases( aliases.join(", ") ); 00644 mDialog->setSendForSpam( sendForSpam ); 00645 mDialog->setDomainName( domainName ); 00646 mDialog->enableDomainAndSendForSpam( !GlobalSettings::allowOutOfOfficeUploadButNoSettings() ); 00647 00648 connect( mDialog, TQT_SIGNAL(okClicked()), TQT_SLOT(slotDialogOk()) ); 00649 connect( mDialog, TQT_SIGNAL(cancelClicked()), TQT_SLOT(slotDialogCancel()) ); 00650 connect( mDialog, TQT_SIGNAL(defaultClicked()), TQT_SLOT(slotDialogDefaults()) ); 00651 00652 mDialog->show(); 00653 } 00654 00655 emit scriptActive( mWasActive ); 00656 if ( mCheckOnly && mWasActive ) { 00657 if ( KMessageBox::questionYesNo( 0, i18n( "There is still an active out-of-office reply configured.\n" 00658 "Do you want to edit it?"), i18n("Out-of-office reply still active"), 00659 KGuiItem( i18n( "Edit"), "edit" ), KGuiItem( i18n("Ignore"), "button_cancel" ) ) 00660 == KMessageBox::Yes ) { 00661 kmkernel->getKMMainWidget()->slotEditVacation(); 00662 } 00663 } 00664 } 00665 00666 void Vacation::slotDialogDefaults() { 00667 if ( !mDialog ) 00668 return; 00669 mDialog->setActivateVacation( true ); 00670 mDialog->setMessageText( defaultMessageText() ); 00671 mDialog->setNotificationInterval( defaultNotificationInterval() ); 00672 mDialog->setMailAliases( defaultMailAliases().join(", ") ); 00673 mDialog->setSendForSpam( defaultSendForSpam() ); 00674 mDialog->setDomainName( defaultDomainName() ); 00675 mDialog->setDomainCheck( false ); 00676 } 00677 00678 void Vacation::slotDialogOk() { 00679 kdDebug(5006) << "Vacation::slotDialogOk()" << endl; 00680 // compose a new script: 00681 const TQString script = composeScript( mDialog->messageText(), 00682 mDialog->notificationInterval(), 00683 mDialog->mailAliases(), 00684 mDialog->sendForSpam(), 00685 mDialog->domainName() ); 00686 const bool active = mDialog->activateVacation(); 00687 emit scriptActive( active ); 00688 00689 kdDebug(5006) << "script:" << endl << script << endl; 00690 00691 // and commit the dialog's settings to the server: 00692 mSieveJob = SieveJob::put( mUrl, script, active, mWasActive ); 00693 connect( mSieveJob, TQT_SIGNAL(gotScript(KMail::SieveJob*,bool,const TQString&,bool)), 00694 active 00695 ? TQT_SLOT(slotPutActiveResult(KMail::SieveJob*,bool)) 00696 : TQT_SLOT(slotPutInactiveResult(KMail::SieveJob*,bool)) ); 00697 00698 // destroy the dialog: 00699 mDialog->delayedDestruct(); 00700 mDialog = 0; 00701 } 00702 00703 void Vacation::slotDialogCancel() { 00704 kdDebug(5006) << "Vacation::slotDialogCancel()" << endl; 00705 mDialog->delayedDestruct(); 00706 mDialog = 0; 00707 emit result( false ); 00708 } 00709 00710 void Vacation::slotPutActiveResult( SieveJob * job, bool success ) { 00711 handlePutResult( job, success, true ); 00712 } 00713 00714 void Vacation::slotPutInactiveResult( SieveJob * job, bool success ) { 00715 handlePutResult( job, success, false ); 00716 } 00717 00718 void Vacation::handlePutResult( SieveJob *, bool success, bool activated ) { 00719 if ( success ) 00720 KMessageBox::information( 0, activated 00721 ? i18n("Sieve script installed successfully on the server.\n" 00722 "Out of Office reply is now active.") 00723 : i18n("Sieve script installed successfully on the server.\n" 00724 "Out of Office reply has been deactivated.") ); 00725 00726 kdDebug(5006) << "Vacation::handlePutResult( ???, " << success << ", ? )" 00727 << endl; 00728 mSieveJob = 0; // job deletes itself after returning from this slot! 00729 emit result( success ); 00730 emit scriptActive( activated ); 00731 } 00732 00733 00734 } // namespace KMail 00735 00736 #include "vacation.moc"