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 "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       // command itself:
00053       VacationCommand,
00054       // tagged args:
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() ) // nothing to do...
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       // assemble Sieve url from the settings of the account:
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()) ); //translate IMAP LOGIN to PLAIN
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     // The stripWhiteSpace() call below prevents parsing errors. The
00262     // slave somehow omits the last \n, which results in a lone \r at
00263     // the end, leading to a parse error.
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; // days
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; // job deletes itself after returning from this slot!
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; // default to inactive
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     // compose a new script:
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     // and commit the dialog's settings to the server:
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     // destroy the dialog:
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; // job deletes itself after returning from this slot!
00410     emit result( success );
00411   }
00412 
00413 
00414 } // namespace KMail
00415 
00416 #include "vacation.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys