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
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"
|
|
|