You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

413 lines
13 KiB

/* -*- c++ -*-
vacation.cpp
KMail, the KDE mail client.
Copyright (c) 2002 Marc Mutz <mutz@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License,
version 2.0, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, US
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "vacation.h"
#include <limits.h>
#include "vacationdialog.h"
#include "sievejob.h"
using KMail::SieveJob;
#include "kmkernel.h"
#include "kmacctmgr.h"
#include "kmacctimap.h"
#include "kmmessage.h"
#include <libkpimidentities/identitymanager.h>
#include <libkpimidentities/identity.h>
#include <kmime_header_parsing.h>
using KMime::Types::AddrSpecList;
#include <ksieve/parser.h>
#include <ksieve/scriptbuilder.h>
#include <ksieve/error.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <qdatetime.h>
#include <cassert>
namespace {
class VacationDataExtractor : public KSieve::ScriptBuilder {
enum Context {
None = 0,
// command itself:
VacationCommand,
// tagged args:
Days, Addresses
};
public:
VacationDataExtractor()
: KSieve::ScriptBuilder(),
mContext( None ), mNotificationInterval( 0 )
{
kdDebug(5006) << "VacationDataExtractor instantiated" << endl;
}
virtual ~VacationDataExtractor() {}
int notificationInterval() const { return mNotificationInterval; }
const QString & messageText() const { return mMessageText; }
const QStringList & aliases() const { return mAliases; }
private:
void commandStart( const QString & identifier ) {
kdDebug( 5006 ) << "VacationDataExtractor::commandStart( \"" << identifier << "\" )" << endl;
if ( identifier != "vacation" )
return;
reset();
mContext = VacationCommand;
}
void commandEnd() {
kdDebug( 5006 ) << "VacationDataExtractor::commandEnd()" << endl;
mContext = None;
}
void testStart( const QString & ) {}
void testEnd() {}
void testListStart() {}
void testListEnd() {}
void blockStart() {}
void blockEnd() {}
void hashComment( const QString & ) {}
void bracketComment( const QString & ) {}
void lineFeed() {}
void error( const KSieve::Error & e ) {
kdDebug( 5006 ) << "VacationDataExtractor::error() ### "
<< e.asString() << " @ " << e.line() << "," << e.column()
<< endl;
}
void finished() {}
void taggedArgument( const QString & tag ) {
kdDebug( 5006 ) << "VacationDataExtractor::taggedArgument( \"" << tag << "\" )" << endl;
if ( mContext != VacationCommand )
return;
if ( tag == "days" )
mContext = Days;
else if ( tag == "addresses" )
mContext = Addresses;
}
void stringArgument( const QString & string, bool, const QString & ) {
kdDebug( 5006 ) << "VacationDataExtractor::stringArgument( \"" << string << "\" )" << endl;
if ( mContext == Addresses ) {
mAliases.push_back( string );
mContext = VacationCommand;
} else if ( mContext == VacationCommand ) {
mMessageText = string;
mContext = VacationCommand;
}
}
void numberArgument( unsigned long number, char ) {
kdDebug( 5006 ) << "VacationDataExtractor::numberArgument( \"" << number << "\" )" << endl;
if ( mContext != Days )
return;
if ( number > INT_MAX )
mNotificationInterval = INT_MAX;
else
mNotificationInterval = number;
mContext = VacationCommand;
}
void stringListArgumentStart() {}
void stringListEntry( const QString & string, bool, const QString & ) {
kdDebug( 5006 ) << "VacationDataExtractor::stringListEntry( \"" << string << "\" )" << endl;
if ( mContext != Addresses )
return;
mAliases.push_back( string );
}
void stringListArgumentEnd() {
kdDebug( 5006 ) << "VacationDataExtractor::stringListArgumentEnd()" << endl;
if ( mContext != Addresses )
return;
mContext = VacationCommand;
}
private:
Context mContext;
int mNotificationInterval;
QString mMessageText;
QStringList mAliases;
void reset() {
kdDebug(5006) << "VacationDataExtractor::reset()" << endl;
mContext = None;
mNotificationInterval = 0;
mAliases.clear();
mMessageText = QString::null;
}
};
}
namespace KMail {
Vacation::Vacation( QObject * parent, const char * name )
: QObject( parent, name ), mSieveJob( 0 ), mDialog( 0 ), mWasActive( false )
{
mUrl = findURL();
kdDebug(5006) << "Vacation: found url \"" << mUrl.prettyURL() << "\"" << endl;
if ( mUrl.isEmpty() ) // nothing to do...
return;
mUrl.setFileName( "kmail-vacation.siv" );
mSieveJob = SieveJob::get( mUrl );
connect( mSieveJob, SIGNAL(result(KMail::SieveJob*,bool,const QString&,bool)),
SLOT(slotGetResult(KMail::SieveJob*,bool,const QString&,bool)) );
}
Vacation::~Vacation() {
if ( mSieveJob ) mSieveJob->kill(); mSieveJob = 0;
delete mDialog; mDialog = 0;
kdDebug(5006) << "~Vacation()" << endl;
}
static inline QString dotstuff( QString s ) {
if ( s.startsWith( "." ) )
return '.' + s.replace( "\n.", "\n.." );
else
return s.replace( "\n.", "\n.." );
}
QString Vacation::composeScript( const QString & messageText,
int notificationInterval,
const AddrSpecList & addrSpecs )
{
QString addressesArgument;
QStringList aliases;
if ( !addrSpecs.empty() ) {
addressesArgument += ":addresses [ ";
QStringList sl;
for ( AddrSpecList::const_iterator it = addrSpecs.begin() ; it != addrSpecs.end() ; ++it ) {
sl.push_back( '"' + (*it).asString().replace( '\\', "\\\\" ).replace( '"', "\\\"" ) + '"' );
aliases.push_back( (*it).asString() );
}
addressesArgument += sl.join( ", " ) + " ] ";
}
QString script = QString::fromLatin1("require \"vacation\";\n"
"\n"
"vacation ");
script += addressesArgument;
if ( notificationInterval > 0 )
script += QString::fromLatin1(":days %1 ").arg( notificationInterval );
script += QString::fromLatin1("text:\n");
script += dotstuff( messageText.isEmpty() ? defaultMessageText() : messageText );
script += QString::fromLatin1( "\n.\n;\n" );
return script;
}
static KURL findUrlForAccount( const KMail::ImapAccountBase * a ) {
assert( a );
SieveConfig sieve = a->sieveConfig();
if ( !sieve.managesieveSupported() )
return KURL();
if ( sieve.reuseConfig() ) {
// assemble Sieve url from the settings of the account:
KURL u;
u.setProtocol( "sieve" );
u.setHost( a->host() );
u.setUser( a->login() );
u.setPass( a->passwd() );
u.setPort( sieve.port() );
return u;
} else {
return sieve.alternateURL();
}
}
KURL Vacation::findURL() const {
KMAcctMgr * am = kmkernel->acctMgr();
assert( am );
for ( KMAccount * a = am->first() ; a ; a = am->next() )
if ( KMail::ImapAccountBase * iab = dynamic_cast<KMail::ImapAccountBase*>( a ) ) {
KURL u = findUrlForAccount( iab );
if ( !u.isEmpty() )
return u;
}
return KURL();
}
bool Vacation::parseScript( const QString & script, QString & messageText,
int & notificationInterval, QStringList & aliases ) {
if ( script.stripWhiteSpace().isEmpty() ) {
messageText = defaultMessageText();
notificationInterval = defaultNotificationInterval();
aliases = defaultMailAliases();
return true;
}
// The stripWhiteSpace() call below prevents parsing errors. The
// slave somehow omits the last \n, which results in a lone \r at
// the end, leading to a parse error.
const QCString scriptUTF8 = script.stripWhiteSpace().utf8();
kdDebug(5006) << "scriptUtf8 = \"" + scriptUTF8 + "\"" << endl;
KSieve::Parser parser( scriptUTF8.begin(),
scriptUTF8.begin() + scriptUTF8.length() );
VacationDataExtractor vdx;
parser.setScriptBuilder( &vdx );
if ( !parser.parse() )
return false;
messageText = vdx.messageText().stripWhiteSpace();
notificationInterval = vdx.notificationInterval();
aliases = vdx.aliases();
return true;
}
QString Vacation::defaultMessageText() {
return i18n("I am out of office till %1.\n"
"\n"
"In urgent cases, please contact Mrs. <vacation replacement>\n"
"\n"
"email: <email address of vacation replacement>\n"
"phone: +49 711 1111 11\n"
"fax.: +49 711 1111 12\n"
"\n"
"Yours sincerely,\n"
"-- <enter your name and email address here>\n")
.arg( KGlobal::locale()->formatDate( QDate::currentDate().addDays( 1 ) ) );
}
int Vacation::defaultNotificationInterval() {
return 7; // days
}
QStringList Vacation::defaultMailAliases() {
QStringList sl;
for ( KPIM::IdentityManager::ConstIterator it = kmkernel->identityManager()->begin() ;
it != kmkernel->identityManager()->end() ; ++it )
if ( !(*it).emailAddr().isEmpty() )
sl.push_back( (*it).emailAddr() );
return sl;
}
void Vacation::slotGetResult( SieveJob * job, bool success,
const QString & script, bool active ) {
kdDebug(5006) << "Vacation::slotGetResult( ??, " << success
<< ", ?, " << active << " )" << endl
<< "script:" << endl
<< script << endl;
mSieveJob = 0; // job deletes itself after returning from this slot!
if ( mUrl.protocol() == "sieve" && !job->sieveCapabilities().isEmpty() &&
!job->sieveCapabilities().contains("vacation") ) {
KMessageBox::sorry( 0, i18n("Your server did not list \"vacation\" in "
"its list of supported Sieve extensions;\n"
"without it, KMail cannot install out-of-"
"office replies for you.\n"
"Please contact you system administrator.") );
emit result( false );
return;
}
if ( !mDialog )
mDialog = new VacationDialog( i18n("Configure \"Out of Office\" Replies"), 0, 0, false );
QString messageText = defaultMessageText();
int notificationInterval = defaultNotificationInterval();
QStringList aliases = defaultMailAliases();
if ( !success ) active = false; // default to inactive
if ( !success || !parseScript( script, messageText, notificationInterval, aliases ) )
KMessageBox::information( 0, i18n("Someone (probably you) changed the "
"vacation script on the server.\n"
"KMail is no longer able to determine "
"the parameters for the autoreplies.\n"
"Default values will be used." ) );
mWasActive = active;
mDialog->setActivateVacation( active );
mDialog->setMessageText( messageText );
mDialog->setNotificationInterval( notificationInterval );
mDialog->setMailAliases( aliases.join(", ") );
connect( mDialog, SIGNAL(okClicked()), SLOT(slotDialogOk()) );
connect( mDialog, SIGNAL(cancelClicked()), SLOT(slotDialogCancel()) );
connect( mDialog, SIGNAL(defaultClicked()), SLOT(slotDialogDefaults()) );
mDialog->show();
}
void Vacation::slotDialogDefaults() {
if ( !mDialog )
return;
mDialog->setActivateVacation( true );
mDialog->setMessageText( defaultMessageText() );
mDialog->setNotificationInterval( defaultNotificationInterval() );
mDialog->setMailAliases( defaultMailAliases().join(", ") );
}
void Vacation::slotDialogOk() {
kdDebug(5006) << "Vacation::slotDialogOk()" << endl;
// compose a new script:
const QString script = composeScript( mDialog->messageText(),
mDialog->notificationInterval(),
mDialog->mailAliases() );
const bool active = mDialog->activateVacation();
kdDebug(5006) << "script:" << endl << script << endl;
// and commit the dialog's settings to the server:
mSieveJob = SieveJob::put( mUrl, script, active, mWasActive );
connect( mSieveJob, SIGNAL(result(KMail::SieveJob*,bool,const QString&,bool)),
active
? SLOT(slotPutActiveResult(KMail::SieveJob*,bool))
: SLOT(slotPutInactiveResult(KMail::SieveJob*,bool)) );
// destroy the dialog:
mDialog->delayedDestruct();
mDialog = 0;
}
void Vacation::slotDialogCancel() {
kdDebug(5006) << "Vacation::slotDialogCancel()" << endl;
mDialog->delayedDestruct();
mDialog = 0;
emit result( false );
}
void Vacation::slotPutActiveResult( SieveJob * job, bool success ) {
handlePutResult( job, success, true );
}
void Vacation::slotPutInactiveResult( SieveJob * job, bool success ) {
handlePutResult( job, success, false );
}
void Vacation::handlePutResult( SieveJob *, bool success, bool activated ) {
if ( success )
KMessageBox::information( 0, activated
? i18n("Sieve script installed successfully on the server.\n"
"Out of Office reply is now active.")
: i18n("Sieve script installed successfully on the server.\n"
"Out of Office reply has been deactivated.") );
kdDebug(5006) << "Vacation::handlePutResult( ???, " << success << ", ? )"
<< endl;
mSieveJob = 0; // job deletes itself after returning from this slot!
emit result( success );
}
} // namespace KMail
#include "vacation.moc"