00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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 "accountmanager.h"
00027 using KMail::AccountManager;
00028 #include "kmacctimap.h"
00029 #include <libkpimidentities/identitymanager.h>
00030 #include <libkpimidentities/identity.h>
00031
00032 #include <kmime_header_parsing.h>
00033 using KMime::Types::AddrSpecList;
00034
00035 #include <ksieve/parser.h>
00036 #include <ksieve/scriptbuilder.h>
00037 #include <ksieve/error.h>
00038
00039 #include <klocale.h>
00040 #include <kmessagebox.h>
00041 #include <kdebug.h>
00042
00043 #include <qdatetime.h>
00044
00045 #include <cassert>
00046
00047 namespace {
00048
00049 class VacationDataExtractor : public KSieve::ScriptBuilder {
00050 enum Context {
00051 None = 0,
00052
00053 VacationCommand,
00054
00055 Days, Addresses
00056 };
00057 public:
00058 VacationDataExtractor()
00059 : KSieve::ScriptBuilder(),
00060 mContext( None ), mNotificationInterval( 0 )
00061 {
00062 kdDebug(5006) << "VacationDataExtractor instantiated" << endl;
00063 }
00064 virtual ~VacationDataExtractor() {}
00065
00066 int notificationInterval() const { return mNotificationInterval; }
00067 const QString & messageText() const { return mMessageText; }
00068 const QStringList & aliases() const { return mAliases; }
00069
00070 private:
00071 void commandStart( const QString & identifier ) {
00072 kdDebug( 5006 ) << "VacationDataExtractor::commandStart( \"" << identifier << "\" )" << endl;
00073 if ( identifier != "vacation" )
00074 return;
00075 reset();
00076 mContext = VacationCommand;
00077 }
00078
00079 void commandEnd() {
00080 kdDebug( 5006 ) << "VacationDataExtractor::commandEnd()" << endl;
00081 mContext = None;
00082 }
00083
00084 void testStart( const QString & ) {}
00085 void testEnd() {}
00086 void testListStart() {}
00087 void testListEnd() {}
00088 void blockStart() {}
00089 void blockEnd() {}
00090 void hashComment( const QString & ) {}
00091 void bracketComment( const QString & ) {}
00092 void lineFeed() {}
00093 void error( const KSieve::Error & e ) {
00094 kdDebug( 5006 ) << "VacationDataExtractor::error() ### "
00095 << e.asString() << " @ " << e.line() << "," << e.column()
00096 << endl;
00097 }
00098 void finished() {}
00099
00100 void taggedArgument( const QString & tag ) {
00101 kdDebug( 5006 ) << "VacationDataExtractor::taggedArgument( \"" << tag << "\" )" << endl;
00102 if ( mContext != VacationCommand )
00103 return;
00104 if ( tag == "days" )
00105 mContext = Days;
00106 else if ( tag == "addresses" )
00107 mContext = Addresses;
00108 }
00109
00110 void stringArgument( const QString & string, bool, const QString & ) {
00111 kdDebug( 5006 ) << "VacationDataExtractor::stringArgument( \"" << string << "\" )" << endl;
00112 if ( mContext == Addresses ) {
00113 mAliases.push_back( string );
00114 mContext = VacationCommand;
00115 } else if ( mContext == VacationCommand ) {
00116 mMessageText = string;
00117 mContext = VacationCommand;
00118 }
00119 }
00120
00121 void numberArgument( unsigned long number, char ) {
00122 kdDebug( 5006 ) << "VacationDataExtractor::numberArgument( \"" << number << "\" )" << endl;
00123 if ( mContext != Days )
00124 return;
00125 if ( number > INT_MAX )
00126 mNotificationInterval = INT_MAX;
00127 else
00128 mNotificationInterval = number;
00129 mContext = VacationCommand;
00130 }
00131
00132 void stringListArgumentStart() {}
00133 void stringListEntry( const QString & string, bool, const QString & ) {
00134 kdDebug( 5006 ) << "VacationDataExtractor::stringListEntry( \"" << string << "\" )" << endl;
00135 if ( mContext != Addresses )
00136 return;
00137 mAliases.push_back( string );
00138 }
00139 void stringListArgumentEnd() {
00140 kdDebug( 5006 ) << "VacationDataExtractor::stringListArgumentEnd()" << endl;
00141 if ( mContext != Addresses )
00142 return;
00143 mContext = VacationCommand;
00144 }
00145
00146 private:
00147 Context mContext;
00148 int mNotificationInterval;
00149 QString mMessageText;
00150 QStringList mAliases;
00151
00152 void reset() {
00153 kdDebug(5006) << "VacationDataExtractor::reset()" << endl;
00154 mContext = None;
00155 mNotificationInterval = 0;
00156 mAliases.clear();
00157 mMessageText = QString::null;
00158 }
00159 };
00160
00161 }
00162
00163 namespace KMail {
00164
00165 Vacation::Vacation( QObject * parent, const char * name )
00166 : QObject( parent, name ), mSieveJob( 0 ), mDialog( 0 ), mWasActive( false )
00167 {
00168 mUrl = findURL();
00169 kdDebug(5006) << "Vacation: found url \"" << mUrl.prettyURL() << "\"" << endl;
00170 if ( mUrl.isEmpty() )
00171 return;
00172 mSieveJob = SieveJob::get( mUrl );
00173 connect( mSieveJob, SIGNAL(gotScript(KMail::SieveJob*,bool,const QString&,bool)),
00174 SLOT(slotGetResult(KMail::SieveJob*,bool,const QString&,bool)) );
00175 }
00176
00177 Vacation::~Vacation() {
00178 if ( mSieveJob ) mSieveJob->kill(); mSieveJob = 0;
00179 delete mDialog; mDialog = 0;
00180 kdDebug(5006) << "~Vacation()" << endl;
00181 }
00182
00183 static inline QString dotstuff( QString s ) {
00184 if ( s.startsWith( "." ) )
00185 return '.' + s.replace( "\n.", "\n.." );
00186 else
00187 return s.replace( "\n.", "\n.." );
00188 }
00189
00190 QString Vacation::composeScript( const QString & messageText,
00191 int notificationInterval,
00192 const AddrSpecList & addrSpecs )
00193 {
00194 QString addressesArgument;
00195 QStringList aliases;
00196 if ( !addrSpecs.empty() ) {
00197 addressesArgument += ":addresses [ ";
00198 QStringList sl;
00199 for ( AddrSpecList::const_iterator it = addrSpecs.begin() ; it != addrSpecs.end() ; ++it ) {
00200 sl.push_back( '"' + (*it).asString().replace( '\\', "\\\\" ).replace( '"', "\\\"" ) + '"' );
00201 aliases.push_back( (*it).asString() );
00202 }
00203 addressesArgument += sl.join( ", " ) + " ] ";
00204 }
00205 QString script = QString::fromLatin1("require \"vacation\";\n"
00206 "\n"
00207 "vacation ");
00208 script += addressesArgument;
00209 if ( notificationInterval > 0 )
00210 script += QString::fromLatin1(":days %1 ").arg( notificationInterval );
00211 script += QString::fromLatin1("text:\n");
00212 script += dotstuff( messageText.isEmpty() ? defaultMessageText() : messageText );
00213 script += QString::fromLatin1( "\n.\n;\n" );
00214 return script;
00215 }
00216
00217 static KURL findUrlForAccount( const KMail::ImapAccountBase * a ) {
00218 assert( a );
00219 SieveConfig sieve = a->sieveConfig();
00220 if ( !sieve.managesieveSupported() )
00221 return KURL();
00222 if ( sieve.reuseConfig() ) {
00223
00224 KURL u;
00225 u.setProtocol( "sieve" );
00226 u.setHost( a->host() );
00227 u.setUser( a->login() );
00228 u.setPass( a->passwd() );
00229 u.setPort( sieve.port() );
00230 u.setQuery( "x-mech=" + (a->auth() == "*" ? "PLAIN" : a->auth()) );
00231 u.setFileName( sieve.vacationFileName() );
00232 return u;
00233 } else {
00234 KURL u = sieve.alternateURL();
00235 u.setFileName( sieve.vacationFileName() );
00236 return u;
00237 }
00238 }
00239
00240 KURL Vacation::findURL() const {
00241 AccountManager * am = kmkernel->acctMgr();
00242 assert( am );
00243 for ( KMAccount * a = am->first() ; a ; a = am->next() )
00244 if ( KMail::ImapAccountBase * iab = dynamic_cast<KMail::ImapAccountBase*>( a ) ) {
00245 KURL u = findUrlForAccount( iab );
00246 if ( !u.isEmpty() )
00247 return u;
00248 }
00249 return KURL();
00250 }
00251
00252 bool Vacation::parseScript( const QString & script, QString & messageText,
00253 int & notificationInterval, QStringList & aliases ) {
00254 if ( script.stripWhiteSpace().isEmpty() ) {
00255 messageText = defaultMessageText();
00256 notificationInterval = defaultNotificationInterval();
00257 aliases = defaultMailAliases();
00258 return true;
00259 }
00260
00261
00262
00263
00264 const QCString scriptUTF8 = script.stripWhiteSpace().utf8();
00265 kdDebug(5006) << "scriptUtf8 = \"" + scriptUTF8 + "\"" << endl;
00266 KSieve::Parser parser( scriptUTF8.begin(),
00267 scriptUTF8.begin() + scriptUTF8.length() );
00268 VacationDataExtractor vdx;
00269 parser.setScriptBuilder( &vdx );
00270 if ( !parser.parse() )
00271 return false;
00272 messageText = vdx.messageText().stripWhiteSpace();
00273 notificationInterval = vdx.notificationInterval();
00274 aliases = vdx.aliases();
00275 return true;
00276 }
00277
00278 QString Vacation::defaultMessageText() {
00279 return i18n("I am out of office till %1.\n"
00280 "\n"
00281 "In urgent cases, please contact Mrs. <vacation replacement>\n"
00282 "\n"
00283 "email: <email address of vacation replacement>\n"
00284 "phone: +49 711 1111 11\n"
00285 "fax.: +49 711 1111 12\n"
00286 "\n"
00287 "Yours sincerely,\n"
00288 "-- <enter your name and email address here>\n")
00289 .arg( KGlobal::locale()->formatDate( QDate::currentDate().addDays( 1 ) ) );
00290 }
00291
00292 int Vacation::defaultNotificationInterval() {
00293 return 7;
00294 }
00295
00296 QStringList Vacation::defaultMailAliases() {
00297 QStringList sl;
00298 for ( KPIM::IdentityManager::ConstIterator it = kmkernel->identityManager()->begin() ;
00299 it != kmkernel->identityManager()->end() ; ++it )
00300 if ( !(*it).emailAddr().isEmpty() )
00301 sl.push_back( (*it).emailAddr() );
00302 return sl;
00303 }
00304
00305
00306 void Vacation::slotGetResult( SieveJob * job, bool success,
00307 const QString & script, bool active ) {
00308 kdDebug(5006) << "Vacation::slotGetResult( ??, " << success
00309 << ", ?, " << active << " )" << endl
00310 << "script:" << endl
00311 << script << endl;
00312 mSieveJob = 0;
00313
00314 if ( mUrl.protocol() == "sieve" && !job->sieveCapabilities().isEmpty() &&
00315 !job->sieveCapabilities().contains("vacation") ) {
00316 KMessageBox::sorry( 0, i18n("Your server did not list \"vacation\" in "
00317 "its list of supported Sieve extensions;\n"
00318 "without it, KMail cannot install out-of-"
00319 "office replies for you.\n"
00320 "Please contact you system administrator.") );
00321 emit result( false );
00322 return;
00323 }
00324
00325 if ( !mDialog )
00326 mDialog = new VacationDialog( i18n("Configure \"Out of Office\" Replies"), 0, 0, false );
00327
00328 QString messageText = defaultMessageText();
00329 int notificationInterval = defaultNotificationInterval();
00330 QStringList aliases = defaultMailAliases();
00331 if ( !success ) active = false;
00332
00333 if ( !success || !parseScript( script, messageText, notificationInterval, aliases ) )
00334 KMessageBox::information( 0, i18n("Someone (probably you) changed the "
00335 "vacation script on the server.\n"
00336 "KMail is no longer able to determine "
00337 "the parameters for the autoreplies.\n"
00338 "Default values will be used." ) );
00339
00340 mWasActive = active;
00341 mDialog->setActivateVacation( active );
00342 mDialog->setMessageText( messageText );
00343 mDialog->setNotificationInterval( notificationInterval );
00344 mDialog->setMailAliases( aliases.join(", ") );
00345
00346 connect( mDialog, SIGNAL(okClicked()), SLOT(slotDialogOk()) );
00347 connect( mDialog, SIGNAL(cancelClicked()), SLOT(slotDialogCancel()) );
00348 connect( mDialog, SIGNAL(defaultClicked()), SLOT(slotDialogDefaults()) );
00349
00350 mDialog->show();
00351 }
00352
00353 void Vacation::slotDialogDefaults() {
00354 if ( !mDialog )
00355 return;
00356 mDialog->setActivateVacation( true );
00357 mDialog->setMessageText( defaultMessageText() );
00358 mDialog->setNotificationInterval( defaultNotificationInterval() );
00359 mDialog->setMailAliases( defaultMailAliases().join(", ") );
00360 }
00361
00362 void Vacation::slotDialogOk() {
00363 kdDebug(5006) << "Vacation::slotDialogOk()" << endl;
00364
00365 const QString script = composeScript( mDialog->messageText(),
00366 mDialog->notificationInterval(),
00367 mDialog->mailAliases() );
00368 const bool active = mDialog->activateVacation();
00369
00370 kdDebug(5006) << "script:" << endl << script << endl;
00371
00372
00373 mSieveJob = SieveJob::put( mUrl, script, active, mWasActive );
00374 connect( mSieveJob, SIGNAL(gotScript(KMail::SieveJob*,bool,const QString&,bool)),
00375 active
00376 ? SLOT(slotPutActiveResult(KMail::SieveJob*,bool))
00377 : SLOT(slotPutInactiveResult(KMail::SieveJob*,bool)) );
00378
00379
00380 mDialog->delayedDestruct();
00381 mDialog = 0;
00382 }
00383
00384 void Vacation::slotDialogCancel() {
00385 kdDebug(5006) << "Vacation::slotDialogCancel()" << endl;
00386 mDialog->delayedDestruct();
00387 mDialog = 0;
00388 emit result( false );
00389 }
00390
00391 void Vacation::slotPutActiveResult( SieveJob * job, bool success ) {
00392 handlePutResult( job, success, true );
00393 }
00394
00395 void Vacation::slotPutInactiveResult( SieveJob * job, bool success ) {
00396 handlePutResult( job, success, false );
00397 }
00398
00399 void Vacation::handlePutResult( SieveJob *, bool success, bool activated ) {
00400 if ( success )
00401 KMessageBox::information( 0, activated
00402 ? i18n("Sieve script installed successfully on the server.\n"
00403 "Out of Office reply is now active.")
00404 : i18n("Sieve script installed successfully on the server.\n"
00405 "Out of Office reply has been deactivated.") );
00406
00407 kdDebug(5006) << "Vacation::handlePutResult( ???, " << success << ", ? )"
00408 << endl;
00409 mSieveJob = 0;
00410 emit result( success );
00411 }
00412
00413
00414 }
00415
00416 #include "vacation.moc"