Branch 'dev/sieve_kep14' - 2 commits - libksieve/ksieve libksieve/ksieveui libksieve/parser libksieve/tests

Sandro Knauß knauss at kolabsys.com
Wed Mar 25 21:46:14 CET 2015


 libksieve/ksieve/scriptbuilder.h                               |    8 
 libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp |    8 
 libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h   |    8 
 libksieve/ksieveui/vacation/multiimapvacationmanager.cpp       |   12 
 libksieve/ksieveui/vacation/multiimapvacationmanager.h         |    2 
 libksieve/ksieveui/vacation/tests/vacationutilstest.cpp        |  155 +++++++
 libksieve/ksieveui/vacation/tests/vacationutilstest.h          |    4 
 libksieve/ksieveui/vacation/vacation.cpp                       |    4 
 libksieve/ksieveui/vacation/vacationcreatescriptjob.cpp        |   50 ++
 libksieve/ksieveui/vacation/vacationcreatescriptjob.h          |    6 
 libksieve/ksieveui/vacation/vacationmanager.cpp                |    1 
 libksieve/ksieveui/vacation/vacationpagewidget.cpp             |    8 
 libksieve/ksieveui/vacation/vacationscriptextractor.cpp        |   78 +++-
 libksieve/ksieveui/vacation/vacationscriptextractor.h          |  154 +++++--
 libksieve/ksieveui/vacation/vacationutils.cpp                  |  195 +++++++++-
 libksieve/ksieveui/vacation/vacationutils.h                    |    6 
 libksieve/parser/parser.cpp                                    |   16 
 libksieve/tests/parsertest.cpp                                 |   16 
 18 files changed, 627 insertions(+), 104 deletions(-)

New commits:
commit b3c0476b3e499cca5d63281ed01e9975ac0ca1a8
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Mar 25 21:45:01 2015 +0100

    Sieve: Adding write support

diff --git a/libksieve/ksieve/scriptbuilder.h b/libksieve/ksieve/scriptbuilder.h
index 9d95b36..7b08ebf 100644
--- a/libksieve/ksieve/scriptbuilder.h
+++ b/libksieve/ksieve/scriptbuilder.h
@@ -51,8 +51,8 @@ namespace KSieve {
     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;
@@ -60,8 +60,8 @@ namespace KSieve {
     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;
diff --git a/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp b/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp
index 0fa9c34..65c382c 100644
--- a/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp
+++ b/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.cpp
@@ -50,7 +50,7 @@ void XMLPrintingScriptBuilder::numberArgument( unsigned long number, char quanti
     write( QLatin1String("num"), ( quantifier ? QString::fromLatin1("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::commandStart( const QString &identifier )
     }
 }
 
-void XMLPrintingScriptBuilder::commandEnd()
+void XMLPrintingScriptBuilder::commandEnd(int lineNumber)
 {
     if (mIsAction) {
         write( QLatin1String("</action>") );
@@ -96,12 +96,12 @@ void XMLPrintingScriptBuilder::testListEnd()
     write( QLatin1String("</testlist>") );
 }
 
-void XMLPrintingScriptBuilder::blockStart()
+void XMLPrintingScriptBuilder::blockStart(int lineNumber)
 {
     write( QLatin1String("<block>") );
 }
 
-void XMLPrintingScriptBuilder::blockEnd()
+void XMLPrintingScriptBuilder::blockEnd(int lineNumber)
 {
     write( QLatin1String("</block>") );
 }
diff --git a/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h b/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h
index 7435647..2e4cb52 100644
--- a/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h
+++ b/libksieve/ksieveui/scriptsparsing/xmlprintingscriptbuilder.h
@@ -33,14 +33,14 @@ public:
     void taggedArgument( const QString &tag );
     void stringArgument( const QString &string, bool multiLine, const QString & /*fixme*/ );
     void numberArgument( unsigned long number, char quantifier );
-    void commandStart( const QString &identifier );
-    void commandEnd();
+    void commandStart( const QString &identifier, int lineNumber);
+    void commandEnd(int lineNumber);
     void testStart( const QString &identifier );
     void testEnd();
     void testListStart();
     void testListEnd();
-    void blockStart();
-    void blockEnd();
+    void blockStart(int lineNumber);
+    void blockEnd(int lineNumber);
     void stringListArgumentStart();
     void stringListArgumentEnd();
     void stringListEntry( const QString &string, bool multiline, const QString &hashComment );
diff --git a/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp b/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp
index f27fdd3..6d6e12d 100644
--- a/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp
+++ b/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp
@@ -27,6 +27,8 @@
 #include <KMessageBox>
 #include <KLocalizedString>
 
+#include <KDebug>
+
 using namespace KSieveUi;
 MultiImapVacationManager::MultiImapVacationManager(QObject *parent)
     : QObject(parent),
@@ -121,3 +123,13 @@ void MultiImapVacationManager::slotCheckKep14Ended(CheckKep14SupportJob *job, bo
             SLOT(slotScriptActive(VacationCheckJob*,QString,bool)));
     checkJob->start();
 }
+
+bool MultiImapVacationManager::kep14Support(QString serverName)
+{
+  if (mKep14Support.contains(serverName)) {
+      return mKep14Support[serverName];
+  } else {
+      kWarning() << "We don't know the KEP:14 support for this server." << serverName;
+  }
+  return  false;
+}
diff --git a/libksieve/ksieveui/vacation/multiimapvacationmanager.h b/libksieve/ksieveui/vacation/multiimapvacationmanager.h
index aee0c83..ca46b38 100644
--- a/libksieve/ksieveui/vacation/multiimapvacationmanager.h
+++ b/libksieve/ksieveui/vacation/multiimapvacationmanager.h
@@ -39,6 +39,8 @@ public:
     QMap<QString, KUrl> serverList();
     void checkVacation(const QString &serverName, const KUrl &url);
 
+    bool kep14Support(QString serverName);
+
 Q_SIGNALS:
     void scriptActive(bool active, const QString &serverName);
     void scriptAvailable(const QString &serverName, const QStringList &sieveCapabilities, const QString &scriptName, const QString &script, bool active);
diff --git a/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp b/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
index 555841e..f0cb13d 100644
--- a/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
+++ b/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
@@ -245,3 +245,59 @@ void VacationUtilsTest::testWriteSimpleScript()
     QCOMPARE(subjectA, subject);
     QCOMPARE(notificationIntervalA, 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<linesA.count(); i++ ){
+        vacation.append(linesA.at(i));
+    }
+
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptA, QString()), scriptA);
+    QCOMPARE(VacationUtils::updateVacationBlock(QString(), scriptB), scriptB);
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptA, scriptB), scriptB);
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptB, scriptA), scriptA);
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptAattend, scriptB), scriptBattend);
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptBattend, scriptA), scriptAattend);
+    QCOMPARE(VacationUtils::updateVacationBlock(scriptA, attend), header.join(QLatin1String("\n")));
+    QStringList output = vacation;
+    output << attend;
+    QCOMPARE(VacationUtils::updateVacationBlock(attend, scriptA), output.join(QLatin1String("\n")));
+    output.insert(0,require);
+    QCOMPARE(VacationUtils::updateVacationBlock(require+ QLatin1String("\n") + attend, scriptA), output.join(QLatin1String("\n")));
+}
+
+void VacationUtilsTest::testMergeRequireLine()
+{
+    QString sEmpty=QLatin1String("require;");
+    QString sOne=QLatin1String("require \"test\";");
+    QString sList1=QLatin1String("require [\"test\"];");
+    QString sList2=QLatin1String("require [\"test\", \"test2\"];");
+    QString sList3=QLatin1String("require [\"test3\",\n \"test4\"];\ntestcmd;");
+
+    QCOMPARE(VacationUtils::mergeRequireLine(sEmpty, sOne), sOne);
+    QCOMPARE(VacationUtils::mergeRequireLine(sOne, sEmpty), sOne);
+    QCOMPARE(VacationUtils::mergeRequireLine(sOne, sList1), sOne);
+    QCOMPARE(VacationUtils::mergeRequireLine(sOne, sList2), sList2);
+    QCOMPARE(VacationUtils::mergeRequireLine(sOne, sList3), QLatin1String("require [\"test\", \"test3\", \"test4\"];") );
+    QCOMPARE(VacationUtils::mergeRequireLine(sList3, sOne), QLatin1String("require [\"test\", \"test3\", \"test4\"];\ntestcmd;") );
+}
diff --git a/libksieve/ksieveui/vacation/tests/vacationutilstest.h b/libksieve/ksieveui/vacation/tests/vacationutilstest.h
index c2fcdc8..41e3e03 100644
--- a/libksieve/ksieveui/vacation/tests/vacationutilstest.h
+++ b/libksieve/ksieveui/vacation/tests/vacationutilstest.h
@@ -34,6 +34,8 @@ private Q_SLOTS:
     void testParseScriptComplex();
     void testWriteScript();
     void testWriteSimpleScript();
