diff --git a/libksieve/ksieveui/CMakeLists.txt b/libksieve/ksieveui/CMakeLists.txt index 8f540dc9b8..9a63744432 100644 --- a/libksieve/ksieveui/CMakeLists.txt +++ b/libksieve/ksieveui/CMakeLists.txt @@ -1,199 +1,200 @@ add_definitions( -DQT_NO_CAST_FROM_ASCII ) add_definitions( -DQT_NO_CAST_TO_ASCII ) if(KDEPIM_ENTERPRISE_BUILD) set(USE_GRAPHICAL_SIEVE_EDITOR true) else() set(USE_GRAPHICAL_SIEVE_EDITOR false) endif() configure_file(settings/sieve-editor.kcfg.cmake ${CMAKE_CURRENT_BINARY_DIR}/sieve-editor.kcfg) include_directories(${CMAKE_SOURCE_DIR}/libksieve ${CMAKE_SOURCE_DIR}/pimcommon ${CMAKE_SOURCE_DIR}/pimcommon/sievehighlighter/ ${CMAKE_BINARY_DIR}/pimcommon ${CMAKE_SOURCE_DIR}/pimcommon/util/ ${CMAKE_SOURCE_DIR}/libksieve/ksieveui ${CMAKE_SOURCE_DIR}/libkdepim/ ) add_subdirectory(tests) add_subdirectory(editor/tests) add_subdirectory(managescriptsjob/tests) set(ksieveui_vacation_LIB_SRCS vacation/vacation.cpp vacation/vacationdialog.cpp vacation/vacationhelperjob.cpp vacation/vacationeditwidget.cpp vacation/multiimapvacationdialog.cpp vacation/vacationpagewidget.cpp vacation/vacationwarningwidget.cpp vacation/vacationscriptextractor.cpp vacation/vacationutils.cpp vacation/vacationcreatescriptjob.cpp vacation/vacationcheckjob.cpp vacation/multiimapvacationmanager.cpp vacation/vacationmanager.cpp ) set(ksieveui_editor_LIB_SRCS editor/sieveeditorwidget.cpp editor/sievetextedit.cpp editor/sieveeditor.cpp editor/sievelinenumberarea.cpp editor/sieveinfowidget.cpp editor/sieveeditortextmodewidget.cpp editor/sieveeditorabstractwidget.cpp editor/sieveeditorwarning.cpp editor/sieveeditorparsingmissingfeaturewarning.cpp editor/sieveeditorhelphtmlwidget.cpp editor/sieveeditortabwidget.cpp editor/sieveeditorutil.cpp editor/sieveeditorloadprogressindicator.cpp ) set(ksieveui_autocreatescripts_LIB_SRCS autocreatescripts/sievescriptparsingerrordialog.cpp autocreatescripts/sieveeditorgraphicalmodewidget.cpp autocreatescripts/autocreatescriptdialog.cpp autocreatescripts/sieveconditionwidgetlister.cpp autocreatescripts/sievescriptlistbox.cpp autocreatescripts/sievescriptdescriptiondialog.cpp autocreatescripts/sieveactionwidgetlister.cpp autocreatescripts/sievescriptpage.cpp autocreatescripts/sievescriptblockwidget.cpp autocreatescripts/sievescripttabwidget.cpp autocreatescripts/autocreatescriptutil.cpp autocreatescripts/sieveincludewidget.cpp autocreatescripts/sieveforeverypartwidget.cpp autocreatescripts/sievewidgetpageabstract.cpp autocreatescripts/sieveglobalvariablewidget.cpp autocreatescripts/sieveactions/sieveactionlist.cpp autocreatescripts/sieveactions/sieveaction.cpp autocreatescripts/sieveactions/sieveactiondiscard.cpp autocreatescripts/sieveactions/sieveactionstop.cpp autocreatescripts/sieveactions/sieveactionsetflags.cpp autocreatescripts/sieveactions/sieveactionaddflags.cpp autocreatescripts/sieveactions/sieveactionfileinto.cpp autocreatescripts/sieveactions/sieveactionreject.cpp autocreatescripts/sieveactions/sieveactionkeep.cpp autocreatescripts/sieveactions/sieveactionredirect.cpp autocreatescripts/sieveactions/sieveactionabstractflags.cpp autocreatescripts/sieveactions/sieveactionremoveflags.cpp autocreatescripts/sieveactions/sieveactionnotify.cpp autocreatescripts/sieveactions/sieveactionabstracteditheader.cpp autocreatescripts/sieveactions/sieveactiondeleteheader.cpp autocreatescripts/sieveactions/sieveactionaddheader.cpp autocreatescripts/sieveactions/sieveactionvacation.cpp autocreatescripts/sieveactions/sieveactionenclose.cpp autocreatescripts/sieveactions/sieveactionreplace.cpp autocreatescripts/sieveactions/sieveactionextracttext.cpp autocreatescripts/sieveactions/sieveactionbreak.cpp autocreatescripts/sieveactions/sieveactionconvert.cpp autocreatescripts/sieveactions/sieveactionsetvariable.cpp autocreatescripts/sieveactions/sieveactionreturn.cpp autocreatescripts/sieveactions/widgets/selectflagswidget.cpp autocreatescripts/sieveactions/widgets/selectfileintowidget.cpp autocreatescripts/sieveactions/widgets/addresslineedit.cpp autocreatescripts/sieveactions/widgets/selectaddheaderpositioncombobox.cpp autocreatescripts/sieveactions/widgets/selectimportancecombobox.cpp autocreatescripts/sieveactions/widgets/multilineedit.cpp autocreatescripts/sieveactions/widgets/selectvacationcombobox.cpp autocreatescripts/sieveactions/widgets/selectvariablemodifiercombobox.cpp autocreatescripts/commonwidgets/selectconvertparameterwidget.cpp autocreatescripts/commonwidgets/selectmatchtypecombobox.cpp autocreatescripts/commonwidgets/selectmimetypecombobox.cpp autocreatescripts/commonwidgets/sievehelpbutton.cpp autocreatescripts/sieveconditions/sievecondition.cpp autocreatescripts/sieveconditions/sieveconditionheader.cpp autocreatescripts/sieveconditions/sieveconditionlist.cpp autocreatescripts/sieveconditions/sieveconditionaddress.cpp autocreatescripts/sieveconditions/sieveconditionsize.cpp autocreatescripts/sieveconditions/sieveconditionenvelope.cpp autocreatescripts/sieveconditions/sieveconditionexists.cpp autocreatescripts/sieveconditions/sieveconditiontrue.cpp autocreatescripts/sieveconditions/sieveconditionbody.cpp autocreatescripts/sieveconditions/sieveconditiondate.cpp autocreatescripts/sieveconditions/sieveconditioncurrentdate.cpp autocreatescripts/sieveconditions/sieveconditionmailboxexists.cpp autocreatescripts/sieveconditions/sieveconditionspamtest.cpp autocreatescripts/sieveconditions/sieveconditionvirustest.cpp autocreatescripts/sieveconditions/sieveconditionihave.cpp autocreatescripts/sieveconditions/sieveconditionfalse.cpp autocreatescripts/sieveconditions/sieveconditionenvironment.cpp autocreatescripts/sieveconditions/sieveconditionhasflag.cpp autocreatescripts/sieveconditions/sieveconditionmetadata.cpp autocreatescripts/sieveconditions/sieveconditionconvert.cpp autocreatescripts/sieveconditions/sieveconditionmetadataexists.cpp autocreatescripts/sieveconditions/sieveconditionservermetadata.cpp autocreatescripts/sieveconditions/sieveconditionservermetadataexists.cpp autocreatescripts/sieveconditions/widgets/selectdatewidget.cpp autocreatescripts/sieveconditions/widgets/selectaddresspartcombobox.cpp autocreatescripts/sieveconditions/widgets/selectheadertypecombobox.cpp autocreatescripts/sieveconditions/widgets/selectbodytypewidget.cpp autocreatescripts/sieveconditions/widgets/selectrelationalmatchtype.cpp autocreatescripts/sieveconditions/widgets/selectcomparatorcombobox.cpp autocreatescripts/sieveconditions/widgets/selectmimecombobox.cpp autocreatescripts/sieveconditions/widgets/selectsizetypecombobox.cpp autocreatescripts/sieveconditions/widgets/selectsizewidget.cpp ) set(ksieveui_scriptsparsing_LIB_SRCS scriptsparsing/xmlprintingscriptbuilder.cpp scriptsparsing/parsingresultdialog.cpp scriptsparsing/xmlprintingsyntaxhighlighter.cpp scriptsparsing/parsingutil.cpp ) set(ksieveui_LIB_SRCS managesievescriptsdialog.cpp widgets/sievetreewidgetitem.cpp widgets/managesievetreeview.cpp widgets/managesievewidget.cpp debug/sievedebugdialog.cpp util/util.cpp + managescriptsjob/checkkep14supportjob.cpp managescriptsjob/generateglobalscriptjob.cpp managescriptsjob/parseuserscriptjob.cpp templates/sievetemplatewidget.cpp templates/sievedefaulttemplate.cpp templates/sievetemplateeditdialog.cpp ${ksieveui_scriptsparsing_LIB_SRCS} ${ksieveui_vacation_LIB_SRCS} ${ksieveui_editor_LIB_SRCS} ${ksieveui_autocreatescripts_LIB_SRCS} ) kde4_add_kcfg_files(ksieveui_LIB_SRCS settings/sieve-vacation.kcfgc settings/sieve-editor.kcfgc) kde4_add_library(ksieveui ${LIBRARY_TYPE} ${ksieveui_LIB_SRCS}) target_link_libraries(ksieveui kmanagesieve ksieve pimcommon kdepim ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KMIME_LIBS} ${KDEPIMLIBS_KPIMIDENTITIES_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KDECORE_LIBS} ${QT_QTWEBKIT_LIBRARY} ${KDE4_SOLID_LIBS} ) set_target_properties( ksieveui PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install(TARGETS ksieveui ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES data/ksieve_script.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install(DIRECTORY data/scripts/ DESTINATION ${DATA_INSTALL_DIR}/sieve/scripts/ ) add_subdirectory(autocreatescripts/tests) add_subdirectory(scriptsparsing/tests) add_subdirectory(vacation/tests) diff --git a/libksieve/ksieveui/managescriptsjob/checkkep14supportjob.cpp b/libksieve/ksieveui/managescriptsjob/checkkep14supportjob.cpp new file mode 100644 index 0000000000..d094e1e3da --- /dev/null +++ b/libksieve/ksieveui/managescriptsjob/checkkep14supportjob.cpp @@ -0,0 +1,93 @@ +/* + 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 + +using namespace KSieveUi; + +CheckKep14SupportJob::CheckKep14SupportJob(QObject *parent) + : QObject(parent), + mSieveJob(0) +{ + +} + +CheckKep14SupportJob::~CheckKep14SupportJob() +{ + +} + +void CheckKep14SupportJob::start() +{ + if (mUrl.isEmpty()) { + qDebug() << " 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 KUrl &url) +{ + mUrl = url; +} + +KUrl 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); +} diff --git a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h b/libksieve/ksieveui/managescriptsjob/checkkep14supportjob.h similarity index 52% copy from libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h copy to libksieve/ksieveui/managescriptsjob/checkkep14supportjob.h index 165a06268f..8b189ef822 100644 --- a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h +++ b/libksieve/ksieveui/managescriptsjob/checkkep14supportjob.h @@ -1,59 +1,65 @@ /* - Copyright (c) 2013, 2014 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 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 */ -#ifndef PARSEUSERSCRIPTJOB_H -#define PARSEUSERSCRIPTJOB_H +#ifndef CHECKKEP14SUPPORTJOB_H +#define CHECKKEP14SUPPORTJOB_H #include -#include +#include + #include "ksieveui_export.h" -class QDomDocument; -class QDomElement; + +#include + namespace KManageSieve { class SieveJob; } namespace KSieveUi { -class KSIEVEUI_EXPORT ParseUserScriptJob : public QObject +class KSIEVEUI_EXPORT CheckKep14SupportJob : public QObject { Q_OBJECT public: - explicit ParseUserScriptJob(QObject *parent=0); - ~ParseUserScriptJob(); + explicit CheckKep14SupportJob(QObject *parent=0); + ~CheckKep14SupportJob(); void start(); - void scriptUrl(const KUrl &url); - static QStringList parsescript(const QString &script, bool &result); - + void setServerUrl(const KUrl &url); + void setServerName(const QString &name); + QString serverName(); -private Q_SLOTS: - void slotGetResult( KManageSieve::SieveJob *, bool, const QString &, bool ); + QStringList availableScripts(); + bool hasKep14Support(); + KUrl serverUrl(); Q_SIGNALS: - void success(const QStringList &activeScriptList); - void error(const QString &msgError); + void result(CheckKep14SupportJob*, bool); private: - static QString loadInclude(const QDomElement &element); - static QStringList extractActiveScript(const QDomDocument &doc); - KUrl mCurrentUrl; + KUrl mUrl; 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 // PARSEUSERSCRIPTJOB_H +#endif // CHECKKEP14SUPPORTJOB_H diff --git a/libksieve/ksieveui/managescriptsjob/generateglobalscriptjob.cpp b/libksieve/ksieveui/managescriptsjob/generateglobalscriptjob.cpp index 4f75ba258f..61edb858fd 100644 --- a/libksieve/ksieveui/managescriptsjob/generateglobalscriptjob.cpp +++ b/libksieve/ksieveui/managescriptsjob/generateglobalscriptjob.cpp @@ -1,135 +1,135 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "generateglobalscriptjob.h" #include "libksieve/kmanagesieve/sievejob.h" #include using namespace KSieveUi; GenerateGlobalScriptJob::GenerateGlobalScriptJob(const KUrl &url, QObject *parent) : QObject(parent), mCurrentUrl(url), mMasterjob(0), mUserJob(0) { } GenerateGlobalScriptJob::~GenerateGlobalScriptJob() { if (mMasterjob) mMasterjob->kill(); if (mUserJob) mUserJob->kill(); } void GenerateGlobalScriptJob::addUserActiveScripts(const QStringList &lstScript) { mListUserActiveScripts = lstScript; } void GenerateGlobalScriptJob::start() { if (mCurrentUrl.isEmpty()) { Q_EMIT error(i18n("Path is not specified.")); return; } - writeMasterScript(); + writeUserScript(); } void GenerateGlobalScriptJob::writeMasterScript() { const QString masterScript = QLatin1String("# MASTER\n" "#\n" "# This file is authoritative for your system and MUST BE KEPT ACTIVE.\n" "#\n" "# Altering it is likely to render your account dysfunctional and may\n" "# be violating your organizational or corporate policies.\n" "# \n" "# For more information on the mechanism and the conventions behind\n" "# this script, see http://wiki.kolab.org/KEP:14\n" "#\n" "\n" "require [\"include\"];\n" "\n" "# OPTIONAL: Includes for all or a group of users\n" "# include :global \"all-users\";\n" "# include :global \"this-group-of-users\";\n" "\n" "# The script maintained by the general management system\n" "include :personal :optional \"MANAGEMENT\";\n" "\n" "# The script(s) maintained by one or more editors available to the user\n" "include :personal :optional \"USER\";\n"); KUrl url(mCurrentUrl); url.setFileName(QLatin1String("MASTER")); mMasterjob = KManageSieve::SieveJob::put(url, masterScript, true, true ); connect( mMasterjob, SIGNAL(result(KManageSieve::SieveJob*,bool,QString,bool)), this, SLOT(slotPutMasterResult(KManageSieve::SieveJob*,bool)) ); } void GenerateGlobalScriptJob::slotPutMasterResult( KManageSieve::SieveJob *, bool success ) { if (!success) { Q_EMIT error(i18n("Error when we wrote \"MASTER\" script on server.")); return; } mMasterjob = 0; writeUserScript(); } void GenerateGlobalScriptJob::writeUserScript() { QString userScript = QLatin1String("# USER Management Script\n" "#\n" "# This script includes the various active sieve scripts\n" "# it is AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY!\n" "# \n" "# For more information, see http://wiki.kolab.org/KEP:14#USER\n" "#\n" "\n" "require [\"include\"];\n"); Q_FOREACH (const QString &activeScript, mListUserActiveScripts) { - userScript += QString::fromLatin1("\ninclude :personal \"%1\"").arg(activeScript); + userScript += QString::fromLatin1("\ninclude :personal \"%1\";").arg(activeScript); } KUrl url(mCurrentUrl); url.setFileName(QLatin1String("USER")); mUserJob = KManageSieve::SieveJob::put(url, userScript, false, false ); connect( mUserJob, SIGNAL(result(KManageSieve::SieveJob*,bool,QString,bool)), this, SLOT(slotPutUserResult(KManageSieve::SieveJob*,bool)) ); } void GenerateGlobalScriptJob::slotPutUserResult( KManageSieve::SieveJob *, bool success ) { mUserJob = 0; if (!success) { Q_EMIT error(i18n("Error when we wrote \"User\" script on server.")); return; } disableAllOtherScripts(); } void GenerateGlobalScriptJob::disableAllOtherScripts() { //TODO Q_EMIT success(); } diff --git a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.cpp b/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.cpp index ef06c50d11..da664e5821 100644 --- a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.cpp +++ b/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.cpp @@ -1,128 +1,154 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "parseuserscriptjob.h" #include "ksieveui/scriptsparsing/parsingutil.h" #include #include using namespace KSieveUi; -ParseUserScriptJob::ParseUserScriptJob(QObject *parent) +ParseUserScriptJob::ParseUserScriptJob(const KUrl &url, QObject *parent) : QObject(parent), mSieveJob(0) + , mCurrentUrl(url) { } ParseUserScriptJob::~ParseUserScriptJob() { if ( mSieveJob ) mSieveJob->kill(); mSieveJob = 0; } -void ParseUserScriptJob::scriptUrl(const KUrl &url) +KUrl 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, SIGNAL(result(KManageSieve::SieveJob*,bool,QString,bool)), this, SLOT(slotGetResult(KManageSieve::SieveJob*,bool,QString,bool)) ); } void ParseUserScriptJob::slotGetResult( KManageSieve::SieveJob *, bool, const QString & script, bool ) { mSieveJob = 0; 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; const QDomDocument doc = ParsingUtil::parseScript(script, result); if (result) { lst = extractActiveScript(doc); } return lst; } +QStringList ParseUserScriptJob::activeScriptList() const +{ + return mActiveScripts; +} + +QString ParseUserScriptJob::error() const +{ + return mError; +} + QStringList ParseUserScriptJob::extractActiveScript(const QDomDocument &doc) { QStringList lstScript; QDomElement docElem = doc.documentElement(); QDomNode n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull()) { const QString tagName = e.tagName(); if (tagName == QLatin1String("action")) { if (e.hasAttribute(QLatin1String("name"))) { const QString actionName = e.attribute(QLatin1String("name")); if (actionName == QLatin1String("include")) { //Load includes const QString str = loadInclude(e); if (!str.isEmpty()) { if (!lstScript.contains(str)) { lstScript.append(str); } } } } } } n = n.nextSibling(); } return lstScript; } QString ParseUserScriptJob::loadInclude(const QDomElement &element) { QString scriptName; QDomNode node = element.firstChild(); while (!node.isNull()) { QDomElement e = node.toElement(); if (!e.isNull()) { const QString tagName = e.tagName(); if (tagName == QLatin1String("str")) { scriptName = e.text(); } } node = node.nextSibling(); } return scriptName; } diff --git a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h b/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h index 165a06268f..595ef66643 100644 --- a/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h +++ b/libksieve/ksieveui/managescriptsjob/parseuserscriptjob.h @@ -1,59 +1,65 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef PARSEUSERSCRIPTJOB_H #define PARSEUSERSCRIPTJOB_H #include +#include #include #include "ksieveui_export.h" class QDomDocument; class QDomElement; namespace KManageSieve { class SieveJob; } namespace KSieveUi { class KSIEVEUI_EXPORT ParseUserScriptJob : public QObject { Q_OBJECT public: - explicit ParseUserScriptJob(QObject *parent=0); + explicit ParseUserScriptJob(const KUrl &url,QObject *parent=0); ~ParseUserScriptJob(); void start(); - void scriptUrl(const KUrl &url); - static QStringList parsescript(const QString &script, bool &result); + KUrl scriptUrl() const; + QStringList activeScriptList() const; + QString error() const; 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); KUrl mCurrentUrl; KManageSieve::SieveJob *mSieveJob; + QStringList mActiveScripts; + QString mError; }; } #endif // PARSEUSERSCRIPTJOB_H diff --git a/libksieve/ksieveui/util/util.cpp b/libksieve/ksieveui/util/util.cpp index 542f586f51..cc1e24bdb7 100644 --- a/libksieve/ksieveui/util/util.cpp +++ b/libksieve/ksieveui/util/util.cpp @@ -1,168 +1,210 @@ /******************************************************************************* ** ** Filename : util ** Created on : 03 April, 2005 ** Copyright : (c) 2005 Till Adam ** Email : ** *******************************************************************************/ /******************************************************************************* ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** It 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 ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of this program with any edition of ** the Qt library by Trolltech AS, Norway (or with modified versions ** of Qt that use the same license as Qt), and distribute linked ** combinations including the two. You must obey the GNU General ** Public License in all respects for all of the code used other than ** Qt. If you modify this file, you may extend this exception to ** your version of the file, but you are not obligated to do so. If ** you do not wish to do so, delete this exception statement from ** your version. ** *******************************************************************************/ #include "util.h" #include "pimcommon/util/pimutil.h" #include "imapresourcesettings.h" #include "sieve-vacation.h" #include #include #include #include using namespace KSieveUi; KUrl KSieveUi::Util::findSieveUrlForAccount( const QString &identifier ) { QScopedPointer interface( PimCommon::Util::createImapSettingsInterface(identifier) ); if ( !interface->sieveSupport() ) return KUrl(); if ( interface->sieveReuseConfig() ) { // assemble Sieve url from the settings of the account: KUrl u; u.setScheme( QLatin1String("sieve") ); QString server; QDBusReply reply = interface->imapServer(); if ( reply.isValid() ) { server = reply; server = server.section( QLatin1Char(':'), 0, 0 ); } else { return KUrl(); } u.setHost( server ); u.setUserName( interface->userName() ); QDBusInterface resourceSettings( QLatin1String( "org.freedesktop.Akonadi.Resource." ) + identifier, QLatin1String("/Settings"), QLatin1String("org.kde.Akonadi.Imap.Wallet") ); QString pwd; QDBusReply replyPass = resourceSettings.call( QLatin1String("password") ); if ( replyPass.isValid() ) { pwd = replyPass; } u.setPassword( pwd ); u.setPort( interface->sievePort() ); QString authStr; switch( interface->authentication() ) { case MailTransport::Transport::EnumAuthenticationType::CLEAR: case MailTransport::Transport::EnumAuthenticationType::PLAIN: authStr = QLatin1String("PLAIN"); break; case MailTransport::Transport::EnumAuthenticationType::LOGIN: authStr = QLatin1String("LOGIN"); break; case MailTransport::Transport::EnumAuthenticationType::CRAM_MD5: authStr = QLatin1String("CRAM-MD5"); break; case MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5: authStr = QLatin1String("DIGEST-MD5"); break; case MailTransport::Transport::EnumAuthenticationType::GSSAPI: authStr = QLatin1String("GSSAPI"); break; case MailTransport::Transport::EnumAuthenticationType::ANONYMOUS: authStr = QLatin1String("ANONYMOUS"); break; default: authStr = QLatin1String("PLAIN"); break; } u.addQueryItem( QLatin1String("x-mech"), authStr ); const QString resultSafety = interface->safety(); if ( resultSafety == QLatin1String("None")) u.addQueryItem( QLatin1String("x-allow-unencrypted"), QLatin1String("true") ); u.setFileName( interface->sieveVacationFilename() ); return u; } else { KUrl u( interface->sieveAlternateUrl() ); const QString resultSafety = interface->safety(); if ( u.protocol().toLower() == QLatin1String("sieve") && ( resultSafety == QLatin1String("None") ) && u.queryItem(QLatin1String("x-allow-unencrypted")).isEmpty() ) u.addQueryItem( QLatin1String("x-allow-unencrypted"), QLatin1String("true") ); const QString resultCustomAuthentication = interface->sieveCustomAuthentification(); if (resultCustomAuthentication == QLatin1String("ImapUserPassword")) { u.setUserName( interface->userName() ); QDBusInterface resourceSettings( QLatin1String( "org.freedesktop.Akonadi.Resource." ) + identifier, QLatin1String("/Settings"), QLatin1String("org.kde.Akonadi.Imap.Wallet") ); QString pwd; QDBusReply replyPass = resourceSettings.call( QLatin1String("password") ); if ( replyPass.isValid() ) { pwd = replyPass; } u.setPassword( pwd ); } else if (resultCustomAuthentication == QLatin1String("CustomUserPassword")) { QDBusInterface resourceSettings( QLatin1String( "org.freedesktop.Akonadi.Resource." ) + identifier, QLatin1String("/Settings"), QLatin1String("org.kde.Akonadi.Imap.Wallet") ); QString pwd; QDBusReply replyPass = resourceSettings.call( QLatin1String("sieveCustomPassword") ); if ( replyPass.isValid() ) { pwd = replyPass; } u.setPassword( pwd ); u.setUserName( interface->sieveCustomUsername() ); } u.setFileName( interface->sieveVacationFilename() ); return u; } } Akonadi::AgentInstance::List KSieveUi::Util::imapAgentInstances() { Akonadi::AgentInstance::List relevantInstances; foreach ( const Akonadi::AgentInstance &instance, Akonadi::AgentManager::self()->instances() ) { if ( instance.type().mimeTypes().contains( KMime::Message::mimeType() ) && instance.type().capabilities().contains( QLatin1String("Resource") ) && !instance.type().capabilities().contains( QLatin1String("Virtual") ) ) { if ( instance.identifier().contains( IMAP_RESOURCE_IDENTIFIER ) ) relevantInstances << instance; } } return relevantInstances; } bool KSieveUi::Util::checkOutOfOfficeOnStartup() { return VacationSettings::self()->checkOutOfOfficeOnStartup(); } bool KSieveUi::Util::allowOutOfOfficeSettings() { return VacationSettings::self()->allowOutOfOfficeSettings(); } + +bool Util::hasKep14Support(const QStringList &sieveCapabilities, const QStringList &availableScripts, const QString &activeScript) +{ + const bool hasIncludeCapability = sieveCapabilities.contains(QLatin1String("include")); + if (!hasIncludeCapability) { + return false; + } + + bool masterIsActive = !activeScript.isEmpty(); + if (masterIsActive) { + const QString scriptName = activeScript.split(QLatin1Char('.')).first().toLower(); + masterIsActive = (scriptName == QLatin1String("master") || scriptName == QLatin1String("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 == QLatin1String("user")) { + hasUserScript = true; + break; + } + } + + return hasIncludeCapability && masterIsActive && hasUserScript; +} + +bool Util::isKep14ProtectedName(const QString &name) +{ + QString n = name.split(QLatin1Char('.')).first().toLower(); + if (n == QLatin1String("master") || + n == QLatin1String("user") || + n == QLatin1String("management")) { + return true; + } + return false; +} \ No newline at end of file diff --git a/libksieve/ksieveui/util/util.h b/libksieve/ksieveui/util/util.h index a1a83cc61d..04621177e0 100644 --- a/libksieve/ksieveui/util/util.h +++ b/libksieve/ksieveui/util/util.h @@ -1,82 +1,94 @@ /******************************************************************************* ** ** Filename : util ** Created on : 03 April, 2005 ** Copyright : (c) 2005 Till Adam ** Email : ** *******************************************************************************/ /******************************************************************************* ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** It 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 ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of this program with any edition of ** the Qt library by Trolltech AS, Norway (or with modified versions ** of Qt that use the same license as Qt), and distribute linked ** combinations including the two. You must obey the GNU General ** Public License in all respects for all of the code used other than ** Qt. If you modify this file, you may extend this exception to ** your version of the file, but you are not obligated to do so. If ** you do not wish to do so, delete this exception statement from ** your version. ** *******************************************************************************/ #ifndef KSIEVE_KSIEVEUI_UTIL_H #define KSIEVE_KSIEVEUI_UTIL_H #include "ksieveui_export.h" #include class KUrl; class QString; +class QStringList; namespace KSieveUi { /** * The Util namespace contains a collection of helper functions use in * various places. */ namespace Util { /** * Returns the sieve url for the account with the given @p identifier. */ KSIEVEUI_EXPORT KUrl findSieveUrlForAccount( const QString &identifier ); /** * Returns the list of configured IMAP agent instances. */ KSIEVEUI_EXPORT Akonadi::AgentInstance::List imapAgentInstances(); /** * Returns whether the availability of a vacation sieve script shall * be checked at the start up of an application. */ KSIEVEUI_EXPORT bool checkOutOfOfficeOnStartup(); /** * Returns whether the functionality of vacation sieve script editing shall * 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); } } #endif diff --git a/libksieve/ksieveui/vacation/multiimapvacationdialog.cpp b/libksieve/ksieveui/vacation/multiimapvacationdialog.cpp index 7afe53ec88..3619759813 100644 --- a/libksieve/ksieveui/vacation/multiimapvacationdialog.cpp +++ b/libksieve/ksieveui/vacation/multiimapvacationdialog.cpp @@ -1,158 +1,156 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "multiimapvacationdialog.h" #include "vacationpagewidget.h" +#include "multiimapvacationmanager.h" #include "ksieveui/util/util.h" #include #include #include #include #include #include #include #include #include using namespace KSieveUi; -MultiImapVacationDialog::MultiImapVacationDialog(QWidget *parent) +MultiImapVacationDialog::MultiImapVacationDialog(MultiImapVacationManager *manager, QWidget *parent) : KDialog(parent) + , mVacationManager(manager) { setCaption( 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)) ); mStackedWidget = new QStackedWidget; setMainWidget(mStackedWidget); mTabWidget = new KTabWidget; mStackedWidget->addWidget(mTabWidget); QWidget *w = new QWidget; QVBoxLayout *vbox = new QVBoxLayout; w->setLayout(vbox); QLabel *lab = new QLabel(i18n("KMail's Out of Office Reply functionality relies on " "server-side filtering. You have not yet configured an " "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(); mStackedWidget->addWidget(w); mStackedWidget->setCurrentIndex(0); init(); readConfig(); connect(this, SIGNAL(okClicked()), this, SLOT(slotOkClicked())); connect(this, SIGNAL(defaultClicked()), this, SLOT(slotDefaultClicked())); } MultiImapVacationDialog::~MultiImapVacationDialog() { writeConfig(); } void MultiImapVacationDialog::switchToServerNamePage(const QString &serverName) { for (int i=0; i < mTabWidget->count(); ++i) { if (mTabWidget->tabText(i) == serverName) { mTabWidget->setCurrentIndex(i); break; } } } QList MultiImapVacationDialog::listCreateJob() const { return mListCreateJob; } void MultiImapVacationDialog::init() { bool foundOneImap = false; - const Akonadi::AgentInstance::List instances = KSieveUi::Util::imapAgentInstances(); - foreach ( const Akonadi::AgentInstance &instance, instances ) { - if ( instance.status() == Akonadi::AgentInstance::Broken ) - continue; - - const KUrl url = KSieveUi::Util::findSieveUrlForAccount( instance.identifier() ); - if ( !url.isEmpty() ) { - const QString serverName = instance.name(); - createPage(serverName, url); - foundOneImap = true; - } + + QMap list = mVacationManager->serverList(); + foreach (const QString &serverName, list.keys()) { + const KUrl url = list.value(serverName); + createPage(serverName, url); + foundOneImap = true; } if (foundOneImap) { setButtons( Ok | Cancel | Default ); setDefaultButton( Ok ); } else { mStackedWidget->setCurrentIndex(1); setButtons( Close ); } if (mTabWidget->count() <= 1) mTabWidget->setTabBarHidden(true); } void MultiImapVacationDialog::createPage(const QString &serverName, const KUrl &url) { VacationPageWidget *page = new VacationPageWidget; page->setServerUrl(url); page->setServerName(serverName); + page->setVacationManager(mVacationManager); mTabWidget->addTab(page,serverName); } void MultiImapVacationDialog::readConfig() { KConfigGroup group( KGlobal::config(), "MultiImapVacationDialog" ); const QSize size = group.readEntry( "Size", QSize() ); if ( size.isValid() ) { resize( size ); } else { resize( sizeHint().width(), sizeHint().height() ); } } void MultiImapVacationDialog::writeConfig() { KConfigGroup group( KGlobal::config(), "MultiImapVacationDialog" ); group.writeEntry( "Size", size() ); } void MultiImapVacationDialog::slotOkClicked() { for (int i=0; i < mTabWidget->count(); ++i) { VacationPageWidget *vacationPage = qobject_cast(mTabWidget->widget(i)); if (vacationPage) { VacationCreateScriptJob *job = vacationPage->writeScript(); if (job) mListCreateJob.append(job); } } } void MultiImapVacationDialog::slotDefaultClicked() { for (int i=0; i < mTabWidget->count(); ++i) { VacationPageWidget *vacationPage = qobject_cast(mTabWidget->widget(i)); if (vacationPage) { vacationPage->setDefault(); } } } diff --git a/libksieve/ksieveui/vacation/multiimapvacationdialog.h b/libksieve/ksieveui/vacation/multiimapvacationdialog.h index b6035882c7..248837b844 100644 --- a/libksieve/ksieveui/vacation/multiimapvacationdialog.h +++ b/libksieve/ksieveui/vacation/multiimapvacationdialog.h @@ -1,55 +1,57 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef MULTIIMAPVACATIONDIALOG_H #define MULTIIMAPVACATIONDIALOG_H #include #include "ksieveui_export.h" class KTabWidget; class QStackedWidget; namespace KSieveUi { class VacationCreateScriptJob; +class MultiImapVacationManager; class KSIEVEUI_EXPORT MultiImapVacationDialog : public KDialog { Q_OBJECT public: - explicit MultiImapVacationDialog(QWidget *parent=0); + explicit MultiImapVacationDialog(MultiImapVacationManager *manager, QWidget *parent=0); ~MultiImapVacationDialog(); QList listCreateJob() const; void switchToServerNamePage(const QString &serverName); private slots: void slotOkClicked(); void slotDefaultClicked(); private: void createPage(const QString &serverName, const KUrl &url); void init(); void readConfig(); void writeConfig(); QList mListCreateJob; KTabWidget *mTabWidget; QStackedWidget *mStackedWidget; + MultiImapVacationManager* mVacationManager; }; } #endif // MULTIIMAPVACATIONDIALOG_H diff --git a/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp b/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp index 561ca28bc1..563b675ed9 100644 --- a/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp +++ b/libksieve/ksieveui/vacation/multiimapvacationmanager.cpp @@ -1,84 +1,123 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "multiimapvacationmanager.h" #include "vacationcheckjob.h" #include "util/util.h" +#include +#include +#include #include #include #include using namespace KSieveUi; MultiImapVacationManager::MultiImapVacationManager(QObject *parent) : QObject(parent), mNumberOfJobs(0), - mQuestionAsked(false), mCheckInProgress(false) { } MultiImapVacationManager::~MultiImapVacationManager() { } -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 KUrl url = KSieveUi::Util::findSieveUrlForAccount( instance.identifier() ); if ( !url.isEmpty() ) { - const QString serverName = instance.name(); - ++mNumberOfJobs; - VacationCheckJob *job = new VacationCheckJob(url, serverName, this); - connect(job, SIGNAL(scriptActive(bool,QString)), this, SLOT(slotScriptActive(bool,QString))); + list.insert(instance.name(), url); } } + return list; } -void MultiImapVacationManager::slotScriptActive(bool active, const QString &serverName) +void MultiImapVacationManager::checkVacation(const QString &serverName, const KUrl &url) { - --mNumberOfJobs; - Q_EMIT scriptActive(active, serverName); - - if (active) { - if (!mQuestionAsked) { - 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 ) { - 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, SIGNAL(result(CheckKep14SupportJob*,bool)), SLOT(slotCheckKep14Ended(CheckKep14SupportJob*,bool))); + checkKep14Job->start(); + } + + VacationCheckJob *job = new VacationCheckJob(url, serverName, this); + job->setKep14Support(mKep14Support[serverName]); + connect(job, SIGNAL(scriptActive(VacationCheckJob*,QString,bool)), this, SLOT(slotScriptActive(VacationCheckJob*,QString,bool))); + job->start(); +} + +void MultiImapVacationManager::checkVacation() +{ + if (mCheckInProgress) + return; + mNumberOfJobs = 0; + mCheckInProgress = true; + + QMap list = serverList(); + foreach ( const QString &serverName, list.keys() ) { + const KUrl url = list.value(serverName); + checkVacation(serverName, url); } +} - if (mNumberOfJobs == 0) +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, SIGNAL(scriptActive(VacationCheckJob*,QString,bool)), + SLOT(slotScriptActive(VacationCheckJob*,QString,bool))); + checkJob->start(); } diff --git a/libksieve/ksieveui/vacation/multiimapvacationmanager.h b/libksieve/ksieveui/vacation/multiimapvacationmanager.h index c61fee4e9d..aee0c83a32 100644 --- a/libksieve/ksieveui/vacation/multiimapvacationmanager.h +++ b/libksieve/ksieveui/vacation/multiimapvacationmanager.h @@ -1,48 +1,57 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef MULTIIMAPVACATIONMANAGER_H #define MULTIIMAPVACATIONMANAGER_H #include +#include #include "ksieveui_export.h" +class KUrl; + namespace KSieveUi { +class CheckKep14SupportJob; +class VacationCheckJob; class KSIEVEUI_EXPORT MultiImapVacationManager : public QObject { Q_OBJECT public: explicit MultiImapVacationManager(QObject *parent=0); ~MultiImapVacationManager(); void checkVacation(); + QMap serverList(); + void checkVacation(const QString &serverName, const KUrl &url); 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 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 diff --git a/libksieve/ksieveui/vacation/tests/main.cpp b/libksieve/ksieveui/vacation/tests/main.cpp index 773a887ec3..9320f1911c 100644 --- a/libksieve/ksieveui/vacation/tests/main.cpp +++ b/libksieve/ksieveui/vacation/tests/main.cpp @@ -1,38 +1,40 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 #include #include #include "vacation/multiimapvacationdialog.h" +#include #include int main( int argc, char** argv ) { KCmdLineArgs::init(argc, argv, "vacationmultiscripttest", 0, ki18n("VacationMultiScriptTest_Gui"), "1.0", ki18n("Test for dialog when server has multiscript")); KApplication app; app.setQuitOnLastWindowClosed( false ); - KSieveUi::MultiImapVacationDialog dlg; + KSieveUi::MultiImapVacationManager manager; + KSieveUi::MultiImapVacationDialog dlg(&manager); dlg.show(); app.exec(); return 0; } diff --git a/libksieve/ksieveui/vacation/vacationcheckjob.cpp b/libksieve/ksieveui/vacation/vacationcheckjob.cpp index 898aad9e89..b323e96c7a 100644 --- a/libksieve/ksieveui/vacation/vacationcheckjob.cpp +++ b/libksieve/ksieveui/vacation/vacationcheckjob.cpp @@ -1,47 +1,196 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "vacationcheckjob.h" +#include "vacationutils.h" +#include +#include #include +#include + using namespace KSieveUi; VacationCheckJob::VacationCheckJob(const KUrl &url, const QString &serverName, QObject *parent) : QObject(parent), mServerName(serverName), mUrl(url) + , mKep14Support(false) + , mSieveJob(0) + , mParseJob(0) + , mNoScriptFound(0) { - mSieveJob = KManageSieve::SieveJob::get( mUrl ); - mSieveJob->setInteractive( false ); - connect( mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)), - SLOT(slotGetResult(KManageSieve::SieveJob*,bool,QString,bool)) ); } VacationCheckJob::~VacationCheckJob() { if ( mSieveJob ) mSieveJob->kill(); mSieveJob = 0; } -void VacationCheckJob::slotGetResult(KManageSieve::SieveJob */*job*/, bool success, const QString &/*script*/, bool active) +void VacationCheckJob::setKep14Support(bool kep14Support) +{ + mKep14Support = kep14Support; +} + +void VacationCheckJob::start() +{ + if (mKep14Support) { + KUrl url = mUrl; + url.setFileName(QLatin1String("USER")); + mParseJob = new ParseUserScriptJob(url); + connect(mParseJob, SIGNAL(finished(ParseUserScriptJob*)), SLOT(slotGotActiveScripts(ParseUserScriptJob*))); + mParseJob->start(); + mSieveJob = KManageSieve::SieveJob::list(url); + connect(mSieveJob, SIGNAL(gotList(KManageSieve::SieveJob*,bool,QStringList,QString)), + this, SLOT(slotGotList(KManageSieve::SieveJob*,bool,QStringList,QString))); + } else { + mSieveJob = KManageSieve::SieveJob::get(mUrl); + mSieveJob->setInteractive(false); + connect(mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)), + SLOT(slotGetResult(KManageSieve::SieveJob*,bool,QString,bool))); + } +} + +void VacationCheckJob::slotGetResult(KManageSieve::SieveJob */*job*/, bool success, const QString &script, bool active) +{ + mScript = script; + mSieveCapabilities = mSieveJob->sieveCapabilities(); + mSieveJob = 0; + + if (mKep14Support) { + if (isVacationScipt(script)) { + const QString &scriptName = mAvailableScripts[mScriptPos-1]; + emit scriptActive(this, scriptName, mActiveScripts.contains(scriptName)); + kDebug() << "vacation script found :)"; + } else if (isLastScript()) { + mNoScriptFound = true; + emit scriptActive(this, QString(), false); + kDebug() << "no vacation script found :("; + } else { + getNextScript(); + } + } else { + if ( !success ) + active = false; // default to inactive + mNoScriptFound = true; + emit scriptActive(this, mUrl.fileName(), active); + } +} + +void VacationCheckJob::slotGotActiveScripts(ParseUserScriptJob *job) +{ + mParseJob = 0; + if (!job->error().isEmpty()) { + emitError(QLatin1String("ParseUserScriptJob failed:")+job->error()); + return; + } + mActiveScripts = job->activeScriptList(); + + if (!mSieveJob) { + searchVacationScript(); + } +} + +void VacationCheckJob::slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &availableScripts, const QString &activeScript) { mSieveJob = 0; - if ( !success ) - active = false; // default to inactive - emit scriptActive( active, mServerName ); + if (!success) { + emitError(QLatin1String("SieveJob list failed.")); + return; + } + + mAvailableScripts = availableScripts; + + if (!mParseJob) { + searchVacationScript(); + } +} + +void VacationCheckJob::emitError(const QString &errorMessage) +{ + qWarning() << errorMessage; + //TODO: emit error +} + +void VacationCheckJob::searchVacationScript() +{ + QStringList scriptList = mActiveScripts; + + // Reorder script list + foreach(const QString &script, mAvailableScripts) { + if (!scriptList.contains(script)) { + scriptList.append(script); + } + } + + mAvailableScripts = scriptList; + mScriptPos = 0; + getNextScript(); +} + +void VacationCheckJob::getNextScript() +{ + if (isLastScript()) { + //TODO: no script found + mNoScriptFound = true; + emit scriptActive(this, QString(), false); + kDebug() << "no vacation script found :("; + } + KUrl url = mUrl; + url.setFileName(mAvailableScripts[mScriptPos]); + mScriptPos += 1; + if (Util::isKep14ProtectedName(url.fileName())) { + getNextScript(); + } + mSieveJob = KManageSieve::SieveJob::get(url); + mSieveJob->setInteractive(false); + connect(mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)), + SLOT(slotGetResult(KManageSieve::SieveJob*,bool,QString,bool))); +} + +bool VacationCheckJob::isLastScript() const +{ + return mScriptPos >= mAvailableScripts.count(); +} + +bool VacationCheckJob::isVacationScipt(const QString &script) const +{ + return KSieveUi::VacationUtils::foundVacationScript(script); +} + +bool VacationCheckJob::noScriptFound() +{ + return mNoScriptFound; +} + +QString VacationCheckJob::serverName() +{ + return mServerName; +} + +QString VacationCheckJob::script() +{ + return mScript; +} + +QStringList VacationCheckJob::sieveCapabilities() +{ + return mSieveCapabilities; } + diff --git a/libksieve/ksieveui/vacation/vacationcheckjob.h b/libksieve/ksieveui/vacation/vacationcheckjob.h index a95c1cd689..b86282209c 100644 --- a/libksieve/ksieveui/vacation/vacationcheckjob.h +++ b/libksieve/ksieveui/vacation/vacationcheckjob.h @@ -1,49 +1,72 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef VACATIONCHECKJOB_H #define VACATIONCHECKJOB_H #include +#include #include namespace KManageSieve { class SieveJob; } namespace KSieveUi { +class ParseUserScriptJob; class VacationCheckJob : public QObject { Q_OBJECT public: explicit VacationCheckJob(const KUrl &url, const QString &serverName, QObject *parent=0); ~VacationCheckJob(); + void setKep14Support(bool kep14Support); + void start(); + 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 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 isVacationScipt(const QString &script) const; + bool isLastScript() const; private: QString mServerName; KUrl mUrl; KManageSieve::SieveJob * mSieveJob; + ParseUserScriptJob *mParseJob; + bool mKep14Support; + QStringList mAvailableScripts; + QStringList mActiveScripts; + int mScriptPos; + bool mNoScriptFound; + QString mScript; + QStringList mSieveCapabilities; }; } #endif // VACATIONCHECKJOB_H diff --git a/libksieve/ksieveui/vacation/vacationmanager.cpp b/libksieve/ksieveui/vacation/vacationmanager.cpp index ea93204d17..6cad984798 100644 --- a/libksieve/ksieveui/vacation/vacationmanager.cpp +++ b/libksieve/ksieveui/vacation/vacationmanager.cpp @@ -1,87 +1,101 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "vacationmanager.h" #include "ksieveui/vacation/multiimapvacationmanager.h" #include "ksieveui/vacation/multiimapvacationdialog.h" #include "ksieveui/vacation/vacationcreatescriptjob.h" #include #include #include using namespace KSieveUi; VacationManager::VacationManager(QWidget *parent) : QObject(parent), mWidget(parent) + , mMultiImapVacationDialog(0) + , mQuestionAsked(false) { + mCheckVacation = new KSieveUi::MultiImapVacationManager( this ); + connect( mCheckVacation, SIGNAL(scriptActive(bool,QString)), SIGNAL(updateVacationScriptStatus(bool,QString)) ); + connect( mCheckVacation, SIGNAL(scriptActive(bool,QString)), SLOT(slotUpdateVacationScriptStatus(bool,QString)) ); } VacationManager::~VacationManager() { + delete mCheckVacation; } void VacationManager::checkVacation() { - delete mCheckVacation; - - mCheckVacation = new KSieveUi::MultiImapVacationManager( this ); - connect( mCheckVacation, SIGNAL(scriptActive(bool,QString)), SIGNAL(updateVacationScriptStatus(bool,QString)) ); - connect( mCheckVacation, SIGNAL(requestEditVacation()), SIGNAL(editVacation()) ); mCheckVacation->checkVacation(); } +void VacationManager::slotUpdateVacationScriptStatus(bool active, const QString &serverName) +{ + if (active) { + if (!mQuestionAsked) { + 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 ( mMultiImapVacationDialog ) { - mMultiImapVacationDialog->show(); mMultiImapVacationDialog->raise(); mMultiImapVacationDialog->activateWindow(); - if (!serverName.isEmpty()) { - mMultiImapVacationDialog->switchToServerNamePage(serverName); - } - return; + } else { + mMultiImapVacationDialog = new KSieveUi::MultiImapVacationDialog(mCheckVacation, mWidget); + connect( mMultiImapVacationDialog, SIGNAL(okClicked()), SLOT(slotDialogOk()) ); + connect( mMultiImapVacationDialog, SIGNAL(cancelClicked()), SLOT(slotDialogCanceled()) ); } - mMultiImapVacationDialog = new KSieveUi::MultiImapVacationDialog(mWidget); - connect( mMultiImapVacationDialog, SIGNAL(okClicked()), SLOT(slotDialogOk()) ); - connect( mMultiImapVacationDialog, SIGNAL(cancelClicked()), SLOT(slotDialogCanceled()) ); mMultiImapVacationDialog->show(); if (!serverName.isEmpty()) { mMultiImapVacationDialog->switchToServerNamePage(serverName); } } void VacationManager::slotDialogCanceled() { mMultiImapVacationDialog->delayedDestruct(); mMultiImapVacationDialog = 0; } void VacationManager::slotDialogOk() { QList listJob = mMultiImapVacationDialog->listCreateJob(); Q_FOREACH (KSieveUi::VacationCreateScriptJob *job, listJob) { connect(job, SIGNAL(scriptActive(bool,QString)), SIGNAL(updateVacationScriptStatus(bool,QString))); job->start(); } mMultiImapVacationDialog->delayedDestruct(); mMultiImapVacationDialog = 0; } diff --git a/libksieve/ksieveui/vacation/vacationmanager.h b/libksieve/ksieveui/vacation/vacationmanager.h index 55fc540da1..5f2678e8a8 100644 --- a/libksieve/ksieveui/vacation/vacationmanager.h +++ b/libksieve/ksieveui/vacation/vacationmanager.h @@ -1,58 +1,60 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef VACATIONMANAGER_H #define VACATIONMANAGER_H #include #include "ksieveui_export.h" #include class QWidget; namespace KSieveUi { class MultiImapVacationManager; class MultiImapVacationDialog; class KSIEVEUI_EXPORT VacationManager : public QObject { Q_OBJECT public: explicit VacationManager(QWidget *parent); ~VacationManager(); void checkVacation(); public Q_SLOTS: void slotEditVacation(const QString &serverName); Q_SIGNALS: - void updateVacationScriptStatus(bool, const QString&); + void updateVacationScriptStatus(bool active, const QString &serverName); void editVacation(); private slots: void slotDialogCanceled(); void slotDialogOk(); + void slotUpdateVacationScriptStatus(bool active, const QString &serverName); private: QPointer mMultiImapVacationDialog; QPointer mCheckVacation; QWidget *mWidget; + bool mQuestionAsked; }; } #endif // VACATIONMANAGER_H diff --git a/libksieve/ksieveui/vacation/vacationpagewidget.cpp b/libksieve/ksieveui/vacation/vacationpagewidget.cpp index dbc42ae74a..784b9a4180 100644 --- a/libksieve/ksieveui/vacation/vacationpagewidget.cpp +++ b/libksieve/ksieveui/vacation/vacationpagewidget.cpp @@ -1,179 +1,188 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "vacationpagewidget.h" #include "vacationeditwidget.h" #include "vacationwarningwidget.h" #include "vacationcreatescriptjob.h" #include "vacationutils.h" +#include "multiimapvacationmanager.h" +#include #include "sieve-vacation.h" #include #include #include #include #include #include #include using namespace KSieveUi; VacationPageWidget::VacationPageWidget(QWidget *parent) : QWidget(parent), mPageScript(Script), - mSieveJob(0), mWasActive(false) { QVBoxLayout *lay = new QVBoxLayout; lay->setMargin(0); mStackWidget = new QStackedWidget; lay->addWidget(mStackWidget); //Main Page QWidget *mainPage = new QWidget; QVBoxLayout *vbox = new QVBoxLayout; mainPage->setLayout(vbox); mVacationWarningWidget = new VacationWarningWidget; vbox->addWidget(mVacationWarningWidget); mVacationEditWidget = new VacationEditWidget; vbox->addWidget(mVacationEditWidget); mStackWidget->addWidget(mainPage); QWidget *w = new QWidget; vbox = new QVBoxLayout; QLabel *lab = new QLabel(i18n( "Your server did not list \"vacation\" in " "its list of supported Sieve extensions;" "without it, KMail cannot install out-of-" "office replies for you." "Please contact your system administrator." ) ); vbox->addWidget(lab); vbox->setAlignment(lab, Qt::AlignVCenter); lab->setWordWrap(true); w->setLayout(vbox); mStackWidget->addWidget(w); mStackWidget->setCurrentIndex(Script); setLayout(lay); } VacationPageWidget::~VacationPageWidget() { - if ( mSieveJob ) - mSieveJob->kill(); - mSieveJob = 0; } void VacationPageWidget::setServerUrl(const KUrl &url) { mUrl = url; mVacationEditWidget->setEnabled(false); - mSieveJob = KManageSieve::SieveJob::get( url ); - connect( mSieveJob, SIGNAL(gotScript(KManageSieve::SieveJob*,bool,QString,bool)), - SLOT(slotGetResult(KManageSieve::SieveJob*,bool,QString,bool)) ); +} + +void VacationPageWidget::setVacationManager(MultiImapVacationManager *vacationManager) +{ + mVacationManager = vacationManager; + connect(mVacationManager, SIGNAL(scriptAvailable(QString,QStringList,QString,QString,bool)), + SLOT(slotGetResult(QString,QStringList,QString,QString,bool))); + 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) { - kDebug() << success - << ", ?," << active << ")" << endl + if (serverName != mServerName) { + return; + } + + kDebug() << serverName << sieveCapabilities << endl + << scriptName << "(" << active << ")" << endl << "script:" << endl << script; - mSieveJob = 0; // job deletes itself after returning from this slot! if ( mUrl.protocol() == QLatin1String("sieve") && - !job->sieveCapabilities().contains(QLatin1String("vacation")) ) { + !sieveCapabilities.contains(QLatin1String("vacation")) ) { mStackWidget->setCurrentIndex(ScriptNotSupported); return; } // Whether the server supports the "date" extension - const bool supportsSieveDate = mUrl.protocol() == QLatin1String("sieve") && job->sieveCapabilities().contains(QLatin1String("date")); + const bool supportsSieveDate = mUrl.protocol() == QLatin1String("sieve") && sieveCapabilities.contains(QLatin1String("date")); - 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 - if ( ( !success || !KSieveUi::VacationUtils::parseScript( script, messageText, subject, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate ) ) ) + const bool bParse = KSieveUi::VacationUtils::parseScript(script, messageText, subject, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate); + + if (!bParse) { mVacationWarningWidget->setVisible(true); + } mWasActive = active; + mVacationEditWidget->setEnabled(true); mVacationEditWidget->setActivateVacation( active ); mVacationEditWidget->setMessageText( messageText ); mVacationEditWidget->setSubject( subject ); mVacationEditWidget->setNotificationInterval( notificationInterval ); mVacationEditWidget->setMailAliases( aliases.join(QLatin1String(", ")) ); mVacationEditWidget->setSendForSpam( sendForSpam ); mVacationEditWidget->setDomainName( domainName ); mVacationEditWidget->enableDomainAndSendForSpam( !VacationSettings::allowOutOfOfficeUploadButNoSettings() ); mVacationEditWidget->enableDates( supportsSieveDate ); if ( supportsSieveDate ) { mVacationEditWidget->setStartDate( startDate ); mVacationEditWidget->setEndDate( endDate ); } //emit scriptActive( mWasActive, mServerName ); } + + 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(), mVacationEditWidget->startDate(), mVacationEditWidget->endDate() ); const bool active = mVacationEditWidget->activateVacation(); createJob->setStatus(active, mWasActive); //Q_EMIT scriptActive( active, mServerName); createJob->setScript(script); return createJob; } return 0; } void VacationPageWidget::setDefault() { if (mVacationEditWidget->isEnabled()) mVacationEditWidget->setDefault(); } diff --git a/libksieve/ksieveui/vacation/vacationpagewidget.h b/libksieve/ksieveui/vacation/vacationpagewidget.h index 2e112353e1..887dc11d80 100644 --- a/libksieve/ksieveui/vacation/vacationpagewidget.h +++ b/libksieve/ksieveui/vacation/vacationpagewidget.h @@ -1,64 +1,70 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef VACATIONPAGEWIDGET_H #define VACATIONPAGEWIDGET_H #include #include class QStackedWidget; namespace KManageSieve { class SieveJob; } namespace KSieveUi { class VacationEditWidget; class VacationWarningWidget; class VacationCreateScriptJob; +class MultiImapVacationManager; +class ParseUserScriptJob; class VacationPageWidget : public QWidget { Q_OBJECT public: explicit VacationPageWidget(QWidget *parent=0); ~VacationPageWidget(); void setServerUrl(const KUrl &url); void setServerName(const QString &serverName); KSieveUi::VacationCreateScriptJob *writeScript(); void setDefault(); + void setVacationManager(MultiImapVacationManager *vacationManager); private 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 }; PageType mPageScript; QString mServerName; KUrl mUrl; QStackedWidget *mStackWidget; VacationEditWidget *mVacationEditWidget; VacationWarningWidget *mVacationWarningWidget; - KManageSieve::SieveJob *mSieveJob; + MultiImapVacationManager *mVacationManager; bool mWasActive; }; } #endif // VACATIONPAGEWIDGET_H diff --git a/libksieve/ksieveui/vacation/vacationutils.cpp b/libksieve/ksieveui/vacation/vacationutils.cpp index 5428508675..0604e127cb 100644 --- a/libksieve/ksieveui/vacation/vacationutils.cpp +++ b/libksieve/ksieveui/vacation/vacationutils.cpp @@ -1,201 +1,221 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 "vacationutils.h" #include "vacationscriptextractor.h" #include "sieve-vacation.h" #include #include #include #include #include #include using KMime::Types::AddrSpecList; static inline QString dotstuff( QString s ) { // krazy:exclude=passbyvalue if ( s.startsWith( QLatin1Char('.') ) ) return QLatin1Char('.') + s.replace( QLatin1String("\n."), QLatin1String("\n..") ); else return s.replace( QLatin1String("\n."), QLatin1String("\n..") ); } static inline QString stringReplace(QString s) { s = s.replace(QRegExp(QLatin1String("[\n\t]+")),QLatin1String(" ")); return s.replace(QLatin1Char('\"'),QLatin1String("\\\"")); } QString KSieveUi::VacationUtils::defaultSubject() { return i18n("Out of office till %1", QLocale().toString(QDate::currentDate().addDays(1))); } QString KSieveUi::VacationUtils::defaultMessageText() { return i18n( "I am out of office till %1.\n" "\n" "In urgent cases, please contact Mrs. \"vacation replacement\"\n" "\n" "email: \"email address of vacation replacement\"\n" "phone: +49 711 1111 11\n" "fax.: +49 711 1111 12\n" "\n" "Yours sincerely,\n" "-- \"enter your name and email address here\"\n", KGlobal::locale()->formatDate( QDate::currentDate().addDays( 1 ) ) ); } int KSieveUi::VacationUtils::defaultNotificationInterval() { return 7; // days } QStringList KSieveUi::VacationUtils::defaultMailAliases() { QStringList sl; KPIMIdentities::IdentityManager manager( true ); KPIMIdentities::IdentityManager::ConstIterator end(manager.end()); for ( KPIMIdentities::IdentityManager::ConstIterator it = manager.begin(); it != end ; ++it ) { if ( !(*it).primaryEmailAddress().isEmpty() ) { sl.push_back( (*it).primaryEmailAddress() ); } sl += (*it).emailAliases(); } return sl; } bool KSieveUi::VacationUtils::defaultSendForSpam() { return VacationSettings::outOfOfficeReactToSpam(); } QString KSieveUi::VacationUtils::defaultDomainName() { return VacationSettings::outOfOfficeDomain(); } QDate KSieveUi::VacationUtils::defaultStartDate() { return QDate::currentDate(); } QDate KSieveUi::VacationUtils::defaultEndDate() { return defaultStartDate().addDays(7); } bool KSieveUi::VacationUtils::parseScript( const QString &script, QString &messageText, QString &subject, int & notificationInterval, QStringList &aliases, bool & sendForSpam, QString &domainName, QDate & startDate, QDate & endDate ) { if ( script.trimmed().isEmpty() ) { messageText = VacationUtils::defaultMessageText(); subject = VacationUtils::defaultSubject(); notificationInterval = VacationUtils::defaultNotificationInterval(); aliases = VacationUtils::defaultMailAliases(); sendForSpam = VacationUtils::defaultSendForSpam(); domainName = VacationUtils::defaultDomainName(); return true; } // The trimmed() call below prevents parsing errors. The // slave somehow omits the last \n, which results in a lone \r at // the end, leading to a parse error. const QByteArray scriptUTF8 = script.trimmed().toUtf8(); kDebug() << "scriptUtf8 = \"" + scriptUTF8 +"\""; KSieve::Parser parser( scriptUTF8.begin(), scriptUTF8.begin() + scriptUTF8.length() ); VacationDataExtractor vdx; SpamDataExtractor sdx; DomainRestrictionDataExtractor drdx; DateExtractor dx; KSieveExt::MultiScriptBuilder tsb( &vdx, &sdx, &drdx, &dx ); parser.setScriptBuilder( &tsb ); if ( !parser.parse() ) return false; messageText = vdx.messageText().trimmed(); if (!vdx.subject().isEmpty()) { subject = vdx.subject().trimmed(); } notificationInterval = vdx.notificationInterval(); aliases = vdx.aliases(); if ( !VacationSettings::allowOutOfOfficeUploadButNoSettings() ) { sendForSpam = !sdx.found(); domainName = drdx.domainName(); } startDate = dx.startDate(); endDate = dx.endDate(); return true; } +bool KSieveUi::VacationUtils::foundVacationScript(const QString &script) +{ + const QByteArray scriptUTF8 = script.trimmed().toUtf8(); + kDebug() << "scriptUtf8 = \"" + scriptUTF8 +"\""; + + if (scriptUTF8.isEmpty()) { + return false; + } + + KSieve::Parser parser( scriptUTF8.begin(), + scriptUTF8.begin() + scriptUTF8.length() ); + VacationDataExtractor vdx; + SpamDataExtractor sdx; + DomainRestrictionDataExtractor drdx; + DateExtractor dx; + KSieveExt::MultiScriptBuilder tsb( &vdx, &sdx, &drdx, &dx ); + parser.setScriptBuilder( &tsb ); + return parser.parse(); +} + QString KSieveUi::VacationUtils::composeScript( const QString & messageText, const QString &subject, int notificationInterval, const AddrSpecList & addrSpecs, bool sendForSpam, const QString & domain, const QDate & startDate, const QDate & endDate ) { 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 script; if ( startDate.isValid() && endDate.isValid() ) { script = QString::fromLatin1("require [\"vacation\", \"relational\", \"date\"];\n\n" ); } else { script = QString::fromLatin1("require \"vacation\";\n\n" ); } if ( !sendForSpam ) script += QString::fromLatin1( "if header :contains \"X-Spam-Flag\" \"YES\"" " { keep; stop; }\n" ); // FIXME? if ( !domain.isEmpty() ) // FIXME script += QString::fromLatin1( "if not address :domain :contains \"from\" \"%1\" { keep; stop; }\n" ).arg( domain ); if ( startDate.isValid() && endDate.isValid() ) { script += QString::fromLatin1( "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 += QLatin1String("vacation "); script += addressesArgument; if ( notificationInterval > 0 ) script += QString::fromLatin1(":days %1 ").arg( notificationInterval ); if (!subject.trimmed().isEmpty()) { script += QString::fromLatin1(":subject \"%1\" ").arg(stringReplace(subject).trimmed()); } script += QString::fromLatin1("text:\n"); script += dotstuff( messageText.isEmpty() ? VacationUtils::defaultMessageText() : messageText ); script += QString::fromLatin1( "\n.\n;\n" ); return script; } diff --git a/libksieve/ksieveui/vacation/vacationutils.h b/libksieve/ksieveui/vacation/vacationutils.h index 1e151ef22d..21965a8b66 100644 --- a/libksieve/ksieveui/vacation/vacationutils.h +++ b/libksieve/ksieveui/vacation/vacationutils.h @@ -1,57 +1,59 @@ /* Copyright (c) 2013, 2014 Montel Laurent 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 */ #ifndef VACATIONUTILS_H #define VACATIONUTILS_H #include #include namespace KMime { namespace Types { struct AddrSpec; typedef QList AddrSpecList; } } class QDate; namespace KSieveUi { namespace VacationUtils { QString defaultMessageText(); QString defaultSubject(); int defaultNotificationInterval(); QStringList 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 & notificationInterval, QStringList & aliases, bool & sendForSpam, QString & domainName, QDate & startDate, QDate & endDate ); +bool foundVacationScript(const QString & script); + } } #endif // VACATIONUTILS_H diff --git a/libksieve/ksieveui/widgets/managesievewidget.cpp b/libksieve/ksieveui/widgets/managesievewidget.cpp index b9e28d1427..a3c5e40838 100644 --- a/libksieve/ksieveui/widgets/managesievewidget.cpp +++ b/libksieve/ksieveui/widgets/managesievewidget.cpp @@ -1,428 +1,477 @@ /* Copyright (c) 2014 Montel Laurent 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 "managesievewidget.h" #include "managesievetreeview.h" #include "widgets/sievetreewidgetitem.h" #include +#include +#include +#include #include #include #include #include #include #include #include #include +#include using namespace KSieveUi; +Q_DECLARE_METATYPE(QTreeWidgetItem*) + ManageSieveWidget::ManageSieveWidget(QWidget *parent) : QWidget(parent), mClearAll( false ), mBlockSignal( false ) { QHBoxLayout *lay = new QHBoxLayout; lay->setMargin(0); mTreeView = new ManageSieveTreeView; #ifndef QT_NO_CONTEXTMENU connect( mTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenuRequested(QPoint)) ); #endif connect( mTreeView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotDoubleClicked(QTreeWidgetItem*)) ); connect( mTreeView, SIGNAL(itemSelectionChanged()), this, SLOT(slotUpdateButtons()) ); connect( mTreeView, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int))); connect ( Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)), this, SLOT(slotSystemNetworkStatusChanged(Solid::Networking::Status)) ); lay->addWidget(mTreeView); setLayout(lay); QTimer::singleShot(0,this, SLOT(slotCheckNetworkStatus())); } ManageSieveWidget::~ManageSieveWidget() { clear(); } void ManageSieveWidget::slotCheckNetworkStatus() { slotSystemNetworkStatusChanged(Solid::Networking::status()); } void ManageSieveWidget::slotSystemNetworkStatusChanged(Solid::Networking::Status status) { if ( status == Solid::Networking::Connected || status == Solid::Networking::Unknown) { mTreeView->setEnabled(true); slotRefresh(); } else { mTreeView->setEnabled(false); mTreeView->setNetworkDown(false); } } ManageSieveTreeView *ManageSieveWidget::treeView() const { return mTreeView; } void ManageSieveWidget::killAllJobs() { mClearAll = true; QMap::const_iterator it = mJobs.constBegin(); while (it != mJobs.constEnd()) { it.key()->kill(); ++it; } mClearAll = false; mJobs.clear(); } bool ManageSieveWidget::serverHasError(QTreeWidgetItem *item) const { const QVariant variant = item->data( 0, SIEVE_SERVER_ERROR ); if ( variant.isValid() && variant.toBool()==true ) return true; return false; } void ManageSieveWidget::slotItemChanged(QTreeWidgetItem *item, int col) { if (!item || mBlockSignal || (col != 0) ) { return; } if ( !isFileNameItem( item ) ) return; QTreeWidgetItem *parent = item->parent(); if ( (mSelectedItems[parent] != item) && itemIsActived( item )) { mSelectedItems[parent] = item; changeActiveScript( parent, true ); } else { mSelectedItems[parent] = item; changeActiveScript( parent, false ); } } void ManageSieveWidget::slotContextMenuRequested( const QPoint& p ) { QTreeWidgetItem *item = mTreeView->itemAt( p ); if ( !item ) return; if ( !item->parent() && !mUrls.count( item )) return; QMenu menu; if ( isFileNameItem( item ) ) { // script items: menu.addAction( i18n( "Edit Script..." ), this, SLOT(slotEditScript()) ); menu.addAction( i18n( "Delete Script" ), this, SLOT(slotDeleteScript()) ); if ( itemIsActived( item ) ) { menu.addSeparator(); menu.addAction( i18n( "Deactivate Script" ), this, SLOT(slotDeactivateScript()) ); } } else if ( !item->parent() ) { // top-levels: if ( !serverHasError(item) && mJobs.keys(item).isEmpty()) menu.addAction( i18n( "New Script..." ), this, SLOT(slotNewScript()) ); } if ( !menu.actions().isEmpty() ) menu.exec( mTreeView->viewport()->mapToGlobal(p) ); } void ManageSieveWidget::slotNewScript() { QTreeWidgetItem *currentItem = mTreeView->currentItem(); if ( !currentItem ) return; if ( currentItem->parent() ) currentItem = currentItem->parent(); if ( !currentItem ) return; if ( !mUrls.count( currentItem ) ) return; KUrl u = mUrls[currentItem]; if ( u.isEmpty() ) return; bool ok = false; const QString name = KInputDialog::getText( i18n( "New Sieve Script" ), i18n( "Please enter a name for the new Sieve script:" ), i18n( "unnamed" ), &ok, this ); if ( !ok || name.isEmpty() ) return; - if (isProtectedName(name.toLower())) { + if (Util::isKep14ProtectedName(name)) { KMessageBox::error(this, i18n("You cannot use protected name."), i18n("New Script")); return; } u.setFileName( name ); QTreeWidgetItem * parentItem = currentItem; if (parentItem) { const int numberOfElement(parentItem->childCount()); for (int i = 0; i child(i)->text(0) == name) { KMessageBox::error( this, i18n( "Script name already used \"%1\".", name ), i18n( "New Script" ) ); return; } } } const QStringList currentCapabilities = currentItem->data(0, SIEVE_SERVER_CAPABILITIES).toStringList(); mBlockSignal = true; QTreeWidgetItem *newItem = new QTreeWidgetItem( currentItem ); newItem->setFlags(newItem->flags() & (Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable)); newItem->setText(0,name); newItem->setCheckState(0,Qt::Unchecked); mBlockSignal = false; Q_EMIT newScript(u, currentCapabilities); } void ManageSieveWidget::slotEditScript() { QTreeWidgetItem *currentItem = mTreeView->currentItem(); if ( !isFileNameItem( currentItem ) ) return; QTreeWidgetItem* parent = currentItem->parent(); if ( !mUrls.count( parent ) ) return; KUrl url = mUrls[parent]; if ( url.isEmpty() ) return; url.setFileName( currentItem->text(0) ); const QStringList currentCapabilities = parent->data(0, SIEVE_SERVER_CAPABILITIES).toStringList(); Q_EMIT editScript(url, currentCapabilities); } void ManageSieveWidget::slotDeactivateScript() { QTreeWidgetItem * item = mTreeView->currentItem(); if ( !isFileNameItem( item ) ) return; QTreeWidgetItem *parent = item->parent(); if ( itemIsActived( item ) ) { mSelectedItems[parent] = item; changeActiveScript( parent, false ); } } void ManageSieveWidget::changeActiveScript( QTreeWidgetItem *item, bool activate ) { if ( !item ) return; if ( !mUrls.count( item ) ) return; if ( !mSelectedItems.count( item ) ) return; KUrl u = mUrls[item]; 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 = mSelectedItems[item]; if ( !selected ) return; u.setFileName( selected->text(0) ); - KManageSieve::SieveJob * job; if ( activate ) job = KManageSieve::SieveJob::activate( u ); else job = KManageSieve::SieveJob::deactivate( u ); mBlockSignal = true; connect( job, SIGNAL(result(KManageSieve::SieveJob*,bool,QString,bool)), this, SLOT(slotRefresh()) ); } bool ManageSieveWidget::itemIsActived( QTreeWidgetItem *item ) const { Q_ASSERT( item && item->parent() ); return (item->checkState(0) == Qt::Checked); } bool ManageSieveWidget::isFileNameItem( QTreeWidgetItem *item ) const { if ( !item || !item->parent() ) return false; return (item->flags() & Qt::ItemIsEnabled); } void ManageSieveWidget::clear() { killAllJobs(); mSelectedItems.clear(); mUrls.clear(); mTreeView->clear(); } void ManageSieveWidget::slotDeleteScript() { QTreeWidgetItem * currentItem = mTreeView->currentItem(); if ( !isFileNameItem( currentItem ) ) return; QTreeWidgetItem *parent = currentItem->parent(); if ( !parent ) return; if ( !mUrls.count( parent ) ) return; KUrl u = mUrls[parent]; if ( u.isEmpty() ) return; u.setFileName( currentItem->text(0) ); if ( KMessageBox::warningContinueCancel( this, i18n( "Really delete script \"%1\" from the server?", u.fileName() ), i18n( "Delete Sieve Script Confirmation" ), KStandardGuiItem::del() ) != KMessageBox::Continue ) return; KManageSieve::SieveJob * job = KManageSieve::SieveJob::del( u ); connect( job, SIGNAL(result(KManageSieve::SieveJob*,bool,QString,bool)), this, SLOT(slotRefresh()) ); 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() { mBlockSignal = true; clear(); const bool noImapFound = refreshList(); slotUpdateButtons(); mTreeView->setNoImapFound(noImapFound); if (!noImapFound) mBlockSignal = false; } void ManageSieveWidget::slotUpdateButtons() { Q_EMIT updateButtons(mTreeView->currentItem()); } void ManageSieveWidget::slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &listScript, const QString &activeScript) { qDebug()<<"void ManageSieveWidget::slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &listScript, const QString &activeScript) success: "<(parent))->stopAnimation(); mJobs.remove( job ); if (!success) { mBlockSignal = false; parent->setData( 0, SIEVE_SERVER_ERROR, true ); QTreeWidgetItem * item = new QTreeWidgetItem( parent ); item->setText( 0, i18n( "Failed to fetch the list of scripts" ) ); item->setFlags( item->flags() & ~Qt::ItemIsEnabled ); mTreeView->expandItem( parent ); return; } 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 ); item->setFlags(item->flags() & (Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable)); item->setText(0, script); const bool isActive = (script == activeScript); item->setCheckState(0, isActive ? Qt::Checked : Qt::Unchecked); if ( isActive ) { mSelectedItems[parent] = item; } } mBlockSignal = false; - qDebug()<<" LOAD"; - const bool hasIncludeCapability = job->sieveCapabilities().contains(QLatin1String("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) { + KUrl u = mUrls[parent]; + u.setFileName(QLatin1String("USER")); + ParseUserScriptJob *parseJob = new ParseUserScriptJob(u); + parseJob->setProperty(QLatin1String("parentItem").latin1(), 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); mTreeView->expandItem( parent ); } +void ManageSieveWidget::setActiveScripts(ParseUserScriptJob *job) +{ + QTreeWidgetItem * parent = job->property(QLatin1String("parentItem").latin1()).value(); + if ( !parent ) { + return; + } + (static_cast(parent))->stopAnimation(); + + if (!job->error().isEmpty()) { + qWarning() << job->error(); + return; + } + + 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]); + } + + mBlockSignal = false; +} + + void ManageSieveWidget::slotDoubleClicked( QTreeWidgetItem * item ) { if ( !isFileNameItem( item ) ) return; slotEditScript(); } void ManageSieveWidget::enableDisableActions(bool &newScriptAction, bool &editScriptAction, bool &deleteScriptAction, bool &desactivateScriptAction) { QTreeWidgetItem * item = mTreeView->currentItem(); bool enabled = true; if ( !item ) enabled = false; else if ( !item->parent() && !mUrls.count( item )) enabled = false; if ( !enabled ) { newScriptAction = false; editScriptAction = false; deleteScriptAction = false; desactivateScriptAction = false; } else { if ( serverHasError(item) || !mJobs.keys(item).isEmpty()) newScriptAction = false; else newScriptAction = mUrls.count( item ); enabled = isFileNameItem( item ); editScriptAction = enabled; deleteScriptAction = enabled; desactivateScriptAction = enabled && itemIsActived( item ); } } diff --git a/libksieve/ksieveui/widgets/managesievewidget.h b/libksieve/ksieveui/widgets/managesievewidget.h index 0e916e6793..dcf8f6c24a 100644 --- a/libksieve/ksieveui/widgets/managesievewidget.h +++ b/libksieve/ksieveui/widgets/managesievewidget.h @@ -1,105 +1,105 @@ /* Copyright (c) 2014 Montel Laurent 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 */ #ifndef MANAGESIEVEWIDGET_H #define MANAGESIEVEWIDGET_H #include "ksieveui_export.h" #include #include #include #include class QTreeWidgetItem; namespace KManageSieve { class SieveJob; } namespace KSieveUi { class ManageSieveTreeView; +class ParseUserScriptJob; class KSIEVEUI_EXPORT ManageSieveWidget : public QWidget { Q_OBJECT public: enum SieveEditorMode { NormalEditorMode = 0, Kep14EditorMode }; explicit ManageSieveWidget(QWidget *parent=0); ~ManageSieveWidget(); ManageSieveTreeView *treeView() const; void enableDisableActions(bool &newScriptAction, bool &editScriptAction, bool &deleteScriptAction, bool &desactivateScriptAction); Q_SIGNALS: void updateButtons(QTreeWidgetItem *item); void newScript(const KUrl &u, const QStringList ¤tCapabilities); void editScript(const KUrl &url, const QStringList ¤tCapabilities); void scriptDeleted(const KUrl &u); protected: virtual bool refreshList() = 0; private Q_SLOTS: void slotItemChanged(QTreeWidgetItem *item, int col); void slotContextMenuRequested(const QPoint &p); void slotUpdateButtons(); void slotDoubleClicked(QTreeWidgetItem *item); void slotSystemNetworkStatusChanged(Solid::Networking::Status status); void slotCheckNetworkStatus(); + void setActiveScripts(ParseUserScriptJob *job); public Q_SLOTS: void slotGotList(KManageSieve::SieveJob *job, bool success, const QStringList &listScript, const QString &activeScript); void slotNewScript(); void slotEditScript(); void slotDeleteScript(); void slotDeactivateScript(); void slotRefresh(); protected: QMap mJobs; QMap mUrls; private: enum sieveServerStatus { SIEVE_SERVER_ERROR = Qt::UserRole +1, SIEVE_SERVER_CAPABILITIES = Qt::UserRole +2, SIEVE_SERVER_MODE = Qt::UserRole +3 }; bool serverHasError(QTreeWidgetItem *item) const; void killAllJobs(); void clear(); bool isFileNameItem(QTreeWidgetItem *item) const; bool itemIsActived(QTreeWidgetItem *item) const; void changeActiveScript(QTreeWidgetItem *item, bool activate); - bool isProtectedName(const QString &name); - // Maps top-level items to their child which has the radio button selection QMap mSelectedItems; ManageSieveTreeView *mTreeView; bool mClearAll : 1; bool mBlockSignal : 1; }; } #endif // MANAGESIEVEWIDGET_H diff --git a/libksieve/parser/lexer.cpp b/libksieve/parser/lexer.cpp index 1dfc848e7f..653c154e97 100644 --- a/libksieve/parser/lexer.cpp +++ b/libksieve/parser/lexer.cpp @@ -1,666 +1,666 @@ /* -*- c++ -*- parser/lexer.cpp This file is part of KSieve, the KDE internet mail/usenet news message filtering library. Copyright (c) 2002-2003 Marc Mutz KSieve 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. KSieve 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 In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include #include #include #include #include #include #include // std::auto_ptr #include #include // isdigit #ifdef STR_DIM # undef STR_DIM #endif #define STR_DIM(x) (sizeof(x) - 1) namespace KSieve { // // // Lexer Bridge implementation // // Lexer::Lexer( const char * scursor, const char * send, int options ) : i( 0 ) { i = new Impl( scursor, send, options ); } Lexer::~Lexer() { delete i; i = 0; } bool Lexer::ignoreComments() const { assert( i ); return i->ignoreComments(); } const Error & Lexer::error() const { assert( i ); return i->error(); } bool Lexer::atEnd() const { assert( i ); return i->atEnd(); } int Lexer::column() const { assert( i ); return i->column(); } int Lexer::line() const { assert( i ); return i->line(); } void Lexer::save() { assert( i ); i->save(); } void Lexer::restore() { assert( i ); i->restore(); } Lexer::Token Lexer::nextToken( QString & result ) { assert( i ); return i->nextToken( result ); } } // namespace KSieve // none except a-zA-Z0-9_ static const unsigned char iTextMap[16] = { 0x00, 0x00, 0x00, 0x00, // CTLs: none 0x00, 0x00, 0xFF, 0xC0, // SP ... '?': 0-9 0x7F, 0xFF, 0xFF, 0xE1, // '@' ... '_': A-Z_ 0x7F, 0xFF, 0xFF, 0xE0 // '`' ... DEL: a-z }; // SP, HT, CR, LF, {}[]();,#/ // ### exclude '['? Why would one want to write identifier["foo"]? static const unsigned char delimMap[16] = { 0x00, 0x64, 0x00, 0x00, // CTLs: CR, HT, LF 0x90, 0xC9, 0x00, 0x10, // SP ... '?': SP, #(),; 0x00, 0x00, 0x00, 0x16, // '@' ... '_': [] 0x00, 0x00, 0x00, 0x16 // '`' ... DEL: {} }; // All except iText, delim, "*: static const unsigned char illegalMap[16] = { 0xFF, 0x9B, 0xFF, 0xFF, 0x4F, 0x16, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0A, 0x80, 0x00, 0x00, 0x0A }; static inline bool isOfSet( const unsigned char map[16], unsigned char ch ) { assert( ch < 128 ); return ( map[ ch/8 ] & 0x80 >> ch%8 ); } static inline bool isIText( unsigned char ch ) { return ch <= 'z' && isOfSet( iTextMap, ch ); } static inline bool isDelim( unsigned char ch ) { return ch <= '}' && isOfSet( delimMap, ch ); } static inline bool isIllegal( unsigned char ch ) { return ch >= '~' || isOfSet( illegalMap, ch ); } #ifndef KDE_USE_FINAL static inline bool is8Bit( signed char ch ) { return ch < 0; } #endif static QString removeCRLF( const QString & s ) { const bool CRLF = s.endsWith( QLatin1String("\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 return s.left( s.length() - e ); } static QString removeDotStuff( const QString & s ) { return s.startsWith( QLatin1String("..") ) ? s.mid( 1 ) : s ; } namespace KSieve { // // // Lexer Implementation // // Lexer::Impl::Impl( const char * scursor, const char * send, int options ) : mState( scursor ? scursor : send ), mEnd( send ? send : scursor ), mIgnoreComments( options & IgnoreComments ), mIgnoreLF( options & IgnoreLineFeeds ) { if ( !scursor || !send ) assert( atEnd() ); } Lexer::Token Lexer::Impl::nextToken( QString & result ) { assert( !atEnd() ); result.clear(); //clearErrors(); const int oldLine = line(); const bool eatingWSSucceeded = ignoreComments() ? eatCWS() : eatWS() ; if ( !ignoreLineFeeds() && oldLine != line() ) { result.setNum( line() - oldLine ); // return number of linefeeds encountered return LineFeeds; } if ( !eatingWSSucceeded ) return None; if ( atEnd() ) return None; switch ( *mState.cursor ) { case '#': // HashComment assert( !ignoreComments() ); ++mState.cursor; if ( !atEnd() ) parseHashComment( result, true ); return HashComment; case '/': // BracketComment assert( !ignoreComments() ); ++mState.cursor; // eat slash if ( atEnd() || *mState.cursor != '*' ) { makeError( Error::SlashWithoutAsterisk ); return BracketComment; } ++mState.cursor; // eat asterisk if ( atEnd() ) { makeError( Error::UnfinishedBracketComment ); return BracketComment; } parseBracketComment( result, true ); return BracketComment; case ':': // Tag ++mState.cursor; if ( atEnd() ) { makeError( Error::UnexpectedCharacter, line(), column() - 1 ); return Tag; } if ( !isIText( *mState.cursor ) ) { makeIllegalCharError( *mState.cursor ); return Tag; } parseTag( result ); return Tag; case '"': // QuotedString ++mState.cursor; parseQuotedString( result ); return QuotedString; case '{': case '}': case '[': case ']': case '(': case ')': case ';': case ',': // Special - result = *mState.cursor++; + result = QLatin1Char(*mState.cursor++); return Special; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // Number parseNumber( result ); return Number; case 't': // maybe MultiLineString, else Identifier if ( _strnicmp( mState.cursor, "text:", STR_DIM("text:") ) == 0 ) { // MultiLineString mState.cursor += STR_DIM("text:"); parseMultiLine( result ); // ### FIXME: There can be a hash-comment between "text:" // and CRLF! That should be preserved somehow... return MultiLineString; } // else fall through: default: // Identifier (first must not be 0-9, and can't (caught by Number above)) if ( !isIText( *mState.cursor ) ) { makeError( Error::IllegalCharacter ); return None; } parseIdentifier( result ); return Identifier; } } bool Lexer::Impl::eatWS() { while ( !atEnd() ) switch ( *mState.cursor ) { case '\r': case '\n': if ( !eatCRLF() ) return false; break; case ' ': case '\t': ++mState.cursor; break; default: return true; } // at end: return true; } bool Lexer::Impl::eatCRLF() { assert( !atEnd() ); assert( *mState.cursor == '\n' || *mState.cursor == '\r' ); if ( *mState.cursor == '\r' ) { ++mState.cursor; if ( atEnd() || *mState.cursor != '\n' ) { // CR w/o LF -> error makeError( Error::CRWithoutLF ); return false; } else { // good CRLF newLine(); return true; } } else /* *mState.cursor == '\n' */ { // good, LF only newLine(); return true; } } bool Lexer::Impl::parseHashComment( QString & result, bool reallySave ) { // hash-comment := "#" *CHAR-NOT-CRLF CRLF // check that the caller plays by the rules: assert( *(mState.cursor-1) == '#' ); const char * const commentStart = mState.cursor; // find next CRLF: while ( !atEnd() ) { if ( *mState.cursor == '\n' || *mState.cursor == '\r' ) break; ++mState.cursor; } const char * const commentEnd = mState.cursor - 1; if ( commentEnd == commentStart ) return true; // # was last char in script... if ( atEnd() || eatCRLF() ) { const int commentLength = commentEnd - commentStart + 1; if ( commentLength > 0 ) { if ( !isValidUtf8( commentStart, commentLength ) ) { makeError( Error::InvalidUTF8 ); return false; } if ( reallySave ) result += QString::fromUtf8( commentStart, commentLength ); } return true; } return false; } bool Lexer::Impl::parseBracketComment( QString & result, bool reallySave ) { // bracket-comment := "/*" *(CHAR-NOT-STAR / ("*" CHAR-NOT-SLASH )) "*/" // check that caller plays by the rules: assert( *(mState.cursor-2) == '/' ); assert( *(mState.cursor-1) == '*' ); const char * const commentStart = mState.cursor; const int commentCol = column() - 2; const int commentLine = line(); // find next asterisk: do { if ( !skipTo( '*' ) ) { if ( !error() ) makeError( Error::UnfinishedBracketComment, commentLine, commentCol ); return false; } } while ( !atEnd() && *++mState.cursor != '/' ); if ( atEnd() ) { makeError( Error::UnfinishedBracketComment, commentLine, commentCol ); return false; } assert( *mState.cursor == '/' ); const int commentLength = mState.cursor - commentStart - 1; if ( commentLength > 0 ) { if ( !isValidUtf8( commentStart, commentLength ) ) { makeError( Error::InvalidUTF8 ); return false; } 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 } } ++mState.cursor; // eat '/' return true; } bool Lexer::Impl::parseComment( QString & result, bool reallySave ) { // comment := hash-comment / bracket-comment switch( *mState.cursor ) { case '#': ++mState.cursor; return parseHashComment( result, reallySave ); case '/': if ( charsLeft() < 2 || mState.cursor[1] != '*' ) { makeError( Error::IllegalCharacter ); return false; } else { mState.cursor += 2; // eat "/*" return parseBracketComment( result, reallySave ); } default: return false; // don't set an error here - there was no comment } } bool Lexer::Impl::eatCWS() { // white-space := 1*(SP / CRLF / HTAB / comment ) while ( !atEnd() ) { switch( *mState.cursor ) { case ' ': case '\t': // SP / HTAB ++mState.cursor; break;; case '\n': case '\r': // CRLF if ( !eatCRLF() ) return false; break; case '#': case '/': // comments { QString dummy; if ( !parseComment( dummy ) ) return false; } break; default: return true; } } return true; } bool Lexer::Impl::parseIdentifier( QString & result ) { // identifier := (ALPHA / "_") *(ALPHA DIGIT "_") assert( isIText( *mState.cursor ) ); const char * const identifierStart = mState.cursor; // first char: if ( isdigit( *mState.cursor ) ) { // no digits for the first makeError( Error::NoLeadingDigits ); return false; } // rest of identifier chars ( now digits are allowed ): for ( ++mState.cursor ; !atEnd() && isIText( *mState.cursor ) ; ++mState.cursor ) ; const int identifierLength = mState.cursor - identifierStart; // Can use the fast fromLatin1 here, since identifiers are always // in the us-ascii subset: result += QString::fromLatin1( identifierStart, identifierLength ); if ( atEnd() || isDelim( *mState.cursor ) ) return true; makeIllegalCharError( *mState.cursor ); return false; } bool Lexer::Impl::parseTag( QString & result ) { // tag := ":" identifier // check that the caller plays by the rules: assert( *(mState.cursor-1) == ':' ); assert( !atEnd() ); assert( isIText( *mState.cursor ) ); return parseIdentifier( result ); } bool Lexer::Impl::parseNumber( QString & result ) { // number := 1*DIGIT [QUANTIFIER] // QUANTIFIER := "K" / "M" / "G" assert( isdigit( *mState.cursor ) ); while ( !atEnd() && isdigit( *mState.cursor ) ) - result += *mState.cursor++; + result += QLatin1Char(*mState.cursor++); if ( atEnd() || isDelim( *mState.cursor ) ) return true; switch ( *mState.cursor ) { case 'G': case 'g': case 'M': case 'm': case 'K': case 'k': - result += *mState.cursor++; + result += QLatin1Char(*mState.cursor++); break; default: makeIllegalCharError(); return false; } // quantifier found. Check for delimiter: if ( atEnd() || isDelim( *mState.cursor ) ) return true; makeIllegalCharError(); return false; } bool Lexer::Impl::parseMultiLine( QString & result ) { // multi-line := "text:" *(SP / HTAB) (hash-comment / CRLF) // *(multi-line-literal / multi-line-dotstuff) // "." CRLF // multi-line-literal := [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF // multi-line-dotstuff := "." 1*CHAR-NOT-CRLF CRLF // ;; A line containing only "." ends the multi-line. // ;; Remove a leading '.' if followed by another '.'. assert( _strnicmp( mState.cursor - 5, "text:", STR_DIM("text:") ) == 0 ); const int mlBeginLine = line(); const int mlBeginCol = column() - 5; while ( !atEnd() ) { switch ( *mState.cursor ) { case ' ': case '\t': ++mState.cursor; break; case '#': { ++mState.cursor; QString dummy; if ( !parseHashComment( dummy ) ) return false; goto MultiLineStart; // break from switch _and_ while } case '\n': case '\r': if ( !eatCRLF() ) return false; goto MultiLineStart; // break from switch _and_ while default: makeError( Error::NonCWSAfterTextColon ); return false; } } MultiLineStart: if ( atEnd() ) { makeError( Error::PrematureEndOfMultiLine, mlBeginLine, mlBeginCol ); return false; } // Now, collect the single lines until one with only a single dot is found: QStringList lines; while ( !atEnd() ) { const char * const oldBeginOfLine = beginOfLine(); if ( !skipToCRLF() ) return false; const int lineLength = mState.cursor - oldBeginOfLine; if ( lineLength > 0 ) { if ( !isValidUtf8( oldBeginOfLine, lineLength ) ) { makeError( Error::InvalidUTF8 ); return false; } const QString line = removeCRLF( QString::fromUtf8( oldBeginOfLine, lineLength ) ); lines.push_back( removeDotStuff( line ) ); - if ( line == "." ) + if ( line == QLatin1String(".") ) break; } else { lines.push_back( QString() ); } } - if ( lines.back() != "." ) { + if ( lines.back() != QLatin1String(".") ) { 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(QLatin1String("\n")); return true; } bool Lexer::Impl::parseQuotedString( QString & result ) { // quoted-string := DQUOTE *CHAR DQUOTE // check that caller plays by the rules: assert( *(mState.cursor-1) == '"' ); const int qsBeginCol = column() - 1; const int qsBeginLine = line(); const QTextCodec * const codec = QTextCodec::codecForMib( 106 ); // UTF-8 assert( codec ); const std::auto_ptr dec( codec->makeDecoder() ); assert( dec.get() ); while ( !atEnd() ) switch ( *mState.cursor ) { case '"': ++mState.cursor; return true; case '\r': case '\n': if ( !eatCRLF() ) return false; - result += '\n'; + result += QLatin1Char('\n'); break; case '\\': ++mState.cursor; if ( atEnd() ) break; // 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(); const int eightBitLen = mState.cursor - eightBitBegin; assert( eightBitLen > 0 ); if ( isValidUtf8( eightBitBegin, eightBitLen ) ) result += dec->toUnicode( eightBitBegin, eightBitLen ); else { assert( column() >= eightBitLen ); makeError( Error::InvalidUTF8, line(), column() - eightBitLen ); return false; } } } makeError( Error::PrematureEndOfQuotedString, qsBeginLine, qsBeginCol ); return false; } void Lexer::Impl::makeIllegalCharError( char ch ) { makeError( isIllegal( ch ) ? Error::IllegalCharacter : Error::UnexpectedCharacter ); } } // namespace KSieve diff --git a/libksieve/parser/parser.cpp b/libksieve/parser/parser.cpp index 2192e68b90..35624526c5 100644 --- a/libksieve/parser/parser.cpp +++ b/libksieve/parser/parser.cpp @@ -1,650 +1,650 @@ /* -*- c++ -*- parser/parser.cpp This file is part of KSieve, the KDE internet mail/usenet news message filtering library. Copyright (c) 2002-2003 Marc Mutz KSieve 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. KSieve 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 In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include #include #include #include #include #include // ULONG_MAX #include // isdigit namespace KSieve { // // // Parser Bridge implementation // // Parser::Parser( const char * scursor, const char * const send, int options ) : i( 0 ) { i = new Impl( scursor, send, options ); } Parser::~Parser() { delete i; i = 0; } void Parser::setScriptBuilder( ScriptBuilder * builder ) { assert( i ); i->mBuilder = builder; } ScriptBuilder * Parser::scriptBuilder() const { assert( i ); return i->mBuilder; } const Error & Parser::error() const { assert( i ); return i->error(); } bool Parser::parse() { assert( i ); return i->parse(); } } static inline unsigned long factorForQuantifier( char ch ) { switch ( ch ) { case 'g': case 'G': return 1024*1024*1024; case 'm': case 'M': return 1024*1024; case 'k': case 'K': return 1024; default: assert( 0 ); // lexer should prohibit this return 1; // make compiler happy } } static inline bool willOverflowULong( unsigned long result, unsigned long add ) { static const unsigned long maxULongByTen = (unsigned long)(ULONG_MAX / 10.0) ; return result > maxULongByTen || ULONG_MAX - 10 * result < add ; } namespace KSieve { // // // Parser Implementation // // Parser::Impl::Impl( const char * scursor, const char * const send, int options ) : mToken( Lexer::None ), lexer( scursor, send, options ), mBuilder( 0 ) { } bool Parser::Impl::isStringToken() const { return token() == Lexer::QuotedString || token() == Lexer::MultiLineString ; } bool Parser::Impl::isArgumentToken() const { return isStringToken() || token() == Lexer::Number || token() == Lexer::Tag || - ( token() == Lexer::Special && mTokenValue == "[" ); + ( token() == Lexer::Special && mTokenValue == QLatin1String("[")) ; } bool Parser::Impl::obtainToken() { while ( !mToken && !lexer.atEnd() && !lexer.error() ) { mToken = lexer.nextToken( mTokenValue ); if ( lexer.error() ) break; // comments and line feeds are semantically invisible and may // appear anywhere, so we handle them here centrally: switch ( token() ) { case Lexer::HashComment: if ( scriptBuilder() ) scriptBuilder()->hashComment( tokenValue() ); consumeToken(); break; case Lexer::BracketComment: if ( scriptBuilder() ) scriptBuilder()->bracketComment( tokenValue() ); consumeToken(); break; case Lexer::LineFeeds: for ( unsigned int i = 0, end = tokenValue().toUInt() ; i < end ; ++i ) if ( scriptBuilder() ) // better check every iteration, b/c // we call out to ScriptBuilder, // where nasty things might happen! scriptBuilder()->lineFeed(); consumeToken(); break; default: ; // make compiler happy } } if ( lexer.error() && scriptBuilder() ) scriptBuilder()->error( lexer.error() ); return !lexer.error(); } bool Parser::Impl::parse() { // this is the entry point: START := command-list if ( !parseCommandList() ) return false; if ( !atEnd() ) { makeUnexpectedTokenError( Error::ExpectedCommand ); return false; } if ( scriptBuilder() ) scriptBuilder()->finished(); return true; } bool Parser::Impl::parseCommandList() { // our ABNF: // command-list := *comand while ( !atEnd() ) { if ( !obtainToken() ) return false; if ( token() == Lexer::None ) continue; if ( token() != Lexer::Identifier ) return true; if ( !parseCommand() ) { assert( error() ); return false; } } return true; } bool Parser::Impl::parseCommand() { // command := identifier arguments ( ";" / block ) // arguments := *argument [ test / test-list ] // block := "{" *command "}" // our ABNF: // block := "{" [ command-list ] "}" if ( atEnd() ) return false; // // identifier // if ( !obtainToken() || token() != Lexer::Identifier ) return false; if ( scriptBuilder() ) scriptBuilder()->commandStart( tokenValue() ); consumeToken(); // // *argument // if ( !obtainToken() ) return false; if ( atEnd() ) { makeError( Error::MissingSemicolonOrBlock ); return false; } if ( isArgumentToken() && !parseArgumentList() ) { assert( error() ); return false; } // // test / test-list // if ( !obtainToken() ) return false; if ( atEnd() ) { makeError( Error::MissingSemicolonOrBlock ); return false; } - if ( token() == Lexer::Special && tokenValue() == "(" ) { // test-list + if ( token() == Lexer::Special && tokenValue() == QLatin1String ("(")) { // test-list if ( !parseTestList() ) { assert( error() ); return false; } } else if ( token() == Lexer::Identifier ) { // should be test: if ( !parseTest() ) { assert( error() ); return false; } } // // ";" / block // if ( !obtainToken() ) return false; if ( atEnd() ) { makeError( Error::MissingSemicolonOrBlock ); return false; } if ( token() != Lexer::Special ) { makeUnexpectedTokenError( Error::ExpectedBlockOrSemicolon ); return false; } - if ( tokenValue() == ";" ) + if ( tokenValue() == QLatin1String (";")) consumeToken(); - else if ( tokenValue() == "{" ) { // block + else if ( tokenValue() == QLatin1String ("{")) { // block if ( !parseBlock() ) return false; // it's an error since we saw '{' } else { makeError( Error::MissingSemicolonOrBlock ); return false; } if ( scriptBuilder() ) scriptBuilder()->commandEnd(); return true; } bool Parser::Impl::parseArgumentList() { // our ABNF: // argument-list := *argument while ( !atEnd() ) { if ( !obtainToken() ) return false; if ( !isArgumentToken() ) return true; if ( !parseArgument() ) return !error(); } return true; } bool Parser::Impl::parseArgument() { // argument := string-list / number / tag if ( !obtainToken() || atEnd() ) return false; if ( token() == Lexer::Number ) { if ( !parseNumber() ) { assert( error() ); return false; } return true; } else if ( token() == Lexer::Tag ) { if ( scriptBuilder() ) scriptBuilder()->taggedArgument( tokenValue() ); consumeToken(); return true; } else if ( isStringToken() ) { if ( scriptBuilder() ) scriptBuilder()->stringArgument( tokenValue(), token() == Lexer::MultiLineString, QString() ); consumeToken(); return true; - } else if ( token() == Lexer::Special && tokenValue() == "[" ) { + } else if ( token() == Lexer::Special && tokenValue() == QLatin1String("[")) { if ( !parseStringList() ) { assert( error() ); return false; } return true; } return false; } bool Parser::Impl::parseTestList() { // test-list := "(" test *("," test) ")" if ( !obtainToken() || atEnd() ) return false; - if ( token() != Lexer::Special || tokenValue() != "(" ) + if ( token() != Lexer::Special || tokenValue() != QLatin1String("(")) return false; if ( scriptBuilder() ) scriptBuilder()->testListStart(); consumeToken(); // generic while/switch construct for comma-separated lists. See // parseStringList() for another one. Any fix here is like to apply there, too. bool lastWasComma = true; while ( !atEnd() ) { if ( !obtainToken() ) return false; switch ( token() ) { case Lexer::None: break; case Lexer::Special: assert( tokenValue().length() == 1 ); assert( tokenValue()[0].toLatin1() ); switch ( tokenValue()[0].toLatin1() ) { case ')': consumeToken(); if ( lastWasComma ) { makeError( Error::ConsecutiveCommasInTestList ); return false; } if ( scriptBuilder() ) scriptBuilder()->testListEnd(); return true; case ',': consumeToken(); if ( lastWasComma ) { makeError( Error::ConsecutiveCommasInTestList ); return false; } lastWasComma = true; break; default: makeError( Error::NonStringInStringList ); return false; } break; case Lexer::Identifier: if ( !lastWasComma ) { makeError( Error::MissingCommaInTestList ); return false; } else { lastWasComma = false; if ( !parseTest() ) { assert( error() ); return false; } } break; default: makeUnexpectedTokenError( Error::NonTestInTestList ); return false; } } makeError( Error::PrematureEndOfTestList ); return false; } bool Parser::Impl::parseTest() { // test := identifier arguments // arguments := *argument [ test / test-list ] // // identifier // if ( !obtainToken() || atEnd() ) return false; if ( token() != Lexer::Identifier ) return false; if ( scriptBuilder() ) scriptBuilder()->testStart( tokenValue() ); consumeToken(); // // *argument // if ( !obtainToken() ) return false; if ( atEnd() ) // a test w/o args goto TestEnd; if ( isArgumentToken() && !parseArgumentList() ) { assert( error() ); return false; } // // test / test-list // if ( !obtainToken() ) return false; if ( atEnd() ) // a test w/o nested tests goto TestEnd; - if ( token() == Lexer::Special && tokenValue() == "(" ) { // test-list + if ( token() == Lexer::Special && tokenValue() == QLatin1String("(")) { // test-list if ( !parseTestList() ) { assert( error() ); return false; } } else if ( token() == Lexer::Identifier ) { // should be test: if ( !parseTest() ) { assert( error() ); return false; } } TestEnd: if ( scriptBuilder() ) scriptBuilder()->testEnd(); return true; } bool Parser::Impl::parseBlock() { // our ABNF: // block := "{" [ command-list ] "}" if ( !obtainToken() || atEnd() ) return false; - if ( token() != Lexer::Special || tokenValue() != "{" ) + if ( token() != Lexer::Special || tokenValue() != QLatin1String("{")) return false; if ( scriptBuilder() ) scriptBuilder()->blockStart(); consumeToken(); if ( !obtainToken() ) return false; if ( atEnd() ) { makeError( Error::PrematureEndOfBlock ); return false; } if ( token() == Lexer::Identifier ) { if ( !parseCommandList() ) { assert( error() ); return false; } } if ( !obtainToken() ) return false; if ( atEnd() ) { makeError( Error::PrematureEndOfBlock ); return false; } - if ( token() != Lexer::Special || tokenValue() != "}" ) { + if ( token() != Lexer::Special || tokenValue() != QLatin1String("}")) { makeError( Error::NonCommandInCommandList ); return false; } if ( scriptBuilder() ) scriptBuilder()->blockEnd(); consumeToken(); return true; } bool Parser::Impl::parseStringList() { // string-list := "[" string *("," string) "]" / string // ;; if there is only a single string, the brackets are optional // // However, since strings are already handled separately from // string lists in parseArgument(), our ABNF is modified to: // string-list := "[" string *("," string) "]" if ( !obtainToken() || atEnd() ) return false; - if ( token() != Lexer::Special || tokenValue() != "[" ) + if ( token() != Lexer::Special || tokenValue() != QLatin1String("[") ) return false; if ( scriptBuilder() ) scriptBuilder()->stringListArgumentStart(); consumeToken(); // generic while/switch construct for comma-separated lists. See // parseTestList() for another one. Any fix here is like to apply there, too. bool lastWasComma = true; while ( !atEnd() ) { if ( !obtainToken() ) return false; switch ( token() ) { case Lexer::None: break; case Lexer::Special: assert( tokenValue().length() == 1 ); switch ( tokenValue()[0].toLatin1() ) { case ']': consumeToken(); if ( lastWasComma ) { makeError( Error::ConsecutiveCommasInStringList ); return false; } if ( scriptBuilder() ) scriptBuilder()->stringListArgumentEnd(); return true; case ',': consumeToken(); if ( lastWasComma ) { makeError( Error::ConsecutiveCommasInStringList ); return false; } lastWasComma = true; break; default: makeError( Error::NonStringInStringList ); return false; } break; case Lexer::QuotedString: case Lexer::MultiLineString: if ( !lastWasComma ) { makeError( Error::MissingCommaInStringList ); return false; } lastWasComma = false; if ( scriptBuilder() ) scriptBuilder()->stringListEntry( tokenValue(), token() == Lexer::MultiLineString, QString() ); consumeToken(); break; default: makeError( Error::NonStringInStringList ); return false; } } makeError( Error::PrematureEndOfStringList ); return false; } bool Parser::Impl::parseNumber() { // The lexer returns the number including the quantifier as a // single token value. Here, we split is an check that the number // is not out of range: if ( !obtainToken() || atEnd() ) return false; if ( token() != Lexer::Number ) return false; // number: unsigned long result = 0; 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; } else { result *= 10 ; result += digitValue ; } } // optional quantifier: char quantifier = '\0'; if ( i < s.length() ) { assert( i + 1 == s.length() ); quantifier = s[i]; const unsigned long factor = factorForQuantifier( quantifier ); if ( result > double(ULONG_MAX) / double(factor) ) { makeError( Error::NumberOutOfRange ); return false; } result *= factor; } if ( scriptBuilder() ) scriptBuilder()->numberArgument( result, quantifier ); consumeToken(); return true; } } // namespace KSieve