Index: libksieve/autotests/parsertest.cpp =================================================================== --- libksieve/autotests/parsertest.cpp +++ libksieve/autotests/parsertest.cpp @@ -462,12 +462,12 @@ const QString txt = "number" + (quantifier ? QString(" quantifier=\"%1\"").arg(quantifier) : QString()) ; write(txt.toLatin1(), QString::number(number)); } - void commandStart(const QString &identifier) Q_DECL_OVERRIDE { + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE { write(""); ++indent; write("identifier", identifier); } - void commandEnd() Q_DECL_OVERRIDE { + void commandEnd(int lineNumber) Q_DECL_OVERRIDE { --indent; write(""); } @@ -488,11 +488,11 @@ --indent; write(""); } - void blockStart() Q_DECL_OVERRIDE { + void blockStart(int lineNumber) Q_DECL_OVERRIDE { write(""); ++indent; } - void blockEnd() Q_DECL_OVERRIDE { + void blockEnd(int lineNumber) Q_DECL_OVERRIDE { --indent; write(""); } @@ -583,12 +583,12 @@ checkEquals(QString::number(number) + (quantifier ? quantifier : ' ')); ++mNextResponse; } - void commandStart(const QString &identifier) Q_DECL_OVERRIDE { + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE { checkIs(CommandStart); checkEquals(identifier); ++mNextResponse; } - void commandEnd() Q_DECL_OVERRIDE { + void commandEnd(int lineNumber) Q_DECL_OVERRIDE { checkIs(CommandEnd); ++mNextResponse; } @@ -609,11 +609,11 @@ checkIs(TestListEnd); ++mNextResponse; } - void blockStart() Q_DECL_OVERRIDE { + void blockStart(int lineNumber) Q_DECL_OVERRIDE { checkIs(BlockStart); ++mNextResponse; } - void blockEnd() Q_DECL_OVERRIDE { + void blockEnd(int lineNumber) Q_DECL_OVERRIDE { checkIs(BlockEnd); ++mNextResponse; } Index: libksieve/src/ksieve/scriptbuilder.h =================================================================== --- libksieve/src/ksieve/scriptbuilder.h +++ libksieve/src/ksieve/scriptbuilder.h @@ -53,17 +53,17 @@ virtual void stringListEntry(const QString &string, bool multiLine, const QString &embeddedHashComment) = 0; virtual void stringListArgumentEnd() = 0; - virtual void commandStart(const QString &identifier) = 0; - virtual void commandEnd() = 0; + virtual void commandStart( const QString & identifier, int lineNumber ) = 0; + virtual void commandEnd(int lineNumber) = 0; virtual void testStart(const QString &identifier) = 0; virtual void testEnd() = 0; virtual void testListStart() = 0; virtual void testListEnd() = 0; - virtual void blockStart() = 0; - virtual void blockEnd() = 0; + virtual void blockStart(int lineNumber) = 0; + virtual void blockEnd(int lineNumber) = 0; /** A hash comment always includes an implicit lineFeed() at it's end. */ virtual void hashComment(const QString &comment) = 0; Index: libksieve/src/ksieveui/CMakeLists.txt =================================================================== --- libksieve/src/ksieveui/CMakeLists.txt +++ libksieve/src/ksieveui/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(editor/autotests) add_subdirectory(autocreatescripts/tests) add_subdirectory(scriptsparsing/tests) + add_subdirectory(vacation/autotests) add_subdirectory(vacation/tests) add_subdirectory(sievescriptdebugger/autotests) add_subdirectory(sievescriptdebugger/tests) @@ -169,6 +170,7 @@ widgets/managesievewidget.cpp debug/sievedebugdialog.cpp util/util.cpp + managescriptsjob/checkkep14supportjob.cpp managescriptsjob/generateglobalscriptjob.cpp managescriptsjob/parseuserscriptjob.cpp templates/sievetemplatewidget.cpp @@ -313,7 +315,5 @@ endif() - - install(FILES data/ksieve_script.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} ) install(DIRECTORY data/scripts/ DESTINATION ${KDE_INSTALL_DATADIR}/sieve/scripts/ ) Index: libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.h =================================================================== --- libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.h +++ libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.h @@ -19,7 +19,9 @@ #define PARSEUSERJOBTEST_H #include -class ParseUserTest : public QObject + +namespace KSieveUi { +class ParseUserJobTest : public QObject { Q_OBJECT private Q_SLOTS: @@ -29,5 +31,5 @@ void testParseUserDuplicateActiveScriptJob(); void testParseUserErrorScriptJob(); }; - +} #endif // PARSEUSERJOBTEST_H Index: libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.cpp =================================================================== --- libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.cpp +++ libksieve/src/ksieveui/managescriptsjob/autotests/parseuserjobtest.cpp @@ -18,18 +18,20 @@ #include "ksieveui/managescriptsjob/parseuserscriptjob.h" #include -QTEST_MAIN(ParseUserTest) +using namespace KSieveUi; -void ParseUserTest::testParseEmptyUserJob() +QTEST_MAIN(ParseUserJobTest) + +void ParseUserJobTest::testParseEmptyUserJob() { const QString script; bool result; - const QStringList lst = KSieveUi::ParseUserScriptJob::parsescript(script, result); + const QStringList lst = ParseUserScriptJob::parsescript(script, result); QCOMPARE(lst.count(), 0); QCOMPARE(result, true); } -void ParseUserTest::testParseUserTwoActiveScriptJob() +void ParseUserJobTest::testParseUserTwoActiveScriptJob() { const QString script = QStringLiteral("# USER Management Script\n" "#\n" @@ -43,12 +45,12 @@ "include :personal \"file1\";\n" "include :personal \"file2\";\n"); bool result; - const QStringList lst = KSieveUi::ParseUserScriptJob::parsescript(script, result); + const QStringList lst = ParseUserScriptJob::parsescript(script, result); QCOMPARE(lst.count(), 2); QCOMPARE(result, true); } -void ParseUserTest::testParseUserNoActiveScriptJob() +void ParseUserJobTest::testParseUserNoActiveScriptJob() { const QString script = QStringLiteral("# USER Management Script\n" "#\n" @@ -60,12 +62,12 @@ "\n" "require [\"include\"];\n"); bool result; - const QStringList lst = KSieveUi::ParseUserScriptJob::parsescript(script, result); + const QStringList lst = ParseUserScriptJob::parsescript(script, result); QCOMPARE(lst.count(), 0); QCOMPARE(result, true); } -void ParseUserTest::testParseUserDuplicateActiveScriptJob() +void ParseUserJobTest::testParseUserDuplicateActiveScriptJob() { const QString script = QStringLiteral("# USER Management Script\n" "#\n" @@ -79,12 +81,12 @@ "include :personal \"file1\";\n" "include :personal \"file1\";\n"); bool result; - const QStringList lst = KSieveUi::ParseUserScriptJob::parsescript(script, result); + const QStringList lst = ParseUserScriptJob::parsescript(script, result); QCOMPARE(lst.count(), 1); QCOMPARE(result, true); } -void ParseUserTest::testParseUserErrorScriptJob() +void ParseUserJobTest::testParseUserErrorScriptJob() { const QString script = QStringLiteral("# USER Management Script\n" "#\n" @@ -96,7 +98,7 @@ "\n" "errorscript\n"); bool result; - const QStringList lst = KSieveUi::ParseUserScriptJob::parsescript(script, result); + const QStringList lst = ParseUserScriptJob::parsescript(script, result); QCOMPARE(lst.count(), 0); QCOMPARE(result, false); } Index: libksieve/src/ksieveui/managescriptsjob/checkkep14supportjob.h =================================================================== --- libksieve/src/ksieveui/managescriptsjob/checkkep14supportjob.h +++ libksieve/src/ksieveui/managescriptsjob/checkkep14supportjob.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2013-2015 Montel Laurent + Copyright (c) 2015 Sandro Knauß This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as @@ -15,51 +15,51 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef VACATIONCREATESCRIPTJOB_H -#define VACATIONCREATESCRIPTJOB_H +#ifndef CHECKKEP14SUPPORTJOB_H +#define CHECKKEP14SUPPORTJOB_H #include +#include + +#include "ksieveui_export.h" #include -namespace KManageSieve -{ +namespace KManageSieve { class SieveJob; } -namespace KSieveUi -{ -class VacationCreateScriptJob : public QObject +namespace KSieveUi { +class KSIEVEUI_EXPORT CheckKep14SupportJob : public QObject { Q_OBJECT public: - explicit VacationCreateScriptJob(QObject *parent = Q_NULLPTR); - ~VacationCreateScriptJob(); + explicit CheckKep14SupportJob(QObject *parent=0); + ~CheckKep14SupportJob(); void start(); void setServerUrl(const QUrl &url); - void setScript(const QString &script); - void setServerName(const QString &servername); - void setStatus(bool activate, bool wasActive); + void setServerName(const QString &name); + QString serverName(); -Q_SIGNALS: - void result(bool); - void scriptActive(bool activated, const QString &serverName); + QStringList availableScripts(); + bool hasKep14Support(); + QUrl serverUrl(); -private Q_SLOTS: - void slotPutActiveResult(KManageSieve::SieveJob *job, bool success); - void slotPutInactiveResult(KManageSieve::SieveJob *job, bool success); +Q_SIGNALS: + void result(CheckKep14SupportJob*, bool); private: - void handlePutResult(KManageSieve::SieveJob *, bool success, bool activated); QUrl mUrl; - QString mScript; - QString mServerName; - bool mActivate; - bool mWasActive; KManageSieve::SieveJob *mSieveJob; + QStringList mAvailableScripts; + bool mKep14Support; + QString mServerName; + +private slots: + void slotCheckKep14Support(KManageSieve::SieveJob *job, bool success, const QStringList &availableScripts, const QString &activeScript); }; } -#endif // VACATIONCREATESCRIPTJOB_H +#endif // CHECKKEP14SUPPORTJOB_H Index: libksieve/src/ksieveui/managescriptsjob/checkkep14supportjob.cpp =================================================================== --- /dev/null +++ libksieve/src/ksieveui/managescriptsjob/checkkep14supportjob.cpp @@ -0,0 +1,94 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "checkkep14supportjob.h" +#include +#include + +#include +#include + +#include "libksieve_debug.h" + +using namespace KSieveUi; + +CheckKep14SupportJob::CheckKep14SupportJob(QObject *parent) + : QObject(parent), + mSieveJob(0) +{ + +} + +CheckKep14SupportJob::~CheckKep14SupportJob() +{ + +} + +void CheckKep14SupportJob::start() +{ + if (mUrl.isEmpty()) { + qCWarning(LIBKSIEVE_LOG) << " server url is empty"; + deleteLater(); + return; + } + mSieveJob = KManageSieve::SieveJob::list(mUrl); + connect(mSieveJob, SIGNAL(gotList(KManageSieve::SieveJob*,bool,QStringList,QString)), + this, SLOT(slotCheckKep14Support(KManageSieve::SieveJob*,bool,QStringList,QString))); +} + +void CheckKep14SupportJob::setServerUrl(const QUrl &url) +{ + mUrl = url; +} + +QUrl CheckKep14SupportJob::serverUrl() +{ + return mUrl; +} + +void CheckKep14SupportJob::setServerName(const QString &name) +{ + mServerName = name; +} + +QString CheckKep14SupportJob::serverName() +{ + return mServerName; +} + + +QStringList CheckKep14SupportJob::availableScripts() +{ + return mAvailableScripts; +} + +bool CheckKep14SupportJob::hasKep14Support() +{ + return mKep14Support; +} + +void CheckKep14SupportJob::slotCheckKep14Support(KManageSieve::SieveJob *job, bool success, const QStringList &availableScripts, const QString &activeScript) +{ + if (!success) { + emit result(this, false); + return; + } + + mKep14Support = Util::hasKep14Support(job->sieveCapabilities(), availableScripts, activeScript); + mAvailableScripts = availableScripts; + emit result(this, true); +} Index: libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.h =================================================================== --- libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.h +++ libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.h @@ -35,6 +35,7 @@ ~GenerateGlobalScriptJob(); void start(); + void kill(); void addUserActiveScripts(const QStringList &lstScript); @@ -52,7 +53,7 @@ void writeUserScript(); QStringList mListUserActiveScripts; QUrl mCurrentUrl; - KManageSieve::SieveJob *mMasterjob; + KManageSieve::SieveJob *mMasterJob; KManageSieve::SieveJob *mUserJob; }; } Index: libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.cpp =================================================================== --- libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.cpp +++ libksieve/src/ksieveui/managescriptsjob/generateglobalscriptjob.cpp @@ -25,19 +25,27 @@ GenerateGlobalScriptJob::GenerateGlobalScriptJob(const QUrl &url, QObject *parent) : QObject(parent), mCurrentUrl(url), - mMasterjob(Q_NULLPTR), + mMasterJob(Q_NULLPTR), mUserJob(Q_NULLPTR) { } GenerateGlobalScriptJob::~GenerateGlobalScriptJob() { - if (mMasterjob) { - mMasterjob->kill(); + kill(); +} + +void GenerateGlobalScriptJob::kill() +{ + if (mMasterJob) { + mMasterJob->kill(); } + mMasterJob = Q_NULLPTR; + if (mUserJob) { mUserJob->kill(); } + mUserJob = Q_NULLPTR; } void GenerateGlobalScriptJob::addUserActiveScripts(const QStringList &lstScript) @@ -51,7 +59,7 @@ Q_EMIT error(i18n("Path is not specified.")); return; } - writeMasterScript(); + writeUserScript(); } void GenerateGlobalScriptJob::writeMasterScript() @@ -82,17 +90,17 @@ QUrl url(mCurrentUrl); url = url.adjusted(QUrl::RemoveFilename); url.setPath(url.path() + QLatin1String("MASTER")); - mMasterjob = KManageSieve::SieveJob::put(url, masterScript, true, true); - connect(mMasterjob, &KManageSieve::SieveJob::result, this, &GenerateGlobalScriptJob::slotPutMasterResult); + mMasterJob = KManageSieve::SieveJob::put(url, masterScript, true, true); + connect(mMasterJob, &KManageSieve::SieveJob::result, this, &GenerateGlobalScriptJob::slotPutMasterResult); } void GenerateGlobalScriptJob::slotPutMasterResult(KManageSieve::SieveJob *, bool success) { if (!success) { Q_EMIT error(i18n("Error when we wrote \"MASTER\" script on server.")); return; } - mMasterjob = Q_NULLPTR; + mMasterJob = Q_NULLPTR; writeUserScript(); } Index: libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.h =================================================================== --- libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.h +++ libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.h @@ -20,6 +20,7 @@ #include #include +#include #include "ksieveui_export.h" class QDomDocument; class QDomElement; @@ -33,27 +34,36 @@ class KSIEVEUI_EXPORT ParseUserScriptJob : public QObject { Q_OBJECT + + friend class ParseUserJobTest; public: - explicit ParseUserScriptJob(QObject *parent = Q_NULLPTR); + explicit ParseUserScriptJob(const QUrl &url, QObject *parent = Q_NULLPTR); ~ParseUserScriptJob(); void start(); - void scriptUrl(const QUrl &url); - static QStringList parsescript(const QString &script, bool &result); + QUrl scriptUrl() const; + + QStringList activeScriptList() const; + QString error() const; + void kill(); private Q_SLOTS: void slotGetResult(KManageSieve::SieveJob *, bool, const QString &, bool); Q_SIGNALS: - void success(const QStringList &activeScriptList); - void error(const QString &msgError); + void finished(ParseUserScriptJob* job); private: + void emitSuccess(const QStringList &activeScriptList); + void emitError(const QString &msgError); static QString loadInclude(const QDomElement &element); static QStringList extractActiveScript(const QDomDocument &doc); + static QStringList parsescript(const QString &script, bool &result); QUrl mCurrentUrl; KManageSieve::SieveJob *mSieveJob; + QStringList mActiveScripts; + QString mError; }; } Index: libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.cpp =================================================================== --- libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.cpp +++ libksieve/src/ksieveui/managescriptsjob/parseuserscriptjob.cpp @@ -22,54 +22,74 @@ #include using namespace KSieveUi; -ParseUserScriptJob::ParseUserScriptJob(QObject *parent) +ParseUserScriptJob::ParseUserScriptJob(const QUrl &url, QObject *parent) : QObject(parent), mSieveJob(Q_NULLPTR) + , mCurrentUrl(url) { } ParseUserScriptJob::~ParseUserScriptJob() { + kill(); +} + +void ParseUserScriptJob::kill() +{ if (mSieveJob) { mSieveJob->kill(); } mSieveJob = Q_NULLPTR; } -void ParseUserScriptJob::scriptUrl(const QUrl &url) +QUrl ParseUserScriptJob::scriptUrl() const { - mCurrentUrl = url; + return mCurrentUrl; } void ParseUserScriptJob::start() { if (mCurrentUrl.isEmpty()) { - Q_EMIT error(i18n("Path is not specified.")); + emitError(i18n("Path is not specified.")); return; } if (mSieveJob) { mSieveJob->kill(); } + mActiveScripts = QStringList(); + mError = QString(); mSieveJob = KManageSieve::SieveJob::get(mCurrentUrl); connect(mSieveJob, &KManageSieve::SieveJob::result, this, &ParseUserScriptJob::slotGetResult); } void ParseUserScriptJob::slotGetResult(KManageSieve::SieveJob *, bool, const QString &script, bool) { mSieveJob = Q_NULLPTR; if (script.isEmpty()) { - Q_EMIT error(i18n("Script is empty.")); + emitError(i18n("Script is empty.")); return; } bool result; const QStringList lst = parsescript(script, result); if (result) { - Q_EMIT success(lst); + emitSuccess(lst); } else { - Q_EMIT error(i18n("Script parsing error")); + emitError(i18n("Script parsing error")); } } +void ParseUserScriptJob::emitError(const QString &msgError) +{ + mError = msgError; + emit finished(this); +} + +void ParseUserScriptJob::emitSuccess(const QStringList &activeScriptList) +{ + mActiveScripts = activeScriptList; + emit finished(this); +} + QStringList ParseUserScriptJob::parsescript(const QString &script, bool &result) { QStringList lst; @@ -80,6 +100,16 @@ return lst; } +QStringList ParseUserScriptJob::activeScriptList() const +{ + return mActiveScripts; +} + +QString ParseUserScriptJob::error() const +{ + return mError; +} + QStringList ParseUserScriptJob::extractActiveScript(const QDomDocument &doc) { QStringList lstScript; Index: libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h =================================================================== --- libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h +++ libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h @@ -34,14 +34,14 @@ void taggedArgument(const QString &tag) Q_DECL_OVERRIDE; void stringArgument(const QString &string, bool multiLine, const QString & /*fixme*/) Q_DECL_OVERRIDE; void numberArgument(unsigned long number, char quantifier) Q_DECL_OVERRIDE; - void commandStart(const QString &identifier) Q_DECL_OVERRIDE; - void commandEnd() Q_DECL_OVERRIDE; + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE; + void commandEnd(int lineNumber) Q_DECL_OVERRIDE; void testStart(const QString &identifier) Q_DECL_OVERRIDE; void testEnd() Q_DECL_OVERRIDE; void testListStart() Q_DECL_OVERRIDE; void testListEnd() Q_DECL_OVERRIDE; - void blockStart() Q_DECL_OVERRIDE; - void blockEnd() Q_DECL_OVERRIDE; + void blockStart(int lineNumber) Q_DECL_OVERRIDE; + void blockEnd(int lineNumber) Q_DECL_OVERRIDE; void stringListArgumentStart() Q_DECL_OVERRIDE; void stringListArgumentEnd() Q_DECL_OVERRIDE; void stringListEntry(const QString &string, bool multiline, const QString &hashComment) Q_DECL_OVERRIDE; Index: libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp =================================================================== --- libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp +++ libksieve/src/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp @@ -50,7 +50,7 @@ write(QStringLiteral("num"), (quantifier ? QStringLiteral("quantifier=\"%1\"").arg(quantifier) : QString()) , QString::number(number)); } -void XMLPrintingScriptBuilder::commandStart(const QString &identifier) +void XMLPrintingScriptBuilder::commandStart( const QString &identifier, int lineNumber ) { if (identifier == QLatin1String("else") || identifier == QLatin1String("break") || @@ -66,7 +66,7 @@ } } -void XMLPrintingScriptBuilder::commandEnd() +void XMLPrintingScriptBuilder::commandEnd(int lineNumber) { if (mIsAction) { write(QStringLiteral("")); @@ -96,12 +96,12 @@ write(QStringLiteral("")); } -void XMLPrintingScriptBuilder::blockStart() +void XMLPrintingScriptBuilder::blockStart(int lineNumber) { write(QStringLiteral("")); } -void XMLPrintingScriptBuilder::blockEnd() +void XMLPrintingScriptBuilder::blockEnd(int lineNumber) { write(QStringLiteral("")); } Index: libksieve/src/ksieveui/util/util.h =================================================================== --- libksieve/src/ksieveui/util/util.h +++ libksieve/src/ksieveui/util/util.h @@ -45,6 +45,7 @@ class QUrl; class QString; +class QStringList; namespace KSieveUi { @@ -76,6 +77,17 @@ * be available at all. */ KSIEVEUI_EXPORT bool allowOutOfOfficeSettings(); + +/** + * Checks if a server has KEP:14 support + */ +bool hasKep14Support(const QStringList &sieveCapabilities, const QStringList &availableScripts, const QString &activeScript); + +/** + * Is the given scriptName a protected KEP:14 name, that a normal user should not touch directly. + * it tests against MASTER, USER and MANAGEMENT script + */ +bool isKep14ProtectedName(const QString &scriptName); } } Index: libksieve/src/ksieveui/util/util.cpp =================================================================== --- libksieve/src/ksieveui/util/util.cpp +++ libksieve/src/ksieveui/util/util.cpp @@ -204,3 +204,45 @@ { return VacationSettings::self()->allowOutOfOfficeSettings(); } + +bool Util::hasKep14Support(const QStringList &sieveCapabilities, const QStringList &availableScripts, const QString &activeScript) +{ + const bool hasIncludeCapability = sieveCapabilities.contains(QStringLiteral("include")); + if (!hasIncludeCapability) { + return false; + } + + bool masterIsActive = !activeScript.isEmpty(); + if (masterIsActive) { + const QString scriptName = activeScript.split(QLatin1Char('.')).first().toLower(); + masterIsActive = (scriptName == QStringLiteral("master") || scriptName == QStringLiteral("user")); + } + if (!masterIsActive) { + return false; + } + + bool hasUserScript = false; + foreach(const QString &script, availableScripts) { + if (script.isEmpty()) { + continue; + } + const QString name = script.split(QLatin1Char('.')).first().toLower(); + if (name == QStringLiteral("user")) { + hasUserScript = true; + break; + } + } + + return hasIncludeCapability && masterIsActive && hasUserScript; +} + +bool Util::isKep14ProtectedName(const QString &name) +{ + const QString n = name.split(QLatin1Char('.')).first().toLower(); + if (n == QStringLiteral("master") || + n == QStringLiteral("user") || + n == QStringLiteral("management")) { + return true; + } + return false; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/CMakeLists.txt =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/CMakeLists.txt @@ -0,0 +1,26 @@ +include_directories(${CMAKE_SOURCE_DIR}/libksieve/src + ) + +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +macro( add_vacation_test _source ) + set( _test ${_source}test.cpp + ../${_source}.cpp + ../vacationscriptextractor.cpp + ${CMAKE_BINARY_DIR}/libksieve/src/ksieveui/sieve-vacation.cpp + ) + get_filename_component(_name ${_source} NAME_WE) + ecm_qt_declare_logging_category(_test HEADER libksieve_debug.h IDENTIFIER LIBKSIEVE_LOG CATEGORY_NAME log_libksieve) + add_executable(${_name} ${_test}) + add_test(vacation-${_name} ${_name}) + ecm_mark_as_test(vacation-${_name}) + set_target_properties(${_name} PROPERTIES COMPILE_FLAGS -DVACATIONTESTDATADIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"") + target_link_libraries(${_name} + KF5::KSieveUi + Qt5::Test + KF5::IdentityManagement + KF5::Mime + ) +endmacro() + +add_vacation_test(vacationutils) Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-active-discard.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-active-discard.siv @@ -0,0 +1,9 @@ +require ["date","relational","vacation"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if allof (not header :contains "X-Spam-Flag" "YES") +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; + discard; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-complex-time.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-complex-time.siv @@ -0,0 +1,8 @@ +require ["date","relational","vacation"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if allof (currentdate :value "ge" "iso8601" "2015-01-02T02:00:00+00:00", currentdate :value "le" "date" "2015-03-04", not header :contains "X-Spam-Flag" "YES") +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-complex.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-complex.siv @@ -0,0 +1,8 @@ +require ["date","relational","vacation"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if allof (currentdate :zone "+0100" :value "ge" "date" "2015-01-02", currentdate :zone "+0100" :value "le" "date" "2015-03-04", not header :contains "X-Spam-Flag" "YES") +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate-complex.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate-complex.siv @@ -0,0 +1,8 @@ +require ["date","relational","vacation"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if false # allof (currentdate :zone "+0100" :value "ge" "date" "2015-01-02", currentdate :zone "+0100" :value "le" "date" "2015-03-04", not header :contains "X-Spam-Flag" "YES") +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate-multiple.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate-multiple.siv @@ -0,0 +1,19 @@ +require ["vacation"]; + +if true +{ + testcommand; +} + +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if false # true +{ + vacation :subject "XXX" "dsfgsdfgsdfg"; +} + +if true +{ + testcommand; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-deactivate.siv @@ -0,0 +1,9 @@ +require ["vacation"]; + +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if false # true +{ + vacation :subject "XXX" "dsfgsdfgsdfg"; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-deactive-copy.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-deactive-copy.siv @@ -0,0 +1,10 @@ +require ["vacation", "copy"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if false +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; + redirect :copy "copy@example.org"; +} +discard; \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-deactive-send.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-deactive-send.siv @@ -0,0 +1,11 @@ +require ["vacation", "copy"]; +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +if false +{ + vacation :days 7 :addresses "test@test.de" :subject "XXX" "dsfgsdfgsdfg"; + redirect "redirect@example.org"; +} + +redirect :copy "argh@example.org"; \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-multiple.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-multiple.siv @@ -0,0 +1,16 @@ +require ["vacation"]; + +if false +{ + testcommand; +} + +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +vacation :subject "XXX" "dsfgsdfgsdfg"; + +if false +{ + testcommand; +} Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-notfound.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-notfound.siv @@ -0,0 +1,15 @@ +#blabla + +testcommand; + +if true { + testcmd2; +} + +if false { + testcmd3; +} + +if true { + testcmd4; +} \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/data/vacation-simple.siv =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/data/vacation-simple.siv @@ -0,0 +1,6 @@ +require ["vacation"]; + +# EDITOR Roundcube (Managesieve) +# EDITOR_VERSION 8.2 +# rule:[Urlaub] +vacation :subject "XXX" "dsfgsdfgsdfg"; \ No newline at end of file Index: libksieve/src/ksieveui/vacation/autotests/vacationutilstest.h =================================================================== --- libksieve/src/ksieveui/vacation/autotests/vacationutilstest.h +++ libksieve/src/ksieveui/vacation/autotests/vacationutilstest.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2013-2015 Montel Laurent + Copyright (c) 2015 Sandro Knauß This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as @@ -15,33 +15,30 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef MULTIIMAPVACATIONMANAGER_H -#define MULTIIMAPVACATIONMANAGER_H +#ifndef VACATIONUTILSTEST_H +#define VACATIONUTILSTEST_H #include -namespace KSieveUi -{ -class MultiImapVacationManager : public QObject +namespace KSieveUi { +class VacationUtilsTest : public QObject { Q_OBJECT -public: - explicit MultiImapVacationManager(QObject *parent = Q_NULLPTR); - ~MultiImapVacationManager(); - - void checkVacation(); - -Q_SIGNALS: - void scriptActive(bool active, const QString &serverName); - void requestEditVacation(); - private Q_SLOTS: - void slotScriptActive(bool active, const QString &serverName); - -private: - int mNumberOfJobs; - bool mQuestionAsked; - bool mCheckInProgress; + void testParseEmptyScript(); + void testParseOnlyComment(); + void testParseActivate_data(); + void testParseActivate(); + void testParseScript_data(); + void testParseScript(); + void testParseScriptComplex(); + void testParseScriptComplexTime(); + void testMailAction_data(); + void testMailAction(); + void testWriteScript(); + void testWriteSimpleScript(); + void testUpdateVacationBlock(); + void testMergeRequireLine(); }; } -#endif // MULTIIMAPVACATIONMANAGER_H +#endif // VACATIONUTILSTEST_H Index: libksieve/src/ksieveui/vacation/autotests/vacationutilstest.cpp =================================================================== --- /dev/null +++ libksieve/src/ksieveui/vacation/autotests/vacationutilstest.cpp @@ -0,0 +1,341 @@ +/* + Copyright (c) 2015 Sandro Knauß + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "vacationutilstest.h" +#include "ksieveui/vacation/vacationutils.h" + +#include + +#include +#include + +using namespace KSieveUi; + +QTEST_MAIN( VacationUtilsTest) + +void testAliases(KMime::Types::AddrSpecList l1, KMime::Types::AddrSpecList l2) +{ + QCOMPARE(l1.count(),l2.count()); + for (int i=0; i < l1.count(); i++) { + QCOMPARE(l1.at(i).asString(),l2.at(i).asString()); + } +} + +void testAliases(KMime::Types::AddrSpecList l1, QStringList l2) +{ + QCOMPARE(l1.count(),l2.count()); + for (int i=0;i < l1.count(); i++) { + QCOMPARE(l1.at(i).asString(),l2.at(i)); + } +} + + +void VacationUtilsTest::testParseEmptyScript() +{ + const QString script; + QCOMPARE(VacationUtils::parseScript(script).isValid(), false); +} + +void VacationUtilsTest::testParseOnlyComment() +{ + QString script(QLatin1String("#comment")); + QCOMPARE(VacationUtils::parseScript(script).isValid(), false); + script = QLatin1String("#comment\n\n#comment\n"); + QCOMPARE(VacationUtils::parseScript(script).isValid(), false); +} + +void VacationUtilsTest::testParseActivate_data() +{ + QTest::addColumn("filename"); + QTest::addColumn("found"); + QTest::addColumn("active"); + + QTest::newRow("notfound") << QStringLiteral("vacation-notfound.siv") << false << false; + QTest::newRow("simple") << QStringLiteral("vacation-simple.siv") << true << true; + QTest::newRow("multile if") << QStringLiteral("vacation-multiple.siv") << true << true; + QTest::newRow("deactivate") << QStringLiteral("vacation-deactivate.siv") << true << false; + QTest::newRow("deactivate-multiple if") << QStringLiteral("vacation-deactivate-multiple.siv") << true << false; + QTest::newRow("deactivate-complex") << QStringLiteral("vacation-deactivate-complex.siv") << true << false; +} + + +void VacationUtilsTest::testParseActivate() +{ + QFETCH(QString, filename); + QFETCH(bool, found); + QFETCH(bool, active); + + QFile file(QLatin1String(VACATIONTESTDATADIR)+filename); + QVERIFY(file.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(file.readAll()); + + VacationUtils::Vacation vacation = VacationUtils::parseScript(script); + QCOMPARE(vacation.isValid(), found); + QCOMPARE(vacation.active, active); +} + +void VacationUtilsTest::testParseScript_data() +{ + QTest::addColumn("activate"); + QTest::addColumn("deactivate"); + + QTest::newRow("simple") << QStringLiteral("vacation-simple.siv") << QStringLiteral("vacation-deactivate.siv"); + QTest::newRow("complex") << QStringLiteral("vacation-complex.siv") << QStringLiteral("vacation-deactivate-complex.siv"); +} + + +void VacationUtilsTest::testParseScript() +{ + QFETCH(QString, activate); + QFETCH(QString, deactivate); + QFile fileA(QLatin1String(VACATIONTESTDATADIR) + activate); + QVERIFY(fileA.open(QIODevice::ReadOnly)); + QString scriptA = QString::fromUtf8(fileA.readAll()); + QFile fileD(QLatin1String(VACATIONTESTDATADIR) + deactivate); + QVERIFY(fileD.open(QIODevice::ReadOnly)); + QString scriptD = QString::fromUtf8(fileD.readAll()); + + VacationUtils::Vacation vacationA = VacationUtils::parseScript(scriptA); + VacationUtils::Vacation vacationD = VacationUtils::parseScript(scriptD); + QCOMPARE(vacationA.active, true); + QCOMPARE(vacationD.active, false); + QCOMPARE(vacationD.messageText, vacationA.messageText); + QCOMPARE(vacationD.subject, vacationA.subject); + QCOMPARE(vacationD.notificationInterval, vacationA.notificationInterval); + testAliases(vacationD.aliases, vacationA.aliases); + QCOMPARE(vacationD.sendForSpam, vacationA.sendForSpam); + QCOMPARE(vacationD.excludeDomain, vacationA.excludeDomain); + QCOMPARE(vacationD.startDate, vacationA.startDate); + QCOMPARE(vacationD.endDate, vacationA.endDate); + QCOMPARE(vacationD.startTime, QTime()); + QCOMPARE(vacationD.endTime, QTime()); +} + +void VacationUtilsTest::testMailAction_data() +{ + QTest::addColumn("filename"); + QTest::addColumn("action"); + QTest::addColumn("recipient"); + + QTest::newRow("keep") << QStringLiteral("vacation-complex.siv") << (int)VacationUtils::Keep << QString(); + QTest::newRow("discard") << QStringLiteral("vacation-active-discard.siv") << (int)VacationUtils::Discard << QString(); + QTest::newRow("send") << QStringLiteral("vacation-deactive-send.siv") << (int)VacationUtils::Sendto << QStringLiteral("redirect@example.org"); + QTest::newRow("copy") << QStringLiteral("vacation-deactive-copy.siv") << (int)VacationUtils::CopyTo << QStringLiteral("copy@example.org"); +} + +void VacationUtilsTest::testMailAction() +{ + QFETCH(QString, filename); + QFETCH(int, action); + QFETCH(QString, recipient); + + QFile file(QLatin1String(VACATIONTESTDATADIR) + filename); + QVERIFY(file.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(file.readAll()); + + VacationUtils::Vacation vacation = VacationUtils::parseScript(script); + QCOMPARE((int)vacation.mailAction, action); + QCOMPARE(vacation.mailActionRecipient, recipient); + + const QString composedScript = VacationUtils::composeScript(vacation); + vacation = VacationUtils::parseScript(composedScript); + QCOMPARE((int)vacation.mailAction, action); + QCOMPARE(vacation.mailActionRecipient, recipient); +} + +void VacationUtilsTest::testParseScriptComplex() +{ + QFile file(QLatin1String(VACATIONTESTDATADIR "vacation-complex.siv")); + QVERIFY(file.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(file.readAll()); + + VacationUtils::Vacation vacation = VacationUtils::parseScript(script); + QCOMPARE(vacation.active, true); + QCOMPARE(vacation.messageText, QLatin1String("dsfgsdfgsdfg")); + QCOMPARE(vacation.subject, QLatin1String("XXX")); + QCOMPARE(vacation.notificationInterval, 7); + testAliases(vacation.aliases, QStringList() << QLatin1String("test@test.de")); + QCOMPARE(vacation.sendForSpam, false); + QCOMPARE(vacation.excludeDomain, QString()); + QCOMPARE(vacation.startDate, QDate(2015, 01, 02)); + QCOMPARE(vacation.endDate, QDate(2015, 03, 04)); + QCOMPARE(vacation.startTime, QTime()); + QCOMPARE(vacation.endTime, QTime()); +} + +void VacationUtilsTest::testParseScriptComplexTime() +{ + QFile file(QLatin1String(VACATIONTESTDATADIR "vacation-complex-time.siv")); + QVERIFY(file.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(file.readAll()); + + VacationUtils::Vacation vacation = VacationUtils::parseScript(script); + QCOMPARE(vacation.active, true); + QCOMPARE(vacation.messageText, QLatin1String("dsfgsdfgsdfg")); + QCOMPARE(vacation.subject, QLatin1String("XXX")); + QCOMPARE(vacation.notificationInterval, 7); + testAliases(vacation.aliases, QStringList() << QLatin1String("test@test.de")); + QCOMPARE(vacation.sendForSpam, false); + QCOMPARE(vacation.excludeDomain, QString()); + QCOMPARE(vacation.startDate, QDate(2015, 01, 02)); + QCOMPARE(vacation.endDate, QDate(2015, 03, 04)); + QCOMPARE(vacation.startTime, QTime(2,0)); + QCOMPARE(vacation.endTime, QTime()); + + QString composedScript = VacationUtils::composeScript(vacation); + vacation = VacationUtils::parseScript(composedScript); + QCOMPARE(vacation.startTime, QTime(2,0)); + QCOMPARE(vacation.endTime, QTime()); +} + +void VacationUtilsTest::testWriteScript() +{ + VacationUtils::Vacation vacation, vacationA; + QStringList aliases = QStringList() << QLatin1String("test@test.de"); + vacation.valid = true; + + vacation.messageText = QLatin1String("dsfgsdfgsdfg"); + vacation.subject = QLatin1String("XXX"); + vacation.notificationInterval = 7; + vacation.sendForSpam = false; + vacation.excludeDomain = QLatin1String("example.org"); + vacation.startDate = QDate(2015, 01, 02); + vacation.endDate = QDate(2015, 03, 04); + vacation.active = true; + + foreach(const QString &alias, aliases) { + KMime::Types::Mailbox a; + a.fromUnicodeString(alias); + vacation.aliases.append(a.addrSpec()); + } + + QString script = VacationUtils::composeScript(vacation); + vacationA = VacationUtils::parseScript(script); + QCOMPARE(vacationA.isValid(), true); + QCOMPARE(vacationA.active, vacation.active); + QCOMPARE(vacationA.messageText, vacation.messageText); + QCOMPARE(vacationA.subject, vacation.subject); + QCOMPARE(vacationA.notificationInterval, vacation.notificationInterval); + testAliases(vacationA.aliases, vacation.aliases); + QCOMPARE(vacationA.sendForSpam, vacation.sendForSpam); + QCOMPARE(vacationA.excludeDomain, vacation.excludeDomain); + QCOMPARE(vacationA.startDate, vacation.startDate); + QCOMPARE(vacationA.endDate, vacation.endDate); + QCOMPARE(vacationA.startTime, QTime()); + QCOMPARE(vacationA.endTime, QTime()); + + vacation.active = false; + script = VacationUtils::composeScript(vacation); + vacationA = VacationUtils::parseScript(script); + QCOMPARE(vacationA.isValid(), true); + QCOMPARE(vacationA.active, vacation.active); + QCOMPARE(vacationA.messageText, vacation.messageText); + QCOMPARE(vacationA.subject, vacation.subject); + QCOMPARE(vacationA.notificationInterval, vacation.notificationInterval); + testAliases(vacationA.aliases, vacation.aliases); + QCOMPARE(vacationA.sendForSpam, vacation.sendForSpam); + QCOMPARE(vacationA.excludeDomain, vacation.excludeDomain); + QCOMPARE(vacationA.startDate, vacation.startDate); + QCOMPARE(vacationA.endDate, vacation.endDate); + QCOMPARE(vacationA.startTime, QTime()); + QCOMPARE(vacationA.endTime, QTime()); +} + + +void VacationUtilsTest::testWriteSimpleScript() +{ + VacationUtils::Vacation vacation; + vacation.valid = true; + vacation.messageText = QLatin1String("dsfgsdfgsdfg"); + vacation.subject = QLatin1String("XXX"); + vacation.notificationInterval = 7; + vacation.active = true; + vacation.sendForSpam = true; + + QString script = VacationUtils::composeScript(vacation); + VacationUtils::Vacation vacationA = VacationUtils::parseScript(script); + QCOMPARE(vacation.isValid(), true); + QCOMPARE(vacationA.active, vacation.active); + QCOMPARE(vacationA.messageText, vacation.messageText); + QCOMPARE(vacationA.subject, vacation.subject); + QCOMPARE(vacationA.notificationInterval, vacation.notificationInterval); + + vacation.active = false; + script = VacationUtils::composeScript(vacation); + vacationA = VacationUtils::parseScript(script); + QCOMPARE(vacation.isValid(), true); + QCOMPARE(vacationA.active, vacation.active); + QCOMPARE(vacationA.messageText, vacation.messageText); + QCOMPARE(vacationA.subject, vacation.subject); + QCOMPARE(vacationA.notificationInterval, vacation.notificationInterval); + +} + +void VacationUtilsTest::testUpdateVacationBlock() +{ + QFile fileA(QLatin1String(VACATIONTESTDATADIR "vacation-simple.siv")); + QVERIFY(fileA.open(QIODevice::ReadOnly)); + QString scriptA = QString::fromUtf8(fileA.readAll()); + + QFile fileB(QLatin1String(VACATIONTESTDATADIR "vacation-deactivate.siv")); + QVERIFY(fileB.open(QIODevice::ReadOnly)); + QString scriptB = QString::fromUtf8(fileB.readAll()); + + const QString attend = QLatin1String("if true\n{\ntestcmd;\n}\n"); + const QString require = QLatin1String("require [\"date\", \"test\"];"); + const QString scriptAattend = scriptA + QLatin1String("\n") + attend; + const QString scriptBattend = scriptB + QLatin1String("\n") + attend; + + QStringList linesA = scriptA.split(QLatin1Char('\n')); + QStringList header; + for(int i=0; i<5;i++ ){ + header.append(linesA.at(i)); + } + + QStringList vacation; + for(int i=5; i listCreateJob() const; Index: libksieve/src/ksieveui/vacation/multiimapvacationdialog.cpp =================================================================== --- libksieve/src/ksieveui/vacation/multiimapvacationdialog.cpp +++ libksieve/src/ksieveui/vacation/multiimapvacationdialog.cpp @@ -17,6 +17,7 @@ #include "multiimapvacationdialog.h" #include "vacationpagewidget.h" +#include "multiimapvacationmanager.h" #include "ksieveui/util.h" #include @@ -45,19 +46,23 @@ MultiImapVacationDialogPrivate() : mTabWidget(Q_NULLPTR), mStackedWidget(Q_NULLPTR) + , mVacationManager(Q_NULLPTR) { } QList mListCreateJob; QTabWidget *mTabWidget; QStackedWidget *mStackedWidget; + MultiImapVacationManager *mVacationManager; }; -MultiImapVacationDialog::MultiImapVacationDialog(QWidget *parent) +MultiImapVacationDialog::MultiImapVacationDialog(MultiImapVacationManager *manager, QWidget *parent) : QDialog(parent), d(new KSieveUi::MultiImapVacationDialogPrivate) { + d->mVacationManager = manager; + setWindowTitle(i18n("Configure \"Out of Office\" Replies")); KWindowSystem::setIcons(winId(), qApp->windowIcon().pixmap(IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop)), qApp->windowIcon().pixmap(IconSize(KIconLoader::Small), IconSize(KIconLoader::Small))); @@ -104,26 +109,21 @@ "IMAP server for this. " "You can do this on the \"Filtering\" tab of the IMAP " "account configuration.")); + lab->setWordWrap(true); vbox->addWidget(lab); vbox->addStretch(); lab->setWordWrap(true); d->mStackedWidget->addWidget(w); d->mStackedWidget->setCurrentIndex(0); bool foundOneImap = false; - const Akonadi::AgentInstance::List instances = KSieveUi::Util::imapAgentInstances(); - foreach (const Akonadi::AgentInstance &instance, instances) { - if (instance.status() == Akonadi::AgentInstance::Broken) { - continue; - } + QDialogButtonBox *buttonBox = Q_NULLPTR; - const QUrl url = KSieveUi::Util::findSieveUrlForAccount(instance.identifier()); - if (!url.isEmpty()) { - const QString serverName = instance.name(); - createPage(serverName, url); - foundOneImap = true; - } + QMap list = d->mVacationManager->serverList(); + foreach (const QString &serverName, list.keys()) { + const QUrl url = list.value(serverName); + createPage(serverName, url); + foundOneImap = true; } - QDialogButtonBox *buttonBox = Q_NULLPTR; if (foundOneImap) { buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); @@ -155,6 +155,7 @@ VacationPageWidget *page = new VacationPageWidget; page->setServerUrl(url); page->setServerName(serverName); + page->setVacationManager(d->mVacationManager); d->mTabWidget->addTab(page, serverName + QStringLiteral(" (%1)").arg(url.userName())); } Index: libksieve/src/ksieveui/vacation/multiimapvacationmanager.h =================================================================== --- libksieve/src/ksieveui/vacation/multiimapvacationmanager.h +++ libksieve/src/ksieveui/vacation/multiimapvacationmanager.h @@ -19,29 +19,42 @@ #define MULTIIMAPVACATIONMANAGER_H #include +#include + +#include "ksieveui_export.h" + +class QUrl; namespace KSieveUi { -class MultiImapVacationManager : public QObject +class CheckKep14SupportJob; +class VacationCheckJob; +class KSIEVEUI_EXPORT MultiImapVacationManager : public QObject { Q_OBJECT public: explicit MultiImapVacationManager(QObject *parent = Q_NULLPTR); ~MultiImapVacationManager(); void checkVacation(); + QMap serverList(); + void checkVacation(const QString &serverName, const QUrl &url); + + bool kep14Support(QString serverName); Q_SIGNALS: void scriptActive(bool active, const QString &serverName); - void requestEditVacation(); + void scriptAvailable(const QString &serverName, const QStringList &sieveCapabilities, const QString &scriptName, const QString &script, bool active); private Q_SLOTS: - void slotScriptActive(bool active, const QString &serverName); + void slotScriptActive(VacationCheckJob* job, QString scriptName, bool active); + void slotCheckKep14Ended(CheckKep14SupportJob *job, bool success); private: int mNumberOfJobs; - bool mQuestionAsked; bool mCheckInProgress; + + QMap mKep14Support; //if the server has KEP:14 support }; } #endif // MULTIIMAPVACATIONMANAGER_H Index: libksieve/src/ksieveui/vacation/multiimapvacationmanager.cpp =================================================================== --- libksieve/src/ksieveui/vacation/multiimapvacationmanager.cpp +++ libksieve/src/ksieveui/vacation/multiimapvacationmanager.cpp @@ -18,17 +18,19 @@ #include "multiimapvacationmanager.h" #include "vacationcheckjob.h" #include "util/util.h" +#include +#include +#include #include -#include -#include + +#include "libksieve_debug.h" using namespace KSieveUi; MultiImapVacationManager::MultiImapVacationManager(QObject *parent) : QObject(parent), mNumberOfJobs(0), - mQuestionAsked(false), mCheckInProgress(false) { } @@ -38,50 +40,95 @@ } -void MultiImapVacationManager::checkVacation() +QMap MultiImapVacationManager::serverList() { - if (mCheckInProgress) { - return; - } - mNumberOfJobs = 0; - mCheckInProgress = true; - mQuestionAsked = false; - + QMap list; const Akonadi::AgentInstance::List instances = KSieveUi::Util::imapAgentInstances(); foreach (const Akonadi::AgentInstance &instance, instances) { if (instance.status() == Akonadi::AgentInstance::Broken) { continue; } const QUrl url = KSieveUi::Util::findSieveUrlForAccount(instance.identifier()); if (!url.isEmpty()) { - const QString serverName = instance.name(); - ++mNumberOfJobs; - VacationCheckJob *job = new VacationCheckJob(url, serverName, this); - connect(job, &VacationCheckJob::scriptActive, this, &MultiImapVacationManager::slotScriptActive); + list.insert(instance.name(), url); } } + return list; } -void MultiImapVacationManager::slotScriptActive(bool active, const QString &serverName) +void MultiImapVacationManager::checkVacation(const QString &serverName, const QUrl &url) { - --mNumberOfJobs; - Q_EMIT scriptActive(active, serverName); - - if (active) { - if (!mQuestionAsked) { - mQuestionAsked = true; - if (KMessageBox::questionYesNo(Q_NULLPTR, i18n("There is still an active out-of-office reply configured.\n" - "Do you want to edit it?"), i18n("Out-of-office reply still active"), - KGuiItem(i18n("Edit"), QStringLiteral("document-properties")), - KGuiItem(i18n("Ignore"), QStringLiteral("dialog-cancel"))) - == KMessageBox::Yes) { - Q_EMIT requestEditVacation(); - } - } + ++mNumberOfJobs; + if (!mKep14Support.contains(serverName)) { + CheckKep14SupportJob *checkKep14Job = new CheckKep14SupportJob(this); + checkKep14Job->setProperty(QLatin1String("triggerScript").latin1(), true); + checkKep14Job->setServerName(serverName); + checkKep14Job->setServerUrl(url); + connect(checkKep14Job, &CheckKep14SupportJob::result, this, &MultiImapVacationManager::slotCheckKep14Ended); + checkKep14Job->start(); } + VacationCheckJob *job = new VacationCheckJob(url, serverName, this); + job->setKep14Support(mKep14Support[serverName]); + connect(job, &VacationCheckJob::scriptActive, this, &MultiImapVacationManager::slotScriptActive); + job->start(); +} + +void MultiImapVacationManager::checkVacation() +{ + if (mCheckInProgress) { + return; + } + mNumberOfJobs = 0; + mCheckInProgress = true; + + QMap list = serverList(); + foreach ( const QString &serverName, list.keys() ) { + const QUrl url = list.value(serverName); + checkVacation(serverName, url); + } +} + +void MultiImapVacationManager::slotScriptActive(VacationCheckJob* job, QString scriptName, bool active) +{ + --mNumberOfJobs; if (mNumberOfJobs == 0) { mCheckInProgress = false; } + + job->deleteLater(); + + if (job->noScriptFound()) { + emit scriptActive(false, job->serverName()); + return; + } + emit scriptActive(active, job->serverName()); + emit scriptAvailable(job->serverName(), job->sieveCapabilities(), scriptName, job->script(), active); +} + +void MultiImapVacationManager::slotCheckKep14Ended(CheckKep14SupportJob *job, bool success) +{ + job->deleteLater(); + if (!success) { + --mNumberOfJobs; + return; + } + + mKep14Support.insert(job->serverName(), job->hasKep14Support()); + + VacationCheckJob *checkJob = new VacationCheckJob(job->serverUrl(), job->serverName(), this); + checkJob->setKep14Support(job->hasKep14Support()); + connect(checkJob, &VacationCheckJob::scriptActive, this, &MultiImapVacationManager::slotScriptActive); + checkJob->start(); +} + +bool MultiImapVacationManager::kep14Support(QString serverName) +{ + if (mKep14Support.contains(serverName)) { + return mKep14Support[serverName]; + } else { + qCWarning(LIBKSIEVE_LOG) << "We don't know the KEP:14 support for this server." << serverName; + } + return false; } Index: libksieve/src/ksieveui/vacation/tests/main.cpp =================================================================== --- libksieve/src/ksieveui/vacation/tests/main.cpp +++ libksieve/src/ksieveui/vacation/tests/main.cpp @@ -22,6 +22,7 @@ #include #include "vacation/multiimapvacationdialog.h" +#include "vacation/multiimapvacationmanager.h" int main(int argc, char **argv) { @@ -41,9 +42,10 @@ parser.process(app); aboutData.processCommandLine(&parser); - app.setQuitOnLastWindowClosed(false); + app.setQuitOnLastWindowClosed(true); - KSieveUi::MultiImapVacationDialog dlg; + KSieveUi::MultiImapVacationManager manager; + KSieveUi::MultiImapVacationDialog dlg(&manager); dlg.show(); app.exec(); Index: libksieve/src/ksieveui/vacation/vacation.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacation.cpp +++ libksieve/src/ksieveui/vacation/vacation.cpp @@ -113,42 +113,44 @@ return; } + const bool supportsDate = job->sieveCapabilities().contains(QLatin1String("date")); + if (!mDialog && !mCheckOnly) { mDialog = new VacationDialog(i18n("Configure \"Out of Office\" Replies"), Q_NULLPTR, false); } - QString messageText = VacationUtils::defaultMessageText(); - QString subject = VacationUtils::defaultSubject(); - int notificationInterval = VacationUtils::defaultNotificationInterval(); - QStringList aliases = VacationUtils::defaultMailAliases(); - bool sendForSpam = VacationUtils::defaultSendForSpam(); - QString domainName = VacationUtils::defaultDomainName(); - QDate startDate = VacationUtils::defaultStartDate(); - QDate endDate = VacationUtils::defaultEndDate(); if (!success) { active = false; // default to inactive } - if (!mCheckOnly && (!success || !KSieveUi::VacationUtils::parseScript(script, messageText, subject, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate))) + KSieveUi::VacationUtils::Vacation vacation = KSieveUi::VacationUtils::parseScript(script); + + if (!mCheckOnly && (!success || (!vacation.isValid() && !script.trimmed().isEmpty())) ) { KMessageBox::information(Q_NULLPTR, 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; if (mDialog) { - mDialog->setActivateVacation(active); - mDialog->setSubject(subject); - mDialog->setMessageText(messageText); - mDialog->setNotificationInterval(notificationInterval); - mDialog->setMailAliases(aliases.join(QStringLiteral(", "))); - mDialog->setSendForSpam(sendForSpam); - mDialog->setDomainName(domainName); + mDialog->setActivateVacation(active && vacation.active); + mDialog->setMailAction(vacation.mailAction, vacation.mailActionRecipient); + mDialog->setSubject(vacation.subject); + mDialog->setMessageText(vacation.messageText); + mDialog->setNotificationInterval(vacation.notificationInterval); + mDialog->setMailAliases(vacation.aliases); + mDialog->setSendForSpam(vacation.sendForSpam); + mDialog->setDomainName(vacation.excludeDomain); mDialog->enableDomainAndSendForSpam(!VacationSettings::allowOutOfOfficeUploadButNoSettings()); - mDialog->enableDates(job->sieveCapabilities().contains(QStringLiteral("date"))); - mDialog->setStartDate(startDate); - mDialog->setEndDate(endDate); + mDialog->enableDates(supportsDate); + + if (supportsDate) { + mDialog->setStartDate(vacation.startDate); + mDialog->setEndTime(vacation.endTime); + mDialog->setEndDate(vacation.endDate); + mDialog->setEndTime(vacation.endTime); + } connect(mDialog, &VacationDialog::okClicked, this, &Vacation::slotDialogOk); connect(mDialog, &VacationDialog::cancelClicked, this, &Vacation::slotDialogCancel); @@ -171,16 +173,24 @@ { qCDebug(LIBKSIEVE_LOG); // compose a new script: - const QString script = VacationUtils::composeScript(mDialog->messageText(), - mDialog->subject(), - mDialog->notificationInterval(), - mDialog->mailAliases(), - mDialog->sendForSpam(), - mDialog->domainName(), - mDialog->startDate(), - mDialog->endDate()); const bool active = mDialog->activateVacation(); - Q_EMIT scriptActive(active, mServerName); + VacationUtils::Vacation vacation; + vacation.valid = true; + vacation.active = active; + vacation.messageText = mDialog->messageText(); + vacation.subject = mDialog->subject(); + vacation.mailAction = mDialog->mailAction(); + vacation.mailActionRecipient = mDialog->mailActionRecipient(); + vacation.notificationInterval = mDialog->notificationInterval(); + vacation.aliases = mDialog->mailAliases(); + vacation.sendForSpam = mDialog->sendForSpam(); + vacation.excludeDomain = mDialog->domainName(); + vacation.startDate = mDialog->startDate(); + vacation.startTime = mDialog->startTime(); + vacation.endDate = mDialog->endDate(); + vacation.endTime = mDialog->endTime(); + const QString script = VacationUtils::composeScript(vacation); + emit scriptActive(active, mServerName); qCDebug(LIBKSIEVE_LOG) << "script:" << endl << script; Index: libksieve/src/ksieveui/vacation/vacationcheckjob.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationcheckjob.h +++ libksieve/src/ksieveui/vacation/vacationcheckjob.h @@ -19,6 +19,7 @@ #define VACATIONCHECKJOB_H #include +#include #include namespace KManageSieve @@ -28,23 +29,45 @@ namespace KSieveUi { +class ParseUserScriptJob; class VacationCheckJob : public QObject { Q_OBJECT public: explicit VacationCheckJob(const QUrl &url, const QString &serverName, QObject *parent = Q_NULLPTR); ~VacationCheckJob(); + void setKep14Support(bool kep14Support); + void start(); + void kill(); + bool noScriptFound(); + QString script(); + QStringList sieveCapabilities(); + QString serverName(); Q_SIGNALS: - void scriptActive(bool active, const QString &serverName); + void scriptActive(VacationCheckJob* job, const QString &sscriptName, bool active); private Q_SLOTS: void slotGetResult(KManageSieve::SieveJob *job, bool success, const QString &script, bool active); + void slotGotActiveScripts(ParseUserScriptJob *job); + void slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &availableScripts, const QString &activeScript); + void emitError(const QString &errorMessage); + void searchVacationScript(); + void getNextScript(); + bool isLastScript() const; private: + QStringList mAvailableScripts; + QStringList mActiveScripts; + QStringList mSieveCapabilities; + QString mScript; QString mServerName; QUrl mUrl; KManageSieve::SieveJob *mSieveJob; + ParseUserScriptJob *mParseJob; + int mScriptPos; + bool mKep14Support; + bool mNoScriptFound; }; } Index: libksieve/src/ksieveui/vacation/vacationcheckjob.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationcheckjob.cpp +++ libksieve/src/ksieveui/vacation/vacationcheckjob.cpp @@ -17,53 +17,201 @@ #include "vacationcheckjob.h" #include "vacationutils.h" +#include "managescriptsjob/parseuserscriptjob.h" +#include "util/util.h" #include #include +#include "libksieve_debug.h" + using namespace KSieveUi; VacationCheckJob::VacationCheckJob(const QUrl &url, const QString &serverName, QObject *parent) : QObject(parent), mServerName(serverName), mUrl(url) + , mSieveJob(Q_NULLPTR) + , mParseJob(Q_NULLPTR) + , mScriptPos(-1) + , mKep14Support(false) + , mNoScriptFound(false) { - mSieveJob = KManageSieve::SieveJob::get(mUrl); - mSieveJob->setInteractive(false); - connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCheckJob::slotGetResult); } VacationCheckJob::~VacationCheckJob() { - if (mSieveJob) { + kill(); +} + +void VacationCheckJob::kill() +{ + if ( mSieveJob ) mSieveJob->kill(); + mSieveJob = Q_NULLPTR; + + if (mParseJob) { + mParseJob->kill(); + } + mParseJob = Q_NULLPTR; +} + + +void VacationCheckJob::setKep14Support(bool kep14Support) +{ + mKep14Support = kep14Support; +} + +void VacationCheckJob::start() +{ + if (mKep14Support) { + QUrl url = mUrl; + url = url.adjusted(QUrl::RemoveFilename); + url.setPath(url.path() + QLatin1String("USER")); + mParseJob = new ParseUserScriptJob(url); + connect(mParseJob, &ParseUserScriptJob::finished, this, &VacationCheckJob::slotGotActiveScripts); + mParseJob->start(); + mSieveJob = KManageSieve::SieveJob::list(url); + connect(mSieveJob, &KManageSieve::SieveJob::gotList, this, &VacationCheckJob::slotGotList); + } else { + mSieveJob = KManageSieve::SieveJob::get(mUrl); + mSieveJob->setInteractive(false); + connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCheckJob::slotGetResult); } +} + +void VacationCheckJob::slotGetResult(KManageSieve::SieveJob *job, bool success, const QString &script, bool active) +{ + Q_ASSERT(job == mSieveJob); + mScript = script; + mSieveCapabilities = mSieveJob->sieveCapabilities(); mSieveJob = Q_NULLPTR; + + if (mKep14Support) { + VacationUtils::Vacation vacation = VacationUtils::parseScript(script); + if (vacation.isValid()) { + const QString &scriptName = mAvailableScripts[mScriptPos-1]; + bool active = mActiveScripts.contains(scriptName) && vacation.active; + if (active && vacation.startDate.isValid() && vacation.endDate.isValid()) { + active = (vacation.startDate <= QDate::currentDate() && vacation.endDate >= QDate::currentDate()); + } + emit scriptActive(this, scriptName, active); + qDebug(LIBKSIEVE_LOG) << "vacation script found :)"; + } else if (isLastScript()) { + mNoScriptFound = true; + emit scriptActive(this, QString(), false); + qDebug(LIBKSIEVE_LOG) << "no vacation script found :("; + } else { + getNextScript(); + } + } else { + if ( !success ) { + active = false; // default to inactive + mNoScriptFound = true; +} + if (active) { + mActiveScripts << mUrl.fileName(); + } + emit scriptActive(this, mUrl.fileName(), active); + } +} + +void VacationCheckJob::slotGotActiveScripts(ParseUserScriptJob *job) +{ + Q_ASSERT(job == mParseJob); + mParseJob = Q_NULLPTR; + if (!job->error().isEmpty()) { + emitError(QLatin1String("ParseUserScriptJob failed:")+job->error()); + return; + } + mActiveScripts = job->activeScriptList(); + + if (!mSieveJob) { + searchVacationScript(); + } } -void VacationCheckJob::slotGetResult(KManageSieve::SieveJob */*job*/, bool success, const QString &script, bool active) +void VacationCheckJob::slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &availableScripts, const QString &activeScript) { + Q_UNUSED(activeScript) + Q_ASSERT(job == mSieveJob); mSieveJob = Q_NULLPTR; if (!success) { - active = false; // default to inactive + emitError(QLatin1String("SieveJob list failed.")); + return; } - QDate startDate, endDate; + mAvailableScripts = availableScripts; - QString dummyStr; - QStringList dummyStrList; - int dummyInt; - bool dummyBool; + if (!mParseJob) { + searchVacationScript(); + } +} + +void VacationCheckJob::emitError(const QString &errorMessage) +{ + qWarning() << errorMessage; + //TODO: emit error +} + +void VacationCheckJob::searchVacationScript() +{ + QStringList scriptList = mActiveScripts; - // If the script is active then parse it, and verify whether it has date range set - if (active) { - bool valid = VacationUtils::parseScript(script, dummyStr, dummyStr, dummyInt, dummyStrList, dummyBool, dummyStr, startDate, endDate); - // If the date range is set, mark the script as active only if the date range - // includes now/today - if (valid && startDate.isValid() && endDate.isValid()) { - active = (startDate <= QDate::currentDate() && endDate >= QDate::currentDate()); + // Reorder script list + foreach(const QString &script, mAvailableScripts) { + if (!scriptList.contains(script)) { + scriptList.append(script); } } - Q_EMIT scriptActive(active, mServerName); + mAvailableScripts = scriptList; + mScriptPos = 0; + getNextScript(); +} + +void VacationCheckJob::getNextScript() +{ + if (isLastScript()) { + //TODO: no script found + mNoScriptFound = true; + emit scriptActive(this, QString(), false); + qDebug(LIBKSIEVE_LOG) << "no vacation script found :("; + } + QUrl url = mUrl; + url = url.adjusted(QUrl::RemoveFilename); + url.setPath(url.path() + mAvailableScripts[mScriptPos]); + mScriptPos += 1; + if (Util::isKep14ProtectedName(url.fileName())) { + getNextScript(); + } + mSieveJob = KManageSieve::SieveJob::get(url); + mSieveJob->setInteractive(false); + connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCheckJob::slotGetResult); +} + +bool VacationCheckJob::isLastScript() const +{ + return mScriptPos >= mAvailableScripts.count(); +} + +bool VacationCheckJob::noScriptFound() +{ + return mNoScriptFound; +} + +QString VacationCheckJob::serverName() +{ + return mServerName; } + +QString VacationCheckJob::script() +{ + return mScript; +} + +QStringList VacationCheckJob::sieveCapabilities() +{ + return mSieveCapabilities; +} + Index: libksieve/src/ksieveui/vacation/vacationcreatescriptjob.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationcreatescriptjob.h +++ libksieve/src/ksieveui/vacation/vacationcreatescriptjob.h @@ -29,36 +29,49 @@ namespace KSieveUi { +class ParseUserScriptJob; +class GenerateGlobalScriptJob; class VacationCreateScriptJob : public QObject { Q_OBJECT public: explicit VacationCreateScriptJob(QObject *parent = Q_NULLPTR); ~VacationCreateScriptJob(); void start(); + void kill(); void setServerUrl(const QUrl &url); void setScript(const QString &script); void setServerName(const QString &servername); + const QString &serverName() const; void setStatus(bool activate, bool wasActive); + void setKep14Support(bool kep14Support); Q_SIGNALS: void result(bool); void scriptActive(bool activated, const QString &serverName); private Q_SLOTS: - void slotPutActiveResult(KManageSieve::SieveJob *job, bool success); - void slotPutInactiveResult(KManageSieve::SieveJob *job, bool success); + void slotPutResult(KManageSieve::SieveJob *job, bool success); + void slotGetScript(KManageSieve::SieveJob *job, bool success, const QString &oldScript, bool active); + void slotGotActiveScripts(ParseUserScriptJob *job); + void slotGenerateDone(const QString &error=QString()); private: - void handlePutResult(KManageSieve::SieveJob *, bool success, bool activated); + void handleResult(); QUrl mUrl; QString mScript; QString mServerName; bool mActivate; - bool mWasActive; + bool mScriptActive; + bool mKep14Support; + bool mUserJobRunning; + bool mScriptJobRunning; + bool mSuccess; KManageSieve::SieveJob *mSieveJob; + ParseUserScriptJob *mParseUserJob; + GenerateGlobalScriptJob *mCreateJob; }; } Index: libksieve/src/ksieveui/vacation/vacationcreatescriptjob.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationcreatescriptjob.cpp +++ libksieve/src/ksieveui/vacation/vacationcreatescriptjob.cpp @@ -16,6 +16,9 @@ */ #include "vacationcreatescriptjob.h" +#include "vacationutils.h" +#include +#include #include #include @@ -25,43 +28,63 @@ using namespace KSieveUi; VacationCreateScriptJob::VacationCreateScriptJob(QObject *parent) - : QObject(parent), - mActivate(false), - mWasActive(false), - mSieveJob(Q_NULLPTR) + : QObject(parent) + , mActivate(false) + , mScriptActive(false) + , mKep14Support(false) + , mUserJobRunning(false) + , mScriptJobRunning(false) + , mSuccess(true) + , mSieveJob(Q_NULLPTR) + , mParseUserJob(Q_NULLPTR) + , mCreateJob(Q_NULLPTR) { } VacationCreateScriptJob::~VacationCreateScriptJob() { + kill(); +} + +void VacationCreateScriptJob::kill() +{ + if (mSieveJob) { + mSieveJob->kill(); + } + mSieveJob = Q_NULLPTR; + + if (mParseUserJob) { + mParseUserJob->kill(); + } + mParseUserJob = Q_NULLPTR; + if (mCreateJob) { + mCreateJob->kill(); } + mParseUserJob = Q_NULLPTR; +} + void VacationCreateScriptJob::setStatus(bool activate, bool wasActive) { mActivate = activate; - mWasActive = wasActive; + mScriptActive = wasActive; } void VacationCreateScriptJob::setServerName(const QString &servername) { mServerName = servername; } -void VacationCreateScriptJob::start() +const QString &VacationCreateScriptJob::serverName() const { - if (mUrl.isEmpty()) { - qCDebug(LIBKSIEVE_LOG) << " server url is empty"; - deleteLater(); - return; - } - mSieveJob = KManageSieve::SieveJob::put(mUrl, mScript, mActivate, mWasActive); - if (mActivate) { - connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCreateScriptJob::slotPutActiveResult); - } else { - connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCreateScriptJob::slotPutInactiveResult); - } + return mServerName; +} + +void VacationCreateScriptJob::setKep14Support(bool kep14Support) +{ + mKep14Support = kep14Support; } void VacationCreateScriptJob::setServerUrl(const QUrl &url) @@ -74,28 +97,108 @@ mScript = script; } -void VacationCreateScriptJob::slotPutActiveResult(KManageSieve::SieveJob *job, bool success) +void VacationCreateScriptJob::start() { - handlePutResult(job, success, true); + if (mUrl.isEmpty()) { + qDebug()<<" server url is empty"; + deleteLater(); + return; + } + + mUserJobRunning = false; + mScriptJobRunning = true; + mSieveJob = KManageSieve::SieveJob::get(mUrl); + mSieveJob->setInteractive(false); + connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCreateScriptJob::slotGetScript); + + if (mKep14Support && mActivate && !mScriptActive) { + mUserJobRunning = true; + QUrl url = mUrl; + url = url.adjusted(QUrl::RemoveFilename); + url.setPath(url.path() + QLatin1String("USER")); + mParseUserJob = new ParseUserScriptJob(url, this); + connect(mParseUserJob, &ParseUserScriptJob::finished, this, &VacationCreateScriptJob::slotGotActiveScripts); + mParseUserJob->start(); + } +} + +void VacationCreateScriptJob::slotGetScript(KManageSieve::SieveJob *job, bool success, const QString &oldScript, bool active) +{ + Q_UNUSED(active) + Q_ASSERT(job == mSieveJob); + mSieveJob = Q_NULLPTR; + QString script = mScript; + if (success || !oldScript.trimmed().isEmpty()) { + script = VacationUtils::mergeRequireLine(oldScript, mScript); + script = VacationUtils::updateVacationBlock(oldScript,mScript); + } + if (mKep14Support) { + mSieveJob = KManageSieve::SieveJob::put( mUrl, mScript, false, false ); + } else { + mSieveJob = KManageSieve::SieveJob::put( mUrl, mScript, mActivate, false ); //Never deactivate + } + connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationCreateScriptJob::slotPutResult); } -void VacationCreateScriptJob::slotPutInactiveResult(KManageSieve::SieveJob *job, bool success) +void VacationCreateScriptJob::slotPutResult( KManageSieve::SieveJob * job, bool success ) { - handlePutResult(job, success, false); + Q_ASSERT(job == mSieveJob); + mSieveJob = Q_NULLPTR; + mScriptJobRunning = false; + if (!success) { + mSuccess = false; + } + handleResult(); } -void VacationCreateScriptJob::handlePutResult(KManageSieve::SieveJob *, bool success, bool activated) +void VacationCreateScriptJob::handleResult() { - if (success) - KMessageBox::information(Q_NULLPTR, activated + if (mUserJobRunning || mScriptJobRunning) { // Not both jobs are done + return; + } + + if (mSuccess) + KMessageBox::information(Q_NULLPTR, mActivate ? i18n("Sieve script installed successfully on the server \'%1\'.\n" "Out of Office reply is now active.", mServerName) : i18n("Sieve script installed successfully on the server \'%1\'.\n" "Out of Office reply has been deactivated.", mServerName)); - qCDebug(LIBKSIEVE_LOG) << "( ???," << success << ", ? )"; + qCDebug(LIBKSIEVE_LOG) << "( ???," << mSuccess << ", ? )"; mSieveJob = Q_NULLPTR; // job deletes itself after returning from this slot! - Q_EMIT result(success); - Q_EMIT scriptActive(activated, mServerName); + Q_EMIT result(mSuccess); + Q_EMIT scriptActive(mActivate, mServerName); deleteLater(); } + +void VacationCreateScriptJob::slotGotActiveScripts(ParseUserScriptJob *job) +{ + Q_ASSERT(job == mParseUserJob); + mParseUserJob = Q_NULLPTR; + if (!job->error().isEmpty()) { + slotGenerateDone(job->error()); + return; + } + + QStringList list = job->activeScriptList(); + + if (!list.contains(mUrl.fileName())) { + list.prepend(mUrl.fileName()); + mCreateJob = new GenerateGlobalScriptJob(mUrl, this); + mCreateJob->addUserActiveScripts(list); + connect( mCreateJob, SIGNAL(success()), SLOT(slotGenerateDone())); + connect( mCreateJob, SIGNAL(error(QString)), SLOT(slotGenerateDone(QString))); + mCreateJob->start(); + } +} + +void VacationCreateScriptJob::slotGenerateDone(const QString &error) +{ + mCreateJob = Q_NULLPTR; + mUserJobRunning = false; + if (!error.isEmpty()) { + qWarning() << error; + mSuccess = false; + } + handleResult(); +} Index: libksieve/src/ksieveui/vacation/vacationdialog.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationdialog.h +++ libksieve/src/ksieveui/vacation/vacationdialog.h @@ -14,10 +14,13 @@ #ifndef KSIEVE_KSIEVEUI_VACATIONDIALOG_H #define KSIEVE_KSIEVEUI_VACATIONDIALOG_H +#include "vacationutils.h" + #include template class QList; class QDate; +class QTime; namespace KMime { @@ -55,6 +58,10 @@ QString subject() const; void setSubject(const QString &subject); + VacationUtils::MailAction mailAction() const; + QString mailActionRecipient() const; + void setMailAction (KSieveUi::VacationUtils::MailAction action, const QString &recipient); + int notificationInterval() const; void setNotificationInterval(int days); @@ -71,9 +78,15 @@ QDate startDate() const; void setStartDate(const QDate &startDate); + QTime startTime() const; + void setStartTime(const QTime &startTime); + QDate endDate() const; void setEndDate(const QDate &endDate); + QTime endTime() const; + void setEndTime(const QTime &endTime); + Q_SIGNALS: void okClicked(); void cancelClicked(); Index: libksieve/src/ksieveui/vacation/vacationdialog.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationdialog.cpp +++ libksieve/src/ksieveui/vacation/vacationdialog.cpp @@ -128,6 +128,21 @@ return mVacationEditWidget->setSubject(subject); } +VacationUtils::MailAction VacationDialog::mailAction() const +{ + return mVacationEditWidget->mailAction(); +} + +QString VacationDialog::mailActionRecipient() const +{ + return mVacationEditWidget->mailActionRecipient(); +} + +void VacationDialog::setMailAction(VacationUtils::MailAction action, const QString& recipient) +{ + mVacationEditWidget->setMailAction(action, recipient); +} + int VacationDialog::notificationInterval() const { return mVacationEditWidget->notificationInterval(); @@ -208,6 +223,16 @@ mVacationEditWidget->setEndDate(endDate); } +QTime VacationDialog::endTime() const +{ + return mVacationEditWidget->endTime(); +} + +void VacationDialog::setEndTime(const QTime &endTime) +{ + mVacationEditWidget->setEndTime(endTime); +} + QDate VacationDialog::startDate() const { return mVacationEditWidget->startDate(); @@ -217,3 +242,13 @@ { mVacationEditWidget->setStartDate(startDate); } + +QTime VacationDialog::startTime() const +{ + return mVacationEditWidget->startTime(); +} + +void VacationDialog::setStartTime(const QTime &startTime) +{ + mVacationEditWidget->setStartTime(startTime); +} Index: libksieve/src/ksieveui/vacation/vacationeditwidget.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationeditwidget.h +++ libksieve/src/ksieveui/vacation/vacationeditwidget.h @@ -19,6 +19,13 @@ #define VACATIONEDITWIDGET_H #include +#include "vacationutils.h" +class KDateComboBox; +class KTimeComboBox; + +class QComboBox; +class QDate; +class QTime; class QLabel; class QSpinBox; @@ -83,13 +90,24 @@ QDate startDate() const; void setStartDate(const QDate &startDate); + QTime startTime() const; + void setStartTime(const QTime &startTime); + QDate endDate() const; void setEndDate(const QDate &endDate); + QTime endTime() const; + void setEndTime(const QTime &endTime); + + VacationUtils::MailAction mailAction() const; + QString mailActionRecipient() const; + void setMailAction(VacationUtils::MailAction action, const QString &recipient); + void setDefault(); private Q_SLOTS: void slotIntervalSpinChanged(int value); + void mailActionChanged(int index); protected: QCheckBox *mActiveCheck; @@ -100,9 +118,15 @@ QCheckBox *mDomainCheck; QLineEdit *mDomainEdit; QLineEdit *mSubject; + QComboBox *mMailAction; + QLineEdit *mMailActionRecipient; KDateComboBox *mStartDate; + KTimeComboBox *mStartTime; + QCheckBox *mStartTimeActive; QLabel *mStartDateLabel; KDateComboBox *mEndDate; + KTimeComboBox *mEndTime; + QCheckBox *mEndTimeActive; QLabel *mEndDateLabel; }; } Index: libksieve/src/ksieveui/vacation/vacationeditwidget.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationeditwidget.cpp +++ libksieve/src/ksieveui/vacation/vacationeditwidget.cpp @@ -20,17 +20,21 @@ #include #include +#include #include #include +#include "libksieve_debug.h" + #include #include #include #include #include #include +#include using KMime::Types::AddrSpecList; using KMime::Types::AddressList; @@ -75,22 +79,51 @@ glay->addWidget(tmpLabel, row, 0); glay->addWidget(mSubject, row, 1); + QHBoxLayout *timeLayout = new QHBoxLayout(this); // Start date - ++row; mStartDate = new KDateComboBox(this); mStartDate->setObjectName(QStringLiteral("mStartDate")); mStartDate->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords); + + mStartTime = new KTimeComboBox(this); + mStartTime->setObjectName(QStringLiteral("mStartTime")); + mStartTime->setOptions(KTimeComboBox::EditTime | KTimeComboBox::SelectTime | KTimeComboBox::EditTime | KTimeComboBox::WarnOnInvalid); + mStartTime->setEnabled(false); // Disable by default - we need an extension to support this + + mStartTimeActive = new QCheckBox(this); + connect(mStartTimeActive, &QCheckBox::toggled, mStartTime, &KTimeComboBox::setEnabled); + + timeLayout->addWidget(mStartDate); + timeLayout->addWidget(mStartTimeActive); + timeLayout->addWidget(mStartTime); + + ++row; mStartDateLabel = new QLabel(i18n("&Start date:"), this); mStartDateLabel->setObjectName(QStringLiteral("mStartDateLabel")); mStartDateLabel->setBuddy(mStartDate); glay->addWidget(mStartDateLabel, row, 0); - glay->addWidget(mStartDate, row, 1); + glay->addLayout(timeLayout, row, 1); // End date - ++row; + timeLayout = new QHBoxLayout(this); + mEndDate = new KDateComboBox(this); mEndDate->setObjectName(QStringLiteral("mEndDate")); mEndDate->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords); + + mEndTime = new KTimeComboBox(this); + mEndTime->setObjectName(QStringLiteral("mEndTime")); + mEndTime->setOptions(KTimeComboBox::EditTime | KTimeComboBox::SelectTime | KTimeComboBox::EditTime | KTimeComboBox::WarnOnInvalid); + mEndTime->setEnabled(false); // Disable by default - we need an extension to support this + + mEndTimeActive = new QCheckBox(this); + connect(mEndTimeActive, &QCheckBox::toggled, mEndTime, &KTimeComboBox::setEnabled); + + timeLayout->addWidget(mEndDate); + timeLayout->addWidget(mEndTimeActive); + timeLayout->addWidget(mEndTime); + + ++row; mEndDateLabel = new QLabel(i18n("&End date:"), this); mEndDateLabel->setObjectName(QStringLiteral("mStartDateLabel")); mEndDateLabel->setBuddy(mEndDate); @@ -127,6 +160,30 @@ glay->addWidget(tmpLabel, row, 0); glay->addWidget(mMailAliasesEdit, row, 1); + // Action for incomming mails + mMailAction = new QComboBox(this); + for (int i=0; i<4; i++) { + mMailAction->addItem(VacationUtils::mailAction((VacationUtils::MailAction) i)); + } + mMailAction->setObjectName(QStringLiteral("mMailAction")); + connect(mMailAction, static_cast(&QComboBox::currentIndexChanged), this, &VacationEditWidget::mailActionChanged); + + mMailActionRecipient = new QLineEdit(this); + mMailActionRecipient->setObjectName(QStringLiteral("mMailActionRecipient")); + mMailActionRecipient->setClearButtonEnabled(true); + mMailActionRecipient->setEnabled(false); + + QHBoxLayout *hLayout = new QHBoxLayout(this); + + hLayout->addWidget(mMailAction); + hLayout->addWidget(mMailActionRecipient); + + ++row; + tmpLabel = new QLabel(i18n("&Action for incomming mails:"), this); + tmpLabel->setBuddy(mMailAction); + glay->addWidget(tmpLabel, row, 0); + glay->addLayout(hLayout, row, 1); + // "Send responses also to SPAM mail" checkbox: ++row; mSpamCheck = new QCheckBox(i18n("Do not send vacation replies to spam messages"), this); @@ -271,6 +328,22 @@ mEndDate->setDate(endDate); } +QTime VacationEditWidget::endTime() const +{ + if (mEndTime->isEnabled()) { + return mEndTime->time(); + } else { + return QTime(); + } +} + +void VacationEditWidget::setEndTime(const QTime &endTime) +{ + mEndTimeActive->setChecked(endTime.isValid()); + mEndTime->setEnabled(endTime.isValid()); + mEndTime->setTime(endTime); +} + QDate VacationEditWidget::startDate() const { if (mStartDate->isEnabled()) { @@ -285,6 +358,22 @@ mStartDate->setDate(startDate); } +QTime VacationEditWidget::startTime() const +{ + if (mStartTime->isEnabled()) { + return mStartTime->time(); + } else { + return QTime(); + } +} + +void VacationEditWidget::setStartTime(const QTime &startTime) +{ + mStartTimeActive->setChecked(startTime.isValid()); + mStartTime->setEnabled(startTime.isValid()); + mStartTime->setTime(startTime); +} + void VacationEditWidget::setSubject(const QString &subject) { mSubject->setText(subject); @@ -307,6 +396,29 @@ mEndDateLabel->setVisible(enable); } +void VacationEditWidget::mailActionChanged(int action) +{ + bool enable = (action == VacationUtils::CopyTo || action == VacationUtils::Sendto); + mMailActionRecipient->setEnabled(enable); +} + + +void VacationEditWidget::setMailAction(VacationUtils::MailAction action, const QString &recipient) +{ + mMailAction->setCurrentIndex(action); + mMailActionRecipient->setText(recipient); +} + +VacationUtils::MailAction VacationEditWidget::mailAction() const +{ + return (VacationUtils::MailAction) mMailAction->currentIndex(); +} + +QString VacationEditWidget::mailActionRecipient() const +{ + return mMailActionRecipient->text(); +} + void VacationEditWidget::enableDomainAndSendForSpam(bool enable) { mDomainCheck->setEnabled(enable); @@ -320,8 +432,9 @@ setMessageText(VacationUtils::defaultMessageText()); setSubject(VacationUtils::defaultSubject()); setNotificationInterval(VacationUtils::defaultNotificationInterval()); - setMailAliases(VacationUtils::defaultMailAliases().join(QStringLiteral(", "))); + setMailAliases(VacationUtils::defaultMailAliases()); setSendForSpam(VacationUtils::defaultSendForSpam()); setDomainName(VacationUtils::defaultDomainName()); + setMailAction(VacationUtils::defaultMailAction(), QString()); setDomainCheck(false); } Index: libksieve/src/ksieveui/vacation/vacationmanager.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationmanager.h +++ libksieve/src/ksieveui/vacation/vacationmanager.h @@ -40,12 +40,13 @@ void slotEditVacation(const QString &serverName); Q_SIGNALS: - void updateVacationScriptStatus(bool, const QString &); + void updateVacationScriptStatus(bool active, const QString &serverName); void editVacation(); private Q_SLOTS: void slotDialogCanceled(); void slotDialogOk(); + void slotUpdateVacationScriptStatus(bool active, const QString &serverName); private: VacationManagerPrivate *const d; Index: libksieve/src/ksieveui/vacation/vacationmanager.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationmanager.cpp +++ libksieve/src/ksieveui/vacation/vacationmanager.cpp @@ -22,26 +22,36 @@ #include +#include +#include + using namespace KSieveUi; class KSieveUi::VacationManagerPrivate { public: VacationManagerPrivate(QWidget *parent) : mWidget(parent) + , mMultiImapVacationDialog(Q_NULLPTR) + , mCheckVacation(Q_NULLPTR) + , mQuestionAsked(false) { } + QWidget *mWidget; QPointer mMultiImapVacationDialog; QPointer mCheckVacation; - QWidget *mWidget; + bool mQuestionAsked; }; VacationManager::VacationManager(QWidget *parent) : QObject(parent), d(new KSieveUi::VacationManagerPrivate(parent)) { + d->mCheckVacation = new KSieveUi::MultiImapVacationManager(this); + connect(d->mCheckVacation.data(), &KSieveUi::MultiImapVacationManager::scriptActive, this, &VacationManager::updateVacationScriptStatus); + connect(d->mCheckVacation.data(), &KSieveUi::MultiImapVacationManager::scriptActive, this, &VacationManager::slotUpdateVacationScriptStatus); } VacationManager::~VacationManager() @@ -51,30 +61,38 @@ void VacationManager::checkVacation() { - delete d->mCheckVacation; - - d->mCheckVacation = new KSieveUi::MultiImapVacationManager(this); - connect(d->mCheckVacation.data(), &MultiImapVacationManager::scriptActive, this, &VacationManager::updateVacationScriptStatus); - connect(d->mCheckVacation.data(), &MultiImapVacationManager::requestEditVacation, this, &VacationManager::editVacation); d->mCheckVacation->checkVacation(); } +void VacationManager::slotUpdateVacationScriptStatus(bool active, const QString &serverName) +{ + if (active) { + if (!d->mQuestionAsked) { + d->mQuestionAsked = true; + if ( KMessageBox::questionYesNo( 0, i18n( "There is still an active out-of-office reply configured.\n" + "Do you want to edit it?"), i18n("Out-of-office reply still active"), + KGuiItem( i18n( "Edit"), QLatin1String("document-properties") ), + KGuiItem( i18n("Ignore"), QLatin1String("dialog-cancel") ) ) + == KMessageBox::Yes ) { + slotEditVacation(serverName); + } + } + } +} + + void VacationManager::slotEditVacation(const QString &serverName) { if (d->mMultiImapVacationDialog) { - d->mMultiImapVacationDialog->show(); d->mMultiImapVacationDialog->raise(); d->mMultiImapVacationDialog->activateWindow(); - if (!serverName.isEmpty()) { - d->mMultiImapVacationDialog->switchToServerNamePage(serverName); - } - return; + } else { + d->mMultiImapVacationDialog = new KSieveUi::MultiImapVacationDialog(d->mCheckVacation, d->mWidget); + connect(d->mMultiImapVacationDialog.data(), &KSieveUi::MultiImapVacationDialog::okClicked, this, &VacationManager::slotDialogOk); + connect(d->mMultiImapVacationDialog.data(), &KSieveUi::MultiImapVacationDialog::cancelClicked, this, &VacationManager::slotDialogCanceled); } - - d->mMultiImapVacationDialog = new KSieveUi::MultiImapVacationDialog(d->mWidget); - connect(d->mMultiImapVacationDialog.data(), &KSieveUi::MultiImapVacationDialog::okClicked, this, &VacationManager::slotDialogOk); - connect(d->mMultiImapVacationDialog.data(), &KSieveUi::MultiImapVacationDialog::cancelClicked, this, &VacationManager::slotDialogCanceled); d->mMultiImapVacationDialog->show(); + if (!serverName.isEmpty()) { d->mMultiImapVacationDialog->switchToServerNamePage(serverName); } @@ -96,6 +114,7 @@ QList listJob = d->mMultiImapVacationDialog->listCreateJob(); Q_FOREACH (KSieveUi::VacationCreateScriptJob *job, listJob) { connect(job, &VacationCreateScriptJob::scriptActive, this, &VacationManager::updateVacationScriptStatus); + job->setKep14Support(d->mCheckVacation->kep14Support(job->serverName())); job->start(); } if (d->mMultiImapVacationDialog->isVisible()) { Index: libksieve/src/ksieveui/vacation/vacationpagewidget.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationpagewidget.h +++ libksieve/src/ksieveui/vacation/vacationpagewidget.h @@ -31,6 +31,8 @@ class VacationEditWidget; class VacationWarningWidget; class VacationCreateScriptJob; +class MultiImapVacationManager; +class ParseUserScriptJob; class VacationPageWidget : public QWidget { Q_OBJECT @@ -42,11 +44,15 @@ void setServerName(const QString &serverName); KSieveUi::VacationCreateScriptJob *writeScript(); void setDefault(); + void setVacationManager(MultiImapVacationManager *vacationManager); private Q_SLOTS: - void slotGetResult(KManageSieve::SieveJob *job, bool success, const QString &script, bool active); + void slotGetResult(const QString &serverName, const QStringList &sieveCapabilities, const QString &scriptName, const QString &script, bool active); private: + + void fillWithDefaults(); + enum PageType { Script = 0, ScriptNotSupported = 1 @@ -57,7 +63,7 @@ QStackedWidget *mStackWidget; VacationEditWidget *mVacationEditWidget; VacationWarningWidget *mVacationWarningWidget; - KManageSieve::SieveJob *mSieveJob; + MultiImapVacationManager *mVacationManager; PageType mPageScript; bool mWasActive; bool mHasDateSupport; Index: libksieve/src/ksieveui/vacation/vacationpagewidget.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationpagewidget.cpp +++ libksieve/src/ksieveui/vacation/vacationpagewidget.cpp @@ -20,6 +20,8 @@ #include "vacationwarningwidget.h" #include "vacationcreatescriptjob.h" #include "vacationutils.h" +#include "multiimapvacationmanager.h" +#include #include "sieve-vacation.h" #include @@ -33,10 +35,11 @@ #include #include +#include "libksieve_debug.h" + using namespace KSieveUi; VacationPageWidget::VacationPageWidget(QWidget *parent) : QWidget(parent), - mSieveJob(Q_NULLPTR), mPageScript(Script), mWasActive(false), mHasDateSupport(false) @@ -80,90 +83,97 @@ VacationPageWidget::~VacationPageWidget() { - if (mSieveJob) { - mSieveJob->kill(); - } - mSieveJob = Q_NULLPTR; } void VacationPageWidget::setServerUrl(const QUrl &url) { mUrl = url; mVacationEditWidget->setEnabled(false); - mSieveJob = KManageSieve::SieveJob::get(url); - connect(mSieveJob, &KManageSieve::SieveJob::gotScript, this, &VacationPageWidget::slotGetResult); +} + +void VacationPageWidget::setVacationManager(MultiImapVacationManager *vacationManager) +{ + mVacationManager = vacationManager; + connect(mVacationManager, &MultiImapVacationManager::scriptAvailable, this, &VacationPageWidget::slotGetResult); + mVacationManager->checkVacation(mServerName, mUrl); } void VacationPageWidget::setServerName(const QString &serverName) { mServerName = serverName; } -void VacationPageWidget::slotGetResult(KManageSieve::SieveJob *job, bool success, const QString &script, bool active) +void VacationPageWidget::slotGetResult(const QString &serverName, const QStringList &sieveCapabilities, const QString &scriptName, const QString &script, bool active) { - qCDebug(LIBKSIEVE_LOG) << success - << ", ?," << active << ")" << endl - << "script:" << endl - << script; - mSieveJob = Q_NULLPTR; // job deletes itself after returning from this slot! - - if (mUrl.scheme() == QLatin1String("sieve") && - !job->sieveCapabilities().contains(QStringLiteral("vacation"))) { + Q_ASSERT(mServerName == serverName); + qCDebug(LIBKSIEVE_LOG) << serverName << sieveCapabilities << endl + << scriptName << "(" << active << ")" << endl; + + + if (mUrl.scheme() == QStringLiteral("sieve") && + !sieveCapabilities.contains(QStringLiteral("vacation"))) { mStackWidget->setCurrentIndex(ScriptNotSupported); return; } - mVacationEditWidget->setEnabled(true); - QString messageText = VacationUtils::defaultMessageText(); - QString subject = VacationUtils::defaultSubject(); - int notificationInterval = VacationUtils::defaultNotificationInterval(); - QStringList aliases = VacationUtils::defaultMailAliases(); - bool sendForSpam = VacationUtils::defaultSendForSpam(); - QString domainName = VacationUtils::defaultDomainName(); - QDate startDate = VacationUtils::defaultStartDate(); - QDate endDate = VacationUtils::defaultEndDate(); - if (!success) { - active = false; // default to inactive - } + mUrl = mUrl.adjusted(QUrl::RemoveFilename); + mUrl.setPath(mUrl.path() + scriptName); + + // Whether the server supports the "date" extension + const bool supportsSieveDate = mUrl.scheme() == QStringLiteral("sieve") && sieveCapabilities.contains(QStringLiteral("date")); - if ((!success || !KSieveUi::VacationUtils::parseScript(script, messageText, subject, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate))) { + KSieveUi::VacationUtils::Vacation vacation = KSieveUi::VacationUtils::parseScript(script); + + if (!vacation.isValid() && !script.trimmed().isEmpty()) { mVacationWarningWidget->setVisible(true); } mWasActive = active; - mVacationEditWidget->setActivateVacation(active); - mVacationEditWidget->setMessageText(messageText); - mVacationEditWidget->setSubject(subject); - mVacationEditWidget->setNotificationInterval(notificationInterval); - mVacationEditWidget->setMailAliases(aliases.join(QStringLiteral(", "))); - mVacationEditWidget->setSendForSpam(sendForSpam); - mVacationEditWidget->setDomainName(domainName); + mVacationEditWidget->setEnabled(true); + mVacationEditWidget->setActivateVacation(active && vacation.active); + mVacationEditWidget->setMessageText(vacation.messageText); + mVacationEditWidget->setSubject(vacation.subject); + mVacationEditWidget->setMailAction(vacation.mailAction, vacation.mailActionRecipient); + mVacationEditWidget->setNotificationInterval(vacation.notificationInterval); + mVacationEditWidget->setMailAliases(vacation.aliases); + mVacationEditWidget->setSendForSpam(vacation.sendForSpam); + mVacationEditWidget->setDomainName(vacation.excludeDomain); mVacationEditWidget->enableDomainAndSendForSpam(!VacationSettings::allowOutOfOfficeUploadButNoSettings()); - mHasDateSupport = job->sieveCapabilities().contains(QStringLiteral("date")); - mVacationEditWidget->enableDates(mHasDateSupport); - mVacationEditWidget->setStartDate(mHasDateSupport ? startDate : QDate()); - mVacationEditWidget->setEndDate(mHasDateSupport ? endDate : QDate()); - //Q_EMIT scriptActive( mWasActive, mServerName ); + mVacationEditWidget->enableDates(supportsSieveDate); + if (supportsSieveDate) { + mVacationEditWidget->setStartDate(vacation.startDate); + mVacationEditWidget->setStartTime(vacation.startTime); + mVacationEditWidget->setEndDate(vacation.endDate); + mVacationEditWidget->setEndTime(vacation.endTime); + } } KSieveUi::VacationCreateScriptJob *VacationPageWidget::writeScript() { if (mPageScript == Script) { KSieveUi::VacationCreateScriptJob *createJob = new KSieveUi::VacationCreateScriptJob; createJob->setServerUrl(mUrl); createJob->setServerName(mServerName); - const QString script = VacationUtils::composeScript(mVacationEditWidget->messageText(), - mVacationEditWidget->subject(), - mVacationEditWidget->notificationInterval(), - mVacationEditWidget->mailAliases(), - mVacationEditWidget->sendForSpam(), - mVacationEditWidget->domainName(), - mHasDateSupport ? mVacationEditWidget->startDate() : QDate(), - mHasDateSupport ? mVacationEditWidget->endDate() : QDate()); const bool active = mVacationEditWidget->activateVacation(); + VacationUtils::Vacation vacation; + vacation.valid = true; + vacation.active = active; + vacation.messageText = mVacationEditWidget->messageText(); + vacation.subject = mVacationEditWidget->subject(); + vacation.mailAction = mVacationEditWidget->mailAction(); + vacation.mailActionRecipient = mVacationEditWidget->mailActionRecipient(); + vacation.notificationInterval = mVacationEditWidget->notificationInterval(); + vacation.aliases = mVacationEditWidget->mailAliases(); + vacation.sendForSpam = mVacationEditWidget->sendForSpam(); + vacation.excludeDomain = mVacationEditWidget->domainName(); + vacation.startDate = mVacationEditWidget->startDate(); + vacation.startTime = mVacationEditWidget->startTime(); + vacation.endDate = mVacationEditWidget->endDate(); + vacation.endTime = mVacationEditWidget->endTime(); + const QString script = VacationUtils::composeScript(vacation); createJob->setStatus(active, mWasActive); - //Q_EMIT scriptActive( active, mServerName); + //Q_EMIT scriptActive(active, mServerName); createJob->setScript(script); return createJob; } Index: libksieve/src/ksieveui/vacation/vacationscriptextractor.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationscriptextractor.h +++ libksieve/src/ksieveui/vacation/vacationscriptextractor.h @@ -20,6 +20,7 @@ #include "sieve-vacation.h" #include "util/util.h" +#include "vacationutils.h" #include #include @@ -82,12 +83,12 @@ #ifdef FOREACH #undef FOREACH #endif -#define FOREACH for ( std::vector::const_iterator it = mBuilders.begin(), end = mBuilders.end() ; it != end ; ++it ) (*it)-> - void commandStart(const QString &identifier) Q_DECL_OVERRIDE { - FOREACH commandStart(identifier); +#define FOREACH for (std::vector::const_iterator it = mBuilders.begin(), end = mBuilders.end() ; it != end ; ++it) (*it)-> + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE { + FOREACH commandStart(identifier, lineNumber); } - void commandEnd() Q_DECL_OVERRIDE { - FOREACH commandEnd(); + void commandEnd(int lineNumber) Q_DECL_OVERRIDE { + FOREACH commandEnd(lineNumber); } void testStart(const QString &identifier) Q_DECL_OVERRIDE { FOREACH testStart(identifier); @@ -101,11 +102,11 @@ void testListEnd() Q_DECL_OVERRIDE { FOREACH testListEnd(); } - void blockStart() Q_DECL_OVERRIDE { - FOREACH blockStart(); + void blockStart(int lineNumber) Q_DECL_OVERRIDE { + FOREACH blockStart(lineNumber); } - void blockEnd() Q_DECL_OVERRIDE { - FOREACH blockEnd(); + void blockEnd(int lineNumber) Q_DECL_OVERRIDE { + FOREACH blockEnd(lineNumber); } void hashComment(const QString &comment) Q_DECL_OVERRIDE { FOREACH hashComment(comment); @@ -186,21 +187,24 @@ unsigned int mState; int mNestingDepth; + int mLineNumber; + public: GenericInformationExtractor(const std::vector &nodes) - : KSieve::ScriptBuilder(), mNodes(nodes), mState(0), mNestingDepth(0) {} + : KSieve::ScriptBuilder(), mNodes(nodes), mState(0), mNestingDepth(0), mLineNumber(0) {} const std::map &results() const { return mResults; } private: - void process(BuilderMethod method, const QString &string = QString()) + virtual void process(BuilderMethod method, const QString &string = QString()) { doProcess(method, string); mRecursionGuard.clear(); } + void doProcess(BuilderMethod method, const QString &string) { mRecursionGuard.insert(mState); @@ -218,7 +222,7 @@ } qCDebug(LIBKSIEVE_LOG) << (found ? "found:" : "not found:") << mState << "->" - << (found ? expected.if_found : expected.if_not_found); + << (found ? expected.if_found : expected.if_not_found); mState = found ? expected.if_found : expected.if_not_found ; assert(mState < mNodes.size()); if (found) @@ -229,11 +233,13 @@ doProcess(method, string); } } - void commandStart(const QString &identifier) Q_DECL_OVERRIDE { + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE { + Q_UNUSED(lineNumber) qCDebug(LIBKSIEVE_LOG) ; process(CommandStart, identifier); } - void commandEnd() Q_DECL_OVERRIDE { + void commandEnd(int lineNumber) Q_DECL_OVERRIDE { + Q_UNUSED(lineNumber) qCDebug(LIBKSIEVE_LOG) ; process(CommandEnd); } @@ -253,12 +259,14 @@ qCDebug(LIBKSIEVE_LOG) ; process(TestListEnd); } - void blockStart() Q_DECL_OVERRIDE { + void blockStart(int lineNumber) Q_DECL_OVERRIDE { + Q_UNUSED(lineNumber) qCDebug(LIBKSIEVE_LOG) ; process(BlockStart); ++mNestingDepth; } - void blockEnd() Q_DECL_OVERRIDE { + void blockEnd(int lineNumber) Q_DECL_OVERRIDE { + Q_UNUSED(lineNumber) qCDebug(LIBKSIEVE_LOG) ; --mNestingDepth; process(BlockEnd); @@ -308,35 +316,40 @@ typedef GenericInformationExtractor GIE; static const GenericInformationExtractor::StateNode spamNodes[] = { - { 0, GIE::CommandStart, "if", 1, 0, Q_NULLPTR }, // 0 - { 0, GIE::TestStart, "header", 2, 0, Q_NULLPTR }, // 1 - { 0, GIE::TaggedArgument, "contains", 3, 0, Q_NULLPTR }, // 2 + { 0, GIE::CommandStart, "if", 1, 0, 0 }, // 0 + { 0, GIE::TestStart, "allof", 2, 3, 0 }, // 1 + { 0, GIE::TestListStart, 0, 3, 0, 0 }, // 2 + { 0, GIE::TestStart, "not", 4, 3, 0 }, // 3 + { 0, GIE::TestStart, "header", 5, 3, 0 }, // 4 + { 0, GIE::TaggedArgument, "contains", 6, 0, 0 }, // 5 // accept both string and string-list: - { 0, GIE::StringArgument, "x-spam-flag", 9, 4, "x-spam-flag" }, // 3 - { 0, GIE::StringListArgumentStart, Q_NULLPTR, 5, 0, Q_NULLPTR }, // 4 - { 0, GIE::StringListEntry, "x-spam-flag", 6, 7, "x-spam-flag" }, // 5 - { 0, GIE::StringListEntry, Q_NULLPTR, 6, 8, Q_NULLPTR }, // 6 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 0, 5, Q_NULLPTR }, // 7 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 9, 0, Q_NULLPTR }, // 8 + { 0, GIE::StringArgument, "x-spam-flag", 12, 7, "x-spam-flag" }, // 6 + { 0, GIE::StringListArgumentStart, 0, 8, 0, 0 }, // 7 + { 0, GIE::StringListEntry, "x-spam-flag", 9, 10, "x-spam-flag" }, // 8 + { 0, GIE::StringListEntry, 0, 9, 11, 0 }, // 9 + { 0, GIE::StringListArgumentEnd, 0, 0, 8, 0 }, // 10 + { 0, GIE::StringListArgumentEnd, 0, 12, 0, 0 }, // 11 // accept both string and string-list: - { 0, GIE::StringArgument, "yes", 15, 10, "spam-flag-yes" }, // 9 - { 0, GIE::StringListArgumentStart, Q_NULLPTR, 11, 0, Q_NULLPTR }, // 10 - { 0, GIE::StringListEntry, "yes", 12, 13, "spam-flag-yes" }, // 11 - { 0, GIE::StringListEntry, Q_NULLPTR, 12, 14, Q_NULLPTR }, // 12 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 0, 11, Q_NULLPTR }, // 13 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 15, 0, Q_NULLPTR }, // 14 + { 0, GIE::StringArgument, "yes", 18, 13, "spam-flag-yes" }, // 12 + { 0, GIE::StringListArgumentStart, 0, 14, 0, 0 }, // 13 + { 0, GIE::StringListEntry, "yes", 15, 16, "spam-flag-yes" }, // 14 + { 0, GIE::StringListEntry, 0, 15, 17, 0 }, // 15 + { 0, GIE::StringListArgumentEnd, 0, 0, 14, 0 }, // 16 + { 0, GIE::StringListArgumentEnd, 0, 18, 0, 0 }, // 17 - { 0, GIE::TestEnd, Q_NULLPTR, 16, 0, Q_NULLPTR }, // 15 + { 0, GIE::TestEnd, 0, 21, 20, 0 }, // 18 + { 0, GIE::Any, 0, 21, 0, 0 }, // 19 + { 0, GIE::TestListEnd, 0, 21, 19, 0 }, // 20 // block of command, find "stop", take nested if's into account: - { 0, GIE::BlockStart, Q_NULLPTR, 17, 0, Q_NULLPTR }, // 16 - { 1, GIE::CommandStart, "stop", 20, 19, "stop" }, // 17 - { -1, GIE::Any, Q_NULLPTR, 17, 0, Q_NULLPTR }, // 18 - { 0, GIE::BlockEnd, Q_NULLPTR, 0, 18, Q_NULLPTR }, // 19 + { 0, GIE::BlockStart, 0, 22, 18, 0 }, // 21 + { 1, GIE::CommandStart, "vacation", 24, 22, "vacation" }, // 22 + { 1, GIE::Any, 0, 24, 0, 0 }, // 23 + { 0, GIE::BlockEnd, 0, 25, 23, 0 }, // 24 - { -1, GIE::Any, Q_NULLPTR, 20, 20, Q_NULLPTR }, // 20 end state + { -1, GIE::Any, 0, 25, 25, 0 }, // 25 end state }; static const unsigned int numSpamNodes = sizeof spamNodes / sizeof *spamNodes ; @@ -353,48 +366,49 @@ { return mResults.count(QStringLiteral("x-spam-flag")) && mResults.count(QStringLiteral("spam-flag-yes")) && - mResults.count(QStringLiteral("stop")) ; + mResults.count(QStringLiteral("vacation")) ; } }; // to understand this table, study the output of // libksieve/tests/parsertest // 'if not address :domain :contains ["from"] ["mydomain.org"] { keep; stop; }' static const GenericInformationExtractor::StateNode domainNodes[] = { - { 0, GIE::CommandStart, "if", 1, 0, Q_NULLPTR }, // 0 - { 0, GIE::TestStart, "not", 2, 0, Q_NULLPTR, }, // 1 - { 0, GIE::TestStart, "address", 3, 0, Q_NULLPTR }, // 2 + { 0, GIE::CommandStart, "if", 1, 0, 0 }, // 0 + { 0, GIE::TestStart, "allof", 2, 3, 0 }, // 1 + { 0, GIE::TestListStart, 0, 3, 0, 0 }, // 2 + { 0, GIE::TestStart, "address", 4, 3, 0 }, // 3 // :domain and :contains in arbitrary order: - { 0, GIE::TaggedArgument, "domain", 4, 5, Q_NULLPTR }, // 3 - { 0, GIE::TaggedArgument, "contains", 7, 0, Q_NULLPTR }, // 4 - { 0, GIE::TaggedArgument, "contains", 6, 0, Q_NULLPTR }, // 5 - { 0, GIE::TaggedArgument, "domain", 7, 0, Q_NULLPTR }, // 6 + { 0, GIE::TaggedArgument, "domain", 5, 6, 0 }, // 4 + { 0, GIE::TaggedArgument, "contains", 8, 0, 0 }, // 5 + { 0, GIE::TaggedArgument, "contains", 7, 0, 0 }, // 6 + { 0, GIE::TaggedArgument, "domain", 8, 0, 0 }, // 7 // accept both string and string-list: - { 0, GIE::StringArgument, "from", 13, 8, "from" }, // 7 - { 0, GIE::StringListArgumentStart, Q_NULLPTR, 9, 0, Q_NULLPTR }, // 8 - { 0, GIE::StringListEntry, "from", 10, 11, "from" }, // 9 - { 0, GIE::StringListEntry, Q_NULLPTR, 10, 12, Q_NULLPTR }, // 10 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 0, 9, Q_NULLPTR }, // 11 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 13, 0, Q_NULLPTR }, // 12 + { 0, GIE::StringArgument, "from", 14, 9, "from" }, // 8 + { 0, GIE::StringListArgumentStart, 0, 10, 0, 0 }, // 9 + { 0, GIE::StringListEntry, "from", 11, 12, "from" }, // 10 + { 0, GIE::StringListEntry, 0, 11, 13, 0 }, // 11 + { 0, GIE::StringListArgumentEnd, 0, 0, 10, 0 }, // 12 + { 0, GIE::StringListArgumentEnd, 0, 14, 0, 0 }, // 13 // string: save, string-list: save last - { 0, GIE::StringArgument, Q_NULLPTR, 17, 14, "domainName" }, // 13 - { 0, GIE::StringListArgumentStart, Q_NULLPTR, 15, 0, Q_NULLPTR }, // 14 - { 0, GIE::StringListEntry, Q_NULLPTR, 15, 16, "domainName" }, // 15 - { 0, GIE::StringListArgumentEnd, Q_NULLPTR, 17, 0, Q_NULLPTR }, // 16 + { 0, GIE::StringArgument, 0, 18, 15, "domainName" }, // 14 + { 0, GIE::StringListArgumentStart, 0, 16, 0, 0 }, // 15 + { 0, GIE::StringListEntry, 0, 16, 17, "domainName" }, // 16 + { 0, GIE::StringListArgumentEnd, 0, 18, 0, 0 }, // 17 - { 0, GIE::TestEnd, Q_NULLPTR, 18, 0, Q_NULLPTR }, // 17 - { 0, GIE::TestEnd, Q_NULLPTR, 19, 0, Q_NULLPTR }, // 18 + { 0, GIE::TestEnd, 0, 18, 20, 0 }, // 18 + { 0, GIE::Any, 0, 18, 0, 0 }, // 19 // block of commands, find "stop", take nested if's into account: - { 0, GIE::BlockStart, Q_NULLPTR, 20, 0, Q_NULLPTR }, // 19 - { 1, GIE::CommandStart, "stop", 23, 22, "stop" }, // 20 - { -1, GIE::Any, Q_NULLPTR, 20, 0, Q_NULLPTR }, // 21 - { 0, GIE::BlockEnd, Q_NULLPTR, 0, 21, Q_NULLPTR }, // 22 + { 0, GIE::BlockStart, 0, 21, 19, 0 }, // 20 + { 1, GIE::CommandStart, "vacation", 23, 21, "vacation" }, // 21 + { 1, GIE::Any, 0, 23, 0, 0 }, // 22 + { 0, GIE::BlockEnd, 0, 24, 22, 0 }, // 23 - { -1, GIE::Any, Q_NULLPTR, 23, 23, Q_NULLPTR } // 23 end state + { -1, GIE::Any, 0, 24, 24, 0 } // 24 end state }; static const unsigned int numDomainNodes = sizeof domainNodes / sizeof *domainNodes ; @@ -409,51 +423,61 @@ QString domainName() /*not const, since map::op[] isn't const*/ { - return mResults.count(QStringLiteral("stop")) && mResults.count(QStringLiteral("from")) + return mResults.count(QStringLiteral("vacation")) && mResults.count(QStringLiteral("from")) ? mResults[QStringLiteral("domainName")] : QString(); } }; -// if not allof (currentDate :value "ge" date "YYYY-MM-DD", -// currentDate :value "le" date "YYYY-MM-DD") { keep; stop; } +// if not allof (currentdate :value "ge" date "YYYY-MM-DD", +// currentfate :value "le" date "YYYY-MM-DD) { keep; stop; } static const GenericInformationExtractor::StateNode datesNodes[] = { - { 0, GIE::CommandStart, "if", 1, 0, Q_NULLPTR }, // 0 - { 0, GIE::TestStart, "not", 2, 0, Q_NULLPTR }, // 1 - { 0, GIE::TestStart, "allof", 3, 0, Q_NULLPTR }, // 2 + { 0, GIE::CommandStart, "if", 1, 0, 0 }, // 0 + { 0, GIE::TestStart, "allof", 2, 0, 0 }, // 1 // handle startDate and endDate in arbitrary order - { 0, GIE::TestListStart, Q_NULLPTR, 4, 0, Q_NULLPTR }, // 3 - { 0, GIE::TestStart, "currentdate", 5, 0, Q_NULLPTR }, // 4 - { 0, GIE::TaggedArgument, "value", 6, 0, Q_NULLPTR }, // 5 - { 0, GIE::StringArgument, "ge", 7, 9, Q_NULLPTR }, // 6 - { 0, GIE::StringArgument, "date", 8, 0, Q_NULLPTR }, // 7 - { 0, GIE::StringArgument, Q_NULLPTR, 12, 0, "startDate" }, // 8 - { 0, GIE::StringArgument, "le", 10, 0, Q_NULLPTR }, // 9 - { 0, GIE::StringArgument, "date", 11, 0, Q_NULLPTR }, // 10 - { 0, GIE::StringArgument, Q_NULLPTR, 12, 0, "endDate" }, // 11 - { 0, GIE::TestEnd, Q_NULLPTR, 13, 0, Q_NULLPTR }, // 12 - - { 0, GIE::TestStart, "currentdate", 14, 0, Q_NULLPTR }, // 13 - { 0, GIE::TaggedArgument, "value", 15, 0, Q_NULLPTR }, // 14 - { 0, GIE::StringArgument, "le", 16, 18, Q_NULLPTR }, // 15 - { 0, GIE::StringArgument, "date", 17, 0, Q_NULLPTR }, // 16 - { 0, GIE::StringArgument, Q_NULLPTR, 21, 0, "endDate" }, // 17 - { 0, GIE::StringArgument, "ge", 19, 0, Q_NULLPTR }, // 18 - { 0, GIE::StringArgument, "date", 20, 0, Q_NULLPTR }, // 19 - { 0, GIE::StringArgument, Q_NULLPTR, 21, 0, "startDate" }, // 20 - { 0, GIE::TestEnd, Q_NULLPTR, 22, 0, Q_NULLPTR }, // 21 - { 0, GIE::TestListEnd, Q_NULLPTR, 23, 0, Q_NULLPTR }, // 22 - - { 0, GIE::TestEnd, Q_NULLPTR, 24, 0, Q_NULLPTR }, // 23 - { 0, GIE::TestEnd, Q_NULLPTR, 25, 0, Q_NULLPTR }, // 24 + { 0, GIE::TestListStart, 0, 3, 0, 0 }, // 2 + { 0, GIE::TestStart, "currentdate", 4, 3, 0 }, // 3 + { 0, GIE::TaggedArgument, "value", 5, 4, 0 }, // 4 + { 0, GIE::StringArgument, "ge", 6, 10, 0 }, // 5 + { 0, GIE::StringArgument, "date", 7, 8, 0 }, // 6 + { 0, GIE::StringArgument, 0, 15, 0, "startDate" }, // 7 + { 0, GIE::StringArgument, "iso8601", 9, 0, 0 }, // 8 + { 0, GIE::StringArgument, 0, 15, 0, "startDateTime" },// 9 + { 0, GIE::StringArgument, "le", 11, 0, 0 }, // 10 + { 0, GIE::StringArgument, "date", 12, 13, 0 }, // 11 + { 0, GIE::StringArgument, 0, 15, 0, "endDate" }, // 12 + { 0, GIE::StringArgument, "iso8601", 14, 0, 0 }, // 13 + { 0, GIE::StringArgument, 0, 15, 0, "endDateTime" }, // 14 + { 0, GIE::TestEnd, 0, 16, 0, 0 }, // 15 + + { 0, GIE::TestStart, "currentdate", 17, 16, 0 }, // 16 + { 0, GIE::TaggedArgument, "value", 18, 17, 0 }, // 17 + { 0, GIE::StringArgument, "le", 19, 23, 0 }, // 18 + { 0, GIE::StringArgument, "date", 20, 21, 0 }, // 19 + { 0, GIE::StringArgument, 0, 28, 0, "endDate" }, // 20 + { 0, GIE::StringArgument, "iso8601", 22, 0, 0 }, // 21 + { 0, GIE::StringArgument, 0, 28, 0, "endDateTime" }, // 22 + { 0, GIE::StringArgument, "ge", 24, 0, 0 }, // 23 + { 0, GIE::StringArgument, "date", 25, 26, 0 }, // 24 + { 0, GIE::StringArgument, 0, 28, 0, "startDate" }, // 25 + { 0, GIE::StringArgument, "iso8601", 27, 0, 0 }, // 26 + { 0, GIE::StringArgument, 0, 28, 0, "startDateTime" }, // 27 + { 0, GIE::TestEnd, 0, 32, 0, 0 }, // 28 + { 0, GIE::TestStart, 0, 31, 30, 0 }, // 29 + { -1, GIE::Any, 0, 32, 0, 0 }, // 30 + { 0, GIE::TestEnd, 0, 32, 30, 0 }, // 31 + { 0, GIE::TestListEnd, 0, 33, 29, 0 }, // 32 + + { 0, GIE::TestEnd, 0, 34, 0, 0 }, // 33 // block of commands, find "stop", take nested if's into account: - { 0, GIE::BlockStart, Q_NULLPTR, 26, 0, Q_NULLPTR }, // 25 - { 1, GIE::CommandStart, "stop", 29, 28, "stop" }, // 26 - { -1, GIE::Any, Q_NULLPTR, 26, 0, Q_NULLPTR }, // 27 - { 0, GIE::BlockEnd, Q_NULLPTR, 0, 27, Q_NULLPTR }, // 28 + { 0, GIE::BlockStart, 0, 36, 33, 0 }, // 34 + { -1, GIE::Any, 0, 36, 0, 0 }, // 35 + { 1, GIE::CommandStart, "vacation", 38, 35, "vacation" }, // 36 + { -1, GIE::Any, 0, 38, 0, 0 }, // 37 + { 0, GIE::BlockEnd, 0, 39, 37, 0 }, // 38 - { -1, GIE::Any, Q_NULLPTR, 27, 27, Q_NULLPTR } // 29 end state + { -1, GIE::Any, 0, 39, 39, 0 } // 39 end state }; static const unsigned int numDatesNodes = sizeof datesNodes / sizeof *datesNodes; @@ -466,25 +490,52 @@ { } - QDate endDate() const - { - return date(QStringLiteral("endDate")); - } - - QDate startDate() const - { - return date(QStringLiteral("startDate")); - } + QDate endDate() const + { + if (results().count(QLatin1String("endDateTime")) == 1) { + return datetime(QLatin1String("endDateTime")).date(); + } else { + return date(QLatin1String("endDate")); + } + } + + QDate startDate() const + { + if (results().count(QLatin1String("startDateTime")) == 1) { + return datetime(QLatin1String("startDateTime")).date(); + } else { + return date(QLatin1String("startDate")); + } + } + + QTime endTime() const + { + return datetime(QLatin1String("endDateTime")).time(); + } + + QTime startTime() const + { + return datetime(QLatin1String("startDateTime")).time(); + } private: - QDate date(const QString &name) const - { - if (mResults.count(name) == 0) { - return QDate(); - } else { - return QDate::fromString(mResults.at(name), Qt::ISODate); - } - } + QDate date(const QString &name) const + { + if (results().count(name) == 0) { + return QDate(); + } else { + return QDate::fromString(results().at(name), Qt::ISODate); + } + } + + QDateTime datetime(const QString &name) const + { + if (results().count(name) == 0) { + return QDateTime(); + } else { + return QDateTime::fromString(results().at(name), Qt::ISODate); + } + } }; class VacationDataExtractor : public KSieve::ScriptBuilder @@ -494,12 +545,23 @@ // command itself: VacationCommand, // tagged args: - Days, Addresses, Subject + Days, Addresses, Subject, + VacationEnd, + IfBlock, + RedirectCommand }; public: VacationDataExtractor(); virtual ~VacationDataExtractor(); + bool commandFound() const + { + return mContext == VacationEnd; + } + bool active() const + { + return mActive; + } int notificationInterval() const { return mNotificationInterval; @@ -512,24 +574,46 @@ { return mAliases; } + const QString &ifComment() const + { + return mIfComment; + } + VacationUtils::MailAction mailAction() const + { + return mMailAction; + } + const QString &mailActionRecipient() const + { + return mMailActionRecipient; + } const QString &subject() const { return mSubject; } + int lineStart() const + { + return mLineStart; + } + + int lineEnd() const + { + return mLineEnd; + } + private: - void commandStart(const QString &identifier) Q_DECL_OVERRIDE; + void commandStart(const QString &identifier, int lineNumber) Q_DECL_OVERRIDE; - void commandEnd() Q_DECL_OVERRIDE; + void commandEnd(int lineNumber) Q_DECL_OVERRIDE; - void testStart(const QString &) Q_DECL_OVERRIDE {} + void testStart(const QString &) Q_DECL_OVERRIDE; void testEnd() Q_DECL_OVERRIDE {} void testListStart() Q_DECL_OVERRIDE {} void testListEnd() Q_DECL_OVERRIDE {} - void blockStart() Q_DECL_OVERRIDE {} - void blockEnd() Q_DECL_OVERRIDE {} - void hashComment(const QString &) Q_DECL_OVERRIDE {} + void blockStart(int lineNumber) Q_DECL_OVERRIDE; + void blockEnd(int lineNumber) Q_DECL_OVERRIDE; + void hashComment(const QString &) Q_DECL_OVERRIDE; void bracketComment(const QString &) Q_DECL_OVERRIDE {} void lineFeed() Q_DECL_OVERRIDE {} void error(const KSieve::Error &e) Q_DECL_OVERRIDE; @@ -551,10 +635,70 @@ QString mMessageText; QString mSubject; QStringList mAliases; + bool mActive; + bool mInIfBlock; + bool mFoundInBlock; + int mBlockLevel; + QString mIfComment; + int mLineStart; + int mLineEnd; + + VacationUtils::MailAction mMailAction; + Context mMailActionContext; + QString mMailActionRecipient; void reset(); }; +class RequireExtractor : public KSieve::ScriptBuilder { + enum Context { + None = 0, + // command itself: + RequireCommand, + EndState + }; +public: + RequireExtractor(); + virtual ~RequireExtractor(); + + bool commandFound() const { return mContext == EndState; } + const QStringList &requirements() const { return mRequirements; } + + int lineStart() const {return mLineStart;} + int lineEnd() const {return mLineEnd;} + +private: + void commandStart(const QString & identifier, int lineNumber); + + void commandEnd(int lineNumber); + + void testStart(const QString &) {} + void testEnd() {} + void testListStart() {} + void testListEnd() {} + void blockStart(int lineNumber) { Q_UNUSED(lineNumber) } + void blockEnd(int lineNumber) { Q_UNUSED(lineNumber) } + void hashComment(const QString &) {} + void bracketComment(const QString &) {} + void lineFeed() {} + void error(const KSieve::Error & e); + void finished(); + + void taggedArgument(const QString & tag) { Q_UNUSED(tag) } + void numberArgument(unsigned long number, char) { Q_UNUSED(number) } + + void stringArgument(const QString & string, bool, const QString &); + + void stringListArgumentStart(){} + void stringListEntry(const QString & string, bool, const QString &); + void stringListArgumentEnd(){} + +private: + Context mContext; + QStringList mRequirements; + int mLineStart; + int mLineEnd; +}; } #endif // VACATIONSCRIPTEXTRACTOR_H Index: libksieve/src/ksieveui/vacation/vacationscriptextractor.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationscriptextractor.cpp +++ libksieve/src/ksieveui/vacation/vacationscriptextractor.cpp @@ -24,6 +24,14 @@ : KSieve::ScriptBuilder(), mContext(None), mNotificationInterval(0) + , mActive(true) + , mInIfBlock(false) + , mFoundInBlock(false) + , mBlockLevel(0) + , mLineStart(0) + , mLineEnd(0) + , mMailAction(VacationUtils::Keep) + , mMailActionContext(None) { qCDebug(LIBKSIEVE_LOG); } @@ -33,20 +41,45 @@ } -void VacationDataExtractor::commandStart(const QString &identifier) +void VacationDataExtractor::commandStart(const QString &identifier, int lineNumber) { - qCDebug(LIBKSIEVE_LOG) << "( \"" << identifier << "\" )"; - if (identifier != QLatin1String("vacation")) { + qCDebug(LIBKSIEVE_LOG) << "(\"" << identifier << "\")"; + if (identifier == QStringLiteral("if") && mContext == None) { + mContext = IfBlock; + mLineStart = lineNumber; + mInIfBlock = true; + } + + if (commandFound() && (!mFoundInBlock || mBlockLevel > 0)) { + if (identifier == QStringLiteral("discard")) { + mMailAction = VacationUtils::Discard; + } else if (identifier == QStringLiteral("redirect")) { + mMailAction = VacationUtils::Sendto; + mMailActionContext = RedirectCommand; + } + } + + if (identifier != QStringLiteral("vacation")) { return; } + + if (mContext != IfBlock) { + mLineStart = lineNumber; + } + reset(); mContext = VacationCommand; + mFoundInBlock = (mBlockLevel > 0); } -void VacationDataExtractor::commandEnd() +void VacationDataExtractor::commandEnd(int lineNumber) { qCDebug(LIBKSIEVE_LOG); - mContext = None; + if (mContext != None && mContext != IfBlock && mContext != VacationEnd) { + mContext = VacationEnd; + mLineEnd = lineNumber; + } + mMailActionContext = None; } void VacationDataExtractor::error(const KSieve::Error &e) @@ -59,24 +92,65 @@ } +void VacationDataExtractor::testStart(const QString &test) +{ + if (mContext == IfBlock) { + if (test == QStringLiteral("true") || test == QStringLiteral("false")) { + mActive = (test == QStringLiteral("true")); + mIfComment = QString(); + } + } +} + +void VacationDataExtractor::hashComment(const QString &comment) +{ + if (mContext == IfBlock) { + mIfComment += comment; + } +} + + +void VacationDataExtractor::blockStart(int lineNumber) +{ + Q_UNUSED(lineNumber) + mBlockLevel++; +} + +void VacationDataExtractor::blockEnd(int lineNumber) +{ + mBlockLevel--; + if(mBlockLevel == 0 && !commandFound()) { //We are in main level again, and didn't found vacation in block + mActive = true; + mIfComment = QString(); + } else if (mInIfBlock && mBlockLevel == 0 && commandFound()) { + mLineEnd = lineNumber; + mInIfBlock = false; + } +} + void VacationDataExtractor::taggedArgument(const QString &tag) { - qCDebug(LIBKSIEVE_LOG) << "( \"" << tag << "\" )"; + qCDebug(LIBKSIEVE_LOG) << "(\"" << tag << "\")"; + if (mMailActionContext == RedirectCommand) { + if (tag == QStringLiteral("copy")) { + mMailAction = VacationUtils::CopyTo; + } + } if (mContext != VacationCommand) { return; } - if (tag == QLatin1String("days")) { + if (tag == QStringLiteral("days")) { mContext = Days; - } else if (tag == QLatin1String("addresses")) { + } else if (tag == QStringLiteral("addresses")) { mContext = Addresses; - } else if (tag == QLatin1String("subject")) { + } else if (tag == QStringLiteral("subject")) { mContext = Subject; } } void VacationDataExtractor::stringArgument(const QString &string, bool, const QString &) { - qCDebug(LIBKSIEVE_LOG) << "( \"" << string << "\" )"; + qCDebug(LIBKSIEVE_LOG) << "(\"" << string << "\")"; if (mContext == Addresses) { mAliases.push_back(string); mContext = VacationCommand; @@ -87,11 +161,14 @@ mMessageText = string; mContext = VacationCommand; } + if (mMailActionContext == RedirectCommand) { + mMailActionRecipient = string; + } } void VacationDataExtractor::numberArgument(unsigned long number, char) { - qCDebug(LIBKSIEVE_LOG) << "( \"" << number << "\" )"; + qCDebug(LIBKSIEVE_LOG) << "(\"" << number << "\")"; if (mContext != Days) { return; } @@ -109,7 +186,7 @@ } void VacationDataExtractor::stringListEntry(const QString &string, bool, const QString &) { - qCDebug(LIBKSIEVE_LOG) << "( \"" << string << "\" )"; + qCDebug(LIBKSIEVE_LOG) << "(\"" << string << "\")"; if (mContext != Addresses) { return; } @@ -129,7 +206,59 @@ { qCDebug(LIBKSIEVE_LOG); mContext = None; + mMailAction = VacationUtils::Keep; + mMailActionRecipient = QString(); mNotificationInterval = 0; mAliases.clear(); mMessageText.clear(); } + +RequireExtractor::RequireExtractor() + : KSieve::ScriptBuilder() + , mContext(None) + , mLineStart(0) + , mLineEnd(0) +{ + +} + +RequireExtractor::~RequireExtractor() +{ + +} + +void RequireExtractor::commandStart(const QString &identifier, int lineNumber) +{ + if (identifier == QStringLiteral("require") && mContext == None) { + mContext = RequireCommand; + mLineStart = lineNumber; + } +} + +void RequireExtractor::commandEnd(int lineNumber) +{ + if (mContext == RequireCommand) { + mContext = EndState; + mLineEnd = lineNumber; + } +} + +void RequireExtractor::error(const KSieve::Error &e) +{ + qCDebug(LIBKSIEVE_LOG) << e.asString() << "@" << e.line() << "," << e.column(); +} + +void RequireExtractor::finished() +{ + +} + +void RequireExtractor::stringArgument(const QString &string, bool, const QString &) +{ + mRequirements << string; +} + +void RequireExtractor::stringListEntry(const QString &string, bool, const QString &) +{ + mRequirements << string; +} Index: libksieve/src/ksieveui/vacation/vacationutils.h =================================================================== --- libksieve/src/ksieveui/vacation/vacationutils.h +++ libksieve/src/ksieveui/vacation/vacationutils.h @@ -19,42 +19,72 @@ #define VACATIONUTILS_H #include #include +#include class QDate; -namespace KMime -{ -namespace Types -{ -struct AddrSpec; -typedef QVector AddrSpecList; -} -} - namespace KSieveUi { namespace VacationUtils { + +enum MailAction { + Keep, + Discard, + Sendto, + CopyTo, +}; + QString defaultMessageText(); QString defaultSubject(); +MailAction defaultMailAction(); int defaultNotificationInterval(); -QStringList defaultMailAliases(); +KMime::Types::AddrSpecList defaultMailAliases(); bool defaultSendForSpam(); QString defaultDomainName(); QDate defaultStartDate(); QDate defaultEndDate(); -QString composeScript(const QString &messageText, - const QString &subject, - int notificationInterval, - const KMime::Types::AddrSpecList &aliases, - bool sendForSpam, const QString &excludeDomain, - const QDate &startDate, const QDate &endDate); -bool parseScript(const QString &script, QString &messageText, - QString &subject, - int ¬ificationInterval, QStringList &aliases, - bool &sendForSpam, QString &domainName, - QDate &startDate, QDate &endDate); +struct Vacation { + Vacation() + : notificationInterval(1) + , mailAction(Keep) + , valid(false) + , active(false) + , sendForSpam(true) + { + } + + bool isValid() const + { + return valid; + } + + QString mailActionRecipient; + QString messageText; + QString subject; + KMime::Types::AddrSpecList aliases; + QString excludeDomain; + QDate startDate; + QTime startTime; + QDate endDate; + QTime endTime; + int notificationInterval; + MailAction mailAction; + bool valid; + bool active; + bool sendForSpam; +}; + +QString composeScript(const Vacation &vacation); + +KSieveUi::VacationUtils::Vacation parseScript(const QString &script); + +QString mergeRequireLine(const QString &script1, const QString script2); + +QString updateVacationBlock(const QString &oldScript, const QString &newScript); + +QString mailAction(MailAction action); } } Index: libksieve/src/ksieveui/vacation/vacationutils.cpp =================================================================== --- libksieve/src/ksieveui/vacation/vacationutils.cpp +++ libksieve/src/ksieveui/vacation/vacationutils.cpp @@ -31,9 +31,9 @@ static inline QString dotstuff(QString s) // krazy:exclude=passbyvalue { if (s.startsWith(QLatin1Char('.'))) { - return QLatin1Char('.') + s.replace(QLatin1String("\n."), QStringLiteral("\n..")); + return QLatin1Char('.') + s.replace(QStringLiteral("\n."), QStringLiteral("\n..")); } else { - return s.replace(QLatin1String("\n."), QStringLiteral("\n..")); + return s.replace(QStringLiteral("\n."), QStringLiteral("\n..")); } } @@ -48,7 +48,29 @@ return i18n("Out of office till %1", QLocale().toString(QDate::currentDate().addDays(1))); } -QString VacationUtils::defaultMessageText() +QString KSieveUi::VacationUtils::mailAction(KSieveUi::VacationUtils::MailAction action) +{ + switch (action) { + case Keep: + return i18n("Keep"); + case Discard: + return i18n("Discard"); + case Sendto: + return i18n("Redirect to"); + case CopyTo: + return i18n("Copy to"); + default: + qCWarning(LIBKSIEVE_LOG) << "Unknown mail action" << action; + return i18n("Unkown action"); + } +} + +KSieveUi::VacationUtils::MailAction KSieveUi::VacationUtils::defaultMailAction() +{ + return KSieveUi::VacationUtils::Keep; +} + +QString KSieveUi::VacationUtils::defaultMessageText() { return i18n("I am out of office till %1.\n" "\n" @@ -68,17 +90,24 @@ return 7; // days } -QStringList VacationUtils::defaultMailAliases() +KMime::Types::AddrSpecList VacationUtils::defaultMailAliases() { - QStringList sl; + KMime::Types::AddrSpecList sl; KIdentityManagement::IdentityManager manager(true); KIdentityManagement::IdentityManager::ConstIterator end(manager.end()); for (KIdentityManagement::IdentityManager::ConstIterator it = manager.begin(); it != end ; ++it) { if (!(*it).primaryEmailAddress().isEmpty()) { - sl.push_back((*it).primaryEmailAddress()); + KMime::Types::Mailbox a; + a.fromUnicodeString((*it).primaryEmailAddress()); + sl.push_back(a.addrSpec()); + } + foreach(const QString &email, (*it).emailAliases()) { + KMime::Types::Mailbox a; + a.fromUnicodeString(email); + sl.push_back(a.addrSpec()); } - sl += (*it).emailAliases(); } + return sl; } @@ -102,20 +131,20 @@ return defaultStartDate().addDays(7); } -bool VacationUtils::parseScript(const QString &script, QString &messageText, - QString &subject, - int ¬ificationInterval, QStringList &aliases, - bool &sendForSpam, QString &domainName, - QDate &startDate, QDate &endDate) +VacationUtils::Vacation VacationUtils::parseScript(const QString &script) { + KSieveUi::VacationUtils::Vacation vacation; if (script.trimmed().isEmpty()) { - messageText = VacationUtils::defaultMessageText(); - subject = VacationUtils::defaultSubject(); - notificationInterval = VacationUtils::defaultNotificationInterval(); - aliases = VacationUtils::defaultMailAliases(); - sendForSpam = VacationUtils::defaultSendForSpam(); - domainName = VacationUtils::defaultDomainName(); - return true; + vacation.valid = false; + vacation.active = false; + vacation.mailAction = VacationUtils::defaultMailAction(); + vacation.messageText = VacationUtils::defaultMessageText(); + vacation.subject = VacationUtils::defaultSubject(); + vacation.notificationInterval = VacationUtils::defaultNotificationInterval(); + vacation.aliases = VacationUtils::defaultMailAliases(); + vacation.sendForSpam = VacationUtils::defaultSendForSpam(); + vacation.excludeDomain = VacationUtils::defaultDomainName(); + return vacation; } // The trimmed() call below prevents parsing errors. The @@ -128,28 +157,53 @@ VacationDataExtractor vdx; SpamDataExtractor sdx; DomainRestrictionDataExtractor drdx; - DateExtractor dtx; - KSieveExt::MultiScriptBuilder tsb(&vdx, &sdx, &drdx, &dtx); + DateExtractor dx; + KSieveExt::MultiScriptBuilder tsb(&vdx , &sdx, &drdx, &dx); parser.setScriptBuilder(&tsb); - if (!parser.parse()) { - return false; + parser.parse(); + if (!parser.parse() || !vdx.commandFound()) { + vacation.active = false; + vacation.valid = false; + return vacation; } - messageText = vdx.messageText().trimmed(); + vacation.valid = true; + vacation.active = vdx.active(); + vacation.mailAction = vdx.mailAction(); + vacation.mailActionRecipient = vdx.mailActionRecipient(); + vacation.messageText = vdx.messageText().trimmed(); if (!vdx.subject().isEmpty()) { - subject = vdx.subject().trimmed(); + vacation.subject = vdx.subject().trimmed(); + } + vacation.notificationInterval = vdx.notificationInterval(); + vacation.aliases = KMime::Types::AddrSpecList(); + foreach(const QString &alias, vdx.aliases()) { + KMime::Types::Mailbox a; + a.fromUnicodeString(alias); + vacation.aliases.append(a.addrSpec()); } - notificationInterval = vdx.notificationInterval(); - aliases = vdx.aliases(); - if (!VacationSettings::allowOutOfOfficeUploadButNoSettings()) { - sendForSpam = !sdx.found(); - domainName = drdx.domainName(); + + if (!vacation.active && !vdx.ifComment().isEmpty()) { + const QByteArray newScript = QString::fromAscii("if ").toUtf8() + vdx.ifComment().toUtf8() + QStringLiteral("{vacation;}").toUtf8(); + tsb = KSieveExt::MultiScriptBuilder(&sdx, &drdx, &dx); + KSieve::Parser parser(newScript.begin(), + newScript.begin() + newScript.length()); + parser.setScriptBuilder(&tsb); + if (!parser.parse()) { + vacation.valid = false; + return vacation; + } } - startDate = dtx.startDate(); - endDate = dtx.endDate(); - return true; + + vacation.sendForSpam = !sdx.found(); + vacation.excludeDomain = drdx.domainName(); + vacation.startDate = dx.startDate(); + vacation.startTime = dx.startTime(); + vacation.endDate = dx.endDate(); + vacation.endTime = dx.endTime(); + return vacation; } -QString VacationUtils::composeScript(const QString &messageText, +QString composeOldScript(const QString & messageText, const QString &subject, int notificationInterval, const AddrSpecList &addrSpecs, @@ -159,24 +213,23 @@ QString addressesArgument; QStringList aliases; if (!addrSpecs.empty()) { - addressesArgument += QLatin1String(":addresses [ "); + addressesArgument += QStringLiteral(":addresses [ "); QStringList sl; AddrSpecList::const_iterator end = addrSpecs.constEnd(); for (AddrSpecList::const_iterator it = addrSpecs.begin() ; it != end; ++it) { sl.push_back(QLatin1Char('"') + (*it).asString().replace(QLatin1Char('\\'), QStringLiteral("\\\\")).replace(QLatin1Char('"'), QStringLiteral("\\\"")) + QLatin1Char('"')); aliases.push_back((*it).asString()); } - addressesArgument += sl.join(QStringLiteral(", ")) + QLatin1String(" ] "); + addressesArgument += sl.join(QStringLiteral(", ")) + QStringLiteral(" ] "); } - QString script = QStringLiteral("require \"vacation\";\n"); + QString script; + if (startDate.isValid() && endDate.isValid()) { - script += QStringLiteral("require \"relational\";\n" - "require \"date\";\n\n"); + script = QStringLiteral("require [\"vacation\", \"relational\", \"date\"];\n\n"); } else { - script += QStringLiteral("\n"); + script = QStringLiteral("require \"vacation\";\n\n"); } - if (!sendForSpam) script += QStringLiteral("if header :contains \"X-Spam-Flag\" \"YES\"" " { keep; stop; }\n"); // FIXME? @@ -192,7 +245,14 @@ endDate.toString(Qt::ISODate)); } - script += QLatin1String("vacation "); + if (startDate.isValid() && endDate.isValid()) { + script += QStringLiteral("if not allof(currentdate :value \"ge\" \"date\" \"%1\"," + " currentdate :value \"le\" \"date\" \"%2\")" + " { keep; stop; }\n").arg(startDate.toString(Qt::ISODate), + endDate.toString(Qt::ISODate)); + } + + script += QStringLiteral("vacation "); script += addressesArgument; if (notificationInterval > 0) { script += QStringLiteral(":days %1 ").arg(notificationInterval); @@ -208,3 +268,217 @@ return script; } +QString KSieveUi::VacationUtils::composeScript(const Vacation &vacation) +{ + QStringList condition; + QStringList require; + + require << QStringLiteral("vacation"); + + if (vacation.startDate.isValid() || vacation.endDate.isValid()) { + require << QStringLiteral("date"); + require << QStringLiteral("relational"); + } + + if (vacation.startDate.isValid()) { + if (vacation.startTime.isValid()) { + QDateTime start(vacation.startDate, vacation.startTime); + condition.append(QStringLiteral("currentdate :value \"ge\" \"iso8601\" \"%1\"") + .arg(start.toString(Qt::ISODate))); + } else { + condition.append(QStringLiteral("currentdate :value \"ge\" \"date\" \"%1\"") + .arg(vacation.startDate.toString(Qt::ISODate))); + } + } + + if (vacation.endDate.isValid()) { + if (vacation.endTime.isValid()) { + QDateTime end(vacation.endDate, vacation.endTime); + condition.append(QStringLiteral("currentdate :value \"le\" \"iso8601\" \"%1\"") + .arg(end.toString(Qt::ISODate))); + } else { + condition.append(QStringLiteral("currentdate :value \"le\" \"date\" \"%1\"") + .arg(vacation.endDate.toString(Qt::ISODate))); + } + } + + if (!vacation.sendForSpam) { + condition.append(QStringLiteral("not header :contains \"X-Spam-Flag\" \"YES\"")); + } + + if (!vacation.excludeDomain.isEmpty()) { + condition.append(QStringLiteral("address :domain :contains \"from\" \"%1\"").arg(vacation.excludeDomain)); + } + + QString addressesArgument; + QStringList aliases; + if (!vacation.aliases.empty()) { + addressesArgument += QStringLiteral(":addresses [ "); + QStringList sl; + AddrSpecList::const_iterator end = vacation.aliases.constEnd(); + for (AddrSpecList::const_iterator it = vacation.aliases.begin() ; it != end; ++it) { + sl.push_back(QLatin1Char('"') + (*it).asString().replace(QLatin1Char('\\'), QStringLiteral("\\\\")).replace(QLatin1Char('"'), QStringLiteral("\\\"")) + QLatin1Char('"')); + aliases.push_back((*it).asString()); + } + addressesArgument += sl.join(QStringLiteral(", ")) + QStringLiteral(" ] "); + } + + QString sVacation(QStringLiteral("vacation ")); + sVacation += addressesArgument; + if (vacation.notificationInterval > 0) + sVacation += QStringLiteral(":days %1 ").arg(vacation.notificationInterval); + + if (!vacation.subject.trimmed().isEmpty()) { + sVacation += QStringLiteral(":subject \"%1\" ").arg(stringReplace(vacation.subject).trimmed()); + } + + sVacation += QStringLiteral("text:\n"); + sVacation += dotstuff(vacation.messageText.isEmpty() ? VacationUtils::defaultMessageText() : vacation.messageText); + sVacation += QStringLiteral("\n.\n;"); + + switch (vacation.mailAction) { + case VacationUtils::Keep: + break; + case VacationUtils::Discard: + sVacation += QStringLiteral("\ndiscard;"); + break; + case VacationUtils::Sendto: + sVacation += QStringLiteral("\nredirect \"") + vacation.mailActionRecipient + QStringLiteral("\";"); + break; + case VacationUtils::CopyTo: + require << QStringLiteral("copy"); + sVacation += QStringLiteral("\nredirect :copy \"") + vacation.mailActionRecipient + QStringLiteral("\";"); + break; + } + + QString script; + + script = QStringLiteral("require [\"%1\"];\n\n"); + script = script.arg(require.join(QStringLiteral("\", \""))); + + if (condition.count() == 0) { + if (vacation.active) { + script += sVacation; + } else { + script += QStringLiteral("if false\n{\n\t"); + script += sVacation; + script += QStringLiteral("\n}"); + } + } else { + if (vacation.active) { + script += QStringLiteral("if allof(%1)\n{\n\t").arg(condition.join(QStringLiteral(", "))); + } else { + script += QStringLiteral("if false # allof(%1)\n{\n\t").arg(condition.join(QStringLiteral(", "))); + } + script += sVacation; + script += QStringLiteral("\n}"); + } + + script += QStringLiteral("\n"); + + return script; +} + + +QString KSieveUi::VacationUtils::mergeRequireLine(const QString &script, const QString scriptUpdate) +{ + const QByteArray scriptUTF8 = script.trimmed().toUtf8(); + const QByteArray scriptUpdateUTF8 = scriptUpdate.trimmed().toUtf8(); + + if (scriptUTF8.isEmpty()) { + return scriptUpdate; + } + + if (scriptUpdateUTF8.isEmpty()) { + return script; + } + + KSieve::Parser parser(scriptUTF8.begin(), + scriptUTF8.begin() + scriptUTF8.length()); + KSieve::Parser parserUpdate(scriptUpdateUTF8.begin(), + scriptUpdateUTF8.begin() + scriptUpdateUTF8.length()); + RequireExtractor rx, rxUpdate; + parser.setScriptBuilder(&rx); + parserUpdate.setScriptBuilder(&rxUpdate); + + int insert(0); + QStringList lines = script.split(QLatin1Char('\n')); + QSet requirements; + + if (parser.parse() && rx.commandFound()) { + insert = rx.lineStart(); + const int endOld(rx.lineEnd()); + for (int i=insert; i<=endOld; i++) { + lines.removeAt(insert); + } + requirements = rx.requirements().toSet(); + } + + if (parserUpdate.parse() && rxUpdate.commandFound()) { + requirements += rxUpdate.requirements().toSet(); + } + + if (requirements.count() > 1) { + QStringList req = requirements.toList(); + req.sort(); + lines.insert(insert, QStringLiteral("require [\"%1\"];").arg(req.join(QStringLiteral("\", \"")))); + } else if (requirements.count() == 1) { + lines.insert(insert, QStringLiteral("require \"%1\";").arg(requirements.toList().first())); + } + + return lines.join(QStringLiteral("\n")); +} + +QString KSieveUi::VacationUtils::updateVacationBlock(const QString &oldScript, const QString &newScript) +{ + const QByteArray oldScriptUTF8 = oldScript.trimmed().toUtf8(); + const QByteArray newScriptUTF8 = newScript.trimmed().toUtf8(); + + if (oldScriptUTF8.isEmpty()) { + return newScript; + } + + if (newScriptUTF8.isEmpty()) { + return oldScript; + } + + KSieve::Parser parserOld(oldScriptUTF8.begin(), + oldScriptUTF8.begin() + oldScriptUTF8.length()); + KSieve::Parser parserNew(newScriptUTF8.begin(), + newScriptUTF8.begin() + newScriptUTF8.length()); + VacationDataExtractor vdxOld, vdxNew; + RequireExtractor rx; + KSieveExt::MultiScriptBuilder tsb(&vdxOld , &rx); + parserOld.setScriptBuilder(&tsb); + parserNew.setScriptBuilder(&vdxNew); + + int startOld(0); + + QStringList lines = oldScript.split(QLatin1Char('\n')); + + QString script; + if (parserOld.parse() && vdxOld.commandFound()) { + startOld = vdxOld.lineStart(); + const int endOld(vdxOld.lineEnd()); + for (int i=startOld; i<=endOld; i++) { + lines.removeAt(startOld); + } + } else { + if (rx.commandFound()) { // after require + startOld = rx.lineEnd() + 1; + } else { + startOld = 0; + } + } + + if (parserNew.parse() && vdxNew.commandFound()) { + const int startNew(vdxNew.lineStart()); + const int endNew(vdxNew.lineEnd()); + QStringList linesNew = newScript.split(QLatin1Char('\n')); + for(int i=endNew;i>=startNew;i--) { + lines.insert(startOld, linesNew.at(i)); + } + } + + return lines.join(QStringLiteral("\n")); +} Index: libksieve/src/ksieveui/widgets/managesievewidget.h =================================================================== --- libksieve/src/ksieveui/widgets/managesievewidget.h +++ libksieve/src/ksieveui/widgets/managesievewidget.h @@ -35,6 +35,8 @@ { class ManageSieveTreeView; class ManageSieveWidgetPrivate; +class ParseUserScriptJob; + class KSIEVEUI_EXPORT ManageSieveWidget : public QWidget { Q_OBJECT @@ -66,6 +68,7 @@ void slotDoubleClicked(QTreeWidgetItem *item); void slotSystemNetworkOnlineStateChanged(bool state); void slotCheckNetworkStatus(); + void setActiveScripts(ParseUserScriptJob *job); void slotCancelFetch(); public Q_SLOTS: @@ -92,7 +95,6 @@ bool isFileNameItem(QTreeWidgetItem *item) const; bool itemIsActived(QTreeWidgetItem *item) const; void changeActiveScript(QTreeWidgetItem *item, bool activate); - bool isProtectedName(const QString &name); ManageSieveWidgetPrivate *const d; }; Index: libksieve/src/ksieveui/widgets/managesievewidget.cpp =================================================================== --- libksieve/src/ksieveui/widgets/managesievewidget.cpp +++ libksieve/src/ksieveui/widgets/managesievewidget.cpp @@ -20,6 +20,9 @@ #include "widgets/sievetreewidgetitem.h" #include +#include +#include +#include #include #include @@ -31,8 +34,11 @@ #include #include "libksieve_debug.h" #include +#include using namespace KSieveUi; +Q_DECLARE_METATYPE(QTreeWidgetItem*) + class KSieveUi::ManageSieveWidgetPrivate { public: @@ -216,7 +222,7 @@ return; } - if (isProtectedName(name.toLower())) { + if (Util::isKep14ProtectedName(name)) { KMessageBox::error(this, i18n("You cannot use protected name."), i18n("New Script")); return; } @@ -297,6 +303,23 @@ if (u.isEmpty()) { return; } + + if (item->data(0, SIEVE_SERVER_MODE).toInt() == Kep14EditorMode) { + QStringList activeScripts; + for(int i=0; i < item->childCount(); i++) { + QTreeWidgetItem *j = item->child(i); + if (itemIsActived(j)) { + activeScripts << j->text(0); + } + } + GenerateGlobalScriptJob *job = new GenerateGlobalScriptJob(u); + job->addUserActiveScripts(activeScripts); + connect( job, SIGNAL(success()), SLOT(slotRefresh())); + connect( job, SIGNAL(error(QString)), SLOT(slotRefresh())); + job->start(); + return; + } + QTreeWidgetItem *selected = d->mSelectedItems[item]; if (!selected) { return; @@ -371,16 +394,6 @@ Q_EMIT scriptDeleted(u); } -bool ManageSieveWidget::isProtectedName(const QString &name) -{ - if (name == QLatin1String("master") || - name == QLatin1String("user") || - name == QLatin1String("management")) { - return true; - } - return false; -} - void ManageSieveWidget::slotRefresh() { d->mBlockSignal = true; @@ -427,8 +440,7 @@ d->mBlockSignal = true; // don't trigger slotItemChanged Q_FOREACH (const QString &script, listScript) { //Hide protected name. - const QString lowerScript(script.toLower()); - if (isProtectedName(lowerScript)) { + if (Util::isKep14ProtectedName(script)) { continue; } QTreeWidgetItem *item = new QTreeWidgetItem(parent); @@ -444,19 +456,61 @@ d->mBlockSignal = false; qCDebug(LIBKSIEVE_LOG) << " LOAD"; - const bool hasIncludeCapability = job->sieveCapabilities().contains(QStringLiteral("include")); - const bool hasUserActiveScript = (activeScript.toLower() == QLatin1String("USER")); - //QStringList mUserActiveScriptList; - if (hasUserActiveScript && hasIncludeCapability) { - //TODO parse file. + const bool hasKep14EditorMode = Util::hasKep14Support(job->sieveCapabilities(), listScript, activeScript); + if (hasKep14EditorMode) { + QUrl u = mUrls[parent]; + u = u.adjusted(QUrl::RemoveFilename); + u.setPath(u.path() + QStringLiteral("USER")); + ParseUserScriptJob *parseJob = new ParseUserScriptJob(u); + parseJob->setProperty("parentItem", QVariant::fromValue(parent)); + connect(parseJob, SIGNAL(finished(ParseUserScriptJob*)), SLOT(setActiveScripts(ParseUserScriptJob*))); + parseJob->start(); + (static_cast(parent))->startAnimation(); } parent->setData(0, SIEVE_SERVER_CAPABILITIES, job->sieveCapabilities()); parent->setData(0, SIEVE_SERVER_ERROR, false); - parent->setData(0, SIEVE_SERVER_MODE, hasIncludeCapability ? Kep14EditorMode : NormalEditorMode); + parent->setData(0, SIEVE_SERVER_MODE, hasKep14EditorMode ? Kep14EditorMode : NormalEditorMode); d->mTreeView->expandItem(parent); } +void ManageSieveWidget::setActiveScripts(ParseUserScriptJob *job) +{ + QTreeWidgetItem * parent = job->property("parentItem").value(); + if ( !parent ) { + return; + } + (static_cast(parent))->stopAnimation(); + + if (!job->error().isEmpty()) { + qWarning() << job->error(); + return; + } + + d->mBlockSignal = true; // don't trigger slotItemChanged + const QStringList activeScriptList = job->activeScriptList(); + QStringList scriptOrder = activeScriptList; + QMap scriptMap; + + const int children = parent->childCount(); + for(int i=0; i < children; i++) { + QTreeWidgetItem *item = parent->takeChild(0); + scriptMap.insert(item->text(0), item); + const bool isActive = activeScriptList.contains(item->text(0)); + item->setCheckState(0, isActive ? Qt::Checked : Qt::Unchecked); + if (!isActive) { + scriptOrder << item->text(0); + } + } + + foreach(const QString &scriptName, scriptOrder) { + parent->addChild(scriptMap[scriptName]); + } + + d->mBlockSignal = false; +} + + void ManageSieveWidget::slotDoubleClicked(QTreeWidgetItem *item) { if (!isFileNameItem(item)) { Index: libksieve/src/parser/lexer.cpp =================================================================== --- libksieve/src/parser/lexer.cpp +++ libksieve/src/parser/lexer.cpp @@ -170,10 +170,11 @@ { return ch < 0; } + static QString removeCRLF(const QString &s) { const bool CRLF = s.endsWith(QStringLiteral("\r\n")); - const bool LF = !CRLF && s.endsWith('\n'); + const bool LF = !CRLF && s.endsWith(QLatin1Char('\n')); const int e = CRLF ? 2 : LF ? 1 : 0 ; // what to chop off at the end @@ -274,7 +275,7 @@ case ')': case ';': case ',': // Special - result = *mState.cursor++; + result = QLatin1Char(*mState.cursor++); return Special; case '0': case '1': @@ -430,7 +431,7 @@ } if (reallySave) { QString tmp = QString::fromUtf8(commentStart, commentLength); - result += tmp.remove('\r'); // get rid of CR in CRLF pairs + result += tmp.remove( QLatin1Char('\r') ); // get rid of CR in CRLF pairs } } @@ -541,7 +542,7 @@ assert(isdigit(*mState.cursor)); while (!atEnd() && isdigit(*mState.cursor)) { - result += *mState.cursor++; + result += QLatin1Char(*mState.cursor++); } if (atEnd() || isDelim(*mState.cursor)) { @@ -555,7 +556,7 @@ case 'm': case 'K': case 'k': - result += *mState.cursor++; + result += QLatin1Char(*mState.cursor++); break; default: makeIllegalCharError(); @@ -632,22 +633,22 @@ } const QString line = removeCRLF(QString::fromUtf8(oldBeginOfLine, lineLength)); lines.push_back(removeDotStuff(line)); - if (line == ".") { + if ( line == QStringLiteral(".") ) { break; } } else { lines.push_back(QString()); } } - if (lines.back() != ".") { + if ( lines.back() != QStringLiteral(".") ) { makeError(Error::PrematureEndOfMultiLine, mlBeginLine, mlBeginCol); return false; } assert(!lines.empty()); lines.erase(--lines.end()); // don't include the lone dot. - result = lines.join("\n"); + result = lines.join(QStringLiteral("\n")); return true; } @@ -676,7 +677,7 @@ if (!eatCRLF()) { return false; } - result += '\n'; + result += QLatin1Char('\n'); break; case '\\': ++mState.cursor; @@ -686,7 +687,7 @@ // else fall through: default: if (!is8Bit(*mState.cursor)) { - result += *mState.cursor++; + result += QLatin1Char(*mState.cursor++); } else { // probably UTF-8 const char *const eightBitBegin = mState.cursor; skipTo8BitEnd(); Index: libksieve/src/parser/parser.cpp =================================================================== --- libksieve/src/parser/parser.cpp +++ libksieve/src/parser/parser.cpp @@ -140,7 +140,7 @@ return isStringToken() || token() == Lexer::Number || token() == Lexer::Tag || - (token() == Lexer::Special && mTokenValue == "["); + (token() == Lexer::Special && mTokenValue == QStringLiteral("[")) ; } bool Parser::Impl::obtainToken() @@ -244,7 +244,7 @@ } if (scriptBuilder()) { - scriptBuilder()->commandStart(tokenValue()); + scriptBuilder()->commandStart(tokenValue(), lexer.line()); } consumeToken(); @@ -279,7 +279,7 @@ return false; } - if (token() == Lexer::Special && tokenValue() == "(") { // test-list + if (token() == Lexer::Special && tokenValue() == QStringLiteral ("(")) { // test-list if (!parseTestList()) { assert(error()); return false; @@ -309,9 +309,9 @@ return false; } - if (tokenValue() == ";") { + if (tokenValue() == QStringLiteral(";")) { consumeToken(); - } else if (tokenValue() == "{") { // block + } else if (tokenValue() == QStringLiteral("{")) { // block if (!parseBlock()) { return false; // it's an error since we saw '{' } @@ -321,7 +321,7 @@ } if (scriptBuilder()) { - scriptBuilder()->commandEnd(); + scriptBuilder()->commandEnd(lexer.line()); } return true; } @@ -371,7 +371,7 @@ } consumeToken(); return true; - } else if (token() == Lexer::Special && tokenValue() == "[") { + } else if (token() == Lexer::Special && tokenValue() == QStringLiteral("[")) { if (!parseStringList()) { assert(error()); return false; @@ -390,7 +390,7 @@ return false; } - if (token() != Lexer::Special || tokenValue() != "(") { + if (token() != Lexer::Special || tokenValue() != QStringLiteral("(")) { return false; } if (scriptBuilder()) { @@ -511,7 +511,7 @@ goto TestEnd; } - if (token() == Lexer::Special && tokenValue() == "(") { // test-list + if (token() == Lexer::Special && tokenValue() == QStringLiteral("(")) { // test-list if (!parseTestList()) { assert(error()); return false; @@ -539,11 +539,11 @@ return false; } - if (token() != Lexer::Special || tokenValue() != "{") { + if (token() != Lexer::Special || tokenValue() != QStringLiteral("{")) { return false; } if (scriptBuilder()) { - scriptBuilder()->blockStart(); + scriptBuilder()->blockStart(lexer.line()); } consumeToken(); @@ -572,12 +572,12 @@ return false; } - if (token() != Lexer::Special || tokenValue() != "}") { + if (token() != Lexer::Special || tokenValue() != QStringLiteral("}")) { makeError(Error::NonCommandInCommandList); return false; } if (scriptBuilder()) { - scriptBuilder()->blockEnd(); + scriptBuilder()->blockEnd(lexer.line()); } consumeToken(); return true; @@ -596,7 +596,7 @@ return false; } - if (token() != Lexer::Special || tokenValue() != "[") { + if (token() != Lexer::Special || tokenValue() != QStringLiteral("[")) { return false; } @@ -685,7 +685,7 @@ int i = 0; const QByteArray s = tokenValue().toLatin1(); for (const int len = s.length() ; i < len && isdigit(s[i]) ; ++i) { - const unsigned long digitValue = s[i] - '0' ; + const unsigned long digitValue = s[i] - QLatin1Char('0').toLatin1() ; if (willOverflowULong(result, digitValue)) { makeError(Error::NumberOutOfRange); return false;