+    void testUpdateVacationBlock();
+    void testMergeRequireLine();
 };
 }
 #endif // VACATIONUTILSTEST_H
diff --git a/libksieve/ksieveui/vacation/vacationcreatescriptjob.cpp b/libksieve/ksieveui/vacation/vacationcreatescriptjob.cpp
index 6d31678..1ccd9c1 100644
--- a/libksieve/ksieveui/vacation/vacationcreatescriptjob.cpp
+++ b/libksieve/ksieveui/vacation/vacationcreatescriptjob.cpp
@@ -16,6 +16,7 @@
 */
 
 #include "vacationcreatescriptjob.h"
+#include "vacationutils.h"
 #include <kmanagesieve/sievejob.h>
 
 #include <KMessageBox>
@@ -29,6 +30,7 @@ VacationCreateScriptJob::VacationCreateScriptJob(QObject *parent)
       mActivate(false),
       mWasActive(false),
       mSieveJob(0)
+    , mKep14Support(false)
 {
 
 }
@@ -49,6 +51,26 @@ void VacationCreateScriptJob::setServerName(const QString &servername)
     mServerName = servername;
 }
 
+const QString &VacationCreateScriptJob::serverName() const
+{
+    return mServerName;
+}
+
+void VacationCreateScriptJob::setKep14Support(bool kep14Support)
+{
+    mKep14Support = kep14Support;
+}
+
+void VacationCreateScriptJob::setServerUrl(const KUrl &url)
+{
+    mUrl = url;
+}
+
+void VacationCreateScriptJob::setScript(const QString &script)
+{
+    mScript = script;
+}
+
 void VacationCreateScriptJob::start()
 {
     if (mUrl.isEmpty()) {
@@ -56,7 +78,24 @@ void VacationCreateScriptJob::start()
         deleteLater();
         return;
     }
-    mSieveJob = KManageSieve::SieveJob::put( mUrl, mScript, mActivate, mWasActive );
+    mSieveJob = KManageSieve::SieveJob::get(mUrl);
+    mSieveJob->setInteractive(false);
+    connect(mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)),
+            SLOT(slotGetScript(KManageSieve::SieveJob*,bool,QString,bool)));
+}
+
+void VacationCreateScriptJob::slotGetScript(KManageSieve::SieveJob *job, bool success, const QString &oldScript, bool active)
+{
+    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
+    }
     if ( mActivate )
         connect( mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)),
                  SLOT(slotPutActiveResult(KManageSieve::SieveJob*,bool)) );
@@ -65,15 +104,6 @@ void VacationCreateScriptJob::start()
                  SLOT(slotPutInactiveResult(KManageSieve::SieveJob*,bool)) );
 }
 
