summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2014-07-23 23:03:51 (GMT)
committerChristian Mollekopf <chrigi_1@fastmail.fm>2014-07-23 23:03:51 (GMT)
commit232d34249773f0219d1632c7ac54f5fc344a4434 (patch)
treef1cc84fab22603f5b201b221117fc632e195adfc
parent2ca9206543d1ba2013093ecb592175c70a6271a1 (diff)
parent088c51125d22ca3f26b35476edc0851a4742acfb (diff)
downloadkolab-utils-232d34249773f0219d1632c7ac54f5fc344a4434.tar.gz
Merge remote-tracking branch 'origin/fbdaemon'
-rw-r--r--CMakeLists.txt5
-rw-r--r--fbdaemon/CMakeLists.txt8
-rw-r--r--fbdaemon/README40
-rw-r--r--fbdaemon/authenticationjob.cpp5
-rw-r--r--fbdaemon/fbcoordinator.cpp4
-rw-r--r--fbdaemon/fbdaemonconnection.cpp329
-rw-r--r--fbdaemon/fbdaemonconnection.h75
-rw-r--r--fbdaemon/fbdaemonserver.cpp69
-rw-r--r--fbdaemon/fbdaemonserver.h42
-rw-r--r--fbdaemon/fbdaemonthread.cpp100
-rw-r--r--fbdaemon/fbdaemonthread.h59
-rw-r--r--fbdaemon/fbgeneratorfolderjob.cpp158
-rw-r--r--fbdaemon/fbgeneratorfolderjob.h63
-rw-r--r--fbdaemon/fbgeneratorjob.cpp86
-rw-r--r--fbdaemon/fbgeneratorjob.h17
-rw-r--r--fbdaemon/generatefbjob.cpp1
-rw-r--r--fbdaemon/kolabjob.h2
-rw-r--r--fbdaemon/main.cpp11
-rw-r--r--fbdaemon/settings.cpp17
-rw-r--r--fbdaemon/settings.h7
-rw-r--r--fbdaemon/tests/CMakeLists.txt32
-rw-r--r--fbdaemon/tests/README17
-rw-r--r--fbdaemon/tests/cyrusfakeserver.cpp113
-rw-r--r--fbdaemon/tests/cyrusfakeserver.h35
-rw-r--r--fbdaemon/tests/daemonconnectiontest.cpp330
-rw-r--r--fbdaemon/tests/daemonconnectiontest.h43
-rw-r--r--fbdaemon/tests/data/aggregator/imap-28470360
-rw-r--r--fbdaemon/tests/data/generatefolder/empty.log34
-rw-r--r--fbdaemon/tests/data/generatefolder/normal.log115
-rw-r--r--fbdaemon/tests/data/generatefolder/notfound.log22
-rw-r--r--fbdaemon/tests/data/generatefolder/setrights.log119
-rw-r--r--fbdaemon/tests/data/generatefolder/systemioerror.log25
-rw-r--r--fbdaemon/tests/data/generator/imap-6398229
-rw-r--r--fbdaemon/tests/data/generator/imap-6398.withoutattach133
-rw-r--r--fbdaemon/tests/fbaggregatortest.cpp58
-rw-r--r--fbdaemon/tests/fbaggregatortest.h8
-rw-r--r--fbdaemon/tests/fbgeneratorfoldertest.cpp205
-rw-r--r--fbdaemon/tests/fbgeneratorfoldertest.h48
-rw-r--r--fbdaemon/tests/fbgeneratortest.cpp145
-rw-r--r--fbdaemon/tests/fbgeneratortest.h20
40 files changed, 3017 insertions, 172 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d7b3429..5e05d64 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,6 +38,7 @@ if (USE_LIBCALENDARING)
find_package(Libcalendaring)
set( KDE_INCLUDES ${Libcalendaring_INCLUDE_DIRS} )
set( KDE_LIBRARIES ${Libcalendaring_LIBRARIES} )
+ find_library(KDE_KIMAPTEST NAMES calendaring-kimaptest)
message("${Libcalendaring_INCLUDE_DIRS} ${Libcalendaring_LIBRARIES}")
else()
find_package(KDE4 4.8 REQUIRED)
@@ -50,6 +51,7 @@ else()
${KDEPIMLIBS_KMIME_LIBS}
${KDEPIMLIBS_KIMAP_LIBS}
)
+ find_library(KDE_KIMAPTEST NAMES kimaptest)
endif()
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-check-new -fno-common -Woverloaded-virtual -fno-threadsafe-statics -fvisibility=hidden -Werror=return-type -fvisibility-inlines-hidden -fexceptions -UQT_NO_EXCEPTIONS -g" )
@@ -63,11 +65,14 @@ set(COMMON_DEPENDENCIES
${Libkolab_LIBRARIES}
${Libkolabxml_LIBRARIES}
${QT_QTCORE_LIBRARY}
+ ${QT_QTNETWORK_LIBRARY}
${QT_QTXML_LIBRARY}
${QT_GUI_LIBRARY}
${KDE_LIBRARIES}
)
+enable_testing()
+
add_subdirectory(lib)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
diff --git a/fbdaemon/CMakeLists.txt b/fbdaemon/CMakeLists.txt
index 23aff08..5ea8b47 100644
--- a/fbdaemon/CMakeLists.txt
+++ b/fbdaemon/CMakeLists.txt
@@ -2,19 +2,27 @@ set (FBDAEMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/authenticationjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/fbcoordinator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/fbgeneratorjob.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbgeneratorfolderjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/generatefbjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/settings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/kolabjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/fbaggregatorjob.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonserver.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonthread.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonconnection.cpp
)
QT4_WRAP_CPP(FB_MOC
${CMAKE_CURRENT_SOURCE_DIR}/authenticationjob.h
${CMAKE_CURRENT_SOURCE_DIR}/fbcoordinator.h
${CMAKE_CURRENT_SOURCE_DIR}/fbgeneratorjob.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbgeneratorfolderjob.h
${CMAKE_CURRENT_SOURCE_DIR}/generatefbjob.h
${CMAKE_CURRENT_SOURCE_DIR}/kolabjob.h
${CMAKE_CURRENT_SOURCE_DIR}/fbaggregatorjob.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonserver.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonthread.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fbdaemonconnection.h
)
set(FBDAEMON_SRCS ${FBDAEMON_SRCS} ${FB_MOC})
diff --git a/fbdaemon/README b/fbdaemon/README
index 096a412..eeb2193 100644
--- a/fbdaemon/README
+++ b/fbdaemon/README
@@ -1,4 +1,4 @@
-The fbdeamon generates and aggreagates fb (freebusy) information on the kolab server.
+The fbdaemon generates and aggreagates fb (freebusy) information on the kolab server.
== Functionality ==
@@ -31,6 +31,42 @@ The aggregator reads all freebusy objects, merges the periods of each object and
The default output directory is /var/lib/kolab-freebusy
+== Daemon mode ==
+
+A daemon that clients can use to generate F/B-Information for users and folders. The daemon starts an AF_INET socket for incoming connections.
+
+The protocol is similar to IMAP but untagged since no parallel requests are supported.
+
+Generate F/B for a user for the period between timestamp1 and timestamp2 which are unix timestamps:
+
+ C: IFB USER "$email" slot:$timestamp1-$timestamp2
+ S: * ({$datasize}\r\n$ifbdata)
+ S: OK
+
+The timestamps are optional, and $now:$now+$defaultvalue are used, if not available.
+
+Generate F/B for a folder for the period between timestamp1 and timestamp2 which are unix timestamps:
+
+ C: IFB FOLDER "$path" slot:$timestamp1-$timestamp2
+ S: * ({$datasize}\r\n$ifbdata)
+ S: OK
+
+The timestamps are optional, and $now:$now+$defaultvalue shall be used if not available.
+
+'interval' is a default nominator for a default interval, i.e.:
+
+ C: IFB EMAIL "$email" slot:interval-1402593346
+
+shall depict F/B $default_interval counting backwards from 1402593346, and
+
+ C: IFB EMAIL "$email" slot:1402593346-interval
+
+shall depict F/B $default_interval counting forwards from 1402593346.
+
+If there is an error:
+
+ S:BAD $reason
+
== Configuration ==
The configruation file is by default at /etc/kolab/kolab.conf. The location can be overridden using --configuration.
@@ -55,4 +91,4 @@ For additional information regarding the design and future features, see:
* http://wiki.kolab.org/Free_Busy
* http://wiki.kolab.org/FreeBusy_in_Kolab_3.0
-[0] https://wiki.kolab.org/User:Mollekopf/Drafts/KEP:17#Freebusy \ No newline at end of file
+[0] https://wiki.kolab.org/User:Mollekopf/Drafts/KEP:17#Freebusy
diff --git a/fbdaemon/authenticationjob.cpp b/fbdaemon/authenticationjob.cpp
index 39368d4..c4a22a5 100644
--- a/fbdaemon/authenticationjob.cpp
+++ b/fbdaemon/authenticationjob.cpp
@@ -29,16 +29,13 @@ AuthenticationJob::AuthenticationJob(const SessionSettings &settings, KIMAP::Ses
}
-
void AuthenticationJob::start()
{
Debug() << "logging in as " << mSessionSettings.userName << " using " << mSessionSettings.authorizationName;
KIMAP::LoginJob *loginJob = new KIMAP::LoginJob( mSession );
+ loginJob->setUserName(mSessionSettings.userName);
if (!mSessionSettings.authorizationName.isEmpty()) {
- loginJob->setUserName(mSessionSettings.userName);
loginJob->setAuthorizationName(mSessionSettings.authorizationName);
- } else {
- loginJob->setUserName(mSessionSettings.userName);
}
loginJob->setPassword(Settings::instance().getPassword());
diff --git a/fbdaemon/fbcoordinator.cpp b/fbdaemon/fbcoordinator.cpp
index 2da2bfa..5d4d34d 100644
--- a/fbdaemon/fbcoordinator.cpp
+++ b/fbdaemon/fbcoordinator.cpp
@@ -127,7 +127,7 @@ void FBCoordinator::generateAllForUser(const QString& user)
SequentialCompositeJob *userJob = new SequentialCompositeJob(true, this);
SessionSettings settings = Settings::instance().getSessionSettings();
settings.userName = user;
- userJob->addSubjob(new FBGeneratorJob(settings, userJob));
+ userJob->addSubjob(new FBGeneratorJob(settings, true, userJob));
userJob->addSubjob(new FBAggregatorJob(settings, userJob));
connect(userJob, SIGNAL(result(KJob*)), this, SLOT(onGeneratorDone(KJob*)));
mUserTimer.start();
@@ -140,7 +140,7 @@ void FBCoordinator::generateForUser(const QString& user)
mTimer.start();
SessionSettings settings = Settings::instance().getSessionSettings();
settings.userName = user;
- FBGeneratorJob *generator = new FBGeneratorJob(settings, this);
+ FBGeneratorJob *generator = new FBGeneratorJob(settings, true, this);
connect(generator, SIGNAL(result(KJob*)), this, SLOT(onGeneratorDone(KJob*)));
generator->start();
}
diff --git a/fbdaemon/fbdaemonconnection.cpp b/fbdaemon/fbdaemonconnection.cpp
new file mode 100644
index 0000000..e155658
--- /dev/null
+++ b/fbdaemon/fbdaemonconnection.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fbdaemonconnection.h"
+#include "fbdaemonthread.h"
+#include "fbgeneratorjob.h"
+#include "fbgeneratorfolderjob.h"
+
+#include "settings.h"
+
+#include <xmlobject.h>
+#include <freebusy.h>
+#include <kpimutils/email.h>
+#include <kimap/rfccodecs.h>
+
+#include <QRegExp>
+#include <QStringList>
+
+FbDaemonConnection::FbDaemonConnection(FbDaemonThread* parent)
+ : QObject(parent)
+ , job(0)
+ , defaultTimeslot(1000)
+{
+ connect(parent, SIGNAL(newLine(const QByteArray&)), SLOT(onNewLine(const QByteArray&)));
+}
+
+FbDaemonConnection::~FbDaemonConnection()
+{
+ setJob(0);
+}
+
+void FbDaemonConnection::setJob(KJob* j)
+{
+ if (j == job) {
+ return;
+ }
+
+ if (job) {
+ emit job->kill();
+ }
+
+ job = j;
+}
+
+
+void FbDaemonConnection::generateForUser(const QStringList parameters)
+{
+ QString user = parameters[0];
+ if (!validateGenerateForUser(user)) {
+ sendError("emailaddress not valid");
+ return;
+ }
+
+ SessionSettings settings = Settings::instance().getSessionSettings();
+ settings.userName = user;
+ FBGeneratorJob *generator = new FBGeneratorJob(settings, false, this);
+
+ foreach(QString parameter, parameters.mid(1)) {
+ if (parameter.startsWith("slot:")) {
+ if (!validateTimeslot(parameter)) {
+ sendError("malformed timeslot");
+ return;
+ }
+ Timeslot slot = parseTimeslot(parameter);
+ generator->setTimeFrame(slot.start, slot.end);
+ } else {
+ sendError("unknown parameter");
+ return;
+ }
+ }
+
+ connect(generator, SIGNAL(result(KJob*)), this, SLOT(onGenerateUserDone(KJob*)));
+ setJob(generator);
+ generator->start();
+}
+
+void FbDaemonConnection::generateForFolder(const QStringList parameters)
+{
+ QString folder = parameters[0];
+ if (!validateGenerateForFolder(folder)) {
+ sendError("folder not valid");
+ return;
+ }
+ folder = KIMAP::decodeImapFolderName(folder);
+ SessionSettings settings = Settings::instance().getSessionSettings();
+ //Login as admin
+ settings.userName = settings.authorizationName;
+ settings.authorizationName = "";
+ FBGeneratorFolderJob *generator = new FBGeneratorFolderJob(settings, folder, this);
+
+ foreach(QString parameter, parameters.mid(1)) {
+ if (parameter.startsWith("slot:")) {
+ if (!validateTimeslot(parameter)) {
+ sendError("malformed timeslot");
+ return;
+ }
+ Timeslot slot = parseTimeslot(parameter);
+ generator->setTimeFrame(slot.start, slot.end);
+ } else {
+ sendError("unknown parameter");
+ return;
+ }
+ }
+
+ connect(generator, SIGNAL(result(KJob*)), this, SLOT(onGenerateFolderDone(KJob*)));
+ setJob(generator);
+ generator->start();
+}
+
+void FbDaemonConnection::onNewLine(const QByteArray& line)
+{
+ if (job) {
+ sendError("Can only process one command after another");
+ return;
+ }
+
+ QStringList tokens;
+ QString token;
+ QRegExp rx("([ \n\"])");
+
+ int pos = 0, oldpos = 0;
+ bool insideString = false;
+
+ line.trimmed();
+ while ((pos = rx.indexIn(line, pos)) != -1) {
+ if (insideString) {
+ if (line[pos - 1] == '\\') { //escaped "
+ token += line.mid(oldpos, pos - oldpos - 1);
+ } else {
+ token += line.mid(oldpos, pos - oldpos);
+ if (rx.cap(1) == QString('"')) {
+ insideString = false;
+ token = token.mid(1, token.size() - 1);
+ }
+ }
+ } else {
+ if (oldpos > 0) {
+ oldpos += 1;
+ }
+ token = line.mid(oldpos, pos - oldpos);
+ if (rx.cap(1) == QString('"')) {
+ insideString = true;
+ }
+ }
+
+ if (!insideString && !token.trimmed().isEmpty()) {
+ tokens << token.trimmed();
+ }
+
+ oldpos = pos;
+ pos += 1;
+ }
+
+ if (insideString) {
+ sendError("no closing \" found");
+ return;
+ }
+
+ token = line.mid(oldpos + 1).trimmed();
+ if (!token.isEmpty()) {
+ tokens << token;
+ }
+
+ qDebug() << "tokens" << tokens;
+ if (tokens.size() < 3) {
+ sendError("command not valid");
+ return;
+ }
+
+ method = tokens[0].toAscii();
+
+ if (!validateMethod(method)) {
+ sendError("method not valid");
+ return;
+ }
+
+ QString submethod = tokens[1].toUpper();
+ QStringList parameters = tokens.mid(2);
+
+ if (submethod == "USER") {
+ generateForUser(parameters);
+ } else if (submethod == "FOLDER") {
+ generateForFolder(parameters);
+ } else {
+ sendError("unknown command: " + submethod.toAscii());
+ }
+}
+
+void FbDaemonConnection::sendError(const QByteArray& error)
+{
+ qDebug() << "An error occured " << error;
+ emit sendData(createStatusLine("BAD", error));
+ setJob(0);
+}
+
+const QByteArray FbDaemonConnection::createStatusLine(const QByteArray &status, const QByteArray &additional)
+{
+ QByteArray line;
+ line += status.toUpper();
+ if (!method.isEmpty()) {
+ line += " " + method;
+ }
+ line += " " + additional + "\r\n";
+ line += "\r\n";
+
+ return line;
+}
+
+bool FbDaemonConnection::validateGenerateForUser(const QString& user)
+{
+ return KPIMUtils::isValidSimpleAddress(user);
+}
+
+bool FbDaemonConnection::validateGenerateForFolder(const QString& folder)
+{
+ QRegExp utf7("^[A-Za-z0-9\\+,\\(\\)<>@,;:\"/\\[\\]?.= ]+$");
+ return utf7.exactMatch(folder);
+}
+
+bool FbDaemonConnection::validateMethod(const QByteArray& method)
+{
+ return (method.toUpper() == "IFB");
+}
+
+Timeslot FbDaemonConnection::parseTimeslot(const QString& timeslot)
+{
+ Timeslot slot;
+ QString start = "interval";
+ QString end = "interval";
+ if (!timeslot.isEmpty()) {
+ QRegExp reSlot("^slot:(interval|[0-9]+)-(interval|[0-9]+)$");
+ int pos = reSlot.indexIn(timeslot);
+ if (pos == -1) {
+ Q_ASSERT(pos);
+ }
+
+ start = reSlot.cap(1);
+ end = reSlot.cap(2);
+ }
+
+ if (start == "interval" && end == "interval") {
+ slot.start = KDateTime::currentUtcDateTime(); // $now
+ } else if (start != "interval") {
+ slot.start.setTime_t(start.toUInt());
+ }
+
+ if (end != "interval") {
+ slot.end.setTime_t(end.toUInt());
+
+ if (start == "interval") {
+ slot.start = slot.end.addDays(-Settings::instance().getTimeframe()); // $now - $defaultvalue
+ }
+
+ } else {
+ slot.end = slot.start.addDays(Settings::instance().getTimeframe()); // $now + $defaultvalue
+ }
+
+ return slot;
+}
+
+bool FbDaemonConnection::validateTimeslot(const QString& timeslot)
+{
+ QRegExp reSlot("^slot:(interval|[0-9]+)-(interval|[0-9]+)$");
+ return reSlot.exactMatch(timeslot);
+}
+
+void FbDaemonConnection::onGenerateFolderDone(KJob* job)
+{
+ if (job != this->job) {
+ qDebug() << "On other job emitted finished, that should not happen";
+ qDebug() << "job:" << job << " this->job:" << this->job;
+ sendError("Something stranged happend");
+ return;
+ }
+ if (job->error()) {
+ sendError(job->errorString().toAscii());
+ return;
+ }
+ FBGeneratorFolderJob *fbJob = qobject_cast<FBGeneratorFolderJob*>(job);
+ Q_ASSERT(fbJob);
+
+ onSendFbObject(fbJob->getFreebusy());
+}
+
+void FbDaemonConnection::onGenerateUserDone(KJob* job)
+{
+ if (job != this->job) {
+ qDebug() << "On other job emitted finished, that should not happen";
+ qDebug() << "job:" << job << " this->job:" << this->job;
+ sendError("Something stranged happend");
+ return;
+ }
+ if (job->error()) {
+ sendError(job->errorString().toAscii());
+ return;
+ }
+ FBGeneratorJob *fbJob = qobject_cast<FBGeneratorJob*>(job);
+ Q_ASSERT(fbJob);
+
+ onSendFbObject(fbJob->getFreebusy());
+}
+
+
+void FbDaemonConnection::onSendFbObject(const Kolab::Freebusy &freebusy)
+{
+ QByteArray block;
+ const std::string &sIFB = Kolab::FreebusyUtils::toIFB(freebusy);
+ block = "* ({" + QByteArray::number((uint)sIFB.length()) + "} \r\n";
+ block += sIFB.c_str();
+ block += ")\r\n";
+ block += createStatusLine("OK", "completed");
+
+ emit sendData(block);
+ setJob(0);
+} \ No newline at end of file
diff --git a/fbdaemon/fbdaemonconnection.h b/fbdaemon/fbdaemonconnection.h
new file mode 100644
index 0000000..baed71a
--- /dev/null
+++ b/fbdaemon/fbdaemonconnection.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FBDAEMONCONNECTION_H
+#define FBDAEMONCONNECTION_H
+
+#include <QObject>
+#include <QFile>
+#include <QStringList>
+#include <kdatetime.h>
+#include <kjob.h>
+#include <kolabfreebusy.h>
+class FbDaemonThread;
+class ConnectionTest;
+
+struct Timeslot {
+ KDateTime start;
+ KDateTime end;
+};
+
+class FbDaemonConnection : public QObject
+{
+ Q_OBJECT
+ friend class ConnectionTest;
+public:
+ FbDaemonConnection(FbDaemonThread* parent);
+ virtual ~FbDaemonConnection();
+
+signals:
+ void sendData(const QByteArray&);
+ void close();
+
+protected:
+ void setJob(KJob*);
+
+private:
+ const QByteArray createStatusLine(const QByteArray &status, const QByteArray &additional);
+ void generateForUser(const QStringList);
+ void generateForFolder(const QStringList);
+
+ void sendError(const QByteArray&);
+
+ bool validateMethod(const QByteArray&);
+ bool validateGenerateForUser(const QString&);
+ bool validateGenerateForFolder(const QString&);
+ bool validateTimeslot(const QString&);
+ Timeslot parseTimeslot(const QString&);
+
+ QByteArray method;
+ KJob *job;
+ int defaultTimeslot;
+private slots:
+ void onNewLine(const QByteArray&);
+ void onGenerateUserDone(KJob*);
+ void onGenerateFolderDone(KJob*);
+ void onSendFbObject(const Kolab::Freebusy&);
+
+
+};
+
+#endif // FBDAEMONCONNECTION_H
diff --git a/fbdaemon/fbdaemonserver.cpp b/fbdaemon/fbdaemonserver.cpp
new file mode 100644
index 0000000..fc96e47
--- /dev/null
+++ b/fbdaemon/fbdaemonserver.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fbdaemonserver.h"
+#include <QThread>
+
+FbDaemonServer::FbDaemonServer(QObject *parent)
+ : QTcpServer(parent)
+{
+}
+
+FbDaemonServer::~FbDaemonServer()
+{
+ foreach(QThread * thread, mThreads) {
+ thread->quit();
+ }
+ foreach(QThread * thread, mThreads) {
+ thread->wait();
+ delete thread;
+ }
+}
+
+void FbDaemonServer::workerFinished(QThread *workerThread)
+{
+ //The worker thread is no longer necessary, so let's clean up.
+ //The worker object is cleaned up by the signal connected to deleteLater().
+ mThreads.remove(mThreads.indexOf(workerThread));
+ workerThread->quit();
+ workerThread->wait();
+ delete workerThread;
+}
+
+void FbDaemonServer::incomingConnection(int socketDescriptor)
+{
+ /**
+ * The concept is:
+ * Hand off the worker QObject to the worker thread, and never again touch it.
+ * All interaction is potentially from the wrong thread, so we're safer by not touch it.
+ * Everything that is happening in the worker object is happening from the worker thread =>
+ * we don't need to pay attention.
+ *
+ * By managing the thread from outside the object we can again ignore threading issues inside the object
+ * and keep the doors open to recycle threads in a thread pool.
+ */
+ FbDaemonThread *worker = new FbDaemonThread(socketDescriptor);
+ QThread *workerThread = new QThread();
+ worker->moveToThread(workerThread);
+ //Makes sure the FBDaemonThread is freed once the thread finishes and that the destructor is called from the worker thread
+ connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
+ connect(worker, SIGNAL(finished(QThread*)), this, SLOT(workerFinished(QThread*)));
+ //Starts the work in the new thread
+ QMetaObject::invokeMethod(worker, "start", Qt::QueuedConnection);
+ mThreads << workerThread;
+ workerThread->start();
+} \ No newline at end of file
diff --git a/fbdaemon/fbdaemonserver.h b/fbdaemon/fbdaemonserver.h
new file mode 100644
index 0000000..22b1316
--- /dev/null
+++ b/fbdaemon/fbdaemonserver.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef FBDAEMONSERVER_H
+#define FBDAEMONSERVER_H
+
+#include "fbdaemonthread.h"
+
+#include <QTcpServer>
+
+class FbDaemonServer : public QTcpServer
+{
+ Q_OBJECT
+
+public:
+ FbDaemonServer(QObject *parent = 0);
+ virtual ~FbDaemonServer();
+
+protected:
+ void incomingConnection(int socketDescriptor);
+private slots:
+ void workerFinished(QThread*);
+private:
+ QVector<QThread*> mThreads;
+};
+
+#endif \ No newline at end of file
diff --git a/fbdaemon/fbdaemonthread.cpp b/fbdaemon/fbdaemonthread.cpp
new file mode 100644
index 0000000..fe0c3f3
--- /dev/null
+++ b/fbdaemon/fbdaemonthread.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fbdaemonthread.h"
+
+#include <QtNetwork>
+
+FbDaemonThread::FbDaemonThread(int socketDescriptor, QObject* parent)
+ : QObject(parent)
+ , socketDescriptor(socketDescriptor)
+ , tcpSocket(0)
+ , connection(0)
+{
+ //This is the only part running in the parent thread, don't do anything here.
+}
+
+FbDaemonThread::~FbDaemonThread()
+{
+ //Ensure this is executed in the worker thread
+ Q_ASSERT(QThread::currentThread() == thread());
+ delete tcpSocket;
+ delete connection;
+}
+
+void FbDaemonThread::start()
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ tcpSocket = new QTcpSocket(this);
+ connection = new FbDaemonConnection(this);
+
+ if (!tcpSocket->setSocketDescriptor(socketDescriptor)) {
+ emit error(tcpSocket->error());
+ emit finished(thread());
+ return;
+ }
+
+ connect(tcpSocket, SIGNAL(readyRead()), SLOT(onData()));
+ connect(tcpSocket, SIGNAL(disconnected()), SLOT(onDisconnected()));
+ connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError)));
+
+ connect(connection, SIGNAL(sendData(const QByteArray&)), SLOT(onSendData(const QByteArray&)));
+ connect(connection, SIGNAL(close()), SLOT(onClose()));
+
+ qDebug() << "New client from " << tcpSocket->peerAddress() << tcpSocket->peerPort();
+}
+
+void FbDaemonThread::onDisconnected()
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ qDebug() << "disconnected";
+ emit finished(thread());
+}
+
+void FbDaemonThread::onSocketError(QAbstractSocket::SocketError error)
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ qWarning() << "Socket error: " << error;
+ emit this->error(error);
+ //Never call disconnectFromHost in error state. It will result in an endless loop because it tries to write data.
+ onClose();
+}
+
+void FbDaemonThread::onData()
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ data += tcpSocket->readAll();
+ if (data.contains('\n')) {
+ int bytes = data.indexOf('\n') + 1;
+ QByteArray message = data.left(bytes);
+ data = data.mid(bytes);
+ emit newLine(message);
+ }
+}
+
+void FbDaemonThread::onSendData(const QByteArray& block)
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ tcpSocket->write(block);
+}
+
+void FbDaemonThread::onClose()
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ tcpSocket->close();
+}
+
diff --git a/fbdaemon/fbdaemonthread.h b/fbdaemon/fbdaemonthread.h
new file mode 100644
index 0000000..f29f31d
--- /dev/null
+++ b/fbdaemon/fbdaemonthread.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FBDEAMONTHREAD_H
+#define FBDEAMONTHREAD_H
+
+#include "fbdaemonconnection.h"
+
+#include <QObject>
+#include <QTcpSocket>
+
+class FbDaemonThread : public QObject
+{
+ Q_OBJECT
+
+public:
+ FbDaemonThread(int socketDescriptor, QObject *parent = 0);
+ virtual ~FbDaemonThread();
+
+public slots:
+ void start();
+
+signals:
+ void error(QAbstractSocket::SocketError socketError);
+ void finished(QThread*);
+ void newLine(const QByteArray&);
+
+private:
+ int socketDescriptor;
+
+ QTcpSocket* tcpSocket;
+ FbDaemonConnection* connection;
+
+ QByteArray data;
+
+private slots:
+ void onData();
+ void onDisconnected();
+ void onSocketError(QAbstractSocket::SocketError);
+
+ void onSendData(const QByteArray &);
+ void onClose();
+};
+
+#endif \ No newline at end of file
diff --git a/fbdaemon/fbgeneratorfolderjob.cpp b/fbdaemon/fbgeneratorfolderjob.cpp
new file mode 100644
index 0000000..0dfc0da
--- /dev/null
+++ b/fbdaemon/fbgeneratorfolderjob.cpp
@@ -0,0 +1,158 @@
+/*
+ Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Affero General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+#include "fbgeneratorfolderjob.h"
+
+#include "settings.h"
+#include "generatefbjob.h"
+#include <jobs/getuserlistjob.h>
+#include <kimap/setacljob.h>
+#include <kimap/myrightsjob.h>
+#include <kimap/listjob.h>
+#include <kolabdefinitions.h>
+#include <commonconversion.h>
+
+FBGeneratorFolderJob::FBGeneratorFolderJob(const SessionSettings &settings, QString folder, QObject* parent)
+ : KolabJob(settings, parent),
+ mFolder(folder),
+ resetRights(false)
+{
+ mStartOfTimeFrame = KDateTime::currentUtcDateTime();
+ mEndOfTimeFrame = mStartOfTimeFrame.addDays(Settings::instance().getTimeframe());
+}
+
+void FBGeneratorFolderJob::startWork()
+{
+ KIMAP::MyRightsJob *rightsJob = new KIMAP::MyRightsJob(mSession);
+ rightsJob->setMailBox(mFolder);
+ connect(rightsJob, SIGNAL(result(KJob*)), this, SLOT(onMyRightsDone(KJob*)));
+ rightsJob->start();
+}
+
+void FBGeneratorFolderJob::onMyRightsDone(KJob* job)
+{
+ if (job->error()) {
+ qWarning() << job->errorString();
+ setError(KJob::UserDefinedError);
+ setErrorText(job->errorString());
+ logout();
+ return;
+ }
+
+ KIMAP::MyRightsJob *rightsJob = qobject_cast<KIMAP::MyRightsJob*>(job);
+ Q_ASSERT(rightsJob);
+
+ if (!rightsJob->hasRightEnabled(KIMAP::Acl::Lookup)
+ || !rightsJob->hasRightEnabled(KIMAP::Acl::Read)
+ || !rightsJob->hasRightEnabled(KIMAP::Acl::KeepSeen)) {
+
+ rights = rightsJob->rights();
+
+ KIMAP::SetAclJob * aclJob = new KIMAP::SetAclJob(mSession);
+ aclJob->setRights(KIMAP::AclJobBase::Add, KIMAP::Acl::Lookup | KIMAP::Acl::Read | KIMAP::Acl::KeepSeen);
+ aclJob->setMailBox(mFolder);
+ connect(aclJob, SIGNAL(result(KJob*)), this, SLOT(onSetAclDone(KJob*)));
+ aclJob->start();
+ } else {
+ startGenerateFBJob();
+ }
+}
+
+void FBGeneratorFolderJob::onSetAclDone(KJob* job)
+{
+ qDebug() << "setAclDone" ;
+ if (job->error()) {
+ qWarning() << job->errorString();
+ setError(KJob::UserDefinedError);
+ logout();
+ return;
+ }
+ resetRights = true;
+ startGenerateFBJob();
+
+}
+
+void FBGeneratorFolderJob::startGenerateFBJob()
+{
+ QStringList folders;
+ folders << mFolder;
+ GenerateFBJob *fbJob = new GenerateFBJob(folders, mStartOfTimeFrame, mEndOfTimeFrame, mSession, this);
+ QObject::connect(fbJob, SIGNAL(result(KJob*)), this, SLOT(onGenerateFBDone(KJob*)));
+ fbJob->start();
+}
+
+
+void FBGeneratorFolderJob::onGenerateFBDone(KJob* job)
+{
+ if (job->error()) {
+ qWarning() << "Error " << job->errorString();
+ setError(KJob::UserDefinedError);
+ logout();
+ return;
+ }
+ GenerateFBJob *fbJob = qobject_cast<GenerateFBJob*>(job);
+ Q_ASSERT(fbJob);
+
+ mFreebusy = fbJob->getFreebusy();
+
+ const Kolab::ContactReference org(Kolab::ContactReference::EmailReference, Kolab::Conversion::toStdString(QLatin1String("fbdaemon@localhost")));
+ mFreebusy.setOrganizer(org);
+
+ logout();
+}
+
+void FBGeneratorFolderJob::logout()
+{
+ if (resetRights) {
+ KIMAP::SetAclJob * aclJob = new KIMAP::SetAclJob(mSession);
+ aclJob->setRights(KIMAP::AclJobBase::Add, rights);
+ aclJob->setMailBox(mFolder);
+ connect(aclJob, SIGNAL(result(KJob*)), this, SLOT(onResetAclDone(KJob*)));
+ aclJob->start();
+ } else {
+ KolabJob::logout();
+ }
+}
+
+void FBGeneratorFolderJob::onResetAclDone(KJob* job)
+{
+ if (job->error()) {
+ qWarning() << "Error " << job->errorString();
+ setError(KJob::UserDefinedError);
+ }
+ KolabJob::logout();
+}
+
+
+void FBGeneratorFolderJob::setTimeFrame(const KDateTime& start, const KDateTime& end)
+{
+ mStartOfTimeFrame = start;
+ mEndOfTimeFrame = end;
+}
+
+Kolab::Freebusy FBGeneratorFolderJob::getFreebusy() const
+{
+ return mFreebusy;
+}
+
+const QString& FBGeneratorFolderJob::getFolder() const
+{
+ return mFolder;
+}
+
diff --git a/fbdaemon/fbgeneratorfolderjob.h b/fbdaemon/fbgeneratorfolderjob.h
new file mode 100644
index 0000000..90cdf5c
--- /dev/null
+++ b/fbdaemon/fbgeneratorfolderjob.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef FBGENERATORFOLDERJOB_H
+#define FBGENERATORFOLDERJOB_H
+#include "kolabjob.h"
+
+#include <kdatetime.h>
+#include <kolabfreebusy.h>
+#include <kimap/acl.h>
+
+class FBGeneratorFolderTest;
+
+class FBGeneratorFolderJob: public KolabJob
+{
+ Q_OBJECT
+ friend class FBGeneratorFolderTest;
+public:
+ explicit FBGeneratorFolderJob(const SessionSettings &settings, QString folder, QObject* parent = 0);
+
+ virtual void startWork();
+ void setTimeFrame(const KDateTime&, const KDateTime&);
+ void setEventFolders(const QStringList &);
+ Kolab::Freebusy getFreebusy() const;
+ const QString& getFolder() const;
+private slots:
+ void onMyRightsDone(KJob*);
+ void onSetAclDone(KJob*);
+ void onGenerateFBDone(KJob*);
+ void onResetAclDone(KJob*);
+
+protected:
+ virtual void logout();
+
+private:
+ void startGenerateFBJob();
+
+ QString mFolder;
+
+ Kolab::Freebusy mFreebusy;
+ KIMAP::Acl::Rights rights;
+ bool resetRights;
+
+ KDateTime mStartOfTimeFrame;
+ KDateTime mEndOfTimeFrame;
+};
+
+#endif // FBGENERATORJOB_H
diff --git a/fbdaemon/fbgeneratorjob.cpp b/fbdaemon/fbgeneratorjob.cpp
index 25afbe6..d2f0ed5 100644
--- a/fbdaemon/fbgeneratorjob.cpp
+++ b/fbdaemon/fbgeneratorjob.cpp
@@ -35,12 +35,13 @@
#include <kimap/logoutjob.h>
#include <kimap/expungejob.h>
-
-FBGeneratorJob::FBGeneratorJob(const SessionSettings &settings, QObject* parent)
-: KolabJob(settings, parent),
- mOldImapUid(-1)
+FBGeneratorJob::FBGeneratorJob(const SessionSettings &settings, bool store, QObject* parent)
+ : KolabJob(settings, parent),
+ mOldImapUid(-1),
+ mStore(store)
{
-
+ mStartOfTimeFrame = KDateTime::currentUtcDateTime();
+ mEndOfTimeFrame = mStartOfTimeFrame.addDays(Settings::instance().getTimeframe());
}
QStringList FBGeneratorJob::requiredFolders()
@@ -48,6 +49,12 @@ QStringList FBGeneratorJob::requiredFolders()
return QStringList() << KOLAB_FOLDER_TYPE_FREEBUSY;
}
+void FBGeneratorJob::startGenerateFBJob()
+{
+ GenerateFBJob *fbJob = new GenerateFBJob(mEventFolders, mStartOfTimeFrame, mEndOfTimeFrame, mSession, this);
+ QObject::connect(fbJob, SIGNAL(result(KJob*)), this, SLOT(onGenerateFBDone(KJob*)));
+ fbJob->start();
+}
void FBGeneratorJob::startWork()
{
@@ -65,9 +72,13 @@ void FBGeneratorJob::startWork()
return;
}
- FetchMessagesJob *fetchJob = new FetchMessagesJob(mFreebusyFolder, mSession, this);
- connect( fetchJob, SIGNAL(result(KJob*)), this, SLOT(onFetchFBDone(KJob*)) );
- fetchJob->start();
+ if (mStore) {
+ FetchMessagesJob *fetchJob = new FetchMessagesJob(mFreebusyFolder, mSession, this);
+ connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onFetchFBDone(KJob*)));
+ fetchJob->start();
+ } else {
+ startGenerateFBJob();
+ }
}
@@ -87,8 +98,6 @@ void FBGeneratorJob::onFetchFBDone(KJob *job)
kDebug() << "existing fb object " << msg->subject()->asUnicodeString();
}
- KDateTime startOfTimeFrame = KDateTime::currentUtcDateTime();
- const KDateTime endOfTimeFrame = startOfTimeFrame.addDays(Settings::instance().getTimeframe());
int threshold = Settings::instance().getThreshold();
/*
@@ -110,13 +119,13 @@ void FBGeneratorJob::onFetchFBDone(KJob *job)
const KDateTime start = Kolab::Conversion::toDate(oldFB.start());
const KDateTime end = Kolab::Conversion::toDate(oldFB.end());
- qDebug() << startOfTimeFrame << start << endOfTimeFrame << end << threshold;
- if (!(startOfTimeFrame < start || endOfTimeFrame > end.addDays(threshold))) {
+ qDebug() << mStartOfTimeFrame << start << mEndOfTimeFrame << end << threshold;
+ if (!(mStartOfTimeFrame < start || mEndOfTimeFrame > end.addDays(threshold))) {
qDebug() << "within threshold, skipping";
// upToDate = true;
continue;
}
-
+
if (mOldImapUid < 0) {
mOldImapUid = fetchJob->getImapUid(msg);
mOldFlags = fetchJob->getFlags(msg);
@@ -138,9 +147,7 @@ void FBGeneratorJob::onFetchFBDone(KJob *job)
*/
// kDebug() << mEventFolder;
- GenerateFBJob *fbJob = new GenerateFBJob(mEventFolders, startOfTimeFrame, endOfTimeFrame, mSession, this);
- QObject::connect(fbJob, SIGNAL(result(KJob*)), this, SLOT(onGenerateFBDone(KJob*)));
- fbJob->start();
+ startGenerateFBJob();
}
void FBGeneratorJob::onGenerateFBDone(KJob *job)
@@ -153,24 +160,37 @@ void FBGeneratorJob::onGenerateFBDone(KJob *job)
GenerateFBJob *fbJob = qobject_cast<GenerateFBJob*>( job );
Q_ASSERT(fbJob);
- Kolab::Freebusy fb = fbJob->getFreebusy();
- if (!fb.isValid()) {
- Debug() << "did not generate a fb object";
- //TODO generate empty fb object to mark time as free
- logout();
- return;
- }
+ mFreebusy = fbJob->getFreebusy();
+
const Kolab::ContactReference org(Kolab::ContactReference::EmailReference, Kolab::Conversion::toStdString(mSessionSettings.userName));
- fb.setOrganizer(org);
- if (!fb.organizer().isValid()) {
-// fb.setOrganizer(/*mUserName, mUserName+QLatin1Char('@')+mHostName.remove("imap.")*/);
+ mFreebusy.setOrganizer(org);
+
+ if (!mFreebusy.organizer().isValid()) {
+// mFreebusy.setOrganizer(/*mUserName, mUserName+QLatin1Char('@')+mHostName.remove("imap.")*/);
Warning() << "no valid organizer";
setError(KJob::UserDefinedError);
logout();
return;
}
- KMime::Message::Ptr message = Kolab::KolabObjectWriter::writeFreebusy(fb);
+ // We want only create the freebusy object and do not want to store it at the IMAP server
+ if (mStore) {
+ storeFBObject();
+ } else {
+ logout();
+ }
+}
+
+void FBGeneratorJob::storeFBObject()
+{
+ if (!mFreebusy.isValid()) {
+ Debug() << "did not generate a fb object";
+ //TODO generate empty fb object to mark time as free
+ logout();
+ return;
+ }
+
+ KMime::Message::Ptr message = Kolab::KolabObjectWriter::writeFreebusy(mFreebusy);
if (Kolab::ErrorHandler::instance().error() > Kolab::ErrorHandler::Debug) {
Warning() << "Error: " << Kolab::ErrorHandler::instance().errorMessage();
setError(KJob::UserDefinedError);
@@ -181,7 +201,7 @@ void FBGeneratorJob::onGenerateFBDone(KJob *job)
qint16 port;
message->appendHeader( new KMime::Headers::Generic( X_ORIGIN_HEADER, message.get(), Settings::instance().getServerUri(port).toUtf8(), "utf-8" ) );
message->assemble();
-
+
// kDebug() << message->encodedContent();
SequentialCompositeJob *seqJob = new SequentialCompositeJob(this);
seqJob->addSubjob(new MessageModifyJob(message, mFreebusyFolder, mOldFlags, mOldImapUid, mSession, this));
@@ -198,3 +218,13 @@ void FBGeneratorJob::onModDone(KJob *job)
logout();
}
+void FBGeneratorJob::setTimeFrame(const KDateTime &start, const KDateTime &end)
+{
+ mStartOfTimeFrame = start;
+ mEndOfTimeFrame = end;
+}
+
+Kolab::Freebusy FBGeneratorJob::getFreebusy()
+{
+ return mFreebusy;
+}
diff --git a/fbdaemon/fbgeneratorjob.h b/fbdaemon/fbgeneratorjob.h
index 47ee128..f730bfc 100644
--- a/fbdaemon/fbgeneratorjob.h
+++ b/fbdaemon/fbgeneratorjob.h
@@ -19,29 +19,44 @@
#ifndef FBGENERATORJOB_H
#define FBGENERATORJOB_H
#include <kjob.h>
+#include <kdatetime.h>
#include <kimap/session.h>
#include <kimap/fetchjob.h>
+#include <kolabfreebusy.h>
#include "kolabjob.h"
+class FBGeneratorTest;
+
class FBGeneratorJob: public KolabJob
{
Q_OBJECT
+ friend class FBGeneratorTest;
public:
- explicit FBGeneratorJob(const SessionSettings &settings, QObject* parent = 0);
+ explicit FBGeneratorJob(const SessionSettings &settings, bool store, QObject* parent = 0);
+
+ Kolab::Freebusy getFreebusy();
+ void setTimeFrame(const KDateTime&, const KDateTime&);
private Q_SLOTS:
void onFetchFBDone(KJob *job);
void onGenerateFBDone(KJob*);
void onModDone(KJob*);
protected:
virtual QStringList requiredFolders();
+ void startGenerateFBJob();
private:
virtual void startWork();
+ void storeFBObject();
QString mFreebusyFolder;
QStringList mEventFolders;
+ Kolab::Freebusy mFreebusy;
+ bool mStore;
qint64 mOldImapUid;
KIMAP::MessageFlags mOldFlags;
+
+ KDateTime mStartOfTimeFrame;
+ KDateTime mEndOfTimeFrame;
};
#endif // FBGENERATORJOB_H
diff --git a/fbdaemon/generatefbjob.cpp b/fbdaemon/generatefbjob.cpp
index 64c5915..f019048 100644
--- a/fbdaemon/generatefbjob.cpp
+++ b/fbdaemon/generatefbjob.cpp
@@ -90,6 +90,7 @@ void GenerateFBJob::onFetchDone(KJob *job)
Debug() << "found " << fetch->getMessages().size() << " messages";
if (fetch->getMessages().isEmpty()) {
+ mFreebusy = Kolab::FreebusyUtils::generateFreeBusy(QList<KCalCore::Event::Ptr>(), mStart, mEnd, KCalCore::Person::Ptr());
checkJobDone();
return;
}
diff --git a/fbdaemon/kolabjob.h b/fbdaemon/kolabjob.h
index 30595b1..c456214 100644
--- a/fbdaemon/kolabjob.h
+++ b/fbdaemon/kolabjob.h
@@ -43,7 +43,7 @@ private Q_SLOTS:
protected:
virtual QStringList requiredFolders();
virtual void startWork() = 0;
- void logout();
+ virtual void logout();
SessionSettings mSessionSettings;
QString mFreebusyFolder;
diff --git a/fbdaemon/main.cpp b/fbdaemon/main.cpp
index 0a9f8dd..451ebe3 100644
--- a/fbdaemon/main.cpp
+++ b/fbdaemon/main.cpp
@@ -22,6 +22,7 @@
#include <kdebug.h>
#include <kglobal.h>
#include "fbcoordinator.h"
+#include "fbdaemonserver.h"
#include "settings.h"
#include "kolabutils-version.h"
@@ -36,7 +37,7 @@ int main(int argc, char *argv[])
options.add("c").add("configuration <file>", ki18n("Configuration file"), "/etc/kolab/kolab.conf");
options.add("g").add("generate", ki18n("Generate partial f/b lists for user"));
options.add("a").add("aggregate", ki18n("Aggregate partial f/b lists for user"));
- options.add("d").add("daemon", ki18n("Run daemon (todo)"));
+ options.add("d").add("daemon", ki18n("Run daemon"));
options.add("generateall", ki18n("Generate and aggregate for all users within domain"));
options.add("+[user/filter]", ki18n("User for generation/aggregation | Contains-Filter for userlist for which freebusy is generated (leave empty to generate for all)"));
@@ -78,8 +79,14 @@ int main(int argc, char *argv[])
}
}
+
if (args->isSet("daemon")) {
-
+ FbDaemonServer *server(new FbDaemonServer(&app));
+ if (!server->listen(QHostAddress::Any, args->arg(0).toUInt())) {
+ kWarning() << "Can't start server";
+ return -1;
+ }
+ nothingTodo = false;
}
if (!nothingTodo) {
app.exec();
diff --git a/fbdaemon/settings.cpp b/fbdaemon/settings.cpp
index 5e639a5..dbe442b 100644
--- a/fbdaemon/settings.cpp
+++ b/fbdaemon/settings.cpp
@@ -22,7 +22,8 @@
Settings::Settings()
: mTimeframe(-1),
- mThreshold(-1)
+ mThreshold(-1),
+ mTestMode(false)
{
}
@@ -84,6 +85,10 @@ QString Settings::getServerUri(qint16 &port) const
KIMAP::LoginJob::EncryptionMode Settings::getEncryptionMode() const
{
+ if (getTestMode()) {
+ qDebug() << "Using unencryptd connection in testmode.";
+ return KIMAP::LoginJob::Unencrypted;
+ }
if (usesImplicitSSL()) {
return KIMAP::LoginJob::AnySslVersion;
}
@@ -132,6 +137,16 @@ QString Settings::getPassword() const
return getValue("admin_password", "NoAdminPassword").toString();
}
+void Settings::setTestMode(bool testMode)
+{
+ mTestMode = testMode;
+}
+
+bool Settings::getTestMode() const
+{
+ return mTestMode;
+}
+
void Settings::setTimeframe(int t)
{
mTimeframe = t;
diff --git a/fbdaemon/settings.h b/fbdaemon/settings.h
index fe8352f..a5282b5 100644
--- a/fbdaemon/settings.h
+++ b/fbdaemon/settings.h
@@ -62,6 +62,12 @@ public:
void setAggregatedICalOutputDirectory(const QString &dir);
QString getAggregatedICalOutputDirectory() const;
+
+ /**Testmode overrides the EncryptionMode to Unencrypted
+ * The default value is false
+ */
+ void setTestMode(bool);
+ bool getTestMode() const;
private:
QString getServerUri() const;
@@ -78,6 +84,7 @@ private:
int mTimeframe;
int mThreshold;
QString mAggregatedICalOutputDirectory;
+ bool mTestMode;
};
#endif // SETTINGS_H
diff --git a/fbdaemon/tests/CMakeLists.txt b/fbdaemon/tests/CMakeLists.txt
index e28ccc0..67d32eb 100644
--- a/fbdaemon/tests/CMakeLists.txt
+++ b/fbdaemon/tests/CMakeLists.txt
@@ -1,14 +1,34 @@
+add_definitions (-DSCENARIO_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data")
+
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_BINARY_DIR}
)
-set(FBTEST_DEPENDENCIES ${COMMON_DEPENDENCIES} ${QT_QTTEST_LIBRARY} kolabutils fb_static)
+set(FBTEST_DEPENDENCIES ${COMMON_DEPENDENCIES} ${QT_QTTEST_LIBRARY} ${KDE_KIMAPTEST} kolabutils fb_static)
+
+set (FBTEST_SRCS
+ fbgeneratortest.cpp
+ fbgeneratorfoldertest.cpp
+ fbaggregatortest.cpp
+ cyrusfakeserver.cpp
+ daemonconnectiontest.cpp
+)
+
+QT4_AUTOMOC("${FBTEST_SRCS}")
+
+add_executable(fbaggregatortest fbaggregatortest.cpp cyrusfakeserver.cpp)
+target_link_libraries(fbaggregatortest ${FBTEST_DEPENDENCIES})
+add_test(fbaggregatortest fbaggregatortest)
-qt4_automoc(fbgeneratortest.cpp)
-add_executable(fbgeneratortest fbgeneratortest.cpp)
+add_executable(fbgeneratortest fbgeneratortest.cpp cyrusfakeserver.cpp)
target_link_libraries(fbgeneratortest ${FBTEST_DEPENDENCIES})
+add_test(fbgeneratortest fbgeneratortest)
+
+add_executable(fbgeneratorfoldertest fbgeneratorfoldertest.cpp cyrusfakeserver.cpp)
+target_link_libraries(fbgeneratorfoldertest ${FBTEST_DEPENDENCIES})
+add_test(fbgeneratorfoldertest fbgeneratorfoldertest)
-qt4_automoc(fbaggregatortest.cpp)
-add_executable(fbaggregatortest fbaggregatortest.cpp)
-target_link_libraries(fbaggregatortest ${FBTEST_DEPENDENCIES}) \ No newline at end of file
+add_executable(daemonconnectiontest daemonconnectiontest.cpp cyrusfakeserver.cpp)
+target_link_libraries(daemonconnectiontest ${FBTEST_DEPENDENCIES})
+add_test(daemonconnectiontest daemonconnectiontest)
diff --git a/fbdaemon/tests/README b/fbdaemon/tests/README
new file mode 100644
index 0000000..ecb6154
--- /dev/null
+++ b/fbdaemon/tests/README
@@ -0,0 +1,17 @@
+You can place here cyrus logs, that you can get if you follow the wiki:
+https://wiki.kolab.org/Cyrus_imap_telemetry_logging
+Unfortunatelly this wiki is quite outdated, but with google you'll find the correct path.
+For Debian 7.0 /var/lib/imap/log/<username>/
+
+If you created the directory, you can just do what you whant to do.
+Cyrus will create for every connection a new file.
+You can now use this file inside a test:
+
+ CyrusFakeServer fakeServer;
+ fakeServer.addScenarioFromFile("<newfile>");
+ fakeServer.startAndWait();
+
+ //your test
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
diff --git a/fbdaemon/tests/cyrusfakeserver.cpp b/fbdaemon/tests/cyrusfakeserver.cpp
new file mode 100644
index 0000000..909430b
--- /dev/null
+++ b/fbdaemon/tests/cyrusfakeserver.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cyrusfakeserver.h"
+
+#include <QDebug>
+#include <QRegExp>
+#include <QFile>
+#include <QTest>
+
+CyrusFakeServer::CyrusFakeServer(QObject* parent)
+ : FakeServer(parent)
+{
+
+}
+
+QByteArray tagReplace(QString line, int tagCount)
+{
+ QRegExp tag(" A[0-9]{6} ");
+ QRegExp tag2("\nA[0-9]{6} ");
+
+ line.replace(tag, " A" + QByteArray::number(tagCount).rightJustified(6, '0') + " ");
+ line.replace(tag2, "\nA" + QByteArray::number(tagCount).rightJustified(6, '0') + " ");
+ return line.toUtf8().trimmed();
+
+}
+
+void CyrusFakeServer::addScenarioFromFile(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.exists()) {
+ qWarning() << "File: " << fileName << "does not exist.";
+ }
+ file.open(QFile::ReadOnly);
+
+ QList<QByteArray> scenario;
+
+ // When loading from files we never have the authentication phase
+ // force jumping directly to authenticated state.
+ scenario << greeting()
+ << "C: A000001 AUTHENTICATE PLAIN"
+ << "S: A000001 OK";
+
+ QString line, newLine;
+ int tagCount = 1;
+ QString type;
+
+ QRegExp server("^>[0-9]{10}>");
+ QRegExp client("^<[0-9]{10}<");
+
+ while (!file.atEnd()) {
+ newLine = file.readLine();
+ if (client.indexIn(newLine) > -1) {
+ //qDebug() << line.trimmed();
+ if (!line.trimmed().isEmpty()) {
+ scenario << tagReplace(line, tagCount);
+ }
+ tagCount++;
+ type = "C: ";
+ line = newLine.replace(client, type);
+ } else if (server.indexIn(newLine) > -1) {
+ //qDebug() << line.trimmed();
+ if (!line.trimmed().isEmpty()) {
+ scenario << tagReplace(line, tagCount);
+ }
+ type = "S: ";
+ line = newLine.replace(server, type);
+ } else if (!type.isEmpty()) {
+ if (newLine.startsWith("* ")) {
+ line = line;
+ scenario << tagReplace(line, tagCount);
+ line = type;
+ }
+ line += newLine;
+ }
+
+ }
+ //qDebug() << line.trimmed();
+ if (!line.trimmed().isEmpty()) {
+ scenario << tagReplace(line, tagCount);
+ }
+
+ file.close();
+
+ addScenario(scenario);
+}
+
+void CyrusFakeServer::compareReceived(const QByteArray& received, const QByteArray& expected) const
+{
+ QRegExp append("^C: A[0-9]{6} APPEND \"Freebusy\"");
+ if (append.indexIn(expected) == 0) {
+ QVERIFY(append.indexIn(received) == 0);
+ } else {
+ FakeServer::compareReceived(received, expected);
+ }
+}
+
+
+#include "cyrusfakeserver.moc" \ No newline at end of file
diff --git a/fbdaemon/tests/cyrusfakeserver.h b/fbdaemon/tests/cyrusfakeserver.h
new file mode 100644
index 0000000..b34192c
--- /dev/null
+++ b/fbdaemon/tests/cyrusfakeserver.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CYRUSFAKESERVER_H
+#define CYRUSFAKESERVER_H
+
+#include <kimaptest/fakeserver.h>
+
+class CyrusFakeServer : public FakeServer
+{
+ Q_OBJECT
+public:
+ CyrusFakeServer(QObject* parent = 0);
+ void addScenarioFromFile(const QString &fileName);
+
+protected:
+ virtual void compareReceived(const QByteArray& received, const QByteArray& expected) const;
+
+};
+
+#endif // CYRUSFAKESERVER_H
diff --git a/fbdaemon/tests/daemonconnectiontest.cpp b/fbdaemon/tests/daemonconnectiontest.cpp
new file mode 100644
index 0000000..0f2b3cb
--- /dev/null
+++ b/fbdaemon/tests/daemonconnectiontest.cpp
@@ -0,0 +1,330 @@
+/*
+ Copyright (C) 2013 Christian Mollekopf <mollekopf@kolabsys.com>
+
+ 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.
+
+ 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 "daemonconnectiontest.h"
+#include "fbdaemonthread.h"
+#include "fbgeneratorjob.h"
+#include "fbgeneratorfolderjob.h"
+#include "cyrusfakeserver.h"
+#include "settings.h"
+
+#include <kcalcore/memorycalendar.h>
+#include <kcalcore/icalformat.h>
+
+#include <kimaptest/fakeserver.h>
+#include <xmlobject.h>
+#include <kcalconversion.h>
+#include <kolabformat.h>
+
+#include <QTcpSocket>
+#include <QtTest>
+#include <QDebug>
+
+ConnectionTest::ConnectionTest(QObject* parent): QObject(parent)
+{
+ Settings::instance().setAuthorizationUser("cyrus-admin");
+ Settings::instance().setTestMode(true);
+ Settings::instance().setPassword("admin");
+ Settings::instance().setServerUri("127.0.0.1:5989");
+ Settings::instance().setThreshold(10);
+ Settings::instance().setTimeframe(60);
+ Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
+}
+
+
+FbDaemonConnection* ConnectionTest::createConnection()
+{
+ FbDaemonThread* thread(new FbDaemonThread(0));
+ FbDaemonConnection* connection(new FbDaemonConnection(thread));
+ mSpySendData = new QSignalSpy(connection, SIGNAL(sendData(QByteArray)));
+ return connection;
+}
+
+
+void ConnectionTest::compareSendData(const QString& sendData)
+{
+ QCOMPARE(mSpySendData->count(), 1);
+ QList<QVariant> arguments = mSpySendData->takeFirst();
+ QCOMPARE(arguments.at(0).type(), QVariant::ByteArray);
+ QCOMPARE(arguments.at(0).toString(), sendData);
+}
+
+
+void ConnectionTest::testDoubleCommand()
+{
+ FbDaemonConnection* connection(createConnection());
+
+ SessionSettings settings = Settings::instance().getSessionSettings();
+ FBGeneratorJob *generator = new FBGeneratorJob(settings, false, this);
+ connection->setJob(generator);
+
+ connection->onNewLine("IFB USER test@example.com");
+
+ compareSendData("BAD Can only process one command after another\r\n\r\n");
+}
+
+void ConnectionTest::testBadCommand()
+{
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB UNKNOWN test@example.com");
+ compareSendData("BAD IFB unknown command: UNKNOWN\r\n\r\n");
+ connection->onNewLine("IFB USER test@example.com asdfaesf");
+ compareSendData("BAD IFB unknown parameter\r\n\r\n");
+}
+
+void ConnectionTest::testBadMethod()
+{
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("UNKNOWN USER test@example.com");
+ compareSendData("BAD UNKNOWN method not valid\r\n\r\n");
+}
+
+void ConnectionTest::testBadUser()
+{
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB USER qasdfaserdsfsar");
+ compareSendData("BAD IFB emailaddress not valid\r\n\r\n");
+}
+
+void ConnectionTest::testBadTimeslot()
+{
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB USER test@example.com slot:asdsadf-xxxx");
+ compareSendData("BAD IFB malformed timeslot\r\n\r\n");
+}
+
+void ConnectionTest::testValidateTimeslot()
+{
+ FbDaemonConnection* connection(createConnection());
+ QCOMPARE(connection->validateTimeslot("slot:interval-1402593346"), true);
+ QCOMPARE(connection->validateTimeslot("slot:interval-interval"), true);
+ QCOMPARE(connection->validateTimeslot("slot:1402593346-interval"), true);
+ QCOMPARE(connection->validateTimeslot("slot:xxxx-ffff"), false);
+}
+
+void ConnectionTest::testParseTimeslot()
+{
+ FbDaemonConnection* connection(createConnection());
+ Timeslot slot;
+
+ slot = connection->parseTimeslot("");
+ QVERIFY(slot.start.secsTo(KDateTime::currentUtcDateTime()) < 2); // $now
+ QCOMPARE(slot.start.daysTo(slot.end), Settings::instance().getTimeframe()); // $now+$defaultvalue
+
+ slot = connection->parseTimeslot("slot:interval-interval");
+ QVERIFY(slot.start.secsTo(KDateTime::currentUtcDateTime()) < 2); // $now
+ QCOMPARE(slot.start.daysTo(slot.end), Settings::instance().getTimeframe()); // $now+$defaultvalue
+
+ slot = connection->parseTimeslot("slot:1402593346-interval");
+ QCOMPARE(slot.start.dateTime(), QDateTime::fromTime_t(1402593346));
+ QCOMPARE(slot.start.daysTo(slot.end), Settings::instance().getTimeframe()); // $now+$defaultvalue
+
+ slot = connection->parseTimeslot("slot:interval-1402593346");
+ QCOMPARE(slot.end.dateTime(), QDateTime::fromTime_t(1402593346));
+ QCOMPARE(slot.start.daysTo(slot.end), Settings::instance().getTimeframe()); // 1402593346 - $defaultvalue
+
+ slot = connection->parseTimeslot("slot:1402500000-1402593346");
+ QCOMPARE(slot.start.dateTime(), QDateTime::fromTime_t(1402500000));
+ QCOMPARE(slot.end.dateTime(), QDateTime::fromTime_t(1402593346));
+}
+
+void ConnectionTest::testGeneratorUserJob()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generator/imap-6398.withoutattach");
+ fakeServer.startAndWait();
+
+ QEventLoop loop(this);
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB USER john.doe@example.com slot:1400604981-1405788981");
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ QRegExp ifb("\\* \\(\\{([0-9]+)\\} \r\n(.*)\\)");
+
+ QCOMPARE(mSpySendData->count(), 1);
+ QList<QVariant> arguments = mSpySendData->takeFirst();
+ QCOMPARE(arguments.at(0).type(), QVariant::ByteArray);
+ QCOMPARE(ifb.indexIn(arguments.at(0).toString()), 0);
+ QCOMPARE(ifb.cap(1).toInt(), ifb.cap(2).size()); // Object size == expected size
+ const QString sFb = ifb.cap(2);
+
+ KCalCore::ICalFormat format;
+ KCalCore::Calendar::Ptr cal(new KCalCore::MemoryCalendar(KDateTime::Spec::UTC()));
+ KCalCore::ScheduleMessage::Ptr msg = format.parseScheduleMessage(cal, sFb);
+ QVERIFY(msg);
+ QCOMPARE(msg->method(), KCalCore::iTIPPublish);
+ QCOMPARE(format.timeSpec(), KDateTime::Spec::UTC());
+ QVERIFY(msg->event());
+
+ KCalCore::FreeBusy::Ptr event = msg->event().staticCast<KCalCore::FreeBusy>();
+ QCOMPARE(event->organizer()->email(), QLatin1String("john.doe@example.com"));
+
+ QCOMPARE(event->dtStart(), start);
+ QCOMPARE(event->dtEnd(), end);
+
+ QVERIFY(!event->uid().isEmpty());
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void ConnectionTest::testGeneratorFolderJob()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/normal.log");
+ fakeServer.startAndWait();
+
+ QEventLoop loop(this);
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB FOLDER shared/Resources/mybeamer@example.com slot:1400604981-1405788981");
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ QRegExp ifb("\\* \\(\\{([0-9]+)\\} \r\n(.*)\\)");
+
+ QCOMPARE(mSpySendData->count(), 1);
+ QList<QVariant> arguments = mSpySendData->takeFirst();
+ QCOMPARE(arguments.at(0).type(), QVariant::ByteArray);
+ QCOMPARE(ifb.indexIn(arguments.at(0).toString()), 0);
+ QCOMPARE(ifb.cap(1).toInt(), ifb.cap(2).size()); // Object size == expected size
+
+ const QString sFb = ifb.cap(2);
+
+ KCalCore::ICalFormat format;
+ KCalCore::Calendar::Ptr cal(new KCalCore::MemoryCalendar(KDateTime::Spec::UTC()));
+ KCalCore::ScheduleMessage::Ptr msg = format.parseScheduleMessage(cal, sFb);
+ QVERIFY(msg);
+ QCOMPARE(msg->method(), KCalCore::iTIPPublish);
+ QCOMPARE(format.timeSpec(), KDateTime::Spec::UTC());
+ QVERIFY(msg->event());
+ KCalCore::FreeBusy::Ptr event = msg->event().staticCast<KCalCore::FreeBusy>();
+ QCOMPARE(event->organizer()->email(), QLatin1String("fbdaemon@localhost"));
+
+ QCOMPARE(event->dtStart(), start);
+ QCOMPARE(event->dtEnd(), end);
+
+ QVERIFY(!event->uid().isEmpty());
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void ConnectionTest::testFailingFirst()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/notfound.log");
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/empty.log");
+ fakeServer.startAndWait();
+
+ QEventLoop loop(this);
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+ FbDaemonConnection* connection(createConnection());
+ connection->onNewLine("IFB FOLDER shared/Resources/bla@example.com");
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ compareSendData("BAD IFB MyRights failed, server replied: A000005 NO Mailbox does not exist \r\n\r\n");
+
+ connection->onNewLine("IFB FOLDER shared/Resources/mybeamer@example.com slot:1400604981-1405788981");
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ QRegExp ifb("\\* \\(\\{([0-9]+)\\} \r\n(.*)\\)");
+ QCOMPARE(mSpySendData->count(), 1);
+ QList<QVariant> arguments = mSpySendData->takeFirst();
+ QCOMPARE(arguments.at(0).type(), QVariant::ByteArray);
+ QCOMPARE(ifb.indexIn(arguments.at(0).toString()), 0);
+ QCOMPARE(ifb.cap(1).toInt(), ifb.cap(2).size()); // Object size == expected size
+
+ const QString sFb = ifb.cap(2);
+
+ KCalCore::ICalFormat format;
+ KCalCore::Calendar::Ptr cal(new KCalCore::MemoryCalendar(KDateTime::Spec::UTC()));
+ KCalCore::ScheduleMessage::Ptr msg = format.parseScheduleMessage(cal, sFb);
+ QVERIFY(msg);
+ QCOMPARE(msg->method(), KCalCore::iTIPPublish);
+ QCOMPARE(format.timeSpec(), KDateTime::Spec::UTC());
+ QVERIFY(msg->event());
+ KCalCore::FreeBusy::Ptr event = msg->event().staticCast<KCalCore::FreeBusy>();
+ QCOMPARE(event->organizer()->email(), QLatin1String("fbdaemon@localhost"));
+
+ QCOMPARE(event->dtStart(), start);
+ QCOMPARE(event->dtEnd(), end);
+
+ QVERIFY(!event->uid().isEmpty());
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void ConnectionTest::testParser()
+{
+ QEventLoop loop(this);
+ FbDaemonConnection* connection(createConnection());
+
+ connection->onNewLine("IFB FOLDER shared/Resources/bla@example.com");
+ FBGeneratorFolderJob *fbJob = qobject_cast<FBGeneratorFolderJob*>(connection->job);
+ QCOMPARE(fbJob->getFolder(), QString("shared/Resources/bla@example.com"));
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ connection->onNewLine("IFB FOLDER \"shared/Resources/bla@example.com\"");
+ fbJob = qobject_cast<FBGeneratorFolderJob*>(connection->job);
+ QCOMPARE(fbJob->getFolder(), QString("shared/Resources/bla@example.com"));
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ connection->onNewLine("IFB FOLDER \"shared/My Resources/bla@example.com\"");
+ fbJob = qobject_cast<FBGeneratorFolderJob*>(connection->job);
+ QCOMPARE(fbJob->getFolder(), QString("shared/My Resources/bla@example.com"));
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+
+ connection->onNewLine("IFB FOLDER \">test\\\" blabla \\\"<\"");
+ fbJob = qobject_cast<FBGeneratorFolderJob*>(connection->job);
+ QCOMPARE(fbJob->getFolder(), QString(">test\" blabla \"<"));
+ connect(connection->job, SIGNAL(result(KJob*)),
+ &loop, SLOT(quit()));
+ loop.exec();
+}
+
+QTEST_MAIN(ConnectionTest)
+
+#include "daemonconnectiontest.moc"
diff --git a/fbdaemon/tests/daemonconnectiontest.h b/fbdaemon/tests/daemonconnectiontest.h
new file mode 100644
index 0000000..ef6f23b
--- /dev/null
+++ b/fbdaemon/tests/daemonconnectiontest.h
@@ -0,0 +1,43 @@
+#ifndef DAEMONTEST_H
+#define DAEMONTEST_H
+
+#include "fbdaemonconnection.h"
+
+#include <QObject>
+#include <QSignalSpy>
+#include "testlib/testutils.h"
+
+class ConnectionTest: public QObject
+{
+ Q_OBJECT
+public:
+ explicit ConnectionTest(QObject* parent = 0);
+
+private Q_SLOTS:
+ void testDoubleCommand();
+ void testBadCommand();
+ void testBadUser();
+ void testBadMethod();
+ void testBadTimeslot();
+ void testValidateTimeslot();
+ void testParseTimeslot();
+ void testGeneratorUserJob();
+ void testGeneratorFolderJob();
+ /*
+ * two commands: first fails, sencond is okay
+ */
+
+ void testFailingFirst();
+
+ /*
+ * parser can handle spaces in folder names
+ * and escaped \" -> "
+ */
+ void testParser();
+private:
+ FbDaemonConnection* createConnection();
+ void compareSendData(const QString &);
+ QSignalSpy* mSpySendData;
+};
+
+#endif
diff --git a/fbdaemon/tests/data/aggregator/imap-28470 b/fbdaemon/tests/data/aggregator/imap-28470
new file mode 100644
index 0000000..34f0c76
--- /dev/null
+++ b/fbdaemon/tests/data/aggregator/imap-28470
@@ -0,0 +1,360 @@
+---------- john.@example.com Thu May 22 11:29:51 2014
+
+<1400750991<A000004 CAPABILITY
+>1400750991>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400750991<A000005 NAMESPACE
+>1400750991>* NAMESPACE (("" "/")) (("Other Users/" "/")) (("Shared Folders/" "/"))
+A000005 OK Completed
+<1400750991<A000006 LIST "" "*"
+>1400750991>* LIST (\Noinferiors \HasNoChildren) "/" INBOX
+* LIST (\HasChildren) "/" Calendar
+* LIST (\HasNoChildren) "/" "Calendar/Personal Calendar"
+* LIST (\HasNoChildren) "/" Freebusy
+A000006 OK Completed (0.010 secs 4 calls)
+<1400750991<A000007 GETMETADATA "INBOX" (/shared/vendor/kolab/folder-type)
+>1400750991>* METADATA INBOX (/shared/vendor/kolab/folder-type NIL)
+A000007 OK Completed
+<1400750991<A000009 GETMETADATA "Calendar" (/shared/vendor/kolab/folder-type)
+>1400750991>* METADATA Calendar (/shared/vendor/kolab/folder-type "event")
+A000009 OK Completed
+<1400750991<A000010 GETMETADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type)
+>1400750991>* METADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type "event")
+A000010 OK Completed
+<1400750991<A000016 GETMETADATA "Freebusy" (/shared/vendor/kolab/folder-type)
+>1400750991>* METADATA Freebusy (/shared/vendor/kolab/folder-type "freebusy")
+A000016 OK Completed
+<1400750991<A000023 EXAMINE "Freebusy"
+>1400750991>* 3 EXISTS
+* 3 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UNSEEN 1] Ok
+* OK [UIDVALIDITY 1399892394] Ok
+* OK [UIDNEXT 5] Ok
+* OK [HIGHESTMODSEQ 5] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000023 OK [READ-ONLY] Completed
+<1400750991<A000024 FETCH 1:3 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)
+>1400750991>* 1 FETCH (FLAGS () UID 1 INTERNALDATE "12-May-2014 13:02:37 +0200" RFC822.SIZE 2427 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {88}
+Date: Mon, 12 May 2014 11:02:39 +0000
+Subject: 54f648b6-1607-4839-b8da-3a918e895234
+
+)
+* 2 FETCH (FLAGS () UID 2 INTERNALDATE "12-May-2014 13:03:00 +0200" RFC822.SIZE 2897 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {88}
+Date: Mon, 12 May 2014 11:03:02 +0000
+Subject: f5213a7a-274f-489f-9ddc-e626084723e0
+
+)
+* 3 FETCH (FLAGS () UID 3 INTERNALDATE "12-May-2014 13:38:50 +0200" RFC822.SIZE 3359 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {88}
+Date: Mon, 12 May 2014 11:39:07 +0000
+Subject: 0b820eb3-cfb5-4ec9-ac31-4aa6174ad535
+
+)
+A000024 OK Completed (0.010 sec)
+<1400750991<A000025 UID FETCH 1,2,3 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)
+>1400750991>* 1 FETCH (FLAGS () UID 1 INTERNALDATE "12-May-2014 13:02:37 +0200" RFC822.SIZE 2427 BODY[] {2430}
+Date: Mon, 12 May 2014 11:02:39 +0000
+X-Kolab-Type: application/x-vnd.kolab.freebusy
+X-Kolab-Mime-Version: 3.0
+User-Agent: Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart2153659.LG6uWRnTG9"
+Subject: 54f648b6-1607-4839-b8da-3a918e895234
+MIME-Version: 1.0
+X-Freebusy-Origin: kolab.example.com
+
+
+--nextPart2153659.LG6uWRnTG9
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart2153659.LG6uWRnTG9
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vfreebusy>
+ <properties>
+ <uid>
+ <text>54f648b6-1607-4839-b8da-3a918e895234</text>
+ </uid>
+ <dtstamp>
+ <date-time>2014-05-12T11:02:39Z</date-time>
+ </dtstamp>
+ <dtstart>
+ <date-time>2014-05-12T11:02:39Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-07-11T11:02:39Z</date-time>
+ </dtend>
+ <organizer>
+ <parameters/>
+ <cal-address>mailto:%3Cjohn.doe%40example.com%3E</cal-address>
+ </organizer>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:02:39Z</start>
+ <end>2014-05-12T12:02:38Z</end>
+ </period>
+ </freebusy>
+ </properties>
+ </vfreebusy>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart2153659.LG6uWRnTG9--
+)
+* 2 FETCH (FLAGS () UID 2 INTERNALDATE "12-May-2014 13:03:00 +0200" RFC822.SIZE 2897 BODY[] {2900}
+Date: Mon, 12 May 2014 11:03:02 +0000
+X-Kolab-Type: application/x-vnd.kolab.freebusy
+X-Kolab-Mime-Version: 3.0
+User-Agent: Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart20900622.dmZn1H714V"
+Subject: f5213a7a-274f-489f-9ddc-e626084723e0
+MIME-Version: 1.0
+X-Freebusy-Origin: kolab.example.com
+
+
+--nextPart20900622.dmZn1H714V
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart20900622.dmZn1H714V
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vfreebusy>
+ <properties>
+ <uid>
+ <text>f5213a7a-274f-489f-9ddc-e626084723e0</text>
+ </uid>
+ <dtstamp>
+ <date-time>2014-05-12T11:03:02Z</date-time>
+ </dtstamp>
+ <dtstart>
+ <date-time>2014-05-12T11:03:02Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-07-11T11:03:02Z</date-time>
+ </dtend>
+ <organizer>
+ <parameters/>
+ <cal-address>mailto:%3Cjohn.doe%40example.com%3E</cal-address>
+ </organizer>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:03:02Z</start>
+ <end>2014-05-12T12:02:38Z</end>
+ </period>
+ </freebusy>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:03:02Z</start>
+ <end>2014-05-12T12:03:01Z</end>
+ </period>
+ </freebusy>
+ </properties>
+ </vfreebusy>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart20900622.dmZn1H714V--
+)
+* 3 FETCH (FLAGS () UID 3 INTERNALDATE "12-May-2014 13:38:50 +0200" RFC822.SIZE 3359 BODY[] {3362}
+Date: Mon, 12 May 2014 11:39:07 +0000
+X-Kolab-Type: application/x-vnd.kolab.freebusy
+X-Kolab-Mime-Version: 3.0
+User-Agent: Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart1630486.Ayxoc6WAWH"
+Subject: 0b820eb3-cfb5-4ec9-ac31-4aa6174ad535
+MIME-Version: 1.0
+X-Freebusy-Origin: kolab.example.com
+
+
+--nextPart1630486.Ayxoc6WAWH
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart1630486.Ayxoc6WAWH
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vfreebusy>
+ <properties>
+ <uid>
+ <text>0b820eb3-cfb5-4ec9-ac31-4aa6174ad535</text>
+ </uid>
+ <dtstamp>
+ <date-time>2014-05-12T11:39:07Z</date-time>
+ </dtstamp>
+ <dtstart>
+ <date-time>2014-05-12T11:39:07Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-07-11T11:39:07Z</date-time>
+ </dtend>
+ <organizer>
+ <parameters/>
+ <cal-address>mailto:%3Cjohn.doe%40example.com%3E</cal-address>
+ </organizer>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:39:07Z</start>
+ <end>2014-05-12T12:02:38Z</end>
+ </period>
+ </freebusy>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:39:07Z</start>
+ <end>2014-05-12T12:03:01Z</end>
+ </period>
+ </freebusy>
+ <freebusy>
+ <parameters>
+ <fbtype>
+ <text>BUSY</text>
+ </fbtype>
+ <x-event>
+ <uid>uid1</uid>
+ <summary/>
+ <location/>
+ </x-event>
+ </parameters>
+ <period>
+ <start>2014-05-12T11:39:07Z</start>
+ <end>2014-05-12T12:39:06Z</end>
+ </period>
+ </freebusy>
+ </properties>
+ </vfreebusy>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart1630486.Ayxoc6WAWH--
+)
+A000025 OK Completed (0.040 sec)
+<1400750991<A000026 LOGOUT
+>1400750991>* BYE LOGOUT received
+A000026 OK Completed
diff --git a/fbdaemon/tests/data/generatefolder/empty.log b/fbdaemon/tests/data/generatefolder/empty.log
new file mode 100644
index 0000000..1e798b9
--- /dev/null
+++ b/fbdaemon/tests/data/generatefolder/empty.log
@@ -0,0 +1,34 @@
+---------- cyrus-admin Thu May 22 11:10:25 2014
+
+<1400749825<A000004 CAPABILITY
+>1400749825>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400749825<A000005 NAMESPACE
+>1400749825>* NAMESPACE NIL (("user/" "/")) (("" "/"))
+A000005 OK Completed
+<1400749825<A000006 LIST "" *
+>1400749825>* LIST (\HasNoChildren) "/" Freebusy
+* LIST (\HasNoChildren) "/" shared/Resources/berlin@example.com
+* LIST (\HasNoChildren) "/" "shared/Resources/mega 1000@example.com"
+* LIST (\HasNoChildren) "/" shared/Resources/mybeamer@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb@example.com
+* LIST (\HasNoChildren) "/" user/bmqvi.nbczhbxkbb/Archive@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb/Calendar@example.com
+A000006 OK Completed (0.030 secs 7 calls)
+<1400749825<A000007 MYRIGHTS "shared/Resources/mybeamer@example.com"
+>1400749825>* MYRIGHTS shared/Resources/mybeamer@example.com lrsa
+A000007 OK Completed
+<1400749825<A000008 EXAMINE "shared/Resources/mybeamer@example.com"
+>1400749825>* 0 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UIDVALIDITY 1400675720] Ok
+* OK [UIDNEXT 1] Ok
+* OK [HIGHESTMODSEQ 1] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000008 OK [READ-ONLY] Completed
+<1400749825<A000009 LOGOUT
+>1400749825>* BYE LOGOUT received
+A000009 OK Completed
diff --git a/fbdaemon/tests/data/generatefolder/normal.log b/fbdaemon/tests/data/generatefolder/normal.log
new file mode 100644
index 0000000..6c4f189
--- /dev/null
+++ b/fbdaemon/tests/data/generatefolder/normal.log
@@ -0,0 +1,115 @@
+---------- cyrus-admin Thu May 22 11:10:25 2014
+
+<1400749825<A000004 CAPABILITY
+>1400749825>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400749825<A000005 NAMESPACE
+>1400749825>* NAMESPACE NIL (("user/" "/")) (("" "/"))
+A000005 OK Completed
+<1400749825<A000006 LIST "" *
+>1400749825>* LIST (\HasNoChildren) "/" Freebusy
+* LIST (\HasNoChildren) "/" shared/Resources/berlin@example.com
+* LIST (\HasNoChildren) "/" "shared/Resources/mega 1000@example.com"
+* LIST (\HasNoChildren) "/" shared/Resources/mybeamer@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb@example.com
+* LIST (\HasNoChildren) "/" user/bmqvi.nbczhbxkbb/Archive@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb/Calendar@example.com
+A000006 OK Completed (0.030 secs 7 calls)
+<1400749825<A000007 MYRIGHTS "shared/Resources/mybeamer@example.com"
+>1400749825>* MYRIGHTS shared/Resources/mybeamer@example.com lrsa
+A000007 OK Completed
+<1400749825<A000008 EXAMINE "shared/Resources/mybeamer@example.com"
+>1400749825>* 1 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UIDVALIDITY 1400675720] Ok
+* OK [UIDNEXT 2] Ok
+* OK [HIGHESTMODSEQ 2] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000008 OK [READ-ONLY] Completed
+<1400091057<A000009 FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {56}
+Date: Mon, 12 May 2014 11:02:38 +0000
+Subject: uid1
+
+)
+A000009 OK Completed (0.000 sec)
+<1400091057<A000010 UID FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[] {1968}
+Date: Mon, 12 May 2014 11:02:38 +0000
+X-Kolab-Type: application/x-vnd.kolab.event
+X-Kolab-Mime-Version: 3.0
+User-Agent: fbtest Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart1645803.C9AV0NgWoz"
+Subject: uid1
+MIME-Version: 1.0
+
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>fbtest Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vevent>
+ <properties>
+ <uid>
+ <text>uid1</text>
+ </uid>
+ <created>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </created>
+ <dtstamp>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </dtstamp>
+ <sequence>
+ <integer>0</integer>
+ </sequence>
+ <class>
+ <text>PUBLIC</text>
+ </class>
+ <dtstart>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-06-12T12:02:38Z</date-time>
+ </dtend>
+ </properties>
+ </vevent>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart1645803.C9AV0NgWoz--
+)
+A000010 OK Completed (0.020 sec)
+<1400749825<A000011 LOGOUT
+>1400749825>* BYE LOGOUT received
+A000011 OK Completed
diff --git a/fbdaemon/tests/data/generatefolder/notfound.log b/fbdaemon/tests/data/generatefolder/notfound.log
new file mode 100644
index 0000000..0585381
--- /dev/null
+++ b/fbdaemon/tests/data/generatefolder/notfound.log
@@ -0,0 +1,22 @@
+---------- cyrus-admin Thu May 22 11:18:49 2014
+
+<1400750329<A000004 CAPABILITY
+>1400750329>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400750329<A000005 NAMESPACE
+>1400750329>* NAMESPACE NIL (("user/" "/")) (("" "/"))
+A000005 OK Completed
+<1400750329<A000006 LIST "" *
+>1400750329>* LIST (\HasNoChildren) "/" Freebusy
+* LIST (\HasNoChildren) "/" shared/Resources/berlin@example.com
+* LIST (\HasNoChildren) "/" "shared/Resources/mega 1000@example.com"
+* LIST (\HasNoChildren) "/" shared/Resources/mybeamer@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb@example.com
+* LIST (\HasNoChildren) "/" user/bmqvi.nbczhbxkbb/Archive@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb/Calendar@example.com
+A000006 OK Completed (0.030 secs 7 calls)
+<1400750329<A000007 MYRIGHTS "shared/Resources/bla@example.com"
+>1400750329>A000007 NO Mailbox does not exist
+<1400750329<A000008 LOGOUT
+>1400750329>* BYE LOGOUT received
+A000008 OK Completed
diff --git a/fbdaemon/tests/data/generatefolder/setrights.log b/fbdaemon/tests/data/generatefolder/setrights.log
new file mode 100644
index 0000000..b22bba0
--- /dev/null
+++ b/fbdaemon/tests/data/generatefolder/setrights.log
@@ -0,0 +1,119 @@
+---------- cyrus-admin Thu May 22 11:10:25 2014
+
+<1400749825<A000004 CAPABILITY
+>1400749825>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400749825<A000005 NAMESPACE
+>1400749825>* NAMESPACE NIL (("user/" "/")) (("" "/"))
+A000005 OK Completed
+<1400749825<A000006 LIST "" *
+>1400749825>* LIST (\HasNoChildren) "/" Freebusy
+* LIST (\HasNoChildren) "/" shared/Resources/berlin@example.com
+* LIST (\HasNoChildren) "/" "shared/Resources/mega 1000@example.com"
+* LIST (\HasNoChildren) "/" shared/Resources/mybeamer@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb@example.com
+* LIST (\HasNoChildren) "/" user/bmqvi.nbczhbxkbb/Archive@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb/Calendar@example.com
+A000006 OK Completed (0.030 secs 7 calls)
+<1400749825<A000007 MYRIGHTS "shared/Resources/mybeamer@example.com"
+>1400749825>* MYRIGHTS shared/Resources/mybeamer@example.com a
+A000007 OK Completed
+<1400749825<A000008 SETACL "shared/Resources/mybeamer@example.com" "" "+lrs"
+>1400749825>A000008 OK Completed
+<1400749825<A000009 EXAMINE "shared/Resources/mybeamer@example.com"
+>1400749825>* 1 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UIDVALIDITY 1400675720] Ok
+* OK [UIDNEXT 2] Ok
+* OK [HIGHESTMODSEQ 2] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000009 OK [READ-ONLY] Completed
+<1400091057<A000010 FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {56}
+Date: Mon, 12 May 2014 11:02:38 +0000
+Subject: uid1
+
+)
+A000010 OK Completed (0.000 sec)
+<1400091057<A000011 UID FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[] {1968}
+Date: Mon, 12 May 2014 11:02:38 +0000
+X-Kolab-Type: application/x-vnd.kolab.event
+X-Kolab-Mime-Version: 3.0
+User-Agent: fbtest Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart1645803.C9AV0NgWoz"
+Subject: uid1
+MIME-Version: 1.0
+
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>fbtest Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vevent>
+ <properties>
+ <uid>
+ <text>uid1</text>
+ </uid>
+ <created>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </created>
+ <dtstamp>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </dtstamp>
+ <sequence>
+ <integer>0</integer>
+ </sequence>
+ <class>
+ <text>PUBLIC</text>
+ </class>
+ <dtstart>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-06-12T12:02:38Z</date-time>
+ </dtend>
+ </properties>
+ </vevent>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart1645803.C9AV0NgWoz--
+)
+A000011 OK Completed (0.020 sec)
+<1400749825<A000012 SETACL "shared/Resources/mybeamer@example.com" "" "+a"
+>1400749825>A000012 OK Completed
+<1400749825<A000013 LOGOUT
+>1400749825>* BYE LOGOUT received
+A000013 OK Completed
diff --git a/fbdaemon/tests/data/generatefolder/systemioerror.log b/fbdaemon/tests/data/generatefolder/systemioerror.log
new file mode 100644
index 0000000..c298270
--- /dev/null
+++ b/fbdaemon/tests/data/generatefolder/systemioerror.log
@@ -0,0 +1,25 @@
+---------- cyrus-admin Thu May 22 11:11:56 2014
+
+<1400749916<A000004 CAPABILITY
+>1400749916>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400749916<A000005 NAMESPACE
+>1400749916>* NAMESPACE NIL (("user/" "/")) (("" "/"))
+A000005 OK Completed
+<1400749916<A000006 LIST "" *
+>1400749916>* LIST (\HasNoChildren) "/" Freebusy
+* LIST (\HasNoChildren) "/" shared/Resources/berlin@example.com
+* LIST (\HasNoChildren) "/" "shared/Resources/mega 1000@example.com"
+* LIST (\HasNoChildren) "/" shared/Resources/mybeamer@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb@example.com
+* LIST (\HasNoChildren) "/" user/bmqvi.nbczhbxkbb/Archive@example.com
+* LIST (\HasChildren) "/" user/bmqvi.nbczhbxkbb/Calendar@example.com
+A000006 OK Completed (0.040 secs 7 calls)
+<1400749916<A000007 MYRIGHTS "shared/Resources/mega 1000@example.com"
+>1400749916>* MYRIGHTS "shared/Resources/mega 1000@example.com" lrsa
+A000007 OK Completed
+<1400749916<A000008 EXAMINE "shared/Resources/mega 1000@example.com"
+>1400749916>A000008 NO System I/O error
+<1400749916<A000009 LOGOUT
+>1400749916>* BYE LOGOUT received
+A000009 OK Completed
diff --git a/fbdaemon/tests/data/generator/imap-6398 b/fbdaemon/tests/data/generator/imap-6398
new file mode 100644
index 0000000..591e3e3
--- /dev/null
+++ b/fbdaemon/tests/data/generator/imap-6398
@@ -0,0 +1,229 @@
+<1400091056<A000004 CAPABILITY
+>1400091056>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400091056<A000005 NAMESPACE
+>1400091056>* NAMESPACE (("" "/")) (("Other Users/" "/")) (("Shared Folders/" "/"))
+A000005 OK Completed
+<1400091056<A000006 LIST "" "*"
+>1400091056>* LIST (\Noinferiors \HasNoChildren) "/" INBOX
+* LIST (\HasChildren) "/" Calendar
+* LIST (\HasNoChildren) "/" "Calendar/Personal Calendar"
+* LIST (\HasNoChildren) "/" Freebusy
+A000006 OK Completed (0.010 secs 19 calls)
+<1400091056<A000007 GETMETADATA "INBOX" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA INBOX (/shared/vendor/kolab/folder-type NIL)
+A000007 OK Completed
+<1400091056<A000009 GETMETADATA "Calendar" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA Calendar (/shared/vendor/kolab/folder-type "event")
+A000009 OK Completed
+<1400091056<A000010 GETMETADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type "event")
+A000010 OK Completed
+<1400091056<A000016 GETMETADATA "Freebusy" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA Freebusy (/shared/vendor/kolab/folder-type "freebusy")
+A000016 OK Completed
+<1400091056<A000023 EXAMINE "Freebusy"
+>1400091056>* 0 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UNSEEN 1] Ok
+* OK [UIDVALIDITY 1399892394] Ok
+* OK [UIDNEXT 0] Ok
+* OK [HIGHESTMODSEQ 0] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000023 OK [READ-ONLY] Completed
+<1400091057<A000026 EXAMINE "Calendar/Personal Calendar"
+>1400091057>* OK [CLOSED] Ok
+* 0 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UIDVALIDITY 1399478510] Ok
+* OK [UIDNEXT 1] Ok
+* OK [HIGHESTMODSEQ 1] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000026 OK [READ-ONLY] Completed
+<1400091057<A000027 EXAMINE "Calendar"
+>1400091057>* OK [CLOSED] Ok
+* 1 EXISTS
+* 1 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UNSEEN 1] Ok
+* OK [UIDVALIDITY 1399478510] Ok
+* OK [UIDNEXT 1] Ok
+* OK [HIGHESTMODSEQ 1] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000027 OK [READ-ONLY] Completed
+<1400091057<A000028 FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {56}
+Date: Mon, 12 May 2014 11:02:38 +0000
+Subject: uid1
+
+)
+A000028 OK Completed (0.000 sec)
+<1400091057<A000029 UID FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[] {1968}
+Date: Mon, 12 May 2014 11:02:38 +0000
+X-Kolab-Type: application/x-vnd.kolab.event
+X-Kolab-Mime-Version: 3.0
+User-Agent: fbtest Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart1645803.C9AV0NgWoz"
+Subject: uid1
+MIME-Version: 1.0
+
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>fbtest Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vevent>
+ <properties>
+ <uid>
+ <text>uid1</text>
+ </uid>
+ <created>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </created>
+ <dtstamp>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </dtstamp>
+ <sequence>
+ <integer>0</integer>
+ </sequence>
+ <class>
+ <text>PUBLIC</text>
+ </class>
+ <dtstart>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-06-12T12:02:38Z</date-time>
+ </dtend>
+ </properties>
+ </vevent>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart1645803.C9AV0NgWoz--
+)
+A000029 OK Completed (0.020 sec)
+<1400091057<A000030 APPEND "Freebusy" {1932}
+Date: Wed, 14 May 2014 18:11:02 +0000
+X-Kolab-Type: application/x-vnd.kolab.freebusy
+X-Kolab-Mime-Version: 3.0
+User-Agent: Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart10956538.CFazvYchSy"
+Subject: 9b23003c-c31a-48e9-b6e9-f63564858663
+MIME-Version: 1.0
+X-Freebusy-Origin: kolab.example.com
+
+
+--nextPart10956538.CFazvYchSy
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart10956538.CFazvYchSy
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vfreebusy>
+ <properties>
+ <uid>
+ <text>9b23003c-c31a-48e9-b6e9-f63564858663</text>
+ </uid>
+ <dtstamp>
+ <date-time>2014-05-14T18:11:02Z</date-time>
+ </dtstamp>
+ <dtstart>
+ <date-time>2014-05-14T18:11:01Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-07-13T18:11:01Z</date-time>
+ </dtend>
+ <organizer>
+ <parameters/>
+ <cal-address>mailto:%3Cx.x%40example.com%3E</cal-address>
+ </organizer>
+ </properties>
+ </vfreebusy>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart10956538.CFazvYchSy--
+)
+>1400091057>A000030 OK [APPENDUID 1399892394 3] Completed
+<1400091057<A000031 SELECT "Freebusy"
+>1400091057>* OK [CLOSED] Ok
+* 3 EXISTS
+* 1 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Ok
+* OK [UNSEEN 1] Ok
+* OK [UIDVALIDITY 1399892394] Ok
+* OK [UIDNEXT 3] Ok
+* OK [HIGHESTMODSEQ 3] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000031 OK [READ-WRITE] Completed
+<1400091057<A000032 EXPUNGE
+>1400091057>A000032 OK Completed
+<1400091057<A000033 LOGOUT
+>1400091057>* BYE LOGOUT received
+A000033 OK Completed
diff --git a/fbdaemon/tests/data/generator/imap-6398.withoutattach b/fbdaemon/tests/data/generator/imap-6398.withoutattach
new file mode 100644
index 0000000..593f431
--- /dev/null
+++ b/fbdaemon/tests/data/generator/imap-6398.withoutattach
@@ -0,0 +1,133 @@
+<1400091056<A000004 CAPABILITY
+>1400091056>* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE ACL RIGHTS=kxten QUOTA MAILBOX-REFERRALS NAMESPACE UIDPLUS NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY CATENATE CONDSTORE ESEARCH SEARCH=FUZZY SORT SORT=MODSEQ SORT=DISPLAY SORT=UID THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE ANNOTATE-EXPERIMENT-1 METADATA LIST-EXTENDED LIST-STATUS LIST-MYRIGHTS WITHIN QRESYNC SCAN XLIST XMOVE MOVE SPECIAL-USE CREATE-SPECIAL-USE DIGEST=SHA1 URLAUTH URLAUTH=BINARY LOGINDISABLED COMPRESS=DEFLATE X-QUOTA=STORAGE X-QUOTA=MESSAGE X-QUOTA=X-ANNOTATION-STORAGE X-QUOTA=X-NUM-FOLDERS IDLE
+A000004 OK Completed
+<1400091056<A000005 NAMESPACE
+>1400091056>* NAMESPACE (("" "/")) (("Other Users/" "/")) (("Shared Folders/" "/"))
+A000005 OK Completed
+<1400091056<A000006 LIST "" "*"
+>1400091056>* LIST (\Noinferiors \HasNoChildren) "/" INBOX
+* LIST (\HasChildren) "/" Calendar
+* LIST (\HasNoChildren) "/" "Calendar/Personal Calendar"
+* LIST (\HasNoChildren) "/" Freebusy
+A000006 OK Completed (0.010 secs 19 calls)
+<1400091056<A000007 GETMETADATA "INBOX" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA INBOX (/shared/vendor/kolab/folder-type NIL)
+A000007 OK Completed
+<1400091056<A000009 GETMETADATA "Calendar" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA Calendar (/shared/vendor/kolab/folder-type "event")
+A000009 OK Completed
+<1400091056<A000010 GETMETADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA "Calendar/Personal Calendar" (/shared/vendor/kolab/folder-type "event")
+A000010 OK Completed
+<1400091056<A000016 GETMETADATA "Freebusy" (/shared/vendor/kolab/folder-type)
+>1400091056>* METADATA Freebusy (/shared/vendor/kolab/folder-type "freebusy")
+A000016 OK Completed
+<1400091057<A000026 EXAMINE "Calendar/Personal Calendar"
+>1400091057>* OK [CLOSED] Ok
+* 0 EXISTS
+* 0 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UIDVALIDITY 1399478510] Ok
+* OK [UIDNEXT 1] Ok
+* OK [HIGHESTMODSEQ 1] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000026 OK [READ-ONLY] Completed
+<1400091057<A000027 EXAMINE "Calendar"
+>1400091057>* OK [CLOSED] Ok
+* 1 EXISTS
+* 1 RECENT
+* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+* OK [PERMANENTFLAGS ()] Ok
+* OK [UNSEEN 1] Ok
+* OK [UIDVALIDITY 1399478510] Ok
+* OK [UIDNEXT 1] Ok
+* OK [HIGHESTMODSEQ 1] Ok
+* OK [URLMECH INTERNAL] Ok
+* OK [ANNOTATIONS 65536] Ok
+A000027 OK [READ-ONLY] Completed
+<1400091057<A000028 FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {56}
+Date: Mon, 12 May 2014 11:02:38 +0000
+Subject: uid1
+
+)
+A000028 OK Completed (0.000 sec)
+<1400091057<A000029 UID FETCH 1 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)
+>1400091057>* 1 FETCH (FLAGS (\Recent) UID 1 INTERNALDATE "12-May-2014 13:02:36 +0200" RFC822.SIZE 1970 BODY[] {1968}
+Date: Mon, 12 May 2014 11:02:38 +0000
+X-Kolab-Type: application/x-vnd.kolab.event
+X-Kolab-Mime-Version: 3.0
+User-Agent: fbtest Libkolab-0.6
+Content-Type: multipart/mixed; boundary="nextPart1645803.C9AV0NgWoz"
+Subject: uid1
+MIME-Version: 1.0
+
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7Bit
+
+This is a Kolab Groupware object.
+To view this object you will need an email client that can understand the Kolab Groupware format.
+For a list of such email clients please visit
+http://www.kolab.org/get-kolab
+
+--nextPart1645803.C9AV0NgWoz
+Content-Type: application/calendar+xml; name="kolab.xml"
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="kolab.xml"
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"no" ?>
+<icalendar xmlns=3D"urn:ietf:params:xml:ns:icalendar-2.0">
+
+ <vcalendar>
+ <properties>
+ <prodid>
+ <text>fbtest Libkolab-0.6 Libkolabxml-1.1</text>
+ </prodid>
+ <version>
+ <text>2.0</text>
+ </version>
+ <x-kolab-version>
+ <text>3.1.0</text>
+ </x-kolab-version>
+ </properties>
+ <components>
+ <vevent>
+ <properties>
+ <uid>
+ <text>uid1</text>
+ </uid>
+ <created>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </created>
+ <dtstamp>
+ <date-time>2014-05-12T11:02:38Z</date-time>
+ </dtstamp>
+ <sequence>
+ <integer>0</integer>
+ </sequence>
+ <class>
+ <text>PUBLIC</text>
+ </class>
+ <dtstart>
+ <date-time>2014-06-12T11:02:38Z</date-time>
+ </dtstart>
+ <dtend>
+ <date-time>2014-06-12T12:02:38Z</date-time>
+ </dtend>
+ </properties>
+ </vevent>
+ </components>
+ </vcalendar>
+
+</icalendar>
+
+--nextPart1645803.C9AV0NgWoz--
+)
+A000029 OK Completed (0.020 sec)
+<1400091057<A000033 LOGOUT
+>1400091057>* BYE LOGOUT received
+A000033 OK Completed
diff --git a/fbdaemon/tests/fbaggregatortest.cpp b/fbdaemon/tests/fbaggregatortest.cpp
index 18569fb..aecb4eb 100644
--- a/fbdaemon/tests/fbaggregatortest.cpp
+++ b/fbdaemon/tests/fbaggregatortest.cpp
@@ -33,15 +33,19 @@
FBAggregatorTest::FBAggregatorTest(QObject* parent)
: QObject(parent),
- targethost("192.168.122.10"),
- user("john.doe@example.org"),
- admin("cyrus-admin"),
- adminpw("admin"),
- port(143)
+ user("john.doe@example.org")
{
+ Settings::instance().setAuthorizationUser("cyrus-admin");
+ Settings::instance().setTestMode(true);
+ Settings::instance().setPassword("admin");
+ Settings::instance().setServerUri("127.0.0.1:5989");
+ Settings::instance().setThreshold(10);
+ Settings::instance().setTimeframe(60);
+ Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
+
Object fbObj1;
Kolab::Freebusy fb;
- fb.setOrganizer(Kolab::ContactReference("mail@example.com", "john doe"));
+ fb.setOrganizer(Kolab::ContactReference(user.toAscii().data(), "john doe"));
fb.setStart(Kolab::Conversion::fromDate(KDateTime::currentUtcDateTime()));
fb.setEnd(Kolab::Conversion::fromDate(KDateTime::currentUtcDateTime().addDays(60)));
fbObj1.object = QVariant::fromValue(Kolab::KolabObjectWriter::writeFreebusy(fb, Kolab::KolabV3, "fbtest"));
@@ -55,27 +59,8 @@ FBAggregatorTest::FBAggregatorTest(QObject* parent)
folders << Folder("Freebusy", Kolab::FreebusyType, QList<Object>() << fbObj1 << fbObj2);
}
-void FBAggregatorTest::setupTargetAccount()
-{
- QObject obj;
- KolabAccount *account = new KolabAccount(&obj);
- account->setHost(targethost, port);
- account->setCredentials(user, adminpw, admin);
- account->setEncryptionMode(KIMAP::LoginJob::TlsV1);
- QVERIFY(account->init());
-
- account->cleanAccount();
- createFolders(account, folders);
-}
-
void FBAggregatorTest::executeAggregation()
{
- Settings::instance().setAuthorizationUser(admin);
- Settings::instance().setPassword(adminpw);
- Settings::instance().setServerUri(targethost);
- Settings::instance().setThreshold(10);
- Settings::instance().setTimeframe(60);
- Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
SessionSettings sessionSettings = Settings::instance().getSessionSettings();
sessionSettings.userName = user;
@@ -89,12 +74,10 @@ void FBAggregatorTest::checkFbObject()
{
QVERIFY(QFileInfo(generatedFile).exists());
QFile file(generatedFile);
- QVERIFY(file.open(QIODevice::ReadOnly|QIODevice::Text));
+ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
QTextStream in(&file);
QString data = in.readAll();
- qDebug() << data;
-
KCalCore::ICalFormat format;
KCalCore::Calendar::Ptr cal(new KCalCore::MemoryCalendar(KDateTime::Spec::UTC()));
KCalCore::ScheduleMessage::Ptr msg = format.parseScheduleMessage(cal, data);
@@ -102,20 +85,25 @@ void FBAggregatorTest::checkFbObject()
QCOMPARE(msg->method(), KCalCore::iTIPPublish);
QCOMPARE(format.timeSpec(), KDateTime::Spec::UTC());
QVERIFY(msg->event());
- QCOMPARE(msg->event()->organizer()->email(), QLatin1String("john.doe@example.org"));
+ QCOMPARE(msg->event()->organizer()->email(), user);
QVERIFY(!msg->event()->uid().isEmpty());
- QVERIFY(msg->event()->lastModified().isValid());
-
- //Check that aggregated fb object has been created
+ //QVERIFY(msg->event()->lastModified().isValid());
+ //TODO:test content of aggregated file
}
-
+
void FBAggregatorTest::testGenerator()
{
- setupTargetAccount();
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/aggregator/imap-28470");
+ fakeServer.startAndWait();
+
executeAggregation();
checkFbObject();
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
}
QTEST_MAIN(FBAggregatorTest)
-#include "fbaggregatortest.moc" \ No newline at end of file
+#include "fbaggregatortest.moc"
diff --git a/fbdaemon/tests/fbaggregatortest.h b/fbdaemon/tests/fbaggregatortest.h
index 9564ed2..2e3863e 100644
--- a/fbdaemon/tests/fbaggregatortest.h
+++ b/fbdaemon/tests/fbaggregatortest.h
@@ -18,6 +18,7 @@
#define FBAGGREGATORTEST_H
#include <QObject>
+#include "cyrusfakeserver.h"
#include "testlib/testutils.h"
class FBAggregatorTest: public QObject
@@ -30,17 +31,14 @@ private slots:
void testGenerator();
private:
- void setupTargetAccount();
void executeAggregation();
void checkFbObject();
- QString targethost;
QString user;
- QString admin;
- QString adminpw;
- qint16 port;
QList<Folder> folders;
QString generatedFile;
+
+ CyrusFakeServer fakeServer;
};
#endif
diff --git a/fbdaemon/tests/fbgeneratorfoldertest.cpp b/fbdaemon/tests/fbgeneratorfoldertest.cpp
new file mode 100644
index 0000000..1d11640
--- /dev/null
+++ b/fbdaemon/tests/fbgeneratorfoldertest.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "fbgeneratorfoldertest.h"
+#include "cyrusfakeserver.h"
+
+#include <QTest>
+#include <QDebug>
+#include <QDir>
+#include <xmlobject.h>
+#include <kolabobject.h>
+#include <freebusy.h>
+#include <kcalconversion.h>
+#include "settings.h"
+#include "kolabaccount.h"
+#include "fbdaemon/fbgeneratorfolderjob.h"
+
+FBGeneratorFolderTest::FBGeneratorFolderTest(QObject* parent)
+ : QObject(parent),
+ user("john.doe@example.org")
+{
+ Settings::instance().setAuthorizationUser("cyrus-admin");
+ Settings::instance().setTestMode(true);
+ Settings::instance().setPassword("admin");
+ Settings::instance().setServerUri("127.0.0.1:5989");
+ Settings::instance().setThreshold(10);
+ Settings::instance().setTimeframe(60);
+ Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
+}
+
+Kolab::Freebusy FBGeneratorFolderTest::executeGeneration(const QString &folder, KDateTime start, KDateTime end)
+{
+
+ SessionSettings sessionSettings = Settings::instance().getSessionSettings();
+ sessionSettings.userName = user;
+
+ QObject obj;
+ FBGeneratorFolderJob *job = new FBGeneratorFolderJob(sessionSettings, folder , &obj);
+ job->setTimeFrame(start, end);
+ job->exec();
+
+ return job->getFreebusy();
+}
+
+void FBGeneratorFolderTest::executeGenerationError(const QString &folder, KDateTime start, KDateTime end, QString expected)
+{
+
+ SessionSettings sessionSettings = Settings::instance().getSessionSettings();
+ sessionSettings.userName = user;
+
+ QObject obj;
+ FBGeneratorFolderJob *job = new FBGeneratorFolderJob(sessionSettings, folder , &obj);
+ job->setTimeFrame(start, end);
+ job->exec();
+
+ QVERIFY(job->error());
+ QCOMPARE(job->errorString(), expected);
+}
+
+void FBGeneratorFolderTest::compareFreeBusy(Kolab::Freebusy fb1, Kolab::Freebusy fb2)
+{
+ QCOMPARE((int)fb1.periods().size(), (int)fb2.periods().size());
+ QCOMPARE(fb1.organizer(), fb2.organizer());
+ QCOMPARE(fb1.start(), fb2.start());
+ QCOMPARE(fb1.end(), fb2.end());
+ for (std::size_t i = 0; i < fb1.periods().size(); i++) {
+ std::cout << i;
+ QCOMPARE(fb1.periods().at(i), fb2.periods().at(i));
+ }
+}
+
+void FBGeneratorFolderTest::testGenerator()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/normal.log");
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/setrights.log");
+ fakeServer.startAndWait();
+
+ KDateTime start;
+ KDateTime end;
+ Kolab::cDateTime s(2014, 6, 12, 11, 2, 38, true);
+ Kolab::cDateTime e(2014, 6, 12, 12, 2, 38, true);
+ Kolab::Period period(s, e);
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+
+ Kolab::Freebusy fb1 = executeGeneration("shared/Resources/mybeamer@example.com", start, end);
+ Kolab::Freebusy fb2 = executeGeneration("shared/Resources/mybeamer@example.com", start, end);
+
+ QCOMPARE((int)fb1.periods().size(), 1);
+ QCOMPARE(fb1.organizer(), Kolab::ContactReference(QString("fbdaemon@localhost").toUtf8().constData()));
+ QCOMPARE(Kolab::Conversion::toDate(fb1.start()), start);
+ QCOMPARE(Kolab::Conversion::toDate(fb1.end()), end);
+ QCOMPARE((int)fb1.periods().at(0).periods().size(), 1);
+ QCOMPARE(fb1.periods().at(0).periods().at(0), period);
+ QVERIFY(!fb1.uid().empty());
+ QVERIFY(!fb2.uid().empty());
+
+ compareFreeBusy(fb2, fb1);
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void FBGeneratorFolderTest::testGeneratorEmpty()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/empty.log");
+ fakeServer.startAndWait();
+
+ KDateTime start;
+ KDateTime end;
+ Kolab::cDateTime s(2014, 6, 12, 11, 2, 38, true);
+ Kolab::cDateTime e(2014, 6, 12, 12, 2, 38, true);
+ Kolab::Period period(s, e);
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+
+ Kolab::Freebusy fb1 = executeGeneration("shared/Resources/mybeamer@example.com", start, end);
+
+ QCOMPARE((int)fb1.periods().size(), 0);
+ QCOMPARE(fb1.organizer(), Kolab::ContactReference(QString("fbdaemon@localhost").toUtf8().constData()));
+ QCOMPARE(Kolab::Conversion::toDate(fb1.start()), start);
+ QCOMPARE(Kolab::Conversion::toDate(fb1.end()), end);
+ QVERIFY(!fb1.uid().empty());
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void FBGeneratorFolderTest::testFailedNotFound()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/notfound.log");
+ fakeServer.startAndWait();
+
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+
+ QString errString = QString("MyRights failed, server replied: A000005 NO Mailbox does not exist ");
+ executeGenerationError("shared/Resources/bla@example.com", start, end, errString);
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+void FBGeneratorFolderTest::testFailedSystemIoError()
+{
+ CyrusFakeServer fakeServer;
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generatefolder/systemioerror.log");
+ fakeServer.startAndWait();
+
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+
+ executeGenerationError("shared/Resources/mega 1000@example.com", start, end, "");
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
+}
+
+
+void FBGeneratorFolderTest::testSetTimeframe()
+{
+ SessionSettings sessionSettings = Settings::instance().getSessionSettings();
+ QObject obj;
+ FBGeneratorFolderJob job(sessionSettings, "", &obj);
+
+ QVERIFY(job.mStartOfTimeFrame.secsTo(KDateTime::currentUtcDateTime()) < 2); // $now
+ QCOMPARE(job.mStartOfTimeFrame.daysTo(job.mEndOfTimeFrame), Settings::instance().getTimeframe()); // $now+$defaultvalue
+
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(12345);
+ end.setTime_t(67890);
+ job.setTimeFrame(start, end);
+
+ QCOMPARE(job.mStartOfTimeFrame, start);
+ QCOMPARE(job.mEndOfTimeFrame, end);
+}
+
+QTEST_MAIN(FBGeneratorFolderTest)
+
+#include "fbgeneratorfoldertest.moc"
diff --git a/fbdaemon/tests/fbgeneratorfoldertest.h b/fbdaemon/tests/fbgeneratorfoldertest.h
new file mode 100644
index 0000000..2577ae1
--- /dev/null
+++ b/fbdaemon/tests/fbgeneratorfoldertest.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Sandro Knauß <knauss@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef FBGENERATORFOLDERTEST_H
+#define FBGENERATORFOLDERTEST_H
+
+#include <QObject>
+
+#include "testlib/testutils.h"
+
+class KolabAccount;
+class KDateTime;
+
+class FBGeneratorFolderTest: public QObject
+{
+ Q_OBJECT
+public:
+ explicit FBGeneratorFolderTest(QObject* parent = 0);
+
+private slots:
+ void testGenerator();
+ void testGeneratorEmpty();
+ void testFailedNotFound();
+ void testFailedSystemIoError();
+ void testSetTimeframe();
+private:
+ Kolab::Freebusy executeGeneration(const QString&, KDateTime, KDateTime);
+ void executeGenerationError(const QString&, KDateTime, KDateTime, QString);
+ void compareFreeBusy(Kolab::Freebusy fb1, Kolab::Freebusy fb2);
+
+ QString user;
+
+};
+
+#endif \ No newline at end of file
diff --git a/fbdaemon/tests/fbgeneratortest.cpp b/fbdaemon/tests/fbgeneratortest.cpp
index 2faf078..a579bd3 100644
--- a/fbdaemon/tests/fbgeneratortest.cpp
+++ b/fbdaemon/tests/fbgeneratortest.cpp
@@ -19,107 +19,102 @@
#include <QTest>
#include <QDebug>
#include <QDir>
+#include <xmlobject.h>
#include <kolabobject.h>
#include <freebusy.h>
#include <kcalconversion.h>
-#include "fbdaemon/fbgeneratorjob.h"
#include "settings.h"
#include "kolabaccount.h"
FBGeneratorTest::FBGeneratorTest(QObject* parent)
: QObject(parent),
- targethost("192.168.122.10"),
- user("john.doe@example.org"),
- admin("cyrus-admin"),
- adminpw("admin"),
- port(143)
+ user("john.doe@example.org")
{
- Object calObj1;
- KCalCore::Event::Ptr event(new KCalCore::Event());
- event->setUid("uid1");
- event->setDtStart(KDateTime::currentUtcDateTime());
- event->setDtEnd(KDateTime::currentUtcDateTime().addSecs(3600));
- calObj1.object = QVariant::fromValue(Kolab::KolabObjectWriter::writeEvent(event, Kolab::KolabV3, "fbtest"));
- folders << Folder("Calendar", Kolab::EventType, QList<Object>() << calObj1);
-
-// folders << Folder("Freebusy", Kolab::FreebusyType, QList<Object>()/* << fbObj1*/);
+ Settings::instance().setAuthorizationUser("cyrus-admin");
+ Settings::instance().setTestMode(true);
+ Settings::instance().setPassword("admin");
+ Settings::instance().setServerUri("127.0.0.1:5989");
+ Settings::instance().setThreshold(10);
+ Settings::instance().setTimeframe(60);
+ Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
}
-void FBGeneratorTest::setupTargetAccount()
+Kolab::Freebusy FBGeneratorTest::executeGeneration(bool saveObject, KDateTime start, KDateTime end)
{
- QObject obj;
- KolabAccount *account = new KolabAccount(&obj);
- account->setHost(targethost, port);
- account->setCredentials(user, adminpw, admin);
- QVERIFY(account->init());
- account->cleanAccount();
- createFolders(account, folders);
-}
-
-void FBGeneratorTest::executeGeneration()
-{
- Settings::instance().setAuthorizationUser(admin);
- Settings::instance().setPassword(adminpw);
- Settings::instance().setServerUri(targethost);
- Settings::instance().setThreshold(10);
- Settings::instance().setTimeframe(60);
- Settings::instance().setAggregatedICalOutputDirectory(QDir::tempPath());
SessionSettings sessionSettings = Settings::instance().getSessionSettings();
sessionSettings.userName = user;
QObject obj;
- FBGeneratorJob *job = new FBGeneratorJob(sessionSettings, &obj);
+ FBGeneratorJob *job = new FBGeneratorJob(sessionSettings, saveObject, &obj);
+ job->setTimeFrame(start, end);
job->exec();
+
+ return job->getFreebusy();
}
-
-void FBGeneratorTest::checkFolders(KolabAccount *account, const QList<Folder> &folders)
+
+void FBGeneratorTest::compareFreeBusy(Kolab::Freebusy fb1, Kolab::Freebusy fb2)
{
- const QStringList &receivedFolders = account->lookupFolderList();
- qDebug() << receivedFolders;
- foreach(const Folder &folder, folders) {
- qDebug() << folder.name;
- QVERIFY(receivedFolders.contains(folder.name));
- const QList<Object> &objects = account->getObjects(folder.name);
- QCOMPARE(objects.size(), folder.objects.size());
-
- QList<Object>::const_iterator recObjIt = objects.constBegin();
- QList<Object>::const_iterator objIt = folder.objects.constBegin();
- for (;objIt != folder.objects.constEnd() && recObjIt != objects.constEnd(); ++objIt, ++recObjIt) {
- //TODO Check fb objects
- }
+ QCOMPARE((int)fb1.periods().size(), (int)fb2.periods().size());
+ QCOMPARE(fb1.organizer(), fb2.organizer());
+ QCOMPARE(fb1.start(), fb2.start());
+ QCOMPARE(fb1.end(), fb2.end());
+ for (std::size_t i = 0; i < fb1.periods().size(); i++) {
+ std::cout << i;
+ QCOMPARE(fb1.periods().at(i), fb2.periods().at(i));
}
}
-void FBGeneratorTest::checkTargetAccount()
+
+
+void FBGeneratorTest::testGenerator()
{
- //Check that fb-object and aggregated fb object has been created
- QObject obj;
- KolabAccount *account = new KolabAccount(&obj);
- account->setHost(targethost, port);
- account->setCredentials(user, adminpw, admin);
- account->init();
-
-
- Object fbObj1;
- Kolab::Freebusy fb;
- fb.setStart(Kolab::Conversion::fromDate(KDateTime::currentUtcDateTime()));
- fb.setEnd(Kolab::Conversion::fromDate(KDateTime::currentUtcDateTime().addDays(60)));
- fbObj1.object = QVariant(Kolab::KolabObjectWriter::writeFreebusy(fb, Kolab::KolabV3, "fbtest"));
-
- QList<Folder> targetFolders;
- targetFolders << Folder("Freebusy", Kolab::FreebusyType, QList<Object>() << fbObj1);
- checkFolders(account, targetFolders);
- account->logout();
+ QDir dir(QLatin1String(SCENARIO_DATA_DIR));
+ fakeServer.addScenarioFromFile(dir.path() + "/generator/imap-6398");
+ fakeServer.addScenarioFromFile(dir.path() + "/generator/imap-6398.withoutattach");
+ fakeServer.startAndWait();
+
+ KDateTime start;
+ KDateTime end;
+ Kolab::cDateTime s(2014, 6, 12, 11, 2, 38, true);
+ Kolab::cDateTime e(2014, 6, 12, 12, 2, 38, true);
+ Kolab::Period period(s, e);
+ start.setTime_t(1400604981);
+ end.setTime_t(1405788981);
+ Kolab::Freebusy fb1 = executeGeneration(true, start, end);
+ Kolab::Freebusy fb2 = executeGeneration(false, start, end);
+
+ QCOMPARE(fb1.organizer(), Kolab::ContactReference(user.toUtf8().constData()));
+ QCOMPARE(Kolab::Conversion::toDate(fb1.start()), start);
+ QCOMPARE(Kolab::Conversion::toDate(fb1.end()), end);
+ QCOMPARE((int)fb1.periods().size(), 1);
+ QCOMPARE((int)fb1.periods().at(0).periods().size(), 1);
+ QCOMPARE(fb1.periods().at(0).periods().at(0), period);
+ compareFreeBusy(fb2, fb1);
+
+ QVERIFY(fakeServer.isAllScenarioDone());
+ fakeServer.quit();
}
-
-void FBGeneratorTest::testGenerator()
+
+void FBGeneratorTest::testSetTimeframe()
{
- setupTargetAccount();
- executeGeneration();
- checkTargetAccount();
+ SessionSettings sessionSettings = Settings::instance().getSessionSettings();
+ QObject obj;
+ FBGeneratorJob job(sessionSettings, true, &obj);
+
+ QVERIFY(job.mStartOfTimeFrame.secsTo(KDateTime::currentUtcDateTime()) < 2); // $now
+ QCOMPARE(job.mStartOfTimeFrame.daysTo(job.mEndOfTimeFrame), Settings::instance().getTimeframe()); // $now+$defaultvalue
+
+ KDateTime start;
+ KDateTime end;
+ start.setTime_t(12345);
+ end.setTime_t(67890);
+ job.setTimeFrame(start, end);
+
+ QCOMPARE(job.mStartOfTimeFrame, start);
+ QCOMPARE(job.mEndOfTimeFrame, end);
}
-QTEST_MAIN( FBGeneratorTest )
+QTEST_MAIN(FBGeneratorTest)
-#include "fbgeneratortest.moc" \ No newline at end of file
+#include "fbgeneratortest.moc"
diff --git a/fbdaemon/tests/fbgeneratortest.h b/fbdaemon/tests/fbgeneratortest.h
index eefbfc9..8c17264 100644
--- a/fbdaemon/tests/fbgeneratortest.h
+++ b/fbdaemon/tests/fbgeneratortest.h
@@ -18,9 +18,14 @@
#define FBGENERATORTEST_H
#include <QObject>
+#include "fbdaemon/fbgeneratorjob.h"
+#include "cyrusfakeserver.h"
+
#include "testlib/testutils.h"
class KolabAccount;
+class KDateTime;
+
class FBGeneratorTest: public QObject
{
Q_OBJECT
@@ -29,19 +34,14 @@ public:
private slots:
void testGenerator();
-
+ void testSetTimeframe();
private:
- void setupTargetAccount();
- void executeGeneration();
- void checkFolders(KolabAccount *account, const QList<Folder> &folders);
- void checkTargetAccount();
+ Kolab::Freebusy executeGeneration(bool, KDateTime, KDateTime);
+ void compareFreeBusy(Kolab::Freebusy fb1, Kolab::Freebusy fb2);
- QString targethost;
QString user;
- QString admin;
- QString adminpw;
- qint16 port;
- QList<Folder> folders;
+
+ CyrusFakeServer fakeServer;
};
#endif \ No newline at end of file