-void VacationCreateScriptJob::setServerUrl(const KUrl &url)
-{
-    mUrl = url;
-}
-
-void VacationCreateScriptJob::setScript(const QString &script)
-{
-    mScript = script;
-}
 
 void VacationCreateScriptJob::slotPutActiveResult( KManageSieve::SieveJob * job, bool success )
 {
diff --git a/libksieve/ksieveui/vacation/vacationcreatescriptjob.h b/libksieve/ksieveui/vacation/vacationcreatescriptjob.h
index c23584e..0172d63 100644
--- a/libksieve/ksieveui/vacation/vacationcreatescriptjob.h
+++ b/libksieve/ksieveui/vacation/vacationcreatescriptjob.h
@@ -41,7 +41,11 @@ public:
     void setServerUrl(const KUrl &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);
+    QString updateVacationBlock(QString oldScript, QString mScript);
+    QString mergeRequireLine(QString oldScript, QString mScript);
 
 Q_SIGNALS:
     void result(bool);
@@ -50,6 +54,7 @@ Q_SIGNALS:
 private slots:
     void slotPutActiveResult(KManageSieve::SieveJob *job, bool success);
     void slotPutInactiveResult(KManageSieve::SieveJob *job, bool success);
+    void slotGetScript(KManageSieve::SieveJob *job, bool success, const QString &oldScript, bool active);
 
 private:
     void handlePutResult(KManageSieve::SieveJob *, bool success, bool activated);
@@ -58,6 +63,7 @@ private:
     QString mServerName;
     bool mActivate;
     bool mWasActive;
+    bool mKep14Support;
     KManageSieve::SieveJob *mSieveJob;
 };
 }
diff --git a/libksieve/ksieveui/vacation/vacationmanager.cpp b/libksieve/ksieveui/vacation/vacationmanager.cpp
index 6cad984..becf6f2 100644
--- a/libksieve/ksieveui/vacation/vacationmanager.cpp
+++ b/libksieve/ksieveui/vacation/vacationmanager.cpp
@@ -94,6 +94,7 @@ void VacationManager::slotDialogOk()
     QList<KSieveUi::VacationCreateScriptJob *> listJob = mMultiImapVacationDialog->listCreateJob();
     Q_FOREACH (KSieveUi::VacationCreateScriptJob *job, listJob) {
         connect(job, SIGNAL(scriptActive(bool,QString)), SIGNAL(updateVacationScriptStatus(bool,QString)));
+        job->setKep14Support(mCheckVacation->kep14Support(job->serverName()));
         job->start();
     }
     mMultiImapVacationDialog->delayedDestruct();
diff --git a/libksieve/ksieveui/vacation/vacationpagewidget.cpp b/libksieve/ksieveui/vacation/vacationpagewidget.cpp
index 1a0aa9a..c26d0f3 100644
--- a/libksieve/ksieveui/vacation/vacationpagewidget.cpp
+++ b/libksieve/ksieveui/vacation/vacationpagewidget.cpp
@@ -118,6 +118,8 @@ void VacationPageWidget::slotGetResult(const QString &serverName, const QStringL
         return;
     }
 
+    mUrl.setFileName(scriptName);
+
     // Whether the server supports the "date" extension
     const bool supportsSieveDate = mUrl.protocol() == QLatin1String("sieve") && sieveCapabilities.contains(QLatin1String("date"));
 
@@ -137,7 +139,7 @@ void VacationPageWidget::slotGetResult(const QString &serverName, const QStringL
         mVacationWarningWidget->setVisible(true);
     }
 
-    mWasActive = active && scriptActive;
+    mWasActive = active;
     mVacationEditWidget->setEnabled(true);
     mVacationEditWidget->setActivateVacation( active && scriptActive );
     mVacationEditWidget->setMessageText( messageText );
diff --git a/libksieve/ksieveui/vacation/vacationscriptextractor.cpp b/libksieve/ksieveui/vacation/vacationscriptextractor.cpp
index 4648058..7e029d2 100644
--- a/libksieve/ksieveui/vacation/vacationscriptextractor.cpp
+++ b/libksieve/ksieveui/vacation/vacationscriptextractor.cpp
@@ -25,6 +25,8 @@ VacationDataExtractor::VacationDataExtractor()
     , mActive(true)
     , mInIfBlock(false)
     , mBlockLevel(0)
+    , mLineStart(0)
+    , mLineEnd(0)
 {
     kDebug();
 }
@@ -34,21 +36,28 @@ VacationDataExtractor::~VacationDataExtractor()
 
 }
 
-void VacationDataExtractor::commandStart( const QString & identifier ) {
+void VacationDataExtractor::commandStart( const QString & identifier, int lineNumber ) {
     kDebug() << "( \"" << identifier <<"\" )";
     if (identifier == QLatin1String("if") && mContext == None) {
         mContext = IfBlock;
+        mLineStart = lineNumber;
+        mInIfBlock = true;
     }
     if ( identifier != QLatin1String("vacation") )
         return;
+
+    if (mContext != IfBlock) {
+        mLineStart = lineNumber;
+    }
+
     reset();
     mContext = VacationCommand;
 }
 
-void VacationDataExtractor::commandEnd() {
-    kDebug();
-    if ( mContext != None && mContext != IfBlock ) {
+void VacationDataExtractor::commandEnd(int lineNumber) {
+    if ( mContext != None && mContext != IfBlock  && mContext != VacationEnd) {
         mContext = VacationEnd;
+        mLineEnd = lineNumber;
     }
 }
 
@@ -81,21 +90,20 @@ void VacationDataExtractor::hashComment(const QString &comment)
 }
 
 
-void VacationDataExtractor::blockStart()
+void VacationDataExtractor::blockStart(int lineNumber)
 {
-    if (mContext == IfBlock) {
-        mContext = None;
-    }
     mBlockLevel++;
 }
 
-void VacationDataExtractor::blockEnd()
+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();
-        kDebug() << "Reset active level";
+    } else if (mInIfBlock && mBlockLevel == 0 && commandFound()) {
+        mLineEnd = lineNumber;
+        mInIfBlock = false;
     }
 }
 
@@ -168,3 +176,53 @@ void VacationDataExtractor::reset()
     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 == QLatin1String("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)
+{
+     kDebug() << 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;
+}
\ No newline at end of file
diff --git a/libksieve/ksieveui/vacation/vacationscriptextractor.h b/libksieve/ksieveui/vacation/vacationscriptextractor.h
index 851221c..1812ada 100644
--- a/libksieve/ksieveui/vacation/vacationscriptextractor.h
+++ b/libksieve/ksieveui/vacation/vacationscriptextractor.h
@@ -80,14 +80,14 @@ private:
 #undef FOREACH
 #endif
 #define FOREACH for ( std::vector<KSieve::ScriptBuilder*>::const_iterator it = mBuilders.begin(), end = mBuilders.end() ; it != end ; ++it ) (*it)->
-    void commandStart( const QString & identifier ) { FOREACH commandStart( identifier ); }
-    void commandEnd() { FOREACH commandEnd(); }
+    void commandStart( const QString & identifier, int lineNumber ) { FOREACH commandStart( identifier, lineNumber ); }
+    void commandEnd(int lineNumber) { FOREACH commandEnd(lineNumber); }
     void testStart( const QString & identifier ) { FOREACH testStart( identifier ); }
     void testEnd() { FOREACH testEnd(); }
     void testListStart() { FOREACH testListStart(); }
     void testListEnd() { FOREACH testListEnd(); }
-    void blockStart() { FOREACH blockStart(); }
-    void blockEnd() { FOREACH blockEnd(); }
+    void blockStart(int lineNumber) { FOREACH blockStart(lineNumber); }
+    void blockEnd(int lineNumber) { FOREACH blockEnd(lineNumber); }
     void hashComment( const QString & comment ) { FOREACH hashComment( comment ); }
     void bracketComment( const QString & comment ) { FOREACH bracketComment( comment ); }
     void lineFeed() { FOREACH lineFeed(); }
@@ -143,9 +143,11 @@ public:
     unsigned int mState;
     int mNestingDepth;
 
+    int mLineNumber;
+
 public:
     GenericInformationExtractor( const std::vector<StateNode> & nodes )
-        : KSieve::ScriptBuilder(), mNodes( nodes ), mState( 0 ), mNestingDepth( 0 ) {}
+        : KSieve::ScriptBuilder(), mNodes( nodes ), mState( 0 ), mNestingDepth( 0 ), mLineNumber(0) {}
 
     const std::map<QString,QString> & results() const { return mResults; }
 
@@ -180,17 +182,17 @@ private:
             doProcess( method, string );
         }
     }
-    void commandStart( const QString & identifier ) { kDebug() << identifier ; process( CommandStart, identifier ); }
-    void commandEnd() { kDebug() ; process( CommandEnd ); }
+    void commandStart( const QString & identifier, int lineNumber ) { kDebug() << identifier ; process( CommandStart, identifier ); }
+    void commandEnd(int lineNumber) { kDebug() ; process( CommandEnd ); }
     void testStart( const QString & identifier ) { kDebug() << identifier ; process( TestStart, identifier ); }
     void testEnd() { kDebug() ; process( TestEnd ); }
     void testListStart() { kDebug() ; process( TestListStart ); }
     void testListEnd() { kDebug() ; process( TestListEnd ); }
-    void blockStart() { kDebug() ; process( BlockStart ); ++mNestingDepth; }
-    void blockEnd() { kDebug() ; --mNestingDepth; process( BlockEnd ); }
+    void blockStart(int lineNumber) { kDebug() ; process( BlockStart ); ++mNestingDepth; }
+    void blockEnd(int lineNumber) { kDebug() ; --mNestingDepth; process( BlockEnd ); }
     void hashComment( const QString & ) { kDebug() ; }
     void bracketComment( const QString & ) { kDebug() ; }
-    void lineFeed() { kDebug() ; }
+    void lineFeed() { kDebug() << ++mLineNumber; }
     void error( const KSieve::Error & ) {
         kDebug() ;
         mState = 0;
@@ -415,19 +417,22 @@ public:
         return mSubject;
     }
 
+    int lineStart() const {return mLineStart;}
+    int lineEnd() const {return mLineEnd;}
+
 private:
-    void commandStart( const QString & identifier );
+    void commandStart( const QString & identifier, int lineNumber );
 
-    void commandEnd();
+    void commandEnd(int lineNumber);
 
     void testStart( const QString &);
     void testEnd() {}
     void testListStart() {}
     void testListEnd() {}
-    void blockStart();
-    void blockEnd();
+    void blockStart(int lineNumber);
+    void blockEnd(int lineNumber);
     void hashComment( const QString & );
-    void bracketComment( const QString & ) {}
+    void bracketComment( const QString &c ) {}
     void lineFeed() {}
     void error( const KSieve::Error & e );
     void finished();
@@ -452,10 +457,61 @@ private:
     bool mInIfBlock;
     int mBlockLevel;
     QString mIfComment;
+    int mLineStart;
+    int mLineEnd;
 
     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){};
+    void blockEnd(int lineNumber){};
+    void hashComment( const QString & ) {}
+    void bracketComment( const QString & ) {}
+    void lineFeed() {}
+    void error( const KSieve::Error & e );
+    void finished();
+
+    void taggedArgument( const QString & tag ) {}
+    void numberArgument( unsigned long number, char ) {}
+
+    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;
+};
 }
 
 
diff --git a/libksieve/ksieveui/vacation/vacationutils.cpp b/libksieve/ksieveui/vacation/vacationutils.cpp
index 9c5c509..806600d 100644
--- a/libksieve/ksieveui/vacation/vacationutils.cpp
+++ b/libksieve/ksieveui/vacation/vacationutils.cpp
@@ -313,3 +313,110 @@ QString KSieveUi::VacationUtils::composeScript( const QString & messageText, boo
 
     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<QString> 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, QString::fromLatin1("require [\"%1\"];").arg(req.join(QLatin1String("\", \""))));
+    } else if  (requirements.count() == 1) {
+        lines.insert(insert, QString::fromLatin1("require \"%1\";").arg(requirements.toList().first()));
+    }
+
+    return lines.join(QLatin1String("\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);
+
+    int startNew(vdxNew.lineStart());
+    int endNew(vdxNew.lineEnd());
+
+    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(QLatin1String("\n"));
+}
diff --git a/libksieve/ksieveui/vacation/vacationutils.h b/libksieve/ksieveui/vacation/vacationutils.h
index a07ebfe..d6b8c6f 100644
--- a/libksieve/ksieveui/vacation/vacationutils.h
+++ b/libksieve/ksieveui/vacation/vacationutils.h
@@ -53,6 +53,10 @@ bool parseScript( const QString & script, bool &active, QString & messageText,
 
 bool foundVacationScript(const QString & script);
 
+QString mergeRequireLine(const QString &script1, const QString script2);
+
+QString updateVacationBlock(const QString &oldScript, const QString &newScript);
+
 }
 }
 
diff --git a/libksieve/parser/parser.cpp b/libksieve/parser/parser.cpp
index 3562452..df9ad7c 100644
--- a/libksieve/parser/parser.cpp
+++ b/libksieve/parser/parser.cpp
@@ -42,6 +42,8 @@
 #include <limits.h> // ULONG_MAX
 #include <ctype.h> // isdigit
 
+#include <KDebug>
+
 namespace KSieve {
 
 //
@@ -218,8 +220,9 @@ bool Parser::Impl::parseCommand() {
     if ( !obtainToken() || token() != Lexer::Identifier )
         return false;
 
-    if ( scriptBuilder() )
-        scriptBuilder()->commandStart( tokenValue() );
+    if ( scriptBuilder() ) {
+        scriptBuilder()->commandStart( tokenValue(), lexer.line() );
+    }
     consumeToken();
 
     //
@@ -290,8 +293,9 @@ bool Parser::Impl::parseCommand() {
         return false;
     }
 
-    if ( scriptBuilder() )
-        scriptBuilder()->commandEnd();
+    if ( scriptBuilder() ) {
+        scriptBuilder()->commandEnd(lexer.line());
+    }
     return true;
 }
 
@@ -491,7 +495,7 @@ bool Parser::Impl::parseBlock() {
     if ( token() != Lexer::Special || tokenValue() != QLatin1String("{"))
         return false;
     if ( scriptBuilder() )
-        scriptBuilder()->blockStart();
+        scriptBuilder()->blockStart(lexer.line());
     consumeToken();
 
     if ( !obtainToken() )
@@ -522,7 +526,7 @@ bool Parser::Impl::parseBlock() {
         return false;
     }
     if ( scriptBuilder() )
-        scriptBuilder()->blockEnd();
+        scriptBuilder()->blockEnd(lexer.line());
     consumeToken();
     return true;
 }
diff --git a/libksieve/tests/parsertest.cpp b/libksieve/tests/parsertest.cpp
index 57660c8..82eb8e1 100644
--- a/libksieve/tests/parsertest.cpp
+++ b/libksieve/tests/parsertest.cpp
@@ -400,12 +400,12 @@ public:
     const QString txt = "number" + ( quantifier ? QString(" quantifier=\"%1\"").arg( quantifier ) : QString() ) ;
     write( txt.toLatin1(), QString::number( number ) );
   }
-  void commandStart( const QString & identifier ) {
+  void commandStart( const QString & identifier, int lineNumber) {
     write( "<command>" );
     ++indent;
     write( "identifier", identifier );
   }
-  void commandEnd() {
+  void commandEnd(int lineNumber) {
     --indent;
     write( "</command>" );
   }
@@ -426,11 +426,11 @@ public:
     --indent;
     write( "</testlist>" );
   }
-  void blockStart() {
+  void blockStart(int lineNumber) {
     write( "<block>" );
     ++indent;
   }
-  void blockEnd() {
+  void blockEnd(int lineNumber) {
     --indent;
     write( "</block>" );
   }
@@ -515,12 +515,12 @@ public:
     checkEquals( QString::number( number ) + ( quantifier ? quantifier : ' ' ) );
     ++mNextResponse;
   }
-  void commandStart( const QString & identifier ) {
+  void commandStart( const QString & identifier, int lineNumber ) {
     checkIs( CommandStart );
     checkEquals( identifier );
     ++mNextResponse;
   }
-  void commandEnd() {
+  void commandEnd(int lineNumber) {
     checkIs( CommandEnd );
     ++mNextResponse;
   }
@@ -541,11 +541,11 @@ public:
     checkIs( TestListEnd );
     ++mNextResponse;
   }
-  void blockStart() {
+  void blockStart(int lineNumber) {
     checkIs( BlockStart );
     ++mNextResponse;
   }
-  void blockEnd() {
+  void blockEnd(int lineNumber) {
     checkIs( BlockEnd );
     ++mNextResponse;
   }


commit b774a4f30288b3d3f56fc5914c30a3b3dafc7855
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Mar 25 12:54:18 2015 +0100

    Fix compose sieve script.

diff --git a/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp b/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
index 876fcb5..555841e 100644
--- a/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
+++ b/libksieve/ksieveui/vacation/tests/vacationutilstest.cpp
@@ -17,6 +17,8 @@
 #include "vacationutilstest.h"
 #include "vacation/vacationutils.h"
 
+#include <kmime/kmime_header_parsing.h>
+
 #include <QFile>
 #include <qtest_kde.h>
 #include <KDebug>
@@ -148,5 +150,98 @@ void VacationUtilsTest::testParseScriptComplex()
     QCOMPARE(sendForSpam, false);
     QCOMPARE(domainName, QString());
     QCOMPARE(startDate, QDate(2015, 01, 02));
-    QCOMPARE(endDate, QDate(2015,03,04));
-}
\ No newline at end of file
+    QCOMPARE(endDate, QDate(2015, 03, 04));
+}
+
+void VacationUtilsTest::testWriteScript()
+{
+    QString messageText(QLatin1String("dsfgsdfgsdfg"));
+    QString subject(QLatin1String("XXX"));
+    int notificationInterval(7);
+    QStringList aliases = QStringList() << QLatin1String("test at test.de");
+    QList<KMime::Types::AddrSpec> addesses;
+    bool sendForSpam(false);
+    QString domainName(QLatin1String("example.org"));
+    QDate startDate(2015, 01, 02);
+    QDate endDate(2015, 03, 04);
+    bool scriptActive(true);
+
+    QString messageTextA;
+    QString subjectA;
+    int notificationIntervalA;
+    QStringList aliasesA;
+    bool sendForSpamA;
+    QString domainNameA;
+    QDate startDateA;
+    QDate endDateA;
+    bool scriptActiveA;
+
+    foreach(const QString &alias, aliases) {
+        KMime::Types::Mailbox a;
+        a.fromUnicodeString(alias);
+        addesses.append(a.addrSpec());
+    }
+
+    QString script = VacationUtils::composeScript(messageText, scriptActive, subject, notificationInterval, addesses, sendForSpam, domainName, startDate, endDate);
+    bool ret = VacationUtils::parseScript(script, scriptActiveA, messageTextA, subjectA, notificationIntervalA, aliasesA, sendForSpamA, domainNameA, startDateA, endDateA);
+    QCOMPARE(ret, true);
+    QCOMPARE(scriptActiveA, scriptActive);
+    QCOMPARE(messageTextA, messageText);
+    QCOMPARE(subjectA, subject);
+    QCOMPARE(notificationIntervalA, notificationInterval);
+    QCOMPARE(aliasesA, aliases);
+    QCOMPARE(sendForSpamA, sendForSpam);
+    QCOMPARE(domainNameA, domainName);
+    QCOMPARE(startDateA, startDate);
+    QCOMPARE(endDateA, endDate);
+
+    scriptActive = false;
+    script = VacationUtils::composeScript(messageText, scriptActive, subject, notificationInterval, addesses, sendForSpam, domainName, startDate, endDate);
+    ret = VacationUtils::parseScript(script, scriptActiveA, messageTextA, subjectA, notificationIntervalA, aliasesA, sendForSpamA, domainNameA, startDateA, endDateA);
+    QCOMPARE(ret, true);
+    QCOMPARE(scriptActiveA, scriptActive);
+    QCOMPARE(messageTextA, messageText);
+    QCOMPARE(subjectA, subject);
+    QCOMPARE(notificationIntervalA, notificationInterval);
+    QCOMPARE(aliasesA, aliases);
+    QCOMPARE(sendForSpamA, sendForSpam);
+    QCOMPARE(domainNameA, domainName);
+    QCOMPARE(startDateA, startDate);
+    QCOMPARE(endDateA, endDate);
+}
+
+
+void VacationUtilsTest::testWriteSimpleScript()
+{
+    QString messageText(QLatin1String("dsfgsdfgsdfg"));
+    QString subject(QLatin1String("XXX"));
+    int notificationInterval(7);
+    bool scriptActive(true);
+
+    QString messageTextA;
+    QString subjectA;
+    int notificationIntervalA;
+    QStringList aliasesA;
+    bool sendForSpamA;
+    QString domainNameA;
+    QDate startDateA;
+    QDate endDateA;
+    bool scriptActiveA;
+
+    QString script = VacationUtils::composeScript(messageText, scriptActive, subject, notificationInterval, QList<KMime::Types::AddrSpec>(), true, QString(), QDate(), QDate());
+    bool ret = VacationUtils::parseScript(script, scriptActiveA, messageTextA, subjectA, notificationIntervalA, aliasesA, sendForSpamA, domainNameA, startDateA, endDateA);
+    QCOMPARE(ret, true);
+    QCOMPARE(scriptActiveA, scriptActive);
+    QCOMPARE(messageTextA, messageText);
+    QCOMPARE(subjectA, subject);
+    QCOMPARE(notificationIntervalA, notificationInterval);
+
+    scriptActive = false;
+    script = VacationUtils::composeScript(messageText, scriptActive, subject, notificationInterval, QList<KMime::Types::AddrSpec>(), true, QString(), QDate(), QDate());
+    ret = VacationUtils::parseScript(script, scriptActiveA, messageTextA, subjectA, notificationIntervalA, aliasesA, sendForSpamA, domainNameA, startDateA, endDateA);
+    QCOMPARE(ret, true);
+    QCOMPARE(scriptActiveA, scriptActive);
+    QCOMPARE(messageTextA, messageText);
+    QCOMPARE(subjectA, subject);
+    QCOMPARE(notificationIntervalA, notificationInterval);
+}
diff --git a/libksieve/ksieveui/vacation/tests/vacationutilstest.h b/libksieve/ksieveui/vacation/tests/vacationutilstest.h
index eac4378..c2fcdc8 100644
--- a/libksieve/ksieveui/vacation/tests/vacationutilstest.h
+++ b/libksieve/ksieveui/vacation/tests/vacationutilstest.h
@@ -32,6 +32,8 @@ private Q_SLOTS:
     void testParseScript_data();
     void testParseScript();
     void testParseScriptComplex();
+    void testWriteScript();
+    void testWriteSimpleScript();
 };
 }
 #endif // VACATIONUTILSTEST_H
diff --git a/libksieve/ksieveui/vacation/vacation.cpp b/libksieve/ksieveui/vacation/vacation.cpp
index a886fcf..0fe799a 100644
--- a/libksieve/ksieveui/vacation/vacation.cpp
+++ b/libksieve/ksieveui/vacation/vacation.cpp
@@ -163,7 +163,8 @@ void Vacation::slotGetResult( KManageSieve::SieveJob * job, bool success,
 void Vacation::slotDialogOk() {
     kDebug();
     // compose a new script:
-    const QString script = VacationUtils::composeScript( mDialog->messageText(),
+    const bool active = mDialog->activateVacation();
+    const QString script = VacationUtils::composeScript( mDialog->messageText(), active,
                                           mDialog->subject(),
                                           mDialog->notificationInterval(),
                                           mDialog->mailAliases(),
@@ -171,7 +172,6 @@ void Vacation::slotDialogOk() {
                                           mDialog->domainName(),
                                           mDialog->startDate(),
                                           mDialog->endDate() );
-    const bool active = mDialog->activateVacation();
     emit scriptActive( active, mServerName);
 
     kDebug() << "script:" << endl << script;
diff --git a/libksieve/ksieveui/vacation/vacationpagewidget.cpp b/libksieve/ksieveui/vacation/vacationpagewidget.cpp
index cbdc4c0..1a0aa9a 100644
--- a/libksieve/ksieveui/vacation/vacationpagewidget.cpp
+++ b/libksieve/ksieveui/vacation/vacationpagewidget.cpp
@@ -165,7 +165,8 @@ KSieveUi::VacationCreateScriptJob *VacationPageWidget::writeScript()
         KSieveUi::VacationCreateScriptJob *createJob = new KSieveUi::VacationCreateScriptJob;
         createJob->setServerUrl(mUrl);
         createJob->setServerName(mServerName);
-        const QString script = VacationUtils::composeScript( mVacationEditWidget->messageText(),
+        const bool active = mVacationEditWidget->activateVacation();
+        const QString script = VacationUtils::composeScript( mVacationEditWidget->messageText(), active,
                                                              mVacationEditWidget->subject(),
                                                              mVacationEditWidget->notificationInterval(),
                                                              mVacationEditWidget->mailAliases(),
@@ -173,7 +174,6 @@ KSieveUi::VacationCreateScriptJob *VacationPageWidget::writeScript()
                                                              mVacationEditWidget->domainName(),
                                                              mVacationEditWidget->startDate(),
                                                              mVacationEditWidget->endDate() );
-        const bool active = mVacationEditWidget->activateVacation();
         createJob->setStatus(active, mWasActive);
         //Q_EMIT scriptActive( active, mServerName);
         createJob->setScript(script);
diff --git a/libksieve/ksieveui/vacation/vacationscriptextractor.h b/libksieve/ksieveui/vacation/vacationscriptextractor.h
index d5f1a5d..851221c 100644
--- a/libksieve/ksieveui/vacation/vacationscriptextractor.h
+++ b/libksieve/ksieveui/vacation/vacationscriptextractor.h
@@ -230,16 +230,17 @@ static const GenericInformationExtractor::StateNode spamNodes[] = {
     { 0,     GIE::StringListArgumentEnd, 0, 0, 14, 0 },                // 16
     { 0,     GIE::StringListArgumentEnd, 0, 18, 0, 0 },                // 17
 
-    { 0,   GIE::TestEnd, 0, 20, 19, 0 }, // 18
-    { 0,   GIE::TestListEnd, 0, 20, 0, 0 }, // 19
+    { 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, 0, 21, 18, 0 },                // 20
-    { 1,     GIE::CommandStart, "vacation", 24, 24, "vacation" }, // 21
-    { -1,    GIE::Any, 0, 21, 0, 0 },                     // 22
-    { 0,   GIE::BlockEnd, 0, 0, 18, 0 },                  // 23
+    { 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, 0, 24, 24, 0 }, // 24 end state
+    { -1, GIE::Any, 0, 25, 25, 0 },   // 25 end state
 };
 static const unsigned int numSpamNodes = sizeof spamNodes / sizeof *spamNodes ;
 
@@ -263,39 +264,40 @@ public:
 //   'if not address :domain :contains ["from"] ["mydomain.org"] { keep; stop; }'
 static const GenericInformationExtractor::StateNode domainNodes[] = {
     { 0, GIE::CommandStart, "if", 1, 0, 0 },       // 0
-    { 0,   GIE::TestStart, "not", 2, 0, 0, },      // 1
-    { 0,     GIE::TestStart, "address", 3, 0, 0 }, // 2
+    { 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, 0 },     // 3
-    { 0,       GIE::TaggedArgument, "contains", 7, 0, 0 },   // 4
-    { 0,       GIE::TaggedArgument, "contains", 6, 0, 0 },   // 5
-    { 0,       GIE::TaggedArgument, "domain", 7, 0, 0 },     // 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, 0, 9, 0, 0 },       // 8
-    { 0,         GIE::StringListEntry, "from", 10, 11, "from" }, // 9
-    { 0,         GIE::StringListEntry, 0, 10, 12, 0 },           // 10
-    { 0,       GIE::StringListArgumentEnd, 0, 0, 9, 0 },         // 11
-    { 0,       GIE::StringListArgumentEnd, 0, 13, 0, 0 },        // 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, 0, 17, 14, "domainName" },    // 13
-    { 0,       GIE::StringListArgumentStart, 0, 15, 0, 0 },       // 14
-    { 0,         GIE::StringListEntry, 0, 15, 16, "domainName" }, // 15
-    { 0,       GIE::StringListArgumentEnd, 0, 17, 0, 0 },         // 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, 0, 18, 0, 0 },  // 17
-    { 0,   GIE::TestEnd, 0, 19, 0, 0 },    // 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, 0, 20, 0, 0 },                 // 19
-    { 1,     GIE::CommandStart, "stop", 23, 22, "stop" },  // 20
-    { -1,    GIE::Any, 0, 20, 0, 0 },                      // 21
-    { 0,   GIE::BlockEnd, 0, 0, 21, 0 },                   // 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, 0, 23, 23, 0 }  // 23 end state
+    { -1, GIE::Any, 0, 24, 24, 0 }  // 24 end state
 };
 static const unsigned int numDomainNodes = sizeof domainNodes / sizeof *domainNodes ;
 
@@ -308,7 +310,7 @@ public:
     }
 
     QString domainName() /*not const, since map::op[] isn't const*/ {
-        return mResults.count( QLatin1String("stop") ) && mResults.count( QLatin1String("from") )
+        return mResults.count( QLatin1String("vacation") ) && mResults.count( QLatin1String("from") )
                 ? mResults[QLatin1String("domainName")] : QString();
     }
 };
@@ -321,7 +323,7 @@ static const GenericInformationExtractor::StateNode datesNodes[] = {
 
     // handle startDate and endDate in arbitrary order
     { 0,       GIE::TestListStart, 0, 3, 0, 0 },                 // 2
-    { 0,         GIE::TestStart, "currentdate", 4, 0, 0 },         // 3
+    { 0,         GIE::TestStart, "currentdate", 4, 3, 0 },         // 3
     { 0,           GIE::TaggedArgument, "value", 5, 4, 0 },          // 4
     { 0,           GIE::StringArgument, "ge", 6, 8, 0 },             // 5
     { 0,           GIE::StringArgument, "date", 7, 0, 0 },           // 6
@@ -331,7 +333,7 @@ static const GenericInformationExtractor::StateNode datesNodes[] = {
     { 0,           GIE::StringArgument, 0, 11, 0, "endDate" },       // 10
     { 0,         GIE::TestEnd, 0, 12, 0, 0 },                      // 11
 
-    { 0,         GIE::TestStart, "currentdate", 13, 0, 0 },        // 12
+    { 0,         GIE::TestStart, "currentdate", 13, 12, 0 },        // 12
     { 0,           GIE::TaggedArgument, "value", 14, 13, 0 },         // 13
     { 0,           GIE::StringArgument, "le", 15, 17, 0 },           // 14
     { 0,           GIE::StringArgument, "date", 16, 0, 0 },          // 15
diff --git a/libksieve/ksieveui/vacation/vacationutils.cpp b/libksieve/ksieveui/vacation/vacationutils.cpp
index a6b69ac..9c5c509 100644
--- a/libksieve/ksieveui/vacation/vacationutils.cpp
+++ b/libksieve/ksieveui/vacation/vacationutils.cpp
@@ -128,6 +128,7 @@ bool KSieveUi::VacationUtils::parseScript( const QString &script, bool &active,
     DateExtractor dx;
     KSieveExt::MultiScriptBuilder tsb( &vdx , &sdx, &drdx, &dx );
     parser.setScriptBuilder( &tsb );
+    parser.parse();
     if ( !parser.parse() || !vdx.commandFound() ) {
         active = false;
         return false;
@@ -174,7 +175,7 @@ bool KSieveUi::VacationUtils::foundVacationScript(const QString &script)
     return parser.parse() && vdx.commandFound();
 }
 
-QString KSieveUi::VacationUtils::composeScript( const QString & messageText,
+QString composeOldScript( const QString & messageText,
                                  const QString &subject,
                                  int notificationInterval,
                                  const AddrSpecList & addrSpecs,
@@ -224,8 +225,91 @@ QString KSieveUi::VacationUtils::composeScript( const QString & messageText,
     }
 
     script += QString::fromLatin1("text:\n");
-    script += dotstuff( messageText.isEmpty() ? VacationUtils::defaultMessageText() : messageText );
+    script += dotstuff( messageText.isEmpty() ? KSieveUi::VacationUtils::defaultMessageText() : messageText );
     script += QString::fromLatin1( "\n.\n;\n" );
     return script;
 }
 
+QString KSieveUi::VacationUtils::composeScript( const QString & messageText, bool active,
+                                 const QString &subject,
+                                 int notificationInterval,
+                                 const AddrSpecList & addrSpecs,
+                                 bool sendForSpam, const QString & domain,
+                                 const QDate & startDate, const QDate & endDate )
+{
+    QStringList condition;
+
+    if (startDate.isValid()) {
+        condition.append(QString::fromLatin1("currentdate :value \"ge\" \"date\" \"%1\"")
+            .arg(startDate.toString(Qt::ISODate)));
+    }
+
+    if (endDate.isValid()) {
+        condition.append(QString::fromLatin1("currentdate :value \"le\" \"date\" \"%1\"")
+            .arg(endDate.toString(Qt::ISODate)));
+    }
+
+    if (!sendForSpam) {
+        condition.append(QString::fromLatin1("not header :contains \"X-Spam-Flag\" \"YES\""));
+    }
+
+    if (!domain.isEmpty()) {
+        condition.append(QString::fromLatin1("address :domain :contains \"from\" \"%1\"").arg( domain ));
+    }
+
+    QString addressesArgument;
+    QStringList aliases;
+    if ( !addrSpecs.empty() ) {
+        addressesArgument += QLatin1String(":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('\\'), QLatin1String("\\\\") ).replace( QLatin1Char('"'), QLatin1String("\\\"") ) + QLatin1Char('"') );
+            aliases.push_back( (*it).asString() );
+        }
+        addressesArgument += sl.join( QLatin1String(", ") ) + QLatin1String(" ] ");
+    }
+
+    QString vacation(QLatin1String("vacation "));
+    vacation += addressesArgument;
+    if ( notificationInterval > 0 )
+        vacation += QString::fromLatin1(":days %1 ").arg(notificationInterval);
+
+    if (!subject.trimmed().isEmpty()) {
+        vacation += QString::fromLatin1(":subject \"%1\" ").arg(stringReplace(subject).trimmed());
+    }
+
+    vacation += QString::fromLatin1("text:\n");
+    vacation += dotstuff( messageText.isEmpty() ? VacationUtils::defaultMessageText() : messageText );
+    vacation += QString::fromLatin1( "\n.\n;" );
+
+    QString script;
+
+    if ( startDate.isValid() || endDate.isValid() ) {
+        script = QString::fromLatin1("require [\"vacation\", \"relational\", \"date\"];\n\n" );
+    } else {
+        script = QString::fromLatin1("require \"vacation\";\n\n" );
+    }
+
+    if (condition.count() == 0) {
+        if (active) {
+            script += vacation;
+        } else {
+            script += QString::fromLatin1("if false\n{\n\t");
+            script += vacation;
+            script += QLatin1String("\n}");
+        }
+    } else {
+        if (active) {
+            script += QString::fromLatin1("if allof(%1)\n{\n\t").arg(condition.join(QLatin1String(", ")));
+        } else {
+            script += QString::fromLatin1("if false # allof(%1)\n{\n\t").arg(condition.join(QLatin1String(", ")));
+        }
+        script += vacation;
+        script += QLatin1String("\n}");
+    }
+
+    script += QLatin1String("\n");
+
+    return script;
+}
diff --git a/libksieve/ksieveui/vacation/vacationutils.h b/libksieve/ksieveui/vacation/vacationutils.h
index 53248c8..a07ebfe 100644
--- a/libksieve/ksieveui/vacation/vacationutils.h
+++ b/libksieve/ksieveui/vacation/vacationutils.h
@@ -40,7 +40,7 @@ QString defaultDomainName();
 QDate defaultStartDate();
 QDate defaultEndDate();
 
-QString composeScript( const QString & messageText, const QString &subject,
+QString composeScript( const QString & messageText, bool active, const QString &subject,
                        int notificationInterval,
                        const KMime::Types::AddrSpecList & aliases,
                        bool sendForSpam, const QString & excludeDomain,




More information about the commits mailing list