diff --git a/agents/maildispatcher/maildispatcheragent.cpp b/agents/maildispatcher/maildispatcheragent.cpp index ad820d164..78b3284ac 100644 --- a/agents/maildispatcher/maildispatcheragent.cpp +++ b/agents/maildispatcher/maildispatcheragent.cpp @@ -1,324 +1,324 @@ /* Copyright 2008 Ingo Klöcker Copyright 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "maildispatcheragent.h" //#include "configdialog.h" #include "maildispatcheragentadaptor.h" #include "outboxqueue.h" #include "sendjob.h" #include "sentactionhandler.h" #include "settings.h" #include "settingsadaptor.h" #include -#include -#include +#include +#include #include #include "maildispatcher_debug.h" #include #include #include #include #include #include #ifdef MAIL_SERIALIZER_PLUGIN_STATIC Q_IMPORT_PLUGIN(akonadi_serializer_mail) #endif using namespace Akonadi; void MailDispatcherAgent::abort() { if (!isOnline()) { qCDebug(MAILDISPATCHER_LOG) << "Offline. Ignoring call."; return; } if (mAborting) { qCDebug(MAILDISPATCHER_LOG) << "Already aborting."; return; } if (!mSendingInProgress && mQueue->isEmpty()) { qCDebug(MAILDISPATCHER_LOG) << "MDA is idle."; Q_ASSERT(status() == AgentBase::Idle); } else { qCDebug(MAILDISPATCHER_LOG) << "Aborting..."; mAborting = true; if (mCurrentJob) { mCurrentJob->abort(); } // Further SendJobs will mark remaining items in the queue as 'aborted'. } } void MailDispatcherAgent::dispatch() { Q_ASSERT(mQueue); if (!isOnline() || mSendingInProgress) { qCDebug(MAILDISPATCHER_LOG) << "Offline or busy. See you later."; return; } if (!mQueue->isEmpty()) { if (!mSentAnything) { mSentAnything = true; mSentItemsSize = 0; Q_EMIT percent(0); } Q_EMIT status(AgentBase::Running, i18np("Sending messages (1 item in queue)...", "Sending messages (%1 items in queue)...", mQueue->count())); qCDebug(MAILDISPATCHER_LOG) << "Attempting to dispatch the next message."; mSendingInProgress = true; mQueue->fetchOne(); // will trigger itemFetched } else { qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; if (mAborting) { // Finished marking messages as 'aborted'. mAborting = false; mSentAnything = false; Q_EMIT status(AgentBase::Idle, i18n("Sending canceled.")); QTimer::singleShot(3000, this, &MailDispatcherAgent::emitStatusReady); } else { if (mSentAnything) { // Finished sending messages in queue. mSentAnything = false; Q_EMIT percent(100); Q_EMIT status(AgentBase::Idle, i18n("Finished sending messages.")); if (!mErrorOccurred && mShowSentNotification) { KNotification *notify = new KNotification(QStringLiteral("emailsent")); notify->setIconName(QStringLiteral("kmail")); notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); notify->setTitle(i18nc("Notification title when email was sent", "E-Mail Successfully Sent")); notify->setText(i18nc("Notification when the email was sent", "Your E-Mail has been sent successfully.")); notify->sendEvent(); } mShowSentNotification = true; } else { // Empty queue. Q_EMIT status(AgentBase::Idle, i18n("No items in queue.")); } QTimer::singleShot(3000, this, &MailDispatcherAgent::emitStatusReady); } mErrorOccurred = false; } } MailDispatcherAgent::MailDispatcherAgent(const QString &id) : AgentBase(id) { Kdelibs4ConfigMigrator migrate(QStringLiteral("maildispatcheragent")); migrate.setConfigFiles(QStringList() << QStringLiteral("maildispatcheragentrc") << QStringLiteral("akonadi_maildispatcher_agent.notifyrc")); migrate.migrate(); qCDebug(MAILDISPATCHER_LOG) << "maildispatcheragent: At your service, sir!"; new SettingsAdaptor(Settings::self()); new MailDispatcherAgentAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), Settings::self(), QDBusConnection::ExportAdaptors); QDBusConnection::sessionBus().registerObject(QStringLiteral("/MailDispatcherAgent"), this, QDBusConnection::ExportAdaptors); QString service = QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent"); if (Akonadi::ServerManager::hasInstanceIdentifier()) { service += QLatin1Char('.') + Akonadi::ServerManager::instanceIdentifier(); } QDBusConnection::sessionBus().registerService(service); mQueue = new OutboxQueue(this); connect(mQueue, &OutboxQueue::newItems, this, &MailDispatcherAgent::dispatch); connect(mQueue, &OutboxQueue::itemReady, this, &MailDispatcherAgent::itemFetched); connect(mQueue, &OutboxQueue::error, this, &MailDispatcherAgent::queueError); connect(this, &MailDispatcherAgent::itemProcessed, mQueue, &OutboxQueue::itemProcessed); connect(this, &MailDispatcherAgent::abortRequested, this, &MailDispatcherAgent::abort); mSentActionHandler = new SentActionHandler(this); setNeedsNetwork(true); } MailDispatcherAgent::~MailDispatcherAgent() { } void MailDispatcherAgent::doSetOnline(bool online) { Q_ASSERT(mQueue); if (online) { qCDebug(MAILDISPATCHER_LOG) << "Online. Dispatching messages."; Q_EMIT status(AgentBase::Idle, i18n("Online, sending messages in queue.")); QTimer::singleShot(0, this, &MailDispatcherAgent::dispatch); } else { qCDebug(MAILDISPATCHER_LOG) << "Offline."; Q_EMIT status(AgentBase::Idle, i18n("Offline, message sending suspended.")); // TODO: This way, the OutboxQueue will continue to react to changes in // the outbox, but the MDA will just not send anything. Is this what we // want? } AgentBase::doSetOnline(online); } void MailDispatcherAgent::itemFetched(const Item &item) { qCDebug(MAILDISPATCHER_LOG) << "Fetched item" << item.id() << "; creating SendJob."; Q_ASSERT(mSendingInProgress); Q_ASSERT(!mCurrentItem.isValid()); mCurrentItem = item; Q_ASSERT(mCurrentJob == nullptr); Q_EMIT itemDispatchStarted(); mCurrentJob = new SendJob(item, this); if (mAborting) { mCurrentJob->setMarkAborted(); } Q_EMIT status(AgentBase::Running, i18nc("Message with given subject is being sent.", "Sending: %1", item.payload()->subject()->asUnicodeString())); connect(mCurrentJob, &KJob::result, this, &MailDispatcherAgent::sendResult); //TODO wait kf6. For the moment we can't convert to new connect api. connect(mCurrentJob, SIGNAL(percent(KJob*,ulong)), this, SLOT(sendPercent(KJob*,ulong))); mCurrentJob->start(); } void MailDispatcherAgent::queueError(const QString &message) { Q_EMIT error(message); mErrorOccurred = true; // FIXME figure out why this does not set the status to Broken, etc. } void MailDispatcherAgent::sendPercent(KJob *job, unsigned long) { Q_ASSERT(mSendingInProgress); Q_ASSERT(job == mCurrentJob); // The progress here is actually the TransportJob, not the entire SendJob, // because the post-job doesn't report progress. This should be fine, // since the TransportJob is the lengthiest operation. // Give the transport 80% of the weight, and move-to-sendmail 20%. const double transportWeight = 0.8; const int percentValue = 100 * (mSentItemsSize + job->processedAmount(KJob::Bytes) * transportWeight) / (mSentItemsSize + mCurrentItem.size() + mQueue->totalSize()); qCDebug(MAILDISPATCHER_LOG) << "sentItemsSize" << mSentItemsSize << "this job processed" << job->processedAmount(KJob::Bytes) << "queue totalSize" << mQueue->totalSize() << "total total size (sent+current+queue)" << (mSentItemsSize + mCurrentItem.size() + mQueue->totalSize()) << "new percentage" << percentValue << "old percentage" << progress(); if (percentValue != progress()) { // The progress can decrease too, if messages got added to the queue. Q_EMIT percent(percentValue); } // It is possible that the number of queued messages has changed. Q_EMIT status(AgentBase::Running, i18np("Sending messages (1 item in queue)...", "Sending messages (%1 items in queue)...", 1 + mQueue->count())); } void MailDispatcherAgent::sendResult(KJob *job) { Q_ASSERT(mSendingInProgress); Q_ASSERT(job == mCurrentJob); mCurrentJob->disconnect(this); mCurrentJob = nullptr; Q_ASSERT(mCurrentItem.isValid()); mSentItemsSize += mCurrentItem.size(); Q_EMIT itemProcessed(mCurrentItem, !job->error()); const Akonadi::Item sentItem = mCurrentItem; mCurrentItem = Item(); if (job->error()) { // The SendJob gave the item an ErrorAttribute, so we don't have to // do anything. qCDebug(MAILDISPATCHER_LOG) << "Sending failed. error:" << job->errorString(); KNotification *notify = new KNotification(QStringLiteral("sendingfailed")); notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); notify->setIconName(QStringLiteral("kmail")); notify->setTitle(i18nc("Notification title when email sending failed", "E-Mail Sending Failed")); notify->setText(job->errorString().toHtmlEscaped()); notify->sendEvent(); mErrorOccurred = true; } else { qCDebug(MAILDISPATCHER_LOG) << "Sending succeeded."; // handle possible sent actions const MailTransport::SentActionAttribute *attribute = sentItem.attribute(); if (attribute) { const MailTransport::SentActionAttribute::Action::List lstAct = attribute->actions(); for (const MailTransport::SentActionAttribute::Action &action : lstAct) { mSentActionHandler->runAction(action); } } const auto bhAttribute = sentItem.attribute(); if (bhAttribute) { mShowSentNotification = !bhAttribute->sendSilently(); } else { mShowSentNotification = true; } } // dispatch next message mSendingInProgress = false; QTimer::singleShot(0, this, &MailDispatcherAgent::dispatch); } void MailDispatcherAgent::emitStatusReady() { if (status() == AgentBase::Idle) { // If still idle after aborting, clear 'aborted' status. Q_EMIT status(AgentBase::Idle, i18n("Ready to dispatch messages.")); } } #ifndef KDEPIM_PLUGIN_AGENT AKONADI_AGENT_MAIN(MailDispatcherAgent) #endif #include "moc_maildispatcheragent.cpp" diff --git a/agents/maildispatcher/outboxqueue.cpp b/agents/maildispatcher/outboxqueue.cpp index 52cdd27fa..a73ec2444 100644 --- a/agents/maildispatcher/outboxqueue.cpp +++ b/agents/maildispatcher/outboxqueue.cpp @@ -1,394 +1,394 @@ /* Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "outboxqueue.h" #include #include "maildispatcher_debug.h" #include #include #include #include #include #include #include #include #include #include -#include -#include -#include +#include +#include +#include using namespace Akonadi; using namespace MailTransport; static const int OUTBOX_DISCOVERY_RETRIES = 3; // number of times we try to find or create the outbox static const int OUTBOX_DISCOVERY_WAIT_TIME = 5000; // number of ms to wait before retrying void OutboxQueue::initQueue() { mTotalSize = 0; mQueue.clear(); qCDebug(MAILDISPATCHER_LOG) << "Fetching items in collection" << mOutbox.id(); ItemFetchJob *job = new ItemFetchJob(mOutbox); job->fetchScope().fetchAllAttributes(); job->fetchScope().fetchFullPayload(false); connect(job, &ItemFetchJob::result, this, &OutboxQueue::collectionFetched); } void OutboxQueue::addIfComplete(const Item &item) { if (mIgnore.contains(item.id())) { qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is ignored."; return; } if (mQueue.contains(item)) { qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "already in queue!"; return; } if (!item.hasAttribute()) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Address."; return; } if (!item.hasAttribute()) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute DispatchMode."; return; } if (!item.hasAttribute()) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute SentBehaviour."; return; } if (!item.hasAttribute()) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Transport."; return; } if (!item.hasFlag(Akonadi::MessageFlags::Queued)) { qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "has no '$QUEUED' flag."; return; } const DispatchModeAttribute *dispatchModeAttribute = item.attribute(); Q_ASSERT(dispatchModeAttribute); if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Manual) { qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is queued to be sent manually."; return; } const TransportAttribute *transportAttribute = item.attribute(); Q_ASSERT(transportAttribute); if (transportAttribute->transport() == nullptr) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid transport."; return; } const SentBehaviourAttribute *sentBehaviourAttribute = item.attribute(); Q_ASSERT(sentBehaviourAttribute); if (sentBehaviourAttribute->sentBehaviour() == SentBehaviourAttribute::MoveToCollection && !sentBehaviourAttribute->moveToCollection().isValid()) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid sent-mail collection."; return; } // This check requires fetchFullPayload. -> slow (?) /* if ( !item.hasPayload() ) { qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have KMime::Message::Ptr payload."; return; } */ if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Automatic && dispatchModeAttribute->sendAfter().isValid() && dispatchModeAttribute->sendAfter() > QDateTime::currentDateTime()) { // All the above was OK, so accept it for the future. qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted to be sent in the future."; mFutureMap.insert(dispatchModeAttribute->sendAfter(), item); Q_ASSERT(!mFutureItems.contains(item)); mFutureItems.insert(item); checkFuture(); return; } qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted into the queue (size" << item.size() << ")."; Q_ASSERT(!mQueue.contains(item)); mTotalSize += item.size(); mQueue.append(item); Q_EMIT newItems(); } void OutboxQueue::checkFuture() { qCDebug(MAILDISPATCHER_LOG) << "The future is here." << mFutureMap.count() << "items in futureMap."; Q_ASSERT(mFutureTimer); mFutureTimer->stop(); // By default, re-check in one hour. mFutureTimer->setInterval(60 * 60 * 1000); // Check items in ascending order of date. while (!mFutureMap.isEmpty()) { QMap::iterator it = mFutureMap.begin(); qCDebug(MAILDISPATCHER_LOG) << "Item with due date" << it.key(); if (it.key() > QDateTime::currentDateTime()) { const int secs = QDateTime::currentDateTime().secsTo(it.key()) + 1; qCDebug(MAILDISPATCHER_LOG) << "Future, in" << secs << "seconds."; Q_ASSERT(secs >= 0); if (secs < 60 * 60) { mFutureTimer->setInterval(secs * 1000); } break; // all others are in the future too } if (!mFutureItems.contains(it.value())) { qCDebug(MAILDISPATCHER_LOG) << "Item disappeared."; } else { qCDebug(MAILDISPATCHER_LOG) << "Due date is here. Queuing."; addIfComplete(it.value()); mFutureItems.remove(it.value()); } mFutureMap.erase(it); } qCDebug(MAILDISPATCHER_LOG) << "Timer set to checkFuture again in" << mFutureTimer->interval() / 1000 << "seconds" << "(that is" << mFutureTimer->interval() / 1000 / 60 << "minutes)."; mFutureTimer->start(); } void OutboxQueue::collectionFetched(KJob *job) { if (job->error()) { qCWarning(MAILDISPATCHER_LOG) << "Failed to fetch outbox collection. Queue will be empty until the outbox changes."; return; } const ItemFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << "items."; const Akonadi::Item::List lst = fetchJob->items(); for (const Item &item : lst) { addIfComplete(item); } } void OutboxQueue::itemFetched(KJob *job) { if (job->error()) { qCDebug(MAILDISPATCHER_LOG) << "Error fetching item:" << job->errorString() << ". Trying next item in queue."; fetchOne(); } const ItemFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); if (fetchJob->items().count() != 1) { qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << ", expected 1. Trying next item in queue."; fetchOne(); } if (!fetchJob->items().isEmpty()) { Q_EMIT itemReady(fetchJob->items().at(0)); } } void OutboxQueue::localFoldersChanged() { // Called on startup, and whenever the local folders change. if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::Outbox)) { // Outbox is ready, init the queue from it. const Collection collection = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox); Q_ASSERT(collection.isValid()); if (mOutbox != collection) { mMonitor->setCollectionMonitored(mOutbox, false); mMonitor->setCollectionMonitored(collection, true); mOutbox = collection; qCDebug(MAILDISPATCHER_LOG) << "Changed outbox to" << mOutbox.id(); initQueue(); } } else { // Outbox is not ready. Request it, since otherwise we will not know when // new messages appear. // (Note that we are a separate process, so we get no notification when // MessageQueueJob requests the Outbox.) mMonitor->setCollectionMonitored(mOutbox, false); mOutbox = Collection(-1); SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(this); job->requestDefaultCollection(SpecialMailCollections::Outbox); connect(job, &SpecialMailCollectionsRequestJob::result, this, &OutboxQueue::localFoldersRequestResult); qCDebug(MAILDISPATCHER_LOG) << "Requesting outbox folder."; job->start(); } // make sure we have a place to dump the sent mails as well if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(this); job->requestDefaultCollection(SpecialMailCollections::SentMail); qCDebug(MAILDISPATCHER_LOG) << "Requesting sent-mail folder"; job->start(); } } void OutboxQueue::localFoldersRequestResult(KJob *job) { if (job->error()) { // We tried to create the outbox, but that failed. This could be because some // other process, the mail app, for example, tried to create it at the // same time. So try again, once or twice, but wait a little in between, longer // each time. If we still haven't managed to create it after a few retries, // error hard. if (++mOutboxDiscoveryRetries <= OUTBOX_DISCOVERY_RETRIES) { const int timeout = OUTBOX_DISCOVERY_WAIT_TIME * mOutboxDiscoveryRetries; qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Retrying in: " << timeout; QTimer::singleShot(timeout, this, &OutboxQueue::localFoldersChanged); } else { qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Giving up."; Q_EMIT error(i18n("Could not access the outbox folder (%1).", job->errorString())); } return; } localFoldersChanged(); } void OutboxQueue::itemAdded(const Item &item) { addIfComplete(item); } void OutboxQueue::itemChanged(const Item &item) { addIfComplete(item); // TODO: if the item is moved out of the outbox, will I get itemChanged? } void OutboxQueue::itemMoved(const Item &item, const Collection &source, const Collection &destination) { if (source == mOutbox) { itemRemoved(item); } else if (destination == mOutbox) { addIfComplete(item); } } void OutboxQueue::itemRemoved(const Item &removedItem) { // @p item has size=0, so get the size from our own copy. const int index = mQueue.indexOf(removedItem); if (index == -1) { // Item was not in queue at all. return; } Item item(mQueue.takeAt(index)); qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "(size" << item.size() << ") was removed from the queue."; mTotalSize -= item.size(); mFutureItems.remove(removedItem); } void OutboxQueue::itemProcessed(const Item &item, bool result) { Q_ASSERT(mIgnore.contains(item.id())); if (!result) { // Give the user a chance to re-send the item if it failed. mIgnore.remove(item.id()); } } OutboxQueue::OutboxQueue(QObject *parent) : QObject(parent) { mMonitor = new Monitor(this); mMonitor->itemFetchScope().fetchAllAttributes(); mMonitor->itemFetchScope().fetchFullPayload(false); connect(mMonitor, &Monitor::itemAdded, this, &OutboxQueue::itemAdded); connect(mMonitor, &Monitor::itemChanged, this, &OutboxQueue::itemChanged); connect(mMonitor, &Monitor::itemMoved, this, &OutboxQueue::itemMoved); connect(mMonitor, &Monitor::itemRemoved, this, &OutboxQueue::itemRemoved); connect(SpecialMailCollections::self(), &SpecialMailCollections::defaultCollectionsChanged, this, &OutboxQueue::localFoldersChanged); localFoldersChanged(); mFutureTimer = new QTimer(this); connect(mFutureTimer, &QTimer::timeout, this, &OutboxQueue::checkFuture); mFutureTimer->start(60 * 60 * 1000); // 1 hour } OutboxQueue::~OutboxQueue() { } bool OutboxQueue::isEmpty() const { return mQueue.isEmpty(); } int OutboxQueue::count() const { const int numberOfElement(mQueue.count()); if (numberOfElement == 0) { // TODO Is this asking for too much? Q_ASSERT(mTotalSize == 0); } return numberOfElement; } qulonglong OutboxQueue::totalSize() const { return mTotalSize; } void OutboxQueue::fetchOne() { if (isEmpty()) { qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; return; } const Item item = mQueue.takeFirst(); mTotalSize -= item.size(); Q_ASSERT(!mIgnore.contains(item.id())); mIgnore.insert(item.id()); ItemFetchJob *job = new ItemFetchJob(item); job->fetchScope().fetchAllAttributes(); job->fetchScope().fetchFullPayload(); connect(job, &ItemFetchJob::result, this, &OutboxQueue::itemFetched); } #include "moc_outboxqueue.cpp" diff --git a/agents/maildispatcher/sendjob.cpp b/agents/maildispatcher/sendjob.cpp index 1e6f77147..449d060e0 100644 --- a/agents/maildispatcher/sendjob.cpp +++ b/agents/maildispatcher/sendjob.cpp @@ -1,445 +1,445 @@ /* Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sendjob.h" #include "storeresultjob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "maildispatcher_debug.h" #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include #include using namespace Akonadi; using namespace KMime; using namespace MailTransport; void SendJob::doTransport() { qCDebug(MAILDISPATCHER_LOG) << "Transporting message."; if (mAborting) { qCDebug(MAILDISPATCHER_LOG) << "Marking message as aborted."; setError(UserDefinedError); setErrorText(i18n("Message sending aborted.")); storeResult(false, i18n("Message sending aborted.")); return; } // Is it an Akonadi transport or a traditional one? const TransportAttribute *transportAttribute = mItem.attribute(); Q_ASSERT(transportAttribute); if (!transportAttribute->transport()) { storeResult(false, i18n("Could not initiate message transport. Possibly invalid transport.")); return; } const TransportType type = transportAttribute->transport()->transportType(); if (!type.isValid()) { storeResult(false, i18n("Could not send message. Invalid transport.")); return; } if (!filterItem(8)) { //BeforeOutbound return; } if (type.isAkonadiResource()) { // Send the item directly to the resource that will send it. mResourceId = transportAttribute->transport()->host(); doAkonadiTransport(); } else { // Use a traditional transport job. doTraditionalTransport(); } } void SendJob::doAkonadiTransport() { Q_ASSERT(!mResourceId.isEmpty()); Q_ASSERT(mInterface == nullptr); const auto service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Resource, mResourceId); mInterface = new QDBusInterface(service, QStringLiteral("/Transport"), QStringLiteral("org.freedesktop.Akonadi.Resource.Transport"), QDBusConnection::sessionBus(), this); if (!mInterface->isValid()) { storeResult(false, i18n("Failed to get D-Bus interface of resource %1.", mResourceId)); delete mInterface; mInterface = nullptr; return; } // Signals. QObject::connect(AgentManager::self(), &AgentManager::instanceProgressChanged, this, &SendJob::resourceProgress); QObject::connect(mInterface, SIGNAL(transportResult(qlonglong,int,QString)), this, SLOT(resourceResult(qlonglong,int,QString))); // Start sending. const QDBusReply reply = mInterface->call(QStringLiteral("send"), mItem.id()); if (!reply.isValid()) { storeResult(false, i18n("Invalid D-Bus reply from resource %1.", mResourceId)); return; } } void SendJob::doTraditionalTransport() { const TransportAttribute *transportAttribute = mItem.attribute(); TransportJob *job = TransportManager::self()->createTransportJob(transportAttribute->transportId()); Q_ASSERT(job); Q_ASSERT(mCurrentJob == nullptr); mCurrentJob = job; // Message. Q_ASSERT(mItem.hasPayload()); const Message::Ptr message = mItem.payload(); bool needAssemble = false; if (message->removeHeader("Bcc")) { needAssemble = true; } if (message->removeHeader("X-KMail-Identity")) { needAssemble = true; } if (message->removeHeader("X-KMail-Dictionary")) { needAssemble = true; } if (message->removeHeader("X-KMail-Transport")) { needAssemble = true; } if (message->removeHeader("X-KMail-Fcc")) { needAssemble = true; } if (message->removeHeader("X-KMail-Identity-Name")) { needAssemble = true; } if (message->removeHeader("X-KMail-Transport-Name")) { needAssemble = true; } if (needAssemble) { message->assemble(); } const QByteArray content = message->encodedContent(true) + "\r\n"; Q_ASSERT(!content.isEmpty()); // Addresses. const AddressAttribute *addressAttribute = mItem.attribute(); Q_ASSERT(addressAttribute); job->setData(content); job->setSender(addressAttribute->from()); job->setTo(addressAttribute->to()); job->setCc(addressAttribute->cc()); job->setBcc(addressAttribute->bcc()); // Signals. connect(job, &TransportJob::result, this, &SendJob::transportResult); //Wait kf6 We have a private signal //connect(job, thisOverload::of(&TransportJob::percent), this, [this](KJob *job,ulong val) {transportPercent(job, val); }); connect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(transportPercent(KJob*,ulong))); job->start(); } void SendJob::transportPercent(KJob *job, unsigned long) { Q_ASSERT(mCurrentJob == job); qCDebug(MAILDISPATCHER_LOG) << "Processed amount" << job->processedAmount(KJob::Bytes) << "total amount" << job->totalAmount(KJob::Bytes); setTotalAmount(KJob::Bytes, job->totalAmount(KJob::Bytes)); // Is not set at the time of start(). setProcessedAmount(KJob::Bytes, job->processedAmount(KJob::Bytes)); } void SendJob::transportResult(KJob *job) { Q_ASSERT(mCurrentJob == job); mCurrentJob = nullptr; doPostJob(!job->error(), job->errorString()); } void SendJob::resourceProgress(const AgentInstance &instance) { if (!mInterface) { // We might have gotten a very late signal. qCWarning(MAILDISPATCHER_LOG) << "called but no resource job running!"; return; } if (instance.identifier() == mResourceId) { // This relies on the resource's progress representing the progress of // sending this item. setPercent(instance.progress()); } } void SendJob::resourceResult(qlonglong itemId, int result, const QString &message) { Q_UNUSED(itemId); Q_ASSERT(mInterface); delete mInterface; // So that abort() knows the transport job is over. mInterface = nullptr; const TransportResourceBase::TransportResult transportResult = static_cast(result); const bool success = (transportResult == TransportResourceBase::TransportSucceeded); Q_ASSERT(itemId == mItem.id()); doPostJob(success, message); } void SendJob::abortPostJob() { // We were unlucky and LocalFolders is recreating its stuff right now. // We will not wait for it. qCWarning(MAILDISPATCHER_LOG) << "Default sent mail collection unavailable, not moving the mail after sending."; setError(UserDefinedError); setErrorText(i18n("Default sent-mail folder unavailable. Keeping message in outbox.")); storeResult(false, errorString()); } void SendJob::doPostJob(bool transportSuccess, const QString &transportMessage) { qCDebug(MAILDISPATCHER_LOG) << "success" << transportSuccess << "message" << transportMessage; if (!transportSuccess) { qCDebug(MAILDISPATCHER_LOG) << "Error transporting."; setError(UserDefinedError); const QString error = mAborting ? i18n("Message transport aborted.") : i18n("Failed to transport message."); setErrorText(error + QLatin1Char(' ') + transportMessage); storeResult(false, errorString()); } else { qCDebug(MAILDISPATCHER_LOG) << "Success transporting."; // Delete or move to sent-mail. const SentBehaviourAttribute *attribute = mItem.attribute(); Q_ASSERT(attribute); if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { qCDebug(MAILDISPATCHER_LOG) << "Deleting item from outbox."; mCurrentJob = new ItemDeleteJob(mItem); QObject::connect(mCurrentJob, &ItemDeleteJob::result, this, &SendJob::postJobResult); } else { if (attribute->sentBehaviour() == SentBehaviourAttribute::MoveToDefaultSentCollection) { if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { mCurrentJob = new ItemMoveJob(mItem, SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail), this); QObject::connect(mCurrentJob, &ItemMoveJob::result, this, &SendJob::postJobResult); } else { abortPostJob(); } } else { qCDebug(MAILDISPATCHER_LOG) << "sentBehaviour=" << attribute->sentBehaviour() << "using collection from attribute"; mCurrentJob = new CollectionFetchJob(attribute->moveToCollection(), Akonadi::CollectionFetchJob::Base); QObject::connect(mCurrentJob, &CollectionFetchJob::result, this, &SendJob::slotSentMailCollectionFetched); } } } } bool SendJob::filterItem(int filterset) { const auto service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent")); QDBusInterface iface(service, QStringLiteral("/MailFilterAgent"), QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), QDBusConnection::sessionBus(), this); if (!iface.isValid()) { storeResult(false, i18n("Failed to get D-Bus interface of mailfilteragent.")); return false; } //Outbound = 0x2 const QDBusReply reply = iface.call(QStringLiteral("filterItem"), mItem.id(), filterset, QString()); if (!reply.isValid()) { storeResult(false, i18n("Invalid D-Bus reply from mailfilteragent")); return false; } return true; } void SendJob::slotSentMailCollectionFetched(KJob *job) { Akonadi::Collection fetchCol; bool ok = false; if (!job->error()) { const CollectionFetchJob *const fetchJob = qobject_cast(job); if (!fetchJob->collections().isEmpty()) { fetchCol = fetchJob->collections().at(0); ok = true; } } if (!ok) { if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { abortPostJob(); return; } fetchCol = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail); } mCurrentJob = new ItemMoveJob(mItem, fetchCol, this); QObject::connect(mCurrentJob, &ItemMoveJob::result, this, &SendJob::postJobResult); } void SendJob::postJobResult(KJob *job) { Q_ASSERT(mCurrentJob == job); mCurrentJob = nullptr; const SentBehaviourAttribute *attribute = mItem.attribute(); Q_ASSERT(attribute); if (job->error()) { qCDebug(MAILDISPATCHER_LOG) << "Error deleting or moving to sent-mail."; QString errorStr; switch (attribute->sentBehaviour()) { case SentBehaviourAttribute::Delete: errorStr = i18n("Sending succeeded, but failed to remove the message from the outbox."); break; default: errorStr = i18n("Sending succeeded, but failed to move the message to the sent-mail folder."); break; } setError(UserDefinedError); setErrorText(errorStr + QLatin1Char(' ') + job->errorString()); storeResult(false, errorString()); } else { qCDebug(MAILDISPATCHER_LOG) << "Success deleting or moving to sent-mail."; if (!filterItem(2)) { //Outbound return; } if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { emitResult(); } else { storeResult(true); } } } void SendJob::storeResult(bool success, const QString &message) { qCDebug(MAILDISPATCHER_LOG) << "success" << success << "message" << message; Q_ASSERT(mCurrentJob == nullptr); mCurrentJob = new StoreResultJob(mItem, success, message); connect(mCurrentJob, &StoreResultJob::result, this, &SendJob::doEmitResult); } void SendJob::doEmitResult(KJob *job) { Q_ASSERT(mCurrentJob == job); mCurrentJob = nullptr; if (job->error()) { qCWarning(MAILDISPATCHER_LOG) << "Error storing result."; setError(UserDefinedError); setErrorText(errorString() + QLatin1Char(' ') + i18n("Failed to store result in item.") + QLatin1Char(' ') + job->errorString()); } else { qCDebug(MAILDISPATCHER_LOG) << "Success storing result."; // It is still possible that the transport failed. StoreResultJob *srJob = static_cast(job); if (!srJob->success()) { setError(UserDefinedError); setErrorText(srJob->message()); } } emitResult(); } SendJob::SendJob(const Item &item, QObject *parent) : KJob(parent) , mItem(item) { } SendJob::~SendJob() { } void SendJob::start() { QTimer::singleShot(0, this, &SendJob::doTransport); } void SendJob::setMarkAborted() { Q_ASSERT(!mAborting); mAborting = true; } void SendJob::abort() { setMarkAborted(); if (dynamic_cast(mCurrentJob)) { qCDebug(MAILDISPATCHER_LOG) << "Abort called, active transport job."; // Abort transport. mCurrentJob->kill(KJob::EmitResult); } else if (mInterface != nullptr) { qCDebug(MAILDISPATCHER_LOG) << "Abort called, propagating to resource."; // Abort resource doing transport. AgentInstance instance = AgentManager::self()->instance(mResourceId); instance.abortCurrentTask(); } else { qCDebug(MAILDISPATCHER_LOG) << "Abort called, but no transport job is active."; // Either transport has not started, in which case doTransport will // mark the item as aborted, or the item has already been sent, in which // case there is nothing we can do. } } #include "moc_sendjob.cpp" diff --git a/agents/maildispatcher/sentactionhandler.h b/agents/maildispatcher/sentactionhandler.h index fe6371eca..7d7039454 100644 --- a/agents/maildispatcher/sentactionhandler.h +++ b/agents/maildispatcher/sentactionhandler.h @@ -1,44 +1,44 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SENTACTIONHANDLER_H #define SENTACTIONHANDLER_H -#include +#include #include class KJob; class SentActionHandler : public QObject { Q_OBJECT public: explicit SentActionHandler(QObject *parent = nullptr); void runAction(const MailTransport::SentActionAttribute::Action &action); private Q_SLOTS: void itemFetchResult(KJob *job); }; #endif diff --git a/agents/maildispatcher/storeresultjob.cpp b/agents/maildispatcher/storeresultjob.cpp index 478b79a25..d00110c5f 100644 --- a/agents/maildispatcher/storeresultjob.cpp +++ b/agents/maildispatcher/storeresultjob.cpp @@ -1,116 +1,116 @@ /* Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "storeresultjob.h" #include #include #include #include #include "maildispatcher_debug.h" #include -#include -#include +#include +#include using namespace Akonadi; using namespace MailTransport; StoreResultJob::StoreResultJob(const Item &item, bool success, const QString &message, QObject *parent) : TransactionSequence(parent) { mItem = item; mSuccess = success; mMessage = message; } StoreResultJob::~StoreResultJob() { } void StoreResultJob::doStart() { // Fetch item in case it was modified elsewhere. ItemFetchJob *job = new ItemFetchJob(mItem, this); connect(job, &ItemFetchJob::result, this, &StoreResultJob::fetchDone); } bool StoreResultJob::success() const { return mSuccess; } QString StoreResultJob::message() const { return mMessage; } void StoreResultJob::fetchDone(KJob *job) { if (job->error()) { return; } qCDebug(MAILDISPATCHER_LOG); const ItemFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); if (fetchJob->items().count() != 1) { qCCritical(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << "items, expected 1."; setError(Unknown); setErrorText(i18n("Failed to fetch item.")); commit(); return; } // Store result in item. Item item = fetchJob->items().at(0); if (mSuccess) { item.clearFlag(Akonadi::MessageFlags::Queued); item.setFlag(Akonadi::MessageFlags::Sent); item.setFlag(Akonadi::MessageFlags::Seen); item.removeAttribute(); } else { item.setFlag(Akonadi::MessageFlags::HasError); ErrorAttribute *errorAttribute = new ErrorAttribute(mMessage); item.addAttribute(errorAttribute); // If dispatch failed, set the DispatchModeAttribute to Manual. // Otherwise, the user will *never* be able to try sending the mail again, // as Send Queued Messages will ignore it. if (item.hasAttribute()) { item.attribute()->setDispatchMode(MailTransport::DispatchModeAttribute::Manual); } else { item.addAttribute(new DispatchModeAttribute(MailTransport::DispatchModeAttribute::Manual)); } } ItemModifyJob *modifyJob = new ItemModifyJob(item, this); QObject::connect(modifyJob, &ItemModifyJob::result, this, &StoreResultJob::modifyDone); } void StoreResultJob::modifyDone(KJob *job) { if (job->error()) { return; } qCDebug(MAILDISPATCHER_LOG); commit(); } diff --git a/resources/imap/settings.h b/resources/imap/settings.h index ee89249f7..f4d9598e1 100644 --- a/resources/imap/settings.h +++ b/resources/imap/settings.h @@ -1,74 +1,74 @@ /* Copyright (c) 2008 Volker Krause Copyright (c) 2008 Omat Holding B.V. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SETTINGS_H #define SETTINGS_H #include "settingsbase.h" #include -#include +#include class ImapAccount; class KJob; class Settings : public SettingsBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.Imap.Wallet") public: static KIMAP::LoginJob::AuthenticationMode mapTransportAuthToKimap(MailTransport::Transport::EnumAuthenticationType::type authType); explicit Settings(WId = 0); void setWinId(WId); virtual void requestPassword(); virtual void loadAccount(ImapAccount *account) const; QString rootRemoteId() const; virtual void renameRootCollection(const QString &newName); virtual void clearCachedPassword(); virtual void cleanup(); virtual QString password(bool *userRejected = nullptr) const; virtual QString sieveCustomPassword(bool *userRejected = nullptr) const; Q_SIGNALS: void passwordRequestCompleted(const QString &password, bool userRejected); public Q_SLOTS: Q_SCRIPTABLE virtual void setPassword(const QString &password); Q_SCRIPTABLE virtual void setSieveCustomPassword(const QString &password); protected Q_SLOTS: virtual void onWalletOpened(bool success); void onRootCollectionFetched(KJob *job); protected: WId m_winId; mutable QString m_password; mutable QString m_customSievePassword; }; #endif diff --git a/resources/imap/setupserver.cpp b/resources/imap/setupserver.cpp index 86fbd16af..fca3fb40e 100644 --- a/resources/imap/setupserver.cpp +++ b/resources/imap/setupserver.cpp @@ -1,719 +1,719 @@ /* This file is part of the KDE project Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens Copyright (C) 2010 Casey Link Copyright (c) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company Copyright (C) 2009 Kevin Ottens Copyright (C) 2006-2008 Omat Holding B.V. Copyright (C) 2006 Frode M. Døving Original copied from showfoto: Copyright 2005 by Gilles Caulier 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 "setupserver.h" #include "settings.h" #include "imapresource.h" #include "serverinfodialog.h" #include "folderarchivesettingpage.h" #include "utils.h" #include -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "imapresource_debug.h" #include #include #include #include #include "imapaccount.h" #include "subscriptiondialog.h" #include "ui_setupserverview_desktop.h" /** static helper functions **/ static QString authenticationModeString(MailTransport::Transport::EnumAuthenticationType::type mode) { switch (mode) { case MailTransport::Transport::EnumAuthenticationType::LOGIN: return QStringLiteral("LOGIN"); case MailTransport::Transport::EnumAuthenticationType::PLAIN: return QStringLiteral("PLAIN"); case MailTransport::Transport::EnumAuthenticationType::CRAM_MD5: return QStringLiteral("CRAM-MD5"); case MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5: return QStringLiteral("DIGEST-MD5"); case MailTransport::Transport::EnumAuthenticationType::GSSAPI: return QStringLiteral("GSSAPI"); case MailTransport::Transport::EnumAuthenticationType::NTLM: return QStringLiteral("NTLM"); case MailTransport::Transport::EnumAuthenticationType::CLEAR: return i18nc("Authentication method", "Clear text"); case MailTransport::Transport::EnumAuthenticationType::ANONYMOUS: return i18nc("Authentication method", "Anonymous"); case MailTransport::Transport::EnumAuthenticationType::XOAUTH2: return i18nc("Authentication method", "Gmail"); default: break; } return QString(); } static void setCurrentAuthMode(QComboBox *authCombo, MailTransport::Transport::EnumAuthenticationType::type authtype) { qCDebug(IMAPRESOURCE_LOG) << "setting authcombo: " << authenticationModeString(authtype); int index = authCombo->findData(authtype); if (index == -1) { qCWarning(IMAPRESOURCE_LOG) << "desired authmode not in the combo"; } qCDebug(IMAPRESOURCE_LOG) << "found corresponding index: " << index << "with data" << authenticationModeString((MailTransport::Transport::EnumAuthenticationType::type)authCombo->itemData( index).toInt()); authCombo->setCurrentIndex(index); MailTransport::Transport::EnumAuthenticationType::type t = (MailTransport::Transport::EnumAuthenticationType::type)authCombo->itemData(authCombo->currentIndex()).toInt(); qCDebug(IMAPRESOURCE_LOG) << "selected auth mode:" << authenticationModeString(t); Q_ASSERT(t == authtype); } static MailTransport::Transport::EnumAuthenticationType::type getCurrentAuthMode(QComboBox *authCombo) { MailTransport::Transport::EnumAuthenticationType::type authtype = (MailTransport::Transport::EnumAuthenticationType::type)authCombo->itemData(authCombo->currentIndex()).toInt(); qCDebug(IMAPRESOURCE_LOG) << "current auth mode: " << authenticationModeString(authtype); return authtype; } static void addAuthenticationItem(QComboBox *authCombo, MailTransport::Transport::EnumAuthenticationType::type authtype) { qCDebug(IMAPRESOURCE_LOG) << "adding auth item " << authenticationModeString(authtype); authCombo->addItem(authenticationModeString(authtype), QVariant(authtype)); } SetupServer::SetupServer(ImapResourceBase *parentResource, WId parent) : QDialog() , m_parentResource(parentResource) , m_ui(new Ui::SetupServerView) , mValidator(this) { QNetworkConfigurationManager *networkConfigMgr = new QNetworkConfigurationManager(QCoreApplication::instance()); m_parentResource->settings()->setWinId(parent); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(mainWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); mOkButton = buttonBox->button(QDialogButtonBox::Ok); mOkButton->setDefault(true); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(mOkButton, &QPushButton::clicked, this, &SetupServer::applySettings); connect(buttonBox, &QDialogButtonBox::rejected, this, &SetupServer::reject); mainLayout->addWidget(buttonBox); m_ui->setupUi(mainWidget); m_folderArchiveSettingPage = new FolderArchiveSettingPage(m_parentResource->identifier()); m_ui->tabWidget->addTab(m_folderArchiveSettingPage, i18n("Archive Folder")); m_ui->safeImapGroup->setId(m_ui->noRadio, KIMAP::LoginJob::Unencrypted); m_ui->safeImapGroup->setId(m_ui->sslRadio, KIMAP::LoginJob::SSLorTLS); m_ui->safeImapGroup->setId(m_ui->tlsRadio, KIMAP::LoginJob::STARTTLS); connect(m_ui->noRadio, &QRadioButton::toggled, this, &SetupServer::slotSafetyChanged); connect(m_ui->sslRadio, &QRadioButton::toggled, this, &SetupServer::slotSafetyChanged); connect(m_ui->tlsRadio, &QRadioButton::toggled, this, &SetupServer::slotSafetyChanged); m_ui->testInfo->hide(); m_ui->testProgress->hide(); m_ui->accountName->setFocus(); m_ui->checkInterval->setSuffix(ki18np(" minute", " minutes")); m_ui->checkInterval->setMinimum(Akonadi::ResourceSettings::self()->minimumCheckInterval()); m_ui->checkInterval->setMaximum(10000); m_ui->checkInterval->setSingleStep(1); // regex for evaluating a valid server name/ip mValidator.setRegExp(QRegExp(QLatin1String("[A-Za-z0-9-_:.]*"))); m_ui->imapServer->setValidator(&mValidator); m_ui->folderRequester->setMimeTypeFilter( QStringList() << KMime::Message::mimeType()); m_ui->folderRequester->setAccessRightsFilter(Akonadi::Collection::CanChangeItem | Akonadi::Collection::CanCreateItem | Akonadi::Collection::CanDeleteItem); m_ui->folderRequester->changeCollectionDialogOptions(Akonadi::CollectionDialog::AllowToCreateNewChildCollection); m_identityCombobox = new KIdentityManagement::IdentityCombo(KIdentityManagement::IdentityManager::self(), this); m_ui->identityLabel->setBuddy(m_identityCombobox); m_ui->identityLayout->addWidget(m_identityCombobox, 1); m_ui->identityLabel->setBuddy(m_identityCombobox); connect(m_ui->testButton, &QPushButton::pressed, this, &SetupServer::slotTest); connect(m_ui->imapServer, &KLineEdit::textChanged, this, &SetupServer::slotServerChanged); connect(m_ui->imapServer, &KLineEdit::textChanged, this, &SetupServer::slotTestChanged); connect(m_ui->imapServer, &KLineEdit::textChanged, this, &SetupServer::slotComplete); connect(m_ui->userName, &KLineEdit::textChanged, this, &SetupServer::slotComplete); connect(m_ui->subscriptionEnabled, &QCheckBox::toggled, this, &SetupServer::slotSubcriptionCheckboxChanged); connect(m_ui->subscriptionButton, &QPushButton::pressed, this, &SetupServer::slotManageSubscriptions); connect(m_ui->managesieveCheck, &QCheckBox::toggled, this, &SetupServer::slotEnableWidgets); connect(m_ui->sameConfigCheck, &QCheckBox::toggled, this, &SetupServer::slotEnableWidgets); connect(m_ui->useDefaultIdentityCheck, &QCheckBox::toggled, this, &SetupServer::slotIdentityCheckboxChanged); connect(m_ui->enableMailCheckBox, &QCheckBox::toggled, this, &SetupServer::slotMailCheckboxChanged); connect(m_ui->safeImapGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &SetupServer::slotEncryptionRadioChanged); connect(m_ui->customSieveGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &SetupServer::slotCustomSieveChanged); connect(m_ui->showServerInfo, &QPushButton::pressed, this, &SetupServer::slotShowServerInfo); readSettings(); slotTestChanged(); slotComplete(); slotCustomSieveChanged(); connect(networkConfigMgr, &QNetworkConfigurationManager::onlineStateChanged, this, &SetupServer::slotTestChanged); } SetupServer::~SetupServer() { delete m_ui; } bool SetupServer::shouldClearCache() const { return m_shouldClearCache; } void SetupServer::slotSubcriptionCheckboxChanged() { m_ui->subscriptionButton->setEnabled(m_ui->subscriptionEnabled->isChecked()); } void SetupServer::slotIdentityCheckboxChanged() { m_identityCombobox->setEnabled(!m_ui->useDefaultIdentityCheck->isChecked()); } void SetupServer::slotMailCheckboxChanged() { m_ui->checkInterval->setEnabled(m_ui->enableMailCheckBox->isChecked()); } void SetupServer::slotEncryptionRadioChanged() { // TODO these really should be defined somewhere else switch (m_ui->safeImapGroup->checkedId()) { case KIMAP::LoginJob::Unencrypted: case KIMAP::LoginJob::STARTTLS: m_ui->portSpin->setValue(143); break; case KIMAP::LoginJob::SSLorTLS: m_ui->portSpin->setValue(993); break; default: qFatal("Shouldn't happen"); } } void SetupServer::slotCustomSieveChanged() { QAbstractButton *checkedButton = m_ui->customSieveGroup->checkedButton(); if (checkedButton == m_ui->imapUserPassword || checkedButton == m_ui->noAuthentification) { m_ui->customUsername->setEnabled(false); m_ui->customPassword->setEnabled(false); } else if (checkedButton == m_ui->customUserPassword) { m_ui->customUsername->setEnabled(true); m_ui->customPassword->setEnabled(true); } } void SetupServer::applySettings() { if (!m_parentResource->settings()->imapServer().isEmpty() && m_ui->imapServer->text() != m_parentResource->settings()->imapServer()) { if (KMessageBox::warningContinueCancel( this, i18n("You have changed the address of the server. Even if this is the same server as before " "we will have to re-download all your emails from this account again. " "Are you sure you want to proceed?"), i18n("Server address change")) == KMessageBox::Cancel) { return; } } if (!m_parentResource->settings()->userName().isEmpty() && m_ui->userName->text() != m_parentResource->settings()->userName()) { if (KMessageBox::warningContinueCancel( this, i18n("You have changed the user name. Even if this is a user name for the same account as before " "we will have to re-download all your emails from this account again. " "Are you sure you want to proceed?"), i18n("User name change")) == KMessageBox::Cancel) { return; } } m_folderArchiveSettingPage->writeSettings(); m_shouldClearCache = (m_parentResource->settings()->imapServer() != m_ui->imapServer->text()) || (m_parentResource->settings()->userName() != m_ui->userName->text()); const MailTransport::Transport::EnumAuthenticationType::type authtype = getCurrentAuthMode(m_ui->authenticationCombo); if (!m_ui->userName->text().contains(QLatin1Char('@')) && authtype == MailTransport::Transport::EnumAuthenticationType::XOAUTH2 && m_ui->imapServer->text().contains(QLatin1String("gmail.com"))) { // Normalize gmail username so that it matches the JSON account info returned by GMail authentication. // If we don't do this, we will look up cached auth without @gmail.com and save it with @gmail.com => very frequent auth dialog popping up. qCDebug(IMAPRESOURCE_LOG) << "Fixing up username" << m_ui->userName->text() << "by adding @gmail.com"; m_ui->userName->setText(m_ui->userName->text() + QLatin1String("@gmail.com")); } m_parentResource->setName(m_ui->accountName->text()); m_parentResource->settings()->setImapServer(m_ui->imapServer->text()); m_parentResource->settings()->setImapPort(m_ui->portSpin->value()); m_parentResource->settings()->setUserName(m_ui->userName->text()); QString encryption; switch (m_ui->safeImapGroup->checkedId()) { case KIMAP::LoginJob::Unencrypted: encryption = QStringLiteral("None"); break; case KIMAP::LoginJob::SSLorTLS: encryption = QStringLiteral("SSL"); break; case KIMAP::LoginJob::STARTTLS: encryption = QStringLiteral("STARTTLS"); break; default: qFatal("Shouldn't happen"); } m_parentResource->settings()->setSafety(encryption); qCDebug(IMAPRESOURCE_LOG) << "saving IMAP auth mode: " << authenticationModeString(authtype); m_parentResource->settings()->setAuthentication(authtype); m_parentResource->settings()->setPassword(m_ui->password->password()); m_parentResource->settings()->setSubscriptionEnabled(m_ui->subscriptionEnabled->isChecked()); m_parentResource->settings()->setIntervalCheckTime(m_ui->checkInterval->value()); m_parentResource->settings()->setDisconnectedModeEnabled(m_ui->disconnectedModeEnabled->isChecked()); m_parentResource->settings()->setUseProxy(m_ui->useProxyCheck->isChecked()); MailTransport::Transport::EnumAuthenticationType::type alternateAuthtype = getCurrentAuthMode(m_ui->authenticationAlternateCombo); qCDebug(IMAPRESOURCE_LOG) << "saving Alternate server sieve auth mode: " << authenticationModeString(alternateAuthtype); m_parentResource->settings()->setAlternateAuthentication(alternateAuthtype); m_parentResource->settings()->setSieveSupport(m_ui->managesieveCheck->isChecked()); m_parentResource->settings()->setSieveReuseConfig(m_ui->sameConfigCheck->isChecked()); m_parentResource->settings()->setSievePort(m_ui->sievePortSpin->value()); m_parentResource->settings()->setSieveAlternateUrl(m_ui->alternateURL->text()); m_parentResource->settings()->setSieveVacationFilename(m_vacationFileName); m_parentResource->settings()->setTrashCollection(m_ui->folderRequester->collection().id()); Akonadi::Collection trash = m_ui->folderRequester->collection(); Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::Trash, trash); Akonadi::EntityDisplayAttribute *attribute = trash.attribute(Akonadi::Collection::AddIfMissing); attribute->setIconName(QStringLiteral("user-trash")); new Akonadi::CollectionModifyJob(trash); if (mOldTrash != trash) { Akonadi::SpecialMailCollections::self()->unregisterCollection(mOldTrash); } m_parentResource->settings()->setAutomaticExpungeEnabled(m_ui->autoExpungeCheck->isChecked()); m_parentResource->settings()->setUseDefaultIdentity(m_ui->useDefaultIdentityCheck->isChecked()); if (!m_ui->useDefaultIdentityCheck->isChecked()) { m_parentResource->settings()->setAccountIdentity(m_identityCombobox->currentIdentity()); } m_parentResource->settings()->setIntervalCheckEnabled(m_ui->enableMailCheckBox->isChecked()); if (m_ui->enableMailCheckBox->isChecked()) { m_parentResource->settings()->setIntervalCheckTime(m_ui->checkInterval->value()); } m_parentResource->settings()->setSieveCustomUsername(m_ui->customUsername->text()); QAbstractButton *checkedButton = m_ui->customSieveGroup->checkedButton(); if (checkedButton == m_ui->imapUserPassword) { m_parentResource->settings()->setSieveCustomAuthentification(QStringLiteral("ImapUserPassword")); } else if (checkedButton == m_ui->noAuthentification) { m_parentResource->settings()->setSieveCustomAuthentification(QStringLiteral("NoAuthentification")); } else if (checkedButton == m_ui->customUserPassword) { m_parentResource->settings()->setSieveCustomAuthentification(QStringLiteral("CustomUserPassword")); } m_parentResource->settings()->setSieveCustomPassword(m_ui->customPassword->password()); m_parentResource->settings()->save(); qCDebug(IMAPRESOURCE_LOG) << "wrote" << m_ui->imapServer->text() << m_ui->userName->text() << m_ui->safeImapGroup->checkedId(); if (m_oldResourceName != m_ui->accountName->text() && !m_ui->accountName->text().isEmpty()) { m_parentResource->settings()->renameRootCollection(m_ui->accountName->text()); } accept(); } void SetupServer::readSettings() { m_folderArchiveSettingPage->loadSettings(); m_ui->accountName->setText(m_parentResource->name()); m_oldResourceName = m_ui->accountName->text(); KUser *currentUser = new KUser(); KEMailSettings esetting; m_ui->imapServer->setText( !m_parentResource->settings()->imapServer().isEmpty() ? m_parentResource->settings()->imapServer() : esetting.getSetting(KEMailSettings::InServer)); m_ui->portSpin->setValue(m_parentResource->settings()->imapPort()); m_ui->userName->setText( !m_parentResource->settings()->userName().isEmpty() ? m_parentResource->settings()->userName() : currentUser->loginName()); const QString safety = m_parentResource->settings()->safety(); int i = 0; if (safety == QLatin1String("SSL")) { i = KIMAP::LoginJob::SSLorTLS; } else if (safety == QLatin1String("STARTTLS")) { i = KIMAP::LoginJob::STARTTLS; } else { i = KIMAP::LoginJob::Unencrypted; } QAbstractButton *safetyButton = m_ui->safeImapGroup->button(i); if (safetyButton) { safetyButton->setChecked(true); } populateDefaultAuthenticationOptions(); i = m_parentResource->settings()->authentication(); qCDebug(IMAPRESOURCE_LOG) << "read IMAP auth mode: " << authenticationModeString((MailTransport::Transport::EnumAuthenticationType::type)i); setCurrentAuthMode(m_ui->authenticationCombo, (MailTransport::Transport::EnumAuthenticationType::type)i); i = m_parentResource->settings()->alternateAuthentication(); setCurrentAuthMode(m_ui->authenticationAlternateCombo, (MailTransport::Transport::EnumAuthenticationType::type)i); bool rejected = false; const QString password = m_parentResource->settings()->password(&rejected); if (rejected) { m_ui->password->setEnabled(false); KMessageBox::information(nullptr, i18n("Could not access KWallet. " "If you want to store the password permanently then you have to " "activate it. If you do not " "want to use KWallet, check the box below, but note that you will be " "prompted for your password when needed."), i18n("Do not use KWallet"), QStringLiteral("warning_kwallet_disabled")); } else { m_ui->password->lineEdit()->insert(password); } m_ui->subscriptionEnabled->setChecked(m_parentResource->settings()->subscriptionEnabled()); m_ui->checkInterval->setValue(m_parentResource->settings()->intervalCheckTime()); m_ui->disconnectedModeEnabled->setChecked(m_parentResource->settings()->disconnectedModeEnabled()); m_ui->useProxyCheck->setChecked(m_parentResource->settings()->useProxy()); m_ui->managesieveCheck->setChecked(m_parentResource->settings()->sieveSupport()); m_ui->sameConfigCheck->setChecked(m_parentResource->settings()->sieveReuseConfig()); m_ui->sievePortSpin->setValue(m_parentResource->settings()->sievePort()); m_ui->alternateURL->setText(m_parentResource->settings()->sieveAlternateUrl()); m_vacationFileName = m_parentResource->settings()->sieveVacationFilename(); Akonadi::Collection trashCollection(m_parentResource->settings()->trashCollection()); if (trashCollection.isValid()) { Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(trashCollection, Akonadi::CollectionFetchJob::Base, this); connect(fetchJob, &Akonadi::CollectionFetchJob::collectionsReceived, this, &SetupServer::targetCollectionReceived); } else { Akonadi::SpecialMailCollectionsRequestJob *requestJob = new Akonadi::SpecialMailCollectionsRequestJob(this); connect(requestJob, &Akonadi::SpecialMailCollectionsRequestJob::result, this, &SetupServer::localFolderRequestJobFinished); requestJob->requestDefaultCollection(Akonadi::SpecialMailCollections::Trash); requestJob->start(); } m_ui->useDefaultIdentityCheck->setChecked(m_parentResource->settings()->useDefaultIdentity()); if (!m_ui->useDefaultIdentityCheck->isChecked()) { m_identityCombobox->setCurrentIdentity(m_parentResource->settings()->accountIdentity()); } m_ui->enableMailCheckBox->setChecked(m_parentResource->settings()->intervalCheckEnabled()); if (m_ui->enableMailCheckBox->isChecked()) { m_ui->checkInterval->setValue(m_parentResource->settings()->intervalCheckTime()); } else { m_ui->checkInterval->setEnabled(false); } m_ui->autoExpungeCheck->setChecked(m_parentResource->settings()->automaticExpungeEnabled()); if (m_vacationFileName.isEmpty()) { m_vacationFileName = QStringLiteral("kmail-vacation.siv"); } m_ui->customUsername->setText(m_parentResource->settings()->sieveCustomUsername()); rejected = false; const QString customPassword = m_parentResource->settings()->sieveCustomPassword(&rejected); if (rejected) { m_ui->customPassword->setEnabled(false); KMessageBox::information(nullptr, i18n("Could not access KWallet. " "If you want to store the password permanently then you have to " "activate it. If you do not " "want to use KWallet, check the box below, but note that you will be " "prompted for your password when needed."), i18n("Do not use KWallet"), QStringLiteral("warning_kwallet_disabled")); } else { m_ui->customPassword->lineEdit()->insert(customPassword); } const QString sieverCustomAuth(m_parentResource->settings()->sieveCustomAuthentification()); if (sieverCustomAuth == QLatin1String("ImapUserPassword")) { m_ui->imapUserPassword->setChecked(true); } else if (sieverCustomAuth == QLatin1String("NoAuthentification")) { m_ui->noAuthentification->setChecked(true); } else if (sieverCustomAuth == QLatin1String("CustomUserPassword")) { m_ui->customUserPassword->setChecked(true); } delete currentUser; } void SetupServer::slotTest() { qCDebug(IMAPRESOURCE_LOG) << m_ui->imapServer->text(); m_ui->testButton->setEnabled(false); m_ui->safeImap->setEnabled(false); m_ui->authenticationCombo->setEnabled(false); m_ui->testInfo->clear(); m_ui->testInfo->hide(); delete m_serverTest; m_serverTest = new MailTransport::ServerTest(this); #ifndef QT_NO_CURSOR qApp->setOverrideCursor(Qt::BusyCursor); #endif const QString server = m_ui->imapServer->text(); const int port = m_ui->portSpin->value(); qCDebug(IMAPRESOURCE_LOG) << "server: " << server << "port: " << port; m_serverTest->setServer(server); if (port != 143 && port != 993) { m_serverTest->setPort(MailTransport::Transport::EnumEncryption::None, port); m_serverTest->setPort(MailTransport::Transport::EnumEncryption::SSL, port); } m_serverTest->setProtocol(QStringLiteral("imap")); m_serverTest->setProgressBar(m_ui->testProgress); connect(m_serverTest, &MailTransport::ServerTest::finished, this, &SetupServer::slotFinished); mOkButton->setEnabled(false); m_serverTest->start(); } void SetupServer::slotFinished(const QVector &testResult) { qCDebug(IMAPRESOURCE_LOG) << testResult; #ifndef QT_NO_CURSOR qApp->restoreOverrideCursor(); #endif mOkButton->setEnabled(true); using namespace MailTransport; if (!m_serverTest->isNormalPossible() && !m_serverTest->isSecurePossible()) { KMessageBox::sorry(this, i18n("Unable to connect to the server, please verify the server address.")); } m_ui->testInfo->show(); m_ui->sslRadio->setEnabled(testResult.contains(Transport::EnumEncryption::SSL)); m_ui->tlsRadio->setEnabled(testResult.contains(Transport::EnumEncryption::TLS)); m_ui->noRadio->setEnabled(testResult.contains(Transport::EnumEncryption::None)); QString text; if (testResult.contains(Transport::EnumEncryption::TLS)) { m_ui->tlsRadio->setChecked(true); text = i18n("STARTTLS is supported and recommended."); } else if (testResult.contains(Transport::EnumEncryption::SSL)) { m_ui->sslRadio->setChecked(true); text = i18n("SSL/TLS is supported and recommended."); } else if (testResult.contains(Transport::EnumEncryption::None)) { m_ui->noRadio->setChecked(true); text = i18n("No security is supported. It is not " "recommended to connect to this server."); } else { text = i18n("It is not possible to use this server."); } m_ui->testInfo->setText(text); m_ui->testButton->setEnabled(true); m_ui->safeImap->setEnabled(true); m_ui->authenticationCombo->setEnabled(true); slotEncryptionRadioChanged(); slotSafetyChanged(); } void SetupServer::slotTestChanged() { delete m_serverTest; m_serverTest = nullptr; slotSafetyChanged(); // do not use imapConnectionPossible, as the data is not yet saved. m_ui->testButton->setEnabled(true /* TODO && Global::connectionPossible() || m_ui->imapServer->text() == "localhost"*/); } void SetupServer::slotEnableWidgets() { const bool haveSieve = m_ui->managesieveCheck->isChecked(); const bool reuseConfig = m_ui->sameConfigCheck->isChecked(); m_ui->sameConfigCheck->setEnabled(haveSieve); m_ui->sievePortSpin->setEnabled(haveSieve); m_ui->alternateURL->setEnabled(haveSieve && !reuseConfig); m_ui->authentication->setEnabled(haveSieve && !reuseConfig); } void SetupServer::slotComplete() { const bool ok = !m_ui->imapServer->text().isEmpty() && !m_ui->userName->text().isEmpty(); mOkButton->setEnabled(ok); } void SetupServer::slotSafetyChanged() { if (m_serverTest == nullptr) { return; } QVector protocols; switch (m_ui->safeImapGroup->checkedId()) { case KIMAP::LoginJob::Unencrypted: qCDebug(IMAPRESOURCE_LOG) << "safeImapGroup: unencrypted"; protocols = m_serverTest->normalProtocols(); break; case KIMAP::LoginJob::SSLorTLS: protocols = m_serverTest->secureProtocols(); qCDebug(IMAPRESOURCE_LOG) << "safeImapGroup: SSL"; break; case KIMAP::LoginJob::STARTTLS: protocols = m_serverTest->tlsProtocols(); qCDebug(IMAPRESOURCE_LOG) << "safeImapGroup: starttls"; break; default: qFatal("Shouldn't happen"); } m_ui->authenticationCombo->clear(); addAuthenticationItem(m_ui->authenticationCombo, MailTransport::Transport::EnumAuthenticationType::CLEAR); for (int prot : qAsConst(protocols)) { addAuthenticationItem(m_ui->authenticationCombo, (MailTransport::Transport::EnumAuthenticationType::type)prot); } if (protocols.isEmpty()) { qCDebug(IMAPRESOURCE_LOG) << "no authmodes found"; } else { setCurrentAuthMode(m_ui->authenticationCombo, (MailTransport::Transport::EnumAuthenticationType::type)protocols.first()); } } void SetupServer::slotManageSubscriptions() { qCDebug(IMAPRESOURCE_LOG) << "manage subscripts"; ImapAccount account; account.setServer(m_ui->imapServer->text()); account.setPort(m_ui->portSpin->value()); account.setUserName(m_ui->userName->text()); account.setSubscriptionEnabled(m_ui->subscriptionEnabled->isChecked()); account.setEncryptionMode( static_cast(m_ui->safeImapGroup->checkedId()) ); account.setAuthenticationMode(Settings::mapTransportAuthToKimap(getCurrentAuthMode(m_ui->authenticationCombo))); QPointer subscriptions = new SubscriptionDialog(this); subscriptions->setWindowTitle(i18nc("@title:window", "Serverside Subscription")); subscriptions->setWindowIcon(QIcon::fromTheme(QStringLiteral("network-server"))); subscriptions->connectAccount(account, m_ui->password->password()); m_subscriptionsChanged = subscriptions->isSubscriptionChanged(); subscriptions->exec(); delete subscriptions; m_ui->subscriptionEnabled->setChecked(account.isSubscriptionEnabled()); } void SetupServer::slotShowServerInfo() { ServerInfoDialog *dialog = new ServerInfoDialog(m_parentResource, this); dialog->show(); } void SetupServer::targetCollectionReceived(const Akonadi::Collection::List &collections) { m_ui->folderRequester->setCollection(collections.first()); mOldTrash = collections.first(); } void SetupServer::localFolderRequestJobFinished(KJob *job) { if (!job->error()) { Akonadi::Collection targetCollection = Akonadi::SpecialMailCollections::self()->defaultCollection(Akonadi::SpecialMailCollections::Trash); Q_ASSERT(targetCollection.isValid()); m_ui->folderRequester->setCollection(targetCollection); mOldTrash = targetCollection; } } void SetupServer::populateDefaultAuthenticationOptions() { populateDefaultAuthenticationOptions(m_ui->authenticationCombo); populateDefaultAuthenticationOptions(m_ui->authenticationAlternateCombo); } void SetupServer::populateDefaultAuthenticationOptions(QComboBox *combo) { combo->clear(); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::CLEAR); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::LOGIN); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::PLAIN); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::CRAM_MD5); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::NTLM); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::GSSAPI); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::ANONYMOUS); addAuthenticationItem(combo, MailTransport::Transport::EnumAuthenticationType::XOAUTH2); } void SetupServer::slotServerChanged() { populateDefaultAuthenticationOptions(m_ui->authenticationCombo); } diff --git a/resources/kolab/autotests/testretrievetagstask.cpp b/resources/kolab/autotests/testretrievetagstask.cpp index bcd43fc27..b7e332007 100644 --- a/resources/kolab/autotests/testretrievetagstask.cpp +++ b/resources/kolab/autotests/testretrievetagstask.cpp @@ -1,185 +1,185 @@ /* 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 "imaptestbase.h" #include "kolabretrievetagstask.h" -#include -#include +#include +#include #include -#include -#include +#include +#include #include #include "kolabhelpers.h" #include typedef QHash Members; Q_DECLARE_METATYPE(TagListAndMembers); Q_DECLARE_METATYPE(Members); class TestRetrieveTagsTask : public ImapTestBase { Q_OBJECT private slots: void initTestCase() { AkonadiTest::checkTestIsIsolated(); } void testRetrieveTags_data() { Akonadi::VirtualResource *resource = new Akonadi::VirtualResource(QLatin1String("akonadi_knut_resource_0"), this); Akonadi::Collection root; root.setName(QLatin1String("akonadi_knut_resource_0")); root.setContentMimeTypes(QStringList() << Akonadi::Collection::mimeType()); root.setParentCollection(Akonadi::Collection::root()); root.setRemoteId("root-id"); root = resource->createRootCollection(root); Akonadi::Collection col; col.setName("Configuration"); col.setContentMimeTypes(QStringList() << KolabHelpers::getMimeType(Kolab::ConfigurationType)); col.setRemoteId("/configuration"); col = resource->createCollection(col); Akonadi::Collection mailcol; mailcol.setName("INBOX"); mailcol.setContentMimeTypes(QStringList() << KMime::Message::mimeType()); mailcol.setRemoteId("/INBOX"); mailcol = resource->createCollection(mailcol); Akonadi::Item item(KMime::Message::mimeType()); item.setRemoteId("20"); item = resource->createItem(item, mailcol); QTest::addColumn< QList >("scenario"); QTest::addColumn("callNames"); QTest::addColumn("expectedTags"); QTest::addColumn("expectedMembers"); QList scenario; QStringList callNames; Akonadi::Tag tag; tag.setName("tagname"); Kolab::KolabObjectWriter writer; QStringList members; members << QLatin1String( "imap:///user/john.doe%40example.org/INBOX/20?message-id=%3Cf06aa3345a25005380b47547ad161d36%40lhm.klab.cc%3E&date=Tue%2C+12+Aug+2014+20%3A42%3A59+%2B0200&subject=Re%3A+test"); KMime::Message::Ptr msg = writer.writeTag(tag, members); // qCDebug(KOLABRESOURCE_LOG) << msg->encodedContent(); const QByteArray &content = msg->encodedContent(true); scenario.clear(); scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"configuration\"" << "S: A000003 OK select done" << "C: A000004 FETCH 1:* (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)" << "S: * 1 FETCH ( FLAGS (\\Seen) UID 7 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" " "RFC822.SIZE 75 BODY[] {" + QByteArray::number(content.size()) + "}\r\n" + content + " )" << "S: A000004 OK fetch done"; callNames.clear(); callNames << "tagsRetrieved"; Akonadi::Tag expectedTag = tag; expectedTag.setRemoteId("7"); QHash expectedMembers; Akonadi::Item member; member.setRemoteId("20"); member.setParentCollection(createCollectionChain("/INBOX")); expectedMembers.insert(expectedTag.remoteId(), (Akonadi::Item::List() << member)); QTest::newRow("list single tag") << scenario << callNames << (Akonadi::Tag::List() << expectedTag) << expectedMembers; } void testRetrieveTags() { QFETCH(QList, scenario); QFETCH(QStringList, callNames); QFETCH(Akonadi::Tag::List, expectedTags); QFETCH(Members, expectedMembers); FakeServer server; server.setScenario(scenario); server.startAndWait(); SessionPool pool(1); pool.setPasswordRequester(createDefaultRequester()); QVERIFY(pool.connect(createDefaultAccount())); QVERIFY(waitForSignal(&pool, SIGNAL(connectDone(int,QString)))); DummyResourceState::Ptr state = DummyResourceState::Ptr(new DummyResourceState); state->setServerCapabilities(QStringList() << "METADATA" << "ACL"); state->setUserName("Hans"); KolabRetrieveTagTask *task = new KolabRetrieveTagTask(state, KolabRetrieveTagTask::RetrieveTags); task->start(&pool); QTRY_COMPARE(state->calls().count(), callNames.size()); for (int i = 0; i < callNames.size(); i++) { QString command = QString::fromUtf8(state->calls().at(i).first); QVariant parameter = state->calls().at(i).second; if (command == "cancelTask" && callNames[i] != "cancelTask") { qCDebug(KOLABRESOURCE_LOG) << "Got a cancel:" << parameter.toString(); } QCOMPARE(command, callNames[i]); if (command == "tagsRetrieved") { QPair > pair = parameter.value(); Akonadi::Tag::List tags = pair.first; QHash members = pair.second; QCOMPARE(tags.size(), expectedTags.size()); for (int i = 0; i < tags.size(); i++) { QCOMPARE(tags[i].name(), expectedTags[i].name()); QCOMPARE(tags[i].remoteId(), expectedTags[i].remoteId()); const Akonadi::Item::List memberlist = members.value(tags[i].remoteId()); const Akonadi::Item::List expectedMemberlist = expectedMembers.value(tags[i].remoteId()); QCOMPARE(memberlist.size(), expectedMemberlist.size()); for (int i = 0; i < expectedMemberlist.size(); i++) { QCOMPARE(memberlist[i].remoteId(), expectedMemberlist[i].remoteId()); Akonadi::Collection parent = memberlist[i].parentCollection(); Akonadi::Collection expectedParent = expectedMemberlist[i].parentCollection(); while (expectedParent.isValid()) { QCOMPARE(parent.remoteId(), expectedParent.remoteId()); expectedParent = expectedParent.parentCollection(); parent = parent.parentCollection(); } } } } } QVERIFY(server.isAllScenarioDone()); server.quit(); } }; QTEST_AKONADIMAIN(TestRetrieveTagsTask, NoGUI) #include "testretrievetagstask.moc" diff --git a/resources/kolab/pimkolab/autotests/formattest.cpp b/resources/kolab/pimkolab/autotests/formattest.cpp index a2a8732fe..1b4e2c7a4 100644 --- a/resources/kolab/pimkolab/autotests/formattest.cpp +++ b/resources/kolab/pimkolab/autotests/formattest.cpp @@ -1,488 +1,488 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "formattest.h" #include #include #include #include #include #include -#include +#include #include #include #include #include "testutils.h" #include "kolabformat/kolabobject.h" #include "kolabformat/errorhandler.h" #include "kolabformat/kolabdefinitions.h" #include "pimkolab_debug.h" static bool compareMimeMessage(const KMime::Message::Ptr &msg, const KMime::Message::Ptr &expectedMsg) { // headers KCOMPARE(msg->subject()->asUnicodeString(), expectedMsg->subject()->asUnicodeString()); if (msg->from()->isEmpty() || expectedMsg->from()->isEmpty()) { KCOMPARE(msg->from()->asUnicodeString(), expectedMsg->from()->asUnicodeString()); } else { KCOMPARE(msg->from()->mailboxes().first().address(), expectedMsg->from()->mailboxes().first().address()); // matching address is enough, we don't need a display name } KCOMPARE(msg->contentType()->mimeType(), expectedMsg->contentType()->mimeType()); KCOMPARE(msg->headerByType(X_KOLAB_TYPE_HEADER)->as7BitString(), expectedMsg->headerByType(X_KOLAB_TYPE_HEADER)->as7BitString()); // date contains conversion time... // KCOMPARE( msg->date()->asUnicodeString(), expectedMsg->date()->asUnicodeString() ); // body parts KCOMPARE(msg->contents().size(), expectedMsg->contents().size()); for (int i = 0; i < msg->contents().size(); ++i) { KMime::Content *part = msg->contents().at(i); KMime::Content *expectedPart = expectedMsg->contents().at(i); // part headers KCOMPARE(part->contentType()->mimeType(), expectedPart->contentType()->mimeType()); KCOMPARE(part->contentDisposition()->filename(), expectedPart->contentDisposition()->filename()); KCOMPARE(part->decodedContent().isEmpty(), false); QString content(QString::fromUtf8(part->decodedContent())); normalizeMimemessage(content); QString expected(QString::fromUtf8(expectedPart->decodedContent())); normalizeMimemessage(expected); // showDiff(expected, content); // part content KCOMPARE(content.simplified(), expected.simplified()); } return true; } void FormatTest::initTestCase() { } void FormatTest::testIncidence_data() { QTest::addColumn("version"); QTest::addColumn("type"); QTest::addColumn("icalFileName"); QTest::addColumn("mimeFileName"); QTest::newRow("v2eventSimple") << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/simple.ics") << getPath("v2/event/simple.ics.mime"); QTest::newRow("v2eventComplex") << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/complex.ics") << getPath("v2/event/complex.ics.mime"); QTest::newRow("v2eventAttachment") << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/attachment.ics") << getPath("v2/event/attachment.ics.mime"); QTest::newRow("v2eventAllday") << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/allday.ics") << getPath("v2/event/allday.ics.mime"); QTest::newRow("v2eventUtf8Attachment") << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/attachmentUtf8.ics") << getPath("v2/event/attachmentUtf8.ics.mime"); //The following test just fails because we have a nicer mime message output than horde // QTest::newRow( "v2eventHorde" ) << Kolab::KolabV2 << Kolab::EventObject << getPath("v2/event/horde.ics") << getPath("v2/event/horde.ics.mime"); QTest::newRow("v2todoSimple") << Kolab::KolabV2 << Kolab::TodoObject << getPath("v2/task/simple.ics") << getPath("v2/task/simple.ics.mime"); QTest::newRow("v2todoComplex") << Kolab::KolabV2 << Kolab::TodoObject << getPath("v2/task/complex.ics") << getPath("v2/task/complex.ics.mime"); QTest::newRow("v2todoPrio1") << Kolab::KolabV2 << Kolab::TodoObject << getPath("v2/task/prioritytest1.ics") << getPath("v2/task/prioritytest1.ics.mime"); QTest::newRow("v2todoPrio2") << Kolab::KolabV2 << Kolab::TodoObject << getPath("v2/task/prioritytest2.ics") << getPath("v2/task/prioritytest2.ics.mime"); QTest::newRow("v2journalSimple") << Kolab::KolabV2 << Kolab::JournalObject << getPath("v2/journal/simple.ics") << getPath("v2/journal/simple.ics.mime"); QTest::newRow("v2journalComplex") << Kolab::KolabV2 << Kolab::JournalObject << getPath("v2/journal/complex.ics") << getPath("v2/journal/complex.ics.mime"); QTest::newRow("v3eventSimple") << Kolab::KolabV3 << Kolab::EventObject << getPath("v3/event/simple.ics") << getPath("v3/event/simple.ics.mime"); QTest::newRow("v3eventComplex") << Kolab::KolabV3 << Kolab::EventObject << getPath("v3/event/complex.ics") << getPath("v3/event/complex.ics.mime"); QTest::newRow("v3todoSimple") << Kolab::KolabV3 << Kolab::TodoObject << getPath("v3/task/simple.ics") << getPath("v3/task/simple.ics.mime"); QTest::newRow("v3todoComplex") << Kolab::KolabV3 << Kolab::TodoObject << getPath("v3/task/complex.ics") << getPath("v3/task/complex.ics.mime"); QTest::newRow("v3journalSimple") << Kolab::KolabV3 << Kolab::JournalObject << getPath("v3/journal/simple.ics") << getPath("v3/journal/simple.ics.mime"); QTest::newRow("v3journalComplex") << Kolab::KolabV3 << Kolab::JournalObject << getPath("v3/journal/complex.ics") << getPath("v3/journal/complex.ics.mime"); QTest::newRow("v3utf8quotedPrintable") << Kolab::KolabV3 << Kolab::EventObject << getPath("v3/event/utf8.ics") << getPath("v3/event/utf8quotedPrintable.ics.mime"); QTest::newRow("v3utf8base64") << Kolab::KolabV3 << Kolab::EventObject << getPath("v3/event/utf8.ics") << getPath("v3/event/utf8base64.ics.mime"); QTest::newRow("v3utf88bit") << Kolab::KolabV3 << Kolab::EventObject << getPath("v3/event/utf8.ics") << getPath("v3/event/utf88bit.ics.mime"); } void FormatTest::testIncidence() { QFETCH(Kolab::Version, version); QFETCH(Kolab::ObjectType, type); QFETCH(QString, icalFileName); //To compare QFETCH(QString, mimeFileName); //For parsing //Parse mime message bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(mimeFileName, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), version); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KCalendarCore::Incidence::Ptr convertedIncidence = reader.getIncidence(); //Parse ICalFile for comparison QFile icalFile(icalFileName); QVERIFY(icalFile.open(QFile::ReadOnly)); KCalendarCore::ICalFormat format; KCalendarCore::Incidence::Ptr realIncidence(format.fromString(QString::fromUtf8(icalFile.readAll()))); // fix up the converted incidence for comparisson normalizeIncidence(convertedIncidence); normalizeIncidence(realIncidence); // recurrence objects are created on demand, but KCalendarCore::Incidence::operator==() doesn't take that into account // so make sure both incidences have one realIncidence->recurrence(); convertedIncidence->recurrence(); realIncidence->setLastModified(convertedIncidence->lastModified()); //The following test is just for debugging and not really relevant if (*(realIncidence.data()) != *(convertedIncidence.data())) { showDiff(format.toString(realIncidence), format.toString(convertedIncidence)); } QVERIFY(*(realIncidence.data()) == *(convertedIncidence.data())); //Write Kolab::overrideTimestamp(Kolab::cDateTime(2012, 5, 5, 5, 5, 5, true)); KMime::Message::Ptr convertedMime = Kolab::KolabObjectWriter::writeIncidence(realIncidence, version); if (!compareMimeMessage(convertedMime, msg)) { showDiff(QString::fromUtf8(msg->encodedContent()), QString::fromUtf8(convertedMime->encodedContent())); QVERIFY(false); } QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } enum TestMode { ReadOnly, ReadWrite }; Q_DECLARE_METATYPE(TestMode) void FormatTest::testContact_data() { QTest::addColumn("version"); QTest::addColumn("type"); QTest::addColumn("vcardFileName"); QTest::addColumn("mimeFileName"); QTest::addColumn("mode"); QTest::newRow("v2contactSimple") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/simple.vcf") << getPath("v2/contacts/simple.vcf.mime") << ReadWrite; //FIXME Reference files needs to be adjusted due to fix in how pictures are stored // QTest::newRow( "v2contactComplex" ) << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/complex.vcf") << getPath("v2/contacts/complex.vcf.mime") << ReadWrite; QTest::newRow("v2contactAddress") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/address.vcf") << getPath("v2/contacts/address.vcf.mime") << ReadWrite; QTest::newRow("v2contactBug238996") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/bug238996.vcf") << getPath("v2/contacts/bug238996.vcf.mime") << ReadWrite; QTest::newRow("v2contactDisplayname") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/displayname.vcf") << getPath("v2/contacts/displayname.vcf.mime") << ReadWrite; QTest::newRow("v2contactEmails") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/emails.vcf") << getPath("v2/contacts/emails.vcf.mime") << ReadWrite; QTest::newRow("v2contactPhonenumbers") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/phonenumbers.vcf") << getPath("v2/contacts/phonenumbers.vcf.mime") << ReadWrite; // FIXME Reference files needs to be adjusted due to fix in how pictures are stored // QTest::newRow( "v2contactPicture" ) << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/picture.vcf") << getPath("v2/contacts/picture.vcf.mime") << ReadWrite; //FIXME the following test fails because the vcard implementation always writes jpeg (which is lossy). The reference vcf file is therefore probably also not really useful // QTest::newRow( "v2pictureJPGHorde" ) << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/pictureJPGHorde.vcf") << getPath("v2/contacts/pictureJPGHorde.vcf.mime"); QTest::newRow("v2contactRelated") << Kolab::KolabV2 << Kolab::ContactObject << getPath("v2/contacts/related.vcf") << getPath("v2/contacts/related.vcf.mime") << ReadWrite; QTest::newRow("v3contactSimple") << Kolab::KolabV3 << Kolab::ContactObject << getPath("v3/contacts/simple.vcf") << getPath("v3/contacts/simple.vcf.mime") << ReadWrite; QTest::newRow("v3contactComplex") << Kolab::KolabV3 << Kolab::ContactObject << getPath("v3/contacts/complex.vcf") << getPath("v3/contacts/complex.vcf.mime") << ReadWrite; QTest::newRow("v3contactPng") << Kolab::KolabV3 << Kolab::ContactObject << getPath("v3/readonly/png.vcf") << getPath("v3/readonly/png.vcf.mime") << ReadOnly; } bool comparePictureToReference(const QImage &picture) { QImage img(getPath("picture.jpg")); QByteArray pic; QBuffer buffer(&pic); buffer.open(QIODevice::WriteOnly); img.save(&buffer, "JPEG"); buffer.close(); QByteArray pic2; QBuffer buffer2(&pic2); buffer2.open(QIODevice::WriteOnly); picture.save(&buffer2, "JPEG"); buffer2.close(); if (pic.toBase64() != pic2.toBase64()) { qDebug() << pic.toBase64(); qDebug() << pic2.toBase64(); return false; } return true; } void FormatTest::testContact() { QFETCH(Kolab::Version, version); QFETCH(Kolab::ObjectType, type); QFETCH(QString, vcardFileName); //To compare QFETCH(QString, mimeFileName); //For parsing QFETCH(TestMode, mode); //For parsing //Parse mime message bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(mimeFileName, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), version); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KContacts::Addressee convertedAddressee = reader.getContact(); QVERIFY(!convertedAddressee.isEmpty()); //Parse vcard QFile vcardFile(vcardFileName); QVERIFY(vcardFile.open(QFile::ReadOnly)); KContacts::VCardConverter converter; const QByteArray &c = vcardFile.readAll(); KContacts::Addressee realAddressee = converter.parseVCard(c); // fix up the converted addressee for comparisson convertedAddressee.setName(realAddressee.name()); // name() apparently is something strange if (version == Kolab::KolabV2) { //No creation date in xcal QVERIFY(!convertedAddressee.custom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate")).isEmpty()); convertedAddressee.removeCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate")); // that's conversion time !? } else { normalizeContact(convertedAddressee); normalizeContact(realAddressee); } QVERIFY(normalizePhoneNumbers(convertedAddressee, realAddressee)); // phone number ids are random QVERIFY(normalizeAddresses(convertedAddressee, realAddressee)); // same here QCOMPARE(realAddressee.photo().type(), convertedAddressee.photo().type()); if (realAddressee != convertedAddressee) { showDiff(normalizeVCardMessage(QString::fromUtf8(converter.createVCard(realAddressee))), normalizeVCardMessage(QString::fromUtf8(converter.createVCard(convertedAddressee)))); } QEXPECT_FAIL("v2contactBug238996", "Currently fails due to missing type=pref attribute of preferred email address. Requires fix in KContacts.", Continue); QEXPECT_FAIL("v2contactEmails", "Currently fails due to missing type=pref attribute of preferred email address. Requires fix in KContacts.", Continue); QEXPECT_FAIL("v3contactComplex", "Currently fails due to missing type=pref attribute of preferred email address. Requires fix in KContacts.", Continue); QCOMPARE(realAddressee, convertedAddressee); //Write if (mode == ReadWrite) { Kolab::overrideTimestamp(Kolab::cDateTime(2012, 5, 5, 5, 5, 5, true)); const KMime::Message::Ptr &convertedMime = Kolab::KolabObjectWriter::writeContact(realAddressee, version); if (!compareMimeMessage(convertedMime, msg)) { QString expected = QString::fromUtf8(msg->encodedContent()); normalizeMimemessage(expected); QString converted = QString::fromUtf8(convertedMime->encodedContent()); normalizeMimemessage(converted); showDiff(expected, converted); QEXPECT_FAIL("v2contactSimple", "The kolab v3 containers don't support postbox, and we therefore loose it in the transformations.", Continue); QEXPECT_FAIL("v2contactAddress", "The kolab v3 containers don't support postbox, and we therefore loose it in the transformations.", Continue); QEXPECT_FAIL("v2contactBug238996", "The kolab v3 containers don't support postbox, and we therefore loose it in the transformations.", Continue); QVERIFY(false); } } QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } void FormatTest::testDistlist_data() { QTest::addColumn("version"); QTest::addColumn("type"); QTest::addColumn("vcardFileName"); QTest::addColumn("mimeFileName"); QTest::newRow("v3distlistSimple") << Kolab::KolabV3 << Kolab::DistlistObject << getPath("v3/contacts/distlist.vcf") << getPath("v3/contacts/distlist.vcf.mime"); } void FormatTest::testDistlist() { QFETCH(Kolab::Version, version); QFETCH(Kolab::ObjectType, type); QFETCH(QString, vcardFileName); //To compare QFETCH(QString, mimeFileName); //For parsing //Parse mime message bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(mimeFileName, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), version); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KContacts::ContactGroup convertedAddressee = reader.getDistlist(); //Parse vcard QFile vcardFile(vcardFileName); QVERIFY(vcardFile.open(QFile::ReadOnly)); KContacts::VCardConverter converter; QByteArray c = vcardFile.readAll(); QBuffer data(&c); data.open(QIODevice::ReadOnly); KContacts::ContactGroup realAddressee; KContacts::ContactGroupTool::convertFromXml(&data, realAddressee); { QBuffer expected; expected.open(QIODevice::WriteOnly); KContacts::ContactGroupTool::convertToXml(realAddressee, &expected); QBuffer converted; converted.open(QIODevice::WriteOnly); KContacts::ContactGroupTool::convertToXml(convertedAddressee, &converted); showDiff(QString::fromUtf8(expected.buffer()), QString::fromUtf8(converted.buffer())); } QCOMPARE(realAddressee, convertedAddressee); //Write const KMime::Message::Ptr &convertedMime = Kolab::KolabObjectWriter::writeDistlist(realAddressee, version); if (!compareMimeMessage(convertedMime, msg)) { QString expected = QString::fromUtf8(msg->encodedContent()); normalizeMimemessage(expected); QString converted = QString::fromUtf8(convertedMime->encodedContent()); normalizeMimemessage(converted); showDiff(expected, converted); QVERIFY(false); } QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } void FormatTest::testNote_data() { QTest::addColumn("version"); QTest::addColumn("type"); QTest::addColumn("noteFileName"); QTest::addColumn("mimeFileName"); QTest::newRow("v3noteSimple") << Kolab::KolabV3 << Kolab::NoteObject << getPath("v3/note/note.mime") << getPath("v3/note/note.mime.mime"); } void FormatTest::testNote() { QFETCH(Kolab::Version, version); QFETCH(Kolab::ObjectType, type); QFETCH(QString, noteFileName); //To compare QFETCH(QString, mimeFileName); //For parsing //Parse mime message bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(mimeFileName, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), version); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KMime::Message::Ptr convertedNote = reader.getNote(); QVERIFY(convertedNote.data()); //Parse note const KMime::Message::Ptr &realNote = readMimeFile(noteFileName, ok); QVERIFY(ok); QVERIFY(realNote.data()); QString expected = QString::fromUtf8(realNote->encodedContent()); normalizeMimemessage(expected); QString converted = QString::fromUtf8(convertedNote->encodedContent()); normalizeMimemessage(converted); QEXPECT_FAIL("", "Header sorting is off", Continue); QCOMPARE(expected, converted); // showDiff(expected, converted); //Write const KMime::Message::Ptr &convertedMime = Kolab::KolabObjectWriter::writeNote(realNote, version); QVERIFY(convertedMime.data()); QVERIFY(msg.data()); QString expected2 = QString::fromUtf8(msg->encodedContent()); normalizeMimemessage(expected2); QString converted2 = QString::fromUtf8(convertedMime->encodedContent()); normalizeMimemessage(converted2); QEXPECT_FAIL("", "Header sorting is off", Continue); QCOMPARE(expected2, converted2); // showDiff(expected2, converted2); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } //This function exists only to generate the reference files, it's not a real test. void FormatTest::generateMimefile() { // QFile icalFile( getPath("v3/journal/complex.ics") ); // QVERIFY( icalFile.open( QFile::ReadOnly ) ); // KCalendarCore::ICalFormat format; // const KCalendarCore::Incidence::Ptr realIncidence( format.fromString( QString::fromUtf8( icalFile.readAll() ) ) ); // // QString result; // QTextStream s(&result); // Kolab::overrideTimestamp(Kolab::cDateTime(2012, 5, 5, 5,5,5, true)); // Kolab::KolabObjectWriter::writeIncidence(realIncidence, Kolab::KolabV3)->toStream(s); // QFile vcardFile( getPath("v3/contacts/complex.vcf") ); // QVERIFY( vcardFile.open( QFile::ReadOnly ) ); // KContacts::VCardConverter converter; // const KContacts::Addressee realAddressee = converter.parseVCard( vcardFile.readAll() ); // // qDebug() << realAddressee.photo().data(); // // QString result; // QTextStream s(&result); // Kolab::overrideTimestamp(Kolab::cDateTime(2012, 5, 5, 5,5,5, true)); // Kolab::KolabObjectWriter::writeContact(realAddressee, Kolab::KolabV3)->toStream(s); // qDebug() << result; } void FormatTest::generateVCard() { // bool ok = false; // const KMime::Message::Ptr &msg = readMimeFile( QString::fromLatin1("../")+getPath("v2/contacts/pictureJPGHorde.vcf.mime"), ok ); // qDebug() << msg->encodedContent(); // Kolab::KolabObjectReader reader; // Kolab::ObjectType t = reader.parseMimeMessage(msg); // // KContacts::Addressee convertedAddressee = reader.getContact(); // KContacts::VCardConverter converter; // qDebug() << converter.createVCard(convertedAddressee); // bool ok = false; // const KMime::Message::Ptr &msg = readMimeFile( getPath("v3/contacts/distlist.vcf.mime"), ok ); // qDebug() << msg->encodedContent(); // Kolab::KolabObjectReader reader; // Kolab::ObjectType t = reader.parseMimeMessage(msg); // // KContacts::ContactGroup convertedAddressee = reader.getDistlist(); // QBuffer buf; // buf.open(QIODevice::WriteOnly); // KContacts::ContactGroupTool::convertToXml(convertedAddressee, &buf); // qDebug() << buf.buffer(); } //Pseudo test to show that JPG is always lossy, even with quality set to 100 void FormatTest::proveJPGisLossy() { // QImage img(getPath("picture.jpg")); // QByteArray pic; // QBuffer buffer(&pic); // buffer.open(QIODevice::WriteOnly); // img.save(&buffer, "JPEG"); // buffer.close(); // qDebug() << pic.toBase64(); // // QImage img2; // QByteArray pic2; // QBuffer buffer2(&pic2); // img2.loadFromData(pic); // img2.save(&buffer2, "JPEG"); // buffer2.close(); // qDebug() << pic2.toBase64(); } QTEST_MAIN(FormatTest) diff --git a/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp b/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp index 9173e242b..3e9067a4c 100644 --- a/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp +++ b/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp @@ -1,581 +1,581 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "kcalconversiontest.h" #include #include #include -#include +#include #include #include "conversion/kcalconversion.h" #include "conversion/kcalconversion.cpp" #include "conversion/kabcconversion.h" #include "testhelpers.h" using namespace Kolab::Conversion; template void comparePointerVectors(const QVector &list, const QVector &other) { QCOMPARE(list.size(), other.size()); for (int i = 0; i < list.size(); i++) { QCOMPARE(*list.at(i), *other.at(i)); } } void compareAttendeesVectors(const KCalendarCore::Attendee::List &list, const KCalendarCore::Attendee::List &other) { QCOMPARE(list.size(), other.size()); for (int i = 0; i < list.size(); i++) { KCalendarCore::Attendee at1 = list.at(i); at1.setUid(QString()); KCalendarCore::Attendee at2 = other.at(i); at2.setUid(QString()); QCOMPARE(at1, at2); } } void KCalConversionTest::initTestCase() { } void KCalConversionTest::testDate_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::newRow("datetime with tz") << Kolab::cDateTime("Europe/Zurich", 2006, 1, 8, 12, 0, 0) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone("Europe/Zurich")); QTest::newRow("floating datetime") << Kolab::cDateTime(2006, 1, 8, 12, 0, 0, false) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0)); QTest::newRow("utc datetime") << Kolab::cDateTime(2006, 1, 8, 12, 0, 0, true) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), Qt::UTC); QTest::newRow("date only") << Kolab::cDateTime(2006, 1, 8) << QDateTime(QDate(2006, 1, 8), {}); } void KCalConversionTest::testDate() { QFETCH(Kolab::cDateTime, input); QFETCH(QDateTime, result); const QDateTime &r = Kolab::Conversion::toDate(input); QCOMPARE(r, result); const Kolab::cDateTime &r2 = Kolab::Conversion::fromDate(result, input.isDateOnly()); QCOMPARE(r2, input); } void KCalConversionTest::testDuration_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::addColumn("fromResult"); QTest::newRow("seconds") << Kolab::Duration(0, 0, 0, 30, false) << KCalendarCore::Duration(30, KCalendarCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 30, false); QTest::newRow("minutes") << Kolab::Duration(0, 0, 1, 30, false) << KCalendarCore::Duration(90, KCalendarCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 90, false); QTest::newRow("hours") << Kolab::Duration(0, 1, 1, 30, false) << KCalendarCore::Duration(60*60+90, KCalendarCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 60*60+90, false); QTest::newRow("days") << Kolab::Duration(1, 1, 1, 30, false) << KCalendarCore::Duration(24*60*60+60*60+90, KCalendarCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 24*60*60+60*60+90, false); QTest::newRow("daysonly") << Kolab::Duration(30, 0, 0, 0, false) << KCalendarCore::Duration(30, KCalendarCore::Duration::Days) << Kolab::Duration(30, 0, 0, 0, false); QTest::newRow("weeks") << Kolab::Duration(30, false) << KCalendarCore::Duration(30*7, KCalendarCore::Duration::Days) << Kolab::Duration(30*7, 0, 0, 0, false); } void KCalConversionTest::testDuration() { QFETCH(Kolab::Duration, input); QFETCH(KCalendarCore::Duration, result); QFETCH(Kolab::Duration, fromResult); const KCalendarCore::Duration &r = Kolab::Conversion::toDuration(input); QCOMPARE(r, result); const Kolab::Duration &r2 = Kolab::Conversion::fromDuration(result); QCOMPARE(r2, fromResult); } void KCalConversionTest::testDateTZ_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::newRow("berlin") << Kolab::cDateTime("Europe/Berlin", 2006, 1, 8, 12, 0, 0) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone("Europe/Berlin")); } void KCalConversionTest::testDateTZ() { QFETCH(Kolab::cDateTime, input); QFETCH(QDateTime, result); const QDateTime &r = Kolab::Conversion::toDate(input); QCOMPARE(QString::fromUtf8(result.timeZone().id()), QString::fromStdString(input.timezone())); QCOMPARE(r.timeZone().offsetFromUtc(QDateTime::currentDateTimeUtc()), result.timeZone().offsetFromUtc(QDateTime::currentDateTimeUtc())); const Kolab::cDateTime &r2 = Kolab::Conversion::fromDate(result, input.isDateOnly()); QCOMPARE(QString::fromStdString(r2.timezone()), QString::fromUtf8(result.timeZone().id())); } void KCalConversionTest::testConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); Kolab::cDateTime date3(2012, 2, 2, 12, 12, 10, true); std::vector intVector; intVector.push_back(1); intVector.push_back(-3); intVector.push_back(2); std::vector stringVector; stringVector.push_back("cat1"); stringVector.push_back("cat2"); stringVector.push_back("parent/child"); { KCalendarCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setRevision(3); kcal.setSecrecy(KCalendarCore::Incidence::SecrecyConfidential); kcal.setCategories(toStringList(stringVector)); kcal.setDtStart(toDate(date)); kcal.setDtEnd(toDate(date2)); kcal.setAllDay(date.isDateOnly()); kcal.setTransparency(KCalendarCore::Event::Transparent); kcal.setRecurrenceId(toDate(date2)); //TODO THISANDFUTURE kcal.recurrence()->setDaily(3); kcal.recurrence()->setDuration(5); kcal.recurrence()->addRDateTime(toDate(date2)); kcal.recurrence()->addRDate(toDate(date2).date()); kcal.recurrence()->addExDateTime(toDate(date3)); kcal.recurrence()->addExDate(toDate(date3).date()); KCalendarCore::RecurrenceRule *rr = kcal.recurrence()->defaultRRule(true); QList intList = QVector::fromStdVector(intVector).toList(); rr->setBySeconds(intList); rr->setByMinutes(intList); rr->setByHours(intList); rr->setByDays(QList() << KCalendarCore::RecurrenceRule::WDayPos(3, 1) << KCalendarCore::RecurrenceRule::WDayPos(5, 4)); rr->setByMonthDays(intList); rr->setByYearDays(intList); rr->setByMonths(intList); rr->setByWeekNumbers(intList); kcal.setSummary(QStringLiteral("summary")); kcal.setDescription(QStringLiteral("description")); kcal.setPriority(3); kcal.setStatus(KCalendarCore::Incidence::StatusConfirmed); kcal.setLocation(QStringLiteral("location")); kcal.setOrganizer(KCalendarCore::Person(QStringLiteral("organizer"), QStringLiteral("organizer@email"))); //Url kcal.setNonKDECustomProperty("X-KOLAB-URL", QStringLiteral("http://test.org")); KCalendarCore::Attendee att(QStringLiteral("attendee"), QStringLiteral("attendee@email"), false, KCalendarCore::Attendee::NeedsAction, KCalendarCore::Attendee::ReqParticipant); att.setDelegate(QStringLiteral("mailto:delegatee")); att.setDelegator(QStringLiteral("mailto:delegator")); kcal.addAttendee(att); kcal.addAttachment(KCalendarCore::Attachment(QStringLiteral("uri"), QStringLiteral("mimetype/mime"))); KCalendarCore::Alarm::Ptr alarm = KCalendarCore::Alarm::Ptr(new KCalendarCore::Alarm(&kcal)); KCalendarCore::Person::List addressees; addressees.append(KCalendarCore::Person(QStringLiteral("name"), QStringLiteral("email@email"))); alarm->setEmailAlarm(QStringLiteral("subject"), QStringLiteral("text"), addressees, QStringList()); //No support for attachments kcal.addAlarm(alarm); //TODO alarms kcal.setNonKDECustomProperty("X-KOLAB-key1", QStringLiteral("value1")); kcal.setNonKDECustomProperty("X-KOLAB-key2", QStringLiteral("value2")); kcal.setCustomProperty("SOMEOTHERAPP", "key2", QStringLiteral("value2")); Q_ASSERT(kcal.nonKDECustomProperty("X-KOLAB-key1") == QLatin1String("value1")); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setSequence(3); kolab.setClassification(Kolab::ClassConfidential); kolab.setCategories(stringVector); kolab.setStart(date); kolab.setEnd(date2); kolab.setTransparency(true); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setCount(5); rrule.setBysecond(intVector); rrule.setByminute(intVector); rrule.setByhour(intVector); rrule.setByday(std::vector() << Kolab::DayPos(3, Kolab::Monday) << Kolab::DayPos(5, Kolab::Thursday)); rrule.setBymonthday(intVector); rrule.setByyearday(intVector); rrule.setByweekno(intVector); rrule.setBymonth(intVector); kolab.setRecurrenceRule(rrule); kolab.setRecurrenceID(date2, true); kolab.setRecurrenceDates(std::vector() << date2 << Kolab::cDateTime(date2.year(), date2.month(), date2.day())); kolab.setExceptionDates(std::vector() << date3 << Kolab::cDateTime(date3.year(), date3.month(), date3.day())); kolab.setSummary("summary"); kolab.setDescription("description"); kolab.setPriority(3); kolab.setStatus(Kolab::StatusConfirmed); kolab.setLocation("location"); kolab.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, "organizer@email", "organizer")); //TODO uid kolab.setUrl("http://test.org"); Kolab::Attendee a(Kolab::ContactReference(Kolab::ContactReference::EmailReference, "attendee@email", "attendee"));//TODO uid a.setDelegatedTo(std::vector() << Kolab::ContactReference(Kolab::ContactReference::EmailReference, "delegatee@email", "delegatee")); a.setDelegatedFrom(std::vector() << Kolab::ContactReference(Kolab::ContactReference::EmailReference, "delegator@email", "delegator")); a.setCutype(Kolab::CutypeIndividual); kolab.setAttendees(std::vector() << a); Kolab::Attachment attach; attach.setUri("uri", "mimetype/mime"); kolab.setAttachments(std::vector() << attach); // std::vector receipents; // receipents.push_back("email@email"); // Kolab::Alarm alarm2("summary", "description", receipents); // kolab.setAlarms(std::vector() << alarm2); //The sorting is random, just sort them here how we think they should arrive so we don't have to sort during compare (due to lazyness). std::vector customproperties; customproperties.push_back(Kolab::CustomProperty("X-KDE-SOMEOTHERAPP-key2", "value2")); customproperties.push_back(Kolab::CustomProperty("key1", "value1")); customproperties.push_back(Kolab::CustomProperty("key2", "value2")); kolab.setCustomProperties(customproperties); QTest::newRow("with endDate and recurrence duration") << kcal << kolab; } { KCalendarCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setRevision(3); kcal.setDtStart(toDate(date)); kcal.setAllDay(date.isDateOnly()); kcal.setDuration(KCalendarCore::Duration(toDate(date), toDate(date2))); kcal.recurrence()->setDaily(3); kcal.recurrence()->setEndDateTime(toDate(date3)); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setSequence(3); kolab.setStart(date); kolab.setDuration(Kolab::Duration(0, 0, 1, 0)); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setEnd(date3); kolab.setRecurrenceRule(rrule); QTest::newRow("with duration and recurrence endDate") << kcal << kolab; } { Kolab::cDateTime start(2011, 1, 1); Kolab::cDateTime end(2011, 1, 3); KCalendarCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setDtStart(toDate(start)); kcal.setDtEnd(toDate(end)); kcal.setAllDay(start.isDateOnly()); kcal.recurrence()->setDaily(3); kcal.recurrence()->setEndDateTime(toDate(end)); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setStart(start); kolab.setEnd(end); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setEnd(end); kolab.setRecurrenceRule(rrule); QTest::newRow("date only dates") << kcal << kolab; } { KCalendarCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setDtStart(toDate(date)); kcal.setAllDay(date.isDateOnly()); kcal.setSummary(QStringLiteral("äöü%@$£é¤¼²°€Š�")); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setStart(date); kolab.setSummary(std::string(QStringLiteral("äöü%@$£é¤¼²°€Š�").toUtf8().constData())); QTest::newRow("latin1+Unicode") << kcal << kolab; } } void KCalConversionTest::testConversion() { QFETCH(KCalendarCore::Event, kcal); QFETCH(Kolab::Event, kolab); KCalendarCore::Event::Ptr e = toKCalendarCore(kolab); const Kolab::Event &b = fromKCalendarCore(kcal); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->created(), kcal.created()); QCOMPARE(e->lastModified(), kcal.lastModified()); QCOMPARE(e->revision(), kcal.revision()); QCOMPARE(e->secrecy(), kcal.secrecy()); QCOMPARE(e->categories(), kcal.categories()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->dtEnd(), kcal.dtEnd()); QCOMPARE(e->duration(), kcal.duration()); QCOMPARE(e->transparency(), kcal.transparency()); QCOMPARE(*e->recurrence(), *kcal.recurrence()); QCOMPARE(e->recurrenceId(), kcal.recurrenceId()); QCOMPARE(e->recurrenceType(), kcal.recurrenceType()); QCOMPARE(e->summary(), kcal.summary()); QCOMPARE(e->description(), kcal.description()); QCOMPARE(e->priority(), kcal.priority()); QCOMPARE(e->status(), kcal.status()); QCOMPARE(e->location(), kcal.location()); QCOMPARE(e->organizer().name(), kcal.organizer().name()); QCOMPARE(e->organizer().email(), kcal.organizer().email()); QCOMPARE(e->nonKDECustomProperty("X-KOLAB-URL"), kcal.nonKDECustomProperty("X-KOLAB-URL")); //otherwise we'd break the customProperties comparison e->removeNonKDECustomProperty("X-KOLAB-URL"); kcal.removeNonKDECustomProperty("X-KOLAB-URL"); compareAttendeesVectors(e->attendees(), kcal.attendees()); QCOMPARE(e->attachments(), kcal.attachments()); // QCOMPARE(e->alarms(), kcal.alarms()); //TODO QCOMPARE(e->customProperties(), kcal.customProperties()); // QBENCHMARK { // toKCalendarCore(kolab); // } QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.created(), kolab.created()); QCOMPARE(b.lastModified(), kolab.lastModified()); QCOMPARE(b.sequence(), kolab.sequence()); QCOMPARE(b.classification(), kolab.classification()); QCOMPARE(b.categories(), kolab.categories()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.end(), kolab.end()); QCOMPARE(b.duration(), kolab.duration()); QCOMPARE(b.transparency(), kolab.transparency()); QCOMPARE(b.recurrenceRule(), kolab.recurrenceRule()); QCOMPARE(b.recurrenceID(), kolab.recurrenceID()); QCOMPARE(b.recurrenceDates(), kolab.recurrenceDates()); QCOMPARE(b.exceptionDates(), kolab.exceptionDates()); QCOMPARE(b.summary(), kolab.summary()); QCOMPARE(b.description(), kolab.description()); QCOMPARE(b.status(), kolab.status()); QCOMPARE(b.location(), kolab.location()); QCOMPARE(b.organizer(), kolab.organizer()); QCOMPARE(b.url(), kolab.url()); QCOMPARE(b.attendees(), kolab.attendees()); QCOMPARE(b.attachments(), kolab.attachments()); QCOMPARE(b.customProperties(), kolab.customProperties()); } void KCalConversionTest::testTodoConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); { KCalendarCore::Todo kcal; kcal.setUid(QStringLiteral("uid")); kcal.setDtStart(toDate(date)); kcal.setDtDue(toDate(date2)); kcal.setAllDay(date.isDateOnly()); kcal.setRelatedTo(QStringLiteral("uid2"), KCalendarCore::Incidence::RelTypeParent); Kolab::Todo kolab; kolab.setUid("uid"); kolab.setStart(date); kolab.setDue(date2); std::vector relateds; relateds.push_back("uid2"); kolab.setRelatedTo(relateds); QTest::newRow("todo") << kcal << kolab; } } void KCalConversionTest::testTodoConversion() { QFETCH(KCalendarCore::Todo, kcal); QFETCH(Kolab::Todo, kolab); const KCalendarCore::Todo::Ptr e = toKCalendarCore(kolab); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->dtDue(), kcal.dtDue()); QCOMPARE(e->relatedTo(KCalendarCore::Incidence::RelTypeParent), kcal.relatedTo(KCalendarCore::Incidence::RelTypeParent)); const Kolab::Todo &b = fromKCalendarCore(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.due(), kolab.due()); QCOMPARE(b.relatedTo(), kolab.relatedTo()); } void KCalConversionTest::testJournalConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); { KCalendarCore::Journal kcal; kcal.setUid(QStringLiteral("uid")); kcal.setDtStart(toDate(date)); kcal.setSummary(QStringLiteral("summary")); Kolab::Journal kolab; kolab.setUid("uid"); kolab.setStart(date); kolab.setSummary("summary"); QTest::newRow("journal") << kcal << kolab; } } void KCalConversionTest::testJournalConversion() { QFETCH(KCalendarCore::Journal, kcal); QFETCH(Kolab::Journal, kolab); const KCalendarCore::Journal::Ptr e = toKCalendarCore(kolab); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->summary(), kcal.summary()); const Kolab::Journal &b = fromKCalendarCore(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.summary(), kolab.summary()); } void KCalConversionTest::testContactConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); kcal.setFormattedName(QStringLiteral("name")); Kolab::Contact kolab; kolab.setUid("uid"); kolab.setName("name"); QTest::newRow("basic") << kcal << kolab; } { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); kcal.setFormattedName(QStringLiteral("name")); kcal.setBirthday(QDateTime(QDate(2012, 2, 2))); //Because QDateTime doesn't know date-only values we always end up with a date-time Kolab::Contact kolab; kolab.setUid("uid"); kolab.setName("name"); kolab.setBDay(Kolab::cDateTime(2012, 2, 2, 0, 0, 0)); QTest::newRow("bday") << kcal << kolab; } { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); //The first address is always the preferred kcal.setEmails(QStringList() << QStringLiteral("email1@example.org") << QStringLiteral("email2@example.org")); kcal.insertCustom(QStringLiteral("KOLAB"), QStringLiteral("EmailTypesemail1@example.org"), QStringLiteral("home,work")); Kolab::Contact kolab; kolab.setUid("uid"); Kolab::Email email1("email1@example.org", Kolab::Email::Work|Kolab::Email::Home); Kolab::Email email2("email2@example.org"); std::vector emails; emails.push_back(email1); emails.push_back(email2); kolab.setEmailAddresses(emails, 0); QTest::newRow("emailTypesAndPreference") << kcal << kolab; } } void KCalConversionTest::testContactConversion() { QFETCH(KContacts::Addressee, kcal); QFETCH(Kolab::Contact, kolab); const KContacts::Addressee &e = toKABC(kolab); QCOMPARE(e.uid(), kcal.uid()); QCOMPARE(e.formattedName(), kcal.formattedName()); QCOMPARE(e.emails(), kcal.emails()); QCOMPARE(e.preferredEmail(), kcal.preferredEmail()); foreach (const QString &mail, e.emails()) { QCOMPARE(e.custom(QLatin1String("KOLAB"), QString::fromLatin1("EmailTypes%1").arg(mail)), kcal.custom(QLatin1String("KOLAB"), QString::fromLatin1("EmailTypes%1").arg(mail))); } QCOMPARE(e.birthday(), kcal.birthday()); const Kolab::Contact &b = fromKABC(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.name(), kolab.name()); QCOMPARE(b.emailAddresses(), kolab.emailAddresses()); QCOMPARE(b.emailAddressPreferredIndex(), kolab.emailAddressPreferredIndex()); QCOMPARE(b.bDay(), kolab.bDay()); } // void KCalConversionTest::BenchmarkRoundtripKCAL() // { // const Kolab::Event &event = Kolab::readEvent(TEST_DATA_PATH "/testfiles/icalEvent.xml", true); // std::string result = Kolab::writeEvent(event); // QBENCHMARK { // Kolab::Conversion::toKCalendarCore(Kolab::readEvent(result, false)); // } // } QTEST_MAIN(KCalConversionTest) diff --git a/resources/kolab/pimkolab/autotests/testhelpers.h b/resources/kolab/pimkolab/autotests/testhelpers.h index 449547213..45df8624f 100644 --- a/resources/kolab/pimkolab/autotests/testhelpers.h +++ b/resources/kolab/pimkolab/autotests/testhelpers.h @@ -1,310 +1,310 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef TESTHELPERS_H #define TESTHELPERS_H #include #include #include -#include +#include #include #include #include #include #include Q_DECLARE_METATYPE(Kolab::Duration) Q_DECLARE_METATYPE(Kolab::cDateTime) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(Kolab::Event) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(Kolab::Todo) Q_DECLARE_METATYPE(Kolab::Journal) Q_DECLARE_METATYPE(Kolab::Contact) Q_DECLARE_METATYPE(Kolab::Period) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(KCalendarCore::Event) Q_DECLARE_METATYPE(KCalendarCore::Todo) Q_DECLARE_METATYPE(KCalendarCore::Journal) namespace QTest { template<> char *toString(const Kolab::cDateTime &dt) { QByteArray ba = "Kolab::cDateTime("; ba += QByteArray::number(dt.year()) + ", " + QByteArray::number(dt.month())+ ", " + QByteArray::number(dt.day()) + ", "; ba += QByteArray::number(dt.hour()) + ", " + QByteArray::number(dt.minute()) + ", " + QByteArray::number(dt.second())+ ", "; ba += QByteArray(dt.isUTC() ? QByteArray("UTC") : QByteArray("TZ: "+QByteArray::fromStdString(dt.timezone()))); ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const KCalendarCore::Attendee &at) { QByteArray ba = "Attendee("; ba += at.name().toLatin1() + ", "; ba += at.email().toLatin1() + ", "; ba += QByteArray::number(at.role()) + ", "; ba += QByteArray::number(at.status()) + ", "; ba += QByteArray::number(at.RSVP()) + ", "; ba += at.delegate().toLatin1() + ", "; ba += at.delegator().toLatin1() + ", "; ba += at.uid().toLatin1() + ", "; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const QList &l) { QByteArray ba = "QList("; foreach (int i, l) { ba += QByteArray::number(i) + ", "; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const QList &l) { QByteArray ba = "QList("; foreach (const KCalendarCore::RecurrenceRule::WDayPos &i, l) { ba += QByteArray::number(i.pos()) + " "; ba += QByteArray::number(i.day()) + ", "; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const KCalendarCore::DateList &l) { QByteArray ba = "KCalendarCore::DateList("; foreach (const QDate &i, l) { ba += i.toString().toLatin1(); } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const KCalendarCore::DateTimeList &l) { QByteArray ba = "KCalendarCore::DateTimeList("; foreach (const QDateTime &i, l) { ba += toString(i); } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const KCalendarCore::Recurrence &at) { // at.dump(); KCalendarCore::RecurrenceRule *r = at.defaultRRule(); QByteArray ba; if (!r) { ba += "Recurrence( )"; } else { Q_ASSERT(r); Q_ASSERT(at.rRules().size() == 1); ba += "Recurrence("; ba += QByteArray::number(r->recurrenceType()) + "\n"; ba += QByteArray::number(r->frequency()) + "\n"; ba += QByteArray::number(r->duration()) + "\n"; ba += QByteArray(toString(r->startDt())) + "\n"; ba += QByteArray(toString(r->endDt())) + "\n"; ba += QByteArray(toString(r->bySeconds())) + "\n"; ba += QByteArray(toString(r->byMinutes())) + "\n"; ba += QByteArray(toString(r->byHours())) + "\n"; ba += QByteArray(toString(r->byDays())) + "\n"; ba += QByteArray(toString(r->byMonthDays())) + "\n"; ba += QByteArray(toString(r->byYearDays())) + "\n"; ba += QByteArray(toString(r->byMonths())) + "\n"; ba += ")\n"; ba += QByteArray(toString(at.exDates())) + "\n"; ba += QByteArray(toString(at.exDateTimes())) + "\n"; ba += QByteArray(toString(at.rDates())) + "\n"; ba += QByteArray(toString(at.rDateTimes())) + "\n"; } return qstrdup(ba.data()); } template<> char *toString(const Kolab::RecurrenceRule &at) { QByteArray ba; ba += "KolabRecurrenceRule("; ba += QByteArray::number(at.weekStart()) + "\n"; ba += QByteArray::number(at.frequency()) + "\n"; ba += QByteArray::number(at.interval()) + "\n"; ba += QByteArray::number(at.count()) + "\n"; ba += QByteArray(toString(at.end())) + "\n"; ba += QByteArray(toString(at.bysecond())) + "\n"; ba += QByteArray(toString(at.byminute())) + "\n"; ba += QByteArray(toString(at.byhour())) + "\n"; ba += QByteArray(toString(at.byday())) + "\n"; ba += QByteArray(toString(at.bymonthday())) + "\n"; ba += QByteArray(toString(at.byyearday())) + "\n"; ba += QByteArray(toString(at.byweekno())) + "\n"; ba += QByteArray(toString(at.bymonth())) + "\n"; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const KCalendarCore::Duration &d) { QByteArray ba; ba += "KCalendarCore::Duration("; ba += QByteArray::number(d.isDaily()) + ", "; ba += QByteArray::number(d.value()) + " "; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::ContactReference &a) { QByteArray ba = "Kolab::ContactReference("; ba += QByteArray::fromStdString(a.email()) + ", "; ba += QByteArray::fromStdString(a.name()) + ", "; ba += QByteArray::fromStdString(a.uid()); ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const std::vector &v) { QByteArray ba = "vector("; for (std::size_t i = 0; i < v.size(); i++) { ba += QByteArray(toString(v.at(i)))+ "\n"; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::Attendee &a) { QByteArray ba = "Kolab::Attendee("; ba += QByteArray::fromStdString(a.contact().email()) + "\n"; ba += QByteArray::fromStdString(a.contact().name()) + "\n"; ba += QByteArray::number(a.partStat()) + "\n"; ba += QByteArray::number(a.role()) + "\n"; ba += QByteArray::number(a.rsvp()) + "\n"; ba += QByteArray::fromStdString(a.contact().uid())+"\n"; ba += QByteArray(toString(a.delegatedTo()))+"\n"; ba += QByteArray(toString(a.delegatedFrom()))+ "\n"; ba += QByteArray::number(a.cutype())+ "\n"; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const std::vector &v) { QByteArray ba = "vector("; for (std::size_t i = 0; i < v.size(); i++) { ba += QByteArray(toString(v.at(i)))+ "\n"; ba += QByteArray("#######################")+ "\n"; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::CustomProperty &a) { QByteArray ba = "Kolab::CustomProperty("; ba += QByteArray::fromStdString(a.identifier) + ", "; ba += QByteArray::fromStdString(a.value); ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const std::vector &v) { QByteArray ba = "vector("; for (std::size_t i = 0; i < v.size(); i++) { ba += QByteArray(toString(v.at(i)))+ "\n"; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::Period &p) { QByteArray ba = "Kolab::Period("; ba += QByteArray(toString(p.start))+ "\n"; ba += QByteArray(toString(p.end))+ "\n"; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const std::vector &v) { QByteArray ba = "vector("; for (std::size_t i = 0; i < v.size(); i++) { ba += QByteArray(toString(v.at(i)))+ "\n"; } ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::FreebusyPeriod &p) { QByteArray ba = "Kolab::FreebusyPeriod("; ba += QByteArray::number(p.type())+ "\n"; ba += QByteArray::fromStdString(p.eventUid())+ "\n"; ba += QByteArray::fromStdString(p.eventLocation())+ "\n"; ba += QByteArray::fromStdString(p.eventSummary())+ "\n"; ba += QByteArray(toString(p.periods()))+ "\n"; ba += ")"; return qstrdup(ba.data()); } template<> char *toString(const Kolab::Duration &p) { QByteArray ba = "Kolab::Duration"; ba += p.isNegative() ? "-" : "+"; ba += "("; ba += QByteArray::number(p.weeks())+ ", "; ba += QByteArray::number(p.days())+ ", "; ba += QByteArray::number(p.hours())+ ", "; ba += QByteArray::number(p.minutes())+ ", "; ba += QByteArray::number(p.seconds()); ba += ")"; return qstrdup(ba.data()); } } #endif diff --git a/resources/kolab/pimkolab/autotests/timezonetest.cpp b/resources/kolab/pimkolab/autotests/timezonetest.cpp index 806f1e73e..422acdf24 100644 --- a/resources/kolab/pimkolab/autotests/timezonetest.cpp +++ b/resources/kolab/pimkolab/autotests/timezonetest.cpp @@ -1,178 +1,178 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "timezonetest.h" #include #include #include #include #include "testutils.h" #include #include -#include +#include void TimezoneTest::initTestCase() { } void TimezoneTest::testFromName() { TimezoneConverter converter; const QString timezone = converter.normalizeTimezone(QStringLiteral("(GMT+01.00) Sarajevo/Warsaw/Zagreb")); QCOMPARE(timezone, QLatin1String("Europe/Sarajevo")); } void TimezoneTest::testFromHardcodedList_data() { QTest::addColumn("timezone"); QTest::newRow("1") << QStringLiteral("(GMT+01:00) West Central Africa"); QTest::newRow("2") << QStringLiteral("(GMT-04:00) Atlantic Time (Canada)"); QTest::newRow("3") << QStringLiteral("(GMT-06:00) Saskatchewan"); QTest::newRow("4") << QStringLiteral("(GMT-01:00) Cape Verde Islands"); QTest::newRow("5") << QStringLiteral("(GMT-06:00) Central America"); QTest::newRow("6") << QStringLiteral("(GMT-06:00) Central Time (US and Canada)"); // QTest::newRow( "7" ) << QString::fromLatin1("(GMT-12:00) International Date Line West"); //Not mappable QTest::newRow("8") << QStringLiteral("(GMT-05:00) Eastern Time (US and Canada)"); // QTest::newRow( "9" ) << QString::fromLatin1("(GMT-02:00) Mid-Atlantic"); //Not mappable QTest::newRow("10") << QStringLiteral("(GMT-07:00) Mountain Time (US and Canada)"); QTest::newRow("11") << QStringLiteral("(GMT-03:30) Newfoundland and Labrador"); QTest::newRow("12") << QStringLiteral("(GMT-08:00) Pacific Time (US and Canada); Tijuana"); QTest::newRow("13") << QStringLiteral("(GMT-11:00) Midway Island, Samoa"); QTest::newRow("14") << QStringLiteral("W. Europe Standard Time"); QTest::newRow("15") << QStringLiteral("(GMT+1.00) Sarajevo/Warsaw/Zagreb"); //Lotus notes uses it's own set of specifiers // QTest::newRow( "Lotus Notes" ) << QString::fromLatin1("W. Europe"); // QTest::newRow( "Google UTC offset" ) << QString::fromLatin1("2013-10-23T04:00:00+02:00"); } void TimezoneTest::testFromHardcodedList() { TimezoneConverter converter; QFETCH(QString, timezone); const QString tz = converter.normalizeTimezone(timezone); qDebug() << tz; QVERIFY(!tz.isEmpty()); QVERIFY(tz != timezone); } void TimezoneTest::testKolabObjectWriter() { KCalendarCore::Event::Ptr event(new KCalendarCore::Event()); event->setDtStart(QDateTime(QDate(2012, 11, 11), QTime(1, 1), QTimeZone(QTimeZone::windowsIdToDefaultIanaId("(GMT+01:00) West Central Africa")))); KMime::Message::Ptr msg = Kolab::KolabObjectWriter::writeEvent(event); Kolab::KolabObjectReader reader(msg); KCalendarCore::Event::Ptr result = reader.getEvent(); qDebug() << result->dtStart().timeZone().id(); QCOMPARE(result->dtStart().timeZone().id(), QTimeZone("Africa/Lagos").id()); } // void TimezoneTest::testKolabObjectReader() // { // const Kolab::Version version = Kolab::KolabV3; // const Kolab::ObjectType type = Kolab::EventObject; // QString icalFileName = TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezone.ics"); //To compare // QString mimeFileName = TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezoneV3.mime"); //For parsing // // //Parse mime message // bool ok = false; // const KMime::Message::Ptr &msg = readMimeFile( mimeFileName, ok ); // QVERIFY(ok); // Kolab::KolabObjectReader reader; // Kolab::ObjectType t = reader.parseMimeMessage(msg); // QCOMPARE(t, type); // QCOMPARE(reader.getVersion(), version); // QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); // // KCalendarCore::Incidence::Ptr convertedIncidence = reader.getIncidence(); // qDebug() << "read incidence"; // // //Parse ICalFile for comparison // QFile icalFile( icalFileName ); // QVERIFY( icalFile.open( QFile::ReadOnly ) ); // KCalendarCore::ICalFormat format; // KCalendarCore::Incidence::Ptr realIncidence( format.fromString( QString::fromUtf8( icalFile.readAll() ) ) ); // // // fix up the converted incidence for comparisson // normalizeIncidence(convertedIncidence); // normalizeIncidence(realIncidence); // // // recurrence objects are created on demand, but KCalendarCore::Incidence::operator==() doesn't take that into account // // so make sure both incidences have one // realIncidence->recurrence(); // convertedIncidence->recurrence(); // // realIncidence->setLastModified(convertedIncidence->lastModified()); // // //The following test is just for debugging and not really relevant // if ( *(realIncidence.data()) != *(convertedIncidence.data()) ) { // showDiff(format.toString( realIncidence ), format.toString( convertedIncidence )); // } // QVERIFY( *(realIncidence.data()) == *(convertedIncidence.data()) ); // } void TimezoneTest::testFindLegacyTimezone() { const QString normalized = TimezoneConverter::normalizeTimezone(QStringLiteral("US/Pacific")); qDebug() << normalized; QEXPECT_FAIL("", "Currently broken", Continue); QVERIFY(!normalized.isEmpty()); } void TimezoneTest::testIgnoreInvalidTimezone() { const QString normalized = TimezoneConverter::normalizeTimezone(QStringLiteral("FOOOOBAR")); qDebug() << normalized; QVERIFY(normalized.isEmpty()); } void TimezoneTest::testUTCOffset() { const Kolab::cDateTime expected(2013, 10, 23, 2, 0, 0, true); const QDateTime input(QDateTime::fromString(QStringLiteral("2013-10-23T04:00:00+02:00"), Qt::ISODate)); const Kolab::cDateTime result = Kolab::Conversion::fromDate(input, false); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); QCOMPARE(result, expected); } void TimezoneTest::localTimezone() { { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), Qt::LocalTime), false); QVERIFY(!result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0)), false); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), QTimeZone()), false); QVERIFY(result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { QDateTime dt(QDate(2013, 10, 10), QTime(2, 0, 0), QTimeZone("/etc/localzone")); const Kolab::cDateTime result = Kolab::Conversion::fromDate(dt, false); qDebug() << result.timezone(); QVERIFY(result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } } QTEST_MAIN(TimezoneTest) diff --git a/resources/kolab/pimkolab/autotests/upgradetest.cpp b/resources/kolab/pimkolab/autotests/upgradetest.cpp index 58cf3344d..1c6df9cf5 100644 --- a/resources/kolab/pimkolab/autotests/upgradetest.cpp +++ b/resources/kolab/pimkolab/autotests/upgradetest.cpp @@ -1,179 +1,179 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "upgradetest.h" #include #include #include #include #include "testutils.h" #include "kolabformat/kolabobject.h" #include -#include +#include #include void UpgradeTest::testIncidence_data() { QTest::addColumn("type"); QTest::addColumn("filename"); QTest::addColumn("forceType"); // QTest::newRow( "v2contactSimple" ) << Kolab::KolabV2 << Kolab::ContactObject << TESTFILEDIR+QString::fromLatin1("v2/contacts/simple.vcf") << TESTFILEDIR+QString::fromLatin1("v2/contacts/simple.vcf.mime"); QTest::newRow("v2eventSimple") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/simple.ics.mime") << false; QTest::newRow("v2eventComplex") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/complex.ics.mime") << false; QTest::newRow("v2eventAllday") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/allday.ics.mime") << false; QTest::newRow("v2eventAttachment") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/attachment.ics.mime") << false; QTest::newRow("v2eventHorde") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/horde.ics.mime") << false; //Still broken, although it seems that rather the event comparison is implemented wrong (not using equals) QTest::newRow("v2taskSimple") << Kolab::TodoObject << TESTFILEDIR+QLatin1String("v2/task/simple.ics.mime") << false; QTest::newRow("v2taskComplex") << Kolab::TodoObject << TESTFILEDIR+QLatin1String("v2/task/complex.ics.mime") << false; QTest::newRow("v2taskPrio1") << Kolab::TodoObject << TESTFILEDIR+QLatin1String("v2/task/prioritytest1.ics.mime") << false; QTest::newRow("v2taskPrio2") << Kolab::TodoObject << TESTFILEDIR+QLatin1String("v2/task/prioritytest2.ics.mime") << false; QTest::newRow("v2journalSimple") << Kolab::JournalObject << TESTFILEDIR+QLatin1String("v2/journal/simple.ics.mime") << false; QTest::newRow("v2journalComplex") << Kolab::JournalObject << TESTFILEDIR+QLatin1String("v2/journal/complex.ics.mime") << false; QTest::newRow("v2eventSimple_missingTypeHeader") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/simple_missingTypeHeader.ics.mime") << true; QTest::newRow("v2eventis8859-1") << Kolab::EventObject << TESTFILEDIR+QLatin1String("v2/event/iso8859-1.mime") << false; } void UpgradeTest::testIncidence() { QFETCH(Kolab::ObjectType, type); QFETCH(QString, filename); //To compare QFETCH(bool, forceType); bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(filename, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; if (forceType) { reader.setObjectType(type); } Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), Kolab::KolabV2); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KCalendarCore::Incidence::Ptr v2result = reader.getIncidence(); QVERIFY(!v2result.isNull()); //write KCalendarCore V3 KMime::Message::Ptr v3message = Kolab::KolabObjectWriter::writeIncidence(v2result, Kolab::KolabV3); QVERIFY(Kolab::error() == Kolab::NoError); // qDebug() << v3message->encodedContent(); //load KCalendarCore V3 Kolab::KolabObjectReader reader2; QCOMPARE(reader2.parseMimeMessage(v3message), type); KCalendarCore::Incidence::Ptr v3result = reader2.getIncidence(); QVERIFY(!v3result.isNull()); normalizeIncidence(v2result); //We have to override the last modified time with a specific value, so we normalize the input to the same normalizeIncidence(v3result); qDebug() <<"--------------------------------------------------------"; KCalendarCore::ICalFormat format; const QString v2 = format.toString(v2result); const QString v3 = format.toString(v3result); if (*v2result != *v3result) { showDiff(v2, v3); // qDebug() << "V2: " << v2; // qDebug() << "V3: " << v3; } QVERIFY(!v2.isEmpty()); QVERIFY(!v3.isEmpty()); QCOMPARE(v2, v3); qDebug() <<"--------------------------------------------------------"; // if (v2result->type() == KCalendarCore::IncidenceBase::TypeTodo) { // KCalendarCore::Todo::Ptr t1 = v2result.dynamicCast(); // KCalendarCore::Todo::Ptr t2 = v3result.dynamicCast(); // QCOMPARE(t1->percentComplete(), t2->percentComplete()); // QCOMPARE(t1->priority(), t2->priority()); // QCOMPARE(t1->uid(), t2->uid()); // QCOMPARE(t1->dtStart(), t2->dtStart()); // QCOMPARE(t1->dtDue(), t2->dtDue()); // QCOMPARE(*t1->organizer().data(), *t2->organizer().data()); // QCOMPARE(t1->uid(), t2->uid()); // QCOMPARE(t1->allDay(), t2->allDay()); // QCOMPARE(t1->duration(), t2->duration()); // QCOMPARE(t1->hasDuration(), t2->hasDuration()); // qDebug() << "V2: " << v2; // } //Test comparator QCOMPARE(*v2result, *v3result); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } void UpgradeTest::testContact_data() { QTest::addColumn("type"); QTest::addColumn("filename"); QTest::newRow("v2contactSimple")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/simple.vcf.mime"); QTest::newRow("v2contactComplex")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/complex.vcf.mime"); QTest::newRow("v2contactAddress")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/address.vcf.mime"); QTest::newRow("v2contactBug238996")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/bug238996.vcf.mime"); QTest::newRow("v2contactDisplayname")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/displayname.vcf.mime"); QTest::newRow("v2contactEmails")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/emails.vcf.mime"); QTest::newRow("v2contactPhonenumber")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/phonenumbers.vcf.mime"); QTest::newRow("v2contactPicture")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/picture.vcf.mime"); QTest::newRow("v2iso8859-1")<< Kolab::ContactObject << TESTFILEDIR+QLatin1String("v2/contacts/iso8859-1.mime"); } void UpgradeTest::testContact() { QFETCH(Kolab::ObjectType, type); QFETCH(QString, filename); //Parse mime message bool ok = false; const KMime::Message::Ptr &msg = readMimeFile(filename, ok); QVERIFY(ok); Kolab::KolabObjectReader reader; Kolab::ObjectType t = reader.parseMimeMessage(msg); QCOMPARE(t, type); QCOMPARE(reader.getVersion(), Kolab::KolabV2); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); KContacts::Addressee v2Addressee = reader.getContact(); QVERIFY(!v2Addressee.isEmpty()); //Write const KMime::Message::Ptr &v3message = Kolab::KolabObjectWriter::writeContact(v2Addressee, Kolab::KolabV3); //load V3 Kolab::KolabObjectReader reader2; QCOMPARE(reader2.parseMimeMessage(v3message), type); KContacts::Addressee v3result = reader2.getContact(); QVERIFY(!v3result.isEmpty()); normalizeContact(v2Addressee); QVERIFY(normalizePhoneNumbers(v3result, v2Addressee)); // phone number ids are random QVERIFY(normalizeAddresses(v3result, v2Addressee)); // same here KContacts::VCardConverter converter; if (v2Addressee != v3result) { showDiff(QString::fromUtf8(converter.createVCard(v2Addressee)), QString::fromUtf8(converter.createVCard(v3result))); QVERIFY(false); } QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } QTEST_MAIN(UpgradeTest) diff --git a/resources/kolab/pimkolab/calendaring/calendaring.h b/resources/kolab/pimkolab/calendaring/calendaring.h index 3dde02909..6ce787068 100644 --- a/resources/kolab/pimkolab/calendaring/calendaring.h +++ b/resources/kolab/pimkolab/calendaring/calendaring.h @@ -1,81 +1,81 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef KOLABCALENDARING_H #define KOLABCALENDARING_H #include "kolab_export.h" #include -#include +#include #include #include namespace Kolab { namespace Calendaring { /** * Returns true if the events conflict (overlap) * Start and end date/time is inclusive. * * Does not take recurrences into account. */ KOLAB_EXPORT bool conflicts(const Kolab::Event &, const Kolab::Event &); /** * Returns sets of the events which are directly conflicting with each other. * The same event may appear in multiple sets. * Non-conflicting events are not returned. * conflicts() is used for conflict detection. * * If the second list is given, each event from the first list is additionally checked against each event of the second set. * Conflicts within the second list are not detected. * * The checked event from the first list comes always first in the returned set. */ KOLAB_EXPORT std::vector< std::vector > getConflictingSets(const std::vector &, const std::vector & = std::vector()); /** * Returns the dates in which the event recurs within the specified timespan. */ KOLAB_EXPORT std::vector timeInInterval(const Kolab::Event &, const Kolab::cDateTime &start, const Kolab::cDateTime &end); /** * In-Memory Calendar Cache */ class KOLAB_EXPORT Calendar { public: explicit Calendar(); /** * Add an event to the in-memory calendar. */ void addEvent(const Kolab::Event &); /** * Returns all events within the specified interval (start and end inclusive). * * @param sort controls if the resulting event set is sorted in ascending order according to the start date */ std::vector getEvents(const Kolab::cDateTime &start, const Kolab::cDateTime &end, bool sort); private: Calendar(const Calendar &); void operator=(const Calendar &); std::unique_ptr mCalendar; }; } //Namespace } //Namespace #endif diff --git a/resources/kolab/pimkolab/conversion/kcalconversion.cpp b/resources/kolab/pimkolab/conversion/kcalconversion.cpp index e807aa37c..6b491b669 100644 --- a/resources/kolab/pimkolab/conversion/kcalconversion.cpp +++ b/resources/kolab/pimkolab/conversion/kcalconversion.cpp @@ -1,898 +1,898 @@ /* * Copyright (C) 2011 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "kcalconversion.h" -#include +#include #include #include #include #include #include "commonconversion.h" #include "pimkolab_debug.h" namespace Kolab { namespace Conversion { //The uid of a contact which refers to the uuid of a contact in the addressbook #define CUSTOM_KOLAB_CONTACT_UUID "X_KOLAB_CONTACT_UUID" #define CUSTOM_KOLAB_CONTACT_CUTYPE "X_KOLAB_CONTACT_CUTYPE" #define CUSTOM_KOLAB_URL "X-KOLAB-URL" KCalendarCore::Duration toDuration(const Kolab::Duration &d) { int value = 0; if (d.hours() || d.minutes() || d.seconds()) { value = ((((d.weeks() * 7 + d.days()) * 24 + d.hours()) * 60 + d.minutes()) * 60 + d.seconds()); if (d.isNegative()) { value = -value; } return KCalendarCore::Duration(value); } value = d.weeks() * 7 + d.days(); if (d.isNegative()) { value = -value; } return KCalendarCore::Duration(value, KCalendarCore::Duration::Days); } Kolab::Duration fromDuration(const KCalendarCore::Duration &d) { int value = d.value(); bool isNegative = false; if (value < 0) { isNegative = true; value = -value; } //We don't know how the seconds/days were distributed before, so no point in distributing them (probably) if (d.isDaily()) { int days = value; return Kolab::Duration(days, 0, 0, 0, isNegative); } int seconds = value; // int minutes = seconds / 60; // seconds = seconds % 60; // int hours = minutes / 60; // minutes = minutes % 60; return Kolab::Duration(0, 0, 0, seconds, isNegative); } KCalendarCore::Incidence::Secrecy toSecrecy(Kolab::Classification c) { switch (c) { case Kolab::ClassPublic: return KCalendarCore::Incidence::SecrecyPublic; case Kolab::ClassPrivate: return KCalendarCore::Incidence::SecrecyPrivate; case Kolab::ClassConfidential: return KCalendarCore::Incidence::SecrecyConfidential; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalendarCore::Incidence::SecrecyPublic; } Kolab::Classification fromSecrecy(KCalendarCore::Incidence::Secrecy c) { switch (c) { case KCalendarCore::Incidence::SecrecyPublic: return Kolab::ClassPublic; case KCalendarCore::Incidence::SecrecyPrivate: return Kolab::ClassPrivate; case KCalendarCore::Incidence::SecrecyConfidential: return Kolab::ClassConfidential; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::ClassPublic; } int toPriority(int priority) { //Same mapping return priority; } int fromPriority(int priority) { //Same mapping return priority; } KCalendarCore::Incidence::Status toStatus(Kolab::Status s) { switch (s) { case Kolab::StatusUndefined: return KCalendarCore::Incidence::StatusNone; case Kolab::StatusNeedsAction: return KCalendarCore::Incidence::StatusNeedsAction; case Kolab::StatusCompleted: return KCalendarCore::Incidence::StatusCompleted; case Kolab::StatusInProcess: return KCalendarCore::Incidence::StatusInProcess; case Kolab::StatusCancelled: return KCalendarCore::Incidence::StatusCanceled; case Kolab::StatusTentative: return KCalendarCore::Incidence::StatusTentative; case Kolab::StatusConfirmed: return KCalendarCore::Incidence::StatusConfirmed; case Kolab::StatusDraft: return KCalendarCore::Incidence::StatusDraft; case Kolab::StatusFinal: return KCalendarCore::Incidence::StatusFinal; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalendarCore::Incidence::StatusNone; } Kolab::Status fromStatus(KCalendarCore::Incidence::Status s) { switch (s) { case KCalendarCore::Incidence::StatusNone: return Kolab::StatusUndefined; case KCalendarCore::Incidence::StatusNeedsAction: return Kolab::StatusNeedsAction; case KCalendarCore::Incidence::StatusCompleted: return Kolab::StatusCompleted; case KCalendarCore::Incidence::StatusInProcess: return Kolab::StatusInProcess; case KCalendarCore::Incidence::StatusCanceled: return Kolab::StatusCancelled; case KCalendarCore::Incidence::StatusTentative: return Kolab::StatusTentative; case KCalendarCore::Incidence::StatusConfirmed: return Kolab::StatusConfirmed; case KCalendarCore::Incidence::StatusDraft: return Kolab::StatusDraft; case KCalendarCore::Incidence::StatusFinal: return Kolab::StatusFinal; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::StatusUndefined; } KCalendarCore::Attendee::PartStat toPartStat(Kolab::PartStatus p) { switch (p) { case Kolab::PartNeedsAction: return KCalendarCore::Attendee::NeedsAction; case Kolab::PartAccepted: return KCalendarCore::Attendee::Accepted; case Kolab::PartDeclined: return KCalendarCore::Attendee::Declined; case Kolab::PartTentative: return KCalendarCore::Attendee::Tentative; case Kolab::PartDelegated: return KCalendarCore::Attendee::Delegated; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalendarCore::Attendee::NeedsAction; } Kolab::PartStatus fromPartStat(KCalendarCore::Attendee::PartStat p) { switch (p) { case KCalendarCore::Attendee::NeedsAction: return Kolab::PartNeedsAction; case KCalendarCore::Attendee::Accepted: return Kolab::PartAccepted; case KCalendarCore::Attendee::Declined: return Kolab::PartDeclined; case KCalendarCore::Attendee::Tentative: return Kolab::PartTentative; case KCalendarCore::Attendee::Delegated: return Kolab::PartDelegated; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::PartNeedsAction; } KCalendarCore::Attendee::Role toRole(Kolab::Role r) { switch (r) { case Kolab::Required: return KCalendarCore::Attendee::ReqParticipant; case Kolab::Chair: return KCalendarCore::Attendee::Chair; case Kolab::Optional: return KCalendarCore::Attendee::OptParticipant; case Kolab::NonParticipant: return KCalendarCore::Attendee::NonParticipant; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalendarCore::Attendee::ReqParticipant; } Kolab::Role fromRole(KCalendarCore::Attendee::Role r) { switch (r) { case KCalendarCore::Attendee::ReqParticipant: return Kolab::Required; case KCalendarCore::Attendee::Chair: return Kolab::Chair; case KCalendarCore::Attendee::OptParticipant: return Kolab::Optional; case KCalendarCore::Attendee::NonParticipant: return Kolab::NonParticipant; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::Required; } template QString getCustomProperty(const QString &id, const T &e) { std::vector &props = e.customProperties(); foreach (const Kolab::CustomProperty &p, props) { if (fromStdString(p.identifier) == id) { return fromStdString(p.value); } } } template void setIncidence(KCalendarCore::Incidence &i, const T &e) { if (!e.uid().empty()) { i.setUid(fromStdString(e.uid())); } i.setCreated(toDate(e.created())); i.setLastModified(toDate(e.lastModified())); i.setRevision(e.sequence()); i.setSecrecy(toSecrecy(e.classification())); i.setCategories(toStringList(e.categories())); if (e.start().isValid()) { i.setDtStart(toDate(e.start())); i.setAllDay(e.start().isDateOnly()); } i.setSummary(fromStdString(e.summary())); //TODO detect richtext i.setDescription(fromStdString(e.description())); //TODO detect richtext i.setStatus(toStatus(e.status())); foreach (const Kolab::Attendee &a, e.attendees()) { /* * KCalendarCore always sets a UID if empty, but that's just a pointer, and not the uid of a real contact. * Since that means the semantics of the two are different, we have to store the kolab uid as a custom property. */ KCalendarCore::Attendee attendee(fromStdString(a.contact().name()), fromStdString(a.contact().email()), a.rsvp(), toPartStat(a.partStat()), toRole(a.role())); if (!a.contact().uid().empty()) { //TODO Identify contact from addressbook based on uid attendee.customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID, fromStdString(a.contact().uid())); } if (!a.delegatedTo().empty()) { if (a.delegatedTo().size() > 1) { qCWarning(PIMKOLAB_LOG) << "multiple delegatees are not supported"; } attendee.setDelegate(toMailto(a.delegatedTo().front().email(), a.delegatedTo().front().name()).toString()); } if (!a.delegatedFrom().empty()) { if (a.delegatedFrom().size() > 1) { qCWarning(PIMKOLAB_LOG) << "multiple delegators are not supported"; } attendee.setDelegator(toMailto(a.delegatedFrom().front().email(), a.delegatedFrom().front().name()).toString()); } if (a.cutype() != Kolab::CutypeIndividual) { attendee.customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE, QString::number(a.cutype())); } i.addAttendee(attendee); } foreach (const Kolab::Attachment &a, e.attachments()) { KCalendarCore::Attachment att; if (!a.uri().empty()) { att = KCalendarCore::Attachment(fromStdString(a.uri()), fromStdString(a.mimetype())); } else { att = KCalendarCore::Attachment(QByteArray::fromRawData(a.data().c_str(), a.data().size()).toBase64(), fromStdString(a.mimetype())); } if (!a.label().empty()) { att.setLabel(fromStdString(a.label())); } i.addAttachment(att); } QMap props; foreach (const Kolab::CustomProperty &prop, e.customProperties()) { QString key; if (prop.identifier.compare(0, 5, "X-KDE")) { key.append(QLatin1String("X-KOLAB-")); } key.append(fromStdString(prop.identifier)); props.insert(key.toLatin1(), fromStdString(prop.value)); // i.setCustomProperty("KOLAB", fromStdString(prop.identifier).toLatin1(), fromStdString(prop.value)); } i.setCustomProperties(props); } template void getIncidence(T &i, const I &e) { i.setUid(toStdString(e.uid())); i.setCreated(fromDate(e.created(), false)); i.setLastModified(fromDate(e.lastModified(), false)); i.setSequence(e.revision()); i.setClassification(fromSecrecy(e.secrecy())); i.setCategories(fromStringList(e.categories())); i.setStart(fromDate(e.dtStart(), e.allDay())); i.setSummary(toStdString(e.summary())); i.setDescription(toStdString(e.description())); i.setStatus(fromStatus(e.status())); std::vector attendees; foreach (const KCalendarCore::Attendee &ptr, e.attendees()) { const QString &uid = ptr.customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID); Kolab::Attendee a(Kolab::ContactReference(toStdString(ptr.email()), toStdString(ptr.name()), toStdString(uid))); a.setRSVP(ptr.RSVP()); a.setPartStat(fromPartStat(ptr.status())); a.setRole(fromRole(ptr.role())); if (!ptr.delegate().isEmpty()) { std::string name; const std::string &email = fromMailto(QUrl(ptr.delegate()), name); a.setDelegatedTo(std::vector() << Kolab::ContactReference(email, name)); } if (!ptr.delegator().isEmpty()) { std::string name; const std::string &email = fromMailto(QUrl(ptr.delegator()), name); a.setDelegatedFrom(std::vector() << Kolab::ContactReference(email, name)); } const QString &cutype = ptr.customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE); if (!cutype.isEmpty()) { a.setCutype(static_cast(cutype.toInt())); } attendees.push_back(a); } i.setAttendees(attendees); std::vector attachments; foreach (const KCalendarCore::Attachment &att, e.attachments()) { Kolab::Attachment a; if (att.isUri()) { a.setUri(toStdString(att.uri()), toStdString(att.mimeType())); } else { a.setData(std::string(att.decodedData().data(), att.decodedData().size()), toStdString(att.mimeType())); } a.setLabel(toStdString(att.label())); attachments.push_back(a); } i.setAttachments(attachments); std::vector customProperties; const QMap &props = e.customProperties(); for (QMap::const_iterator it = props.begin(), end(props.end()); it != end; ++it) { QString key(QString::fromUtf8(it.key())); if (key == QLatin1String(CUSTOM_KOLAB_URL)) { continue; } customProperties.push_back(Kolab::CustomProperty(toStdString(key.remove(QStringLiteral("X-KOLAB-"))), toStdString(it.value()))); } i.setCustomProperties(customProperties); } int toWeekDay(Kolab::Weekday wday) { switch (wday) { case Kolab::Monday: return 1; case Kolab::Tuesday: return 2; case Kolab::Wednesday: return 3; case Kolab::Thursday: return 4; case Kolab::Friday: return 5; case Kolab::Saturday: return 6; case Kolab::Sunday: return 7; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return 1; } Kolab::Weekday fromWeekDay(int wday) { switch (wday) { case 1: return Kolab::Monday; case 2: return Kolab::Tuesday; case 3: return Kolab::Wednesday; case 4: return Kolab::Thursday; case 5: return Kolab::Friday; case 6: return Kolab::Saturday; case 7: return Kolab::Sunday; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::Monday; } KCalendarCore::RecurrenceRule::PeriodType toRecurrenceType(Kolab::RecurrenceRule::Frequency freq) { switch (freq) { case Kolab::RecurrenceRule::FreqNone: qCWarning(PIMKOLAB_LOG) <<"no recurrence?"; break; case Kolab::RecurrenceRule::Yearly: return KCalendarCore::RecurrenceRule::rYearly; case Kolab::RecurrenceRule::Monthly: return KCalendarCore::RecurrenceRule::rMonthly; case Kolab::RecurrenceRule::Weekly: return KCalendarCore::RecurrenceRule::rWeekly; case Kolab::RecurrenceRule::Daily: return KCalendarCore::RecurrenceRule::rDaily; case Kolab::RecurrenceRule::Hourly: return KCalendarCore::RecurrenceRule::rHourly; case Kolab::RecurrenceRule::Minutely: return KCalendarCore::RecurrenceRule::rMinutely; case Kolab::RecurrenceRule::Secondly: return KCalendarCore::RecurrenceRule::rSecondly; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalendarCore::RecurrenceRule::rNone; } Kolab::RecurrenceRule::Frequency fromRecurrenceType(KCalendarCore::RecurrenceRule::PeriodType freq) { switch (freq) { case KCalendarCore::RecurrenceRule::rNone: qCWarning(PIMKOLAB_LOG) <<"no recurrence?"; break; case KCalendarCore::RecurrenceRule::rYearly: return Kolab::RecurrenceRule::Yearly; case KCalendarCore::RecurrenceRule::rMonthly: return Kolab::RecurrenceRule::Monthly; case KCalendarCore::RecurrenceRule::rWeekly: return Kolab::RecurrenceRule::Weekly; case KCalendarCore::RecurrenceRule::rDaily: return Kolab::RecurrenceRule::Daily; case KCalendarCore::RecurrenceRule::rHourly: return Kolab::RecurrenceRule::Hourly; case KCalendarCore::RecurrenceRule::rMinutely: return Kolab::RecurrenceRule::Minutely; case KCalendarCore::RecurrenceRule::rSecondly: return Kolab::RecurrenceRule::Secondly; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::RecurrenceRule::FreqNone; } KCalendarCore::RecurrenceRule::WDayPos toWeekDayPos(const Kolab::DayPos &dp) { return KCalendarCore::RecurrenceRule::WDayPos(dp.occurence(), toWeekDay(dp.weekday())); } Kolab::DayPos fromWeekDayPos(const KCalendarCore::RecurrenceRule::WDayPos &dp) { return Kolab::DayPos(dp.pos(), fromWeekDay(dp.day())); } template void setRecurrence(KCalendarCore::Incidence &e, const T &event) { const Kolab::RecurrenceRule &rrule = event.recurrenceRule(); if (rrule.isValid()) { KCalendarCore::Recurrence *rec = e.recurrence(); KCalendarCore::RecurrenceRule *defaultRR = rec->defaultRRule(true); Q_ASSERT(defaultRR); defaultRR->setWeekStart(toWeekDay(rrule.weekStart())); defaultRR->setRecurrenceType(toRecurrenceType(rrule.frequency())); defaultRR->setFrequency(rrule.interval()); if (rrule.end().isValid()) { rec->setEndDateTime(toDate(rrule.end())); //TODO date/datetime setEndDate(). With date-only the start date has to be taken into account. } else { rec->setDuration(rrule.count()); } if (!rrule.bysecond().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setBySeconds(QVector::fromStdVector(rrule.bysecond()).toList()); #else const std::vector bySecond = rrule.bysecond(); const QVector stdVector = QVector(bySecond.begin(), bySecond.end()); defaultRR->setBySeconds(stdVector.toList()); #endif } if (!rrule.byminute().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMinutes(QVector::fromStdVector(rrule.byminute()).toList()); #else const std::vector byMinutes = rrule.byminute(); const QVector stdVector = QVector(byMinutes.begin(), byMinutes.end()); defaultRR->setByMinutes(stdVector.toList()); #endif } if (!rrule.byhour().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByHours(QVector::fromStdVector(rrule.byhour()).toList()); #else const std::vector byHours = rrule.byhour(); const QVector stdVector = QVector(byHours.begin(), byHours.end()); defaultRR->setByHours(stdVector.toList()); #endif } if (!rrule.byday().empty()) { QList daypos; foreach (const Kolab::DayPos &dp, rrule.byday()) { daypos.append(toWeekDayPos(dp)); } defaultRR->setByDays(daypos); } if (!rrule.bymonthday().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMonthDays(QVector::fromStdVector(rrule.bymonthday()).toList()); #else const std::vector byMonthDays = rrule.bymonthday(); const QVector stdVector = QVector(byMonthDays.begin(), byMonthDays.end()); defaultRR->setByMonthDays(stdVector.toList()); #endif } if (!rrule.byyearday().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByYearDays(QVector::fromStdVector(rrule.byyearday()).toList()); #else const std::vector byYearDays = rrule.byyearday(); const QVector stdVector = QVector(byYearDays.begin(), byYearDays.end()); defaultRR->setByYearDays(stdVector.toList()); #endif } if (!rrule.byweekno().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByWeekNumbers(QVector::fromStdVector(rrule.byweekno()).toList()); #else const std::vector byWeekNumbers = rrule.byweekno(); const QVector stdVector = QVector(byWeekNumbers.begin(), byWeekNumbers.end()); defaultRR->setByWeekNumbers(stdVector.toList()); #endif } if (!rrule.bymonth().empty()) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMonths(QVector::fromStdVector(rrule.bymonth()).toList()); #else const std::vector byMonths = rrule.bymonth(); const QVector stdVector = QVector(byMonths.begin(), byMonths.end()); defaultRR->setByMonths(stdVector.toList()); #endif } } foreach (const Kolab::cDateTime &dt, event.recurrenceDates()) { const QDateTime &date = toDate(dt); if (dt.isDateOnly()) { e.recurrence()->addRDate(date.date()); } else { e.recurrence()->addRDateTime(date); } } foreach (const Kolab::cDateTime &dt, event.exceptionDates()) { const QDateTime &date = toDate(dt); if (dt.isDateOnly()) { e.recurrence()->addExDate(date.date()); } else { e.recurrence()->addExDateTime(date); } } } template void getRecurrence(T &i, const I &e) { if (!e.recurs()) { return; } KCalendarCore::Recurrence *rec = e.recurrence(); KCalendarCore::RecurrenceRule *defaultRR = rec->defaultRRule(false); if (!defaultRR) { qCWarning(PIMKOLAB_LOG) <<"no recurrence"; return; } Q_ASSERT(defaultRR); Kolab::RecurrenceRule rrule; rrule.setWeekStart(fromWeekDay(defaultRR->weekStart())); rrule.setFrequency(fromRecurrenceType(defaultRR->recurrenceType())); rrule.setInterval(defaultRR->frequency()); if (defaultRR->duration() != 0) { //Inidcates if end date is set or not if (defaultRR->duration() > 0) { rrule.setCount(defaultRR->duration()); } } else { rrule.setEnd(fromDate(defaultRR->endDt(), e.allDay())); } rrule.setBysecond(defaultRR->bySeconds().toVector().toStdVector()); rrule.setByminute(defaultRR->byMinutes().toVector().toStdVector()); rrule.setByhour(defaultRR->byHours().toVector().toStdVector()); std::vector daypos; daypos.reserve(defaultRR->byDays().count()); foreach (const KCalendarCore::RecurrenceRule::WDayPos &dp, defaultRR->byDays()) { daypos.push_back(fromWeekDayPos(dp)); } rrule.setByday(daypos); rrule.setBymonthday(defaultRR->byMonthDays().toVector().toStdVector()); rrule.setByyearday(defaultRR->byYearDays().toVector().toStdVector()); rrule.setByweekno(defaultRR->byWeekNumbers().toVector().toStdVector()); rrule.setBymonth(defaultRR->byMonths().toVector().toStdVector()); i.setRecurrenceRule(rrule); std::vector rdates; foreach (const QDateTime &dt, rec->rDateTimes()) { rdates.push_back(fromDate(dt, e.allDay())); } foreach (const QDate &dt, rec->rDates()) { rdates.push_back(fromDate(QDateTime(dt, {}), true)); } i.setRecurrenceDates(rdates); std::vector exdates; foreach (const QDateTime &dt, rec->exDateTimes()) { exdates.push_back(fromDate(dt, e.allDay())); } foreach (const QDate &dt, rec->exDates()) { exdates.push_back(fromDate(QDateTime(dt, {}), true)); } i.setExceptionDates(exdates); if (!rec->exRules().empty()) { qCWarning(PIMKOLAB_LOG) <<"exrules are not supported"; } } template void setTodoEvent(KCalendarCore::Incidence &i, const T &e) { i.setPriority(toPriority(e.priority())); if (!e.location().empty()) { i.setLocation(fromStdString(e.location())); //TODO detect richtext } if (e.organizer().isValid()) { i.setOrganizer(KCalendarCore::Person(fromStdString(e.organizer().name()), fromStdString(e.organizer().email()))); //TODO handle uid too } if (!e.url().empty()) { i.setNonKDECustomProperty(CUSTOM_KOLAB_URL, fromStdString(e.url())); } if (e.recurrenceID().isValid()) { i.setRecurrenceId(toDate(e.recurrenceID())); //TODO THISANDFUTURE } setRecurrence(i, e); foreach (const Kolab::Alarm &a, e.alarms()) { KCalendarCore::Alarm::Ptr alarm = KCalendarCore::Alarm::Ptr(new KCalendarCore::Alarm(&i)); switch (a.type()) { case Kolab::Alarm::EMailAlarm: { KCalendarCore::Person::List receipents; foreach (Kolab::ContactReference c, a.attendees()) { KCalendarCore::Person person(fromStdString(c.name()), fromStdString(c.email())); receipents.append(person); } alarm->setEmailAlarm(fromStdString(a.summary()), fromStdString(a.description()), receipents); break; } case Kolab::Alarm::DisplayAlarm: alarm->setDisplayAlarm(fromStdString(a.text())); break; case Kolab::Alarm::AudioAlarm: alarm->setAudioAlarm(fromStdString(a.audioFile().uri())); break; default: qCCritical(PIMKOLAB_LOG) << "invalid alarm"; } if (a.start().isValid()) { alarm->setTime(toDate(a.start())); } else if (a.relativeStart().isValid()) { if (a.relativeTo() == Kolab::End) { alarm->setEndOffset(toDuration(a.relativeStart())); } else { alarm->setStartOffset(toDuration(a.relativeStart())); } } alarm->setSnoozeTime(toDuration(a.duration())); alarm->setRepeatCount(a.numrepeat()); alarm->setEnabled(true); i.addAlarm(alarm); } } template void getTodoEvent(T &i, const I &e) { i.setPriority(fromPriority(e.priority())); i.setLocation(toStdString(e.location())); if (!e.organizer().email().isEmpty()) { i.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, toStdString(e.organizer().email()), toStdString(e.organizer().name()))); //TODO handle uid too } i.setUrl(toStdString(e.nonKDECustomProperty(CUSTOM_KOLAB_URL))); i.setRecurrenceID(fromDate(e.recurrenceId(), e.allDay()), false); //TODO THISANDFUTURE getRecurrence(i, e); std::vector alarms; foreach (const KCalendarCore::Alarm::Ptr &a, e.alarms()) { Kolab::Alarm alarm; //TODO KCalendarCore disables alarms using KCalendarCore::Alarm::enabled() (X-KDE-KCALCORE-ENABLED) We should either delete the alarm, or store the attribute . //Ideally we would store the alarm somewhere and temporarily delete it, so we can restore it when parsing. For now we just remove disabled alarms. if (!a->enabled()) { qCWarning(PIMKOLAB_LOG) <<"skipping disabled alarm"; continue; } switch (a->type()) { case KCalendarCore::Alarm::Display: alarm = Kolab::Alarm(toStdString(a->text())); break; case KCalendarCore::Alarm::Email: { std::vector receipents; foreach (const KCalendarCore::Person &p, a->mailAddresses()) { receipents.push_back(Kolab::ContactReference(toStdString(p.email()), toStdString(p.name()))); } alarm = Kolab::Alarm(toStdString(a->mailSubject()), toStdString(a->mailText()), receipents); break; } case KCalendarCore::Alarm::Audio: { Kolab::Attachment audioFile; audioFile.setUri(toStdString(a->audioFile()), std::string()); alarm = Kolab::Alarm(audioFile); break; } default: qCCritical(PIMKOLAB_LOG) << "unhandled alarm"; } if (a->hasTime()) { alarm.setStart(fromDate(a->time(), false)); } else if (a->hasStartOffset()) { alarm.setRelativeStart(fromDuration(a->startOffset()), Kolab::Start); } else if (a->hasEndOffset()) { alarm.setRelativeStart(fromDuration(a->endOffset()), Kolab::End); } else { qCCritical(PIMKOLAB_LOG) << "alarm trigger is missing"; continue; } alarm.setDuration(fromDuration(a->snoozeTime()), a->repeatCount()); alarms.push_back(alarm); } i.setAlarms(alarms); } KCalendarCore::Event::Ptr toKCalendarCore(const Kolab::Event &event) { KCalendarCore::Event::Ptr e(new KCalendarCore::Event); setIncidence(*e, event); setTodoEvent(*e, event); if (event.end().isValid()) { e->setDtEnd(toDate(event.end())); } if (event.duration().isValid()) { e->setDuration(toDuration(event.duration())); } if (event.transparency()) { e->setTransparency(KCalendarCore::Event::Transparent); } else { e->setTransparency(KCalendarCore::Event::Opaque); } return e; } Kolab::Event fromKCalendarCore(const KCalendarCore::Event &event) { Kolab::Event e; getIncidence(e, event); getTodoEvent(e, event); if (event.hasEndDate()) { e.setEnd(fromDate(event.dtEnd(), event.allDay())); } else if (event.hasDuration()) { e.setDuration(fromDuration(event.duration())); } if (event.transparency() == KCalendarCore::Event::Transparent) { e.setTransparency(true); } else { e.setTransparency(false); } return e; } KCalendarCore::Todo::Ptr toKCalendarCore(const Kolab::Todo &todo) { KCalendarCore::Todo::Ptr e(new KCalendarCore::Todo); setIncidence(*e, todo); setTodoEvent(*e, todo); if (todo.due().isValid()) { e->setDtDue(toDate(todo.due())); } if (!todo.relatedTo().empty()) { e->setRelatedTo(Kolab::Conversion::fromStdString(todo.relatedTo().front()), KCalendarCore::Incidence::RelTypeParent); if (todo.relatedTo().size() > 1) { qCCritical(PIMKOLAB_LOG) << "only one relation support but got multiple"; } } e->setPercentComplete(todo.percentComplete()); return e; } Kolab::Todo fromKCalendarCore(const KCalendarCore::Todo &todo) { Kolab::Todo t; getIncidence(t, todo); getTodoEvent(t, todo); t.setDue(fromDate(todo.dtDue(true), todo.allDay())); t.setPercentComplete(todo.percentComplete()); const QString relatedTo = todo.relatedTo(KCalendarCore::Incidence::RelTypeParent); if (!relatedTo.isEmpty()) { std::vector relateds; relateds.push_back(Kolab::Conversion::toStdString(relatedTo)); t.setRelatedTo(relateds); } return t; } KCalendarCore::Journal::Ptr toKCalendarCore(const Kolab::Journal &journal) { KCalendarCore::Journal::Ptr e(new KCalendarCore::Journal); setIncidence(*e, journal); //TODO contacts return e; } Kolab::Journal fromKCalendarCore(const KCalendarCore::Journal &journal) { Kolab::Journal j; getIncidence(j, journal); //TODO contacts return j; } } } diff --git a/resources/kolab/pimkolab/freebusy/freebusy.cpp b/resources/kolab/pimkolab/freebusy/freebusy.cpp index 9e90c102b..8a8bf4664 100644 --- a/resources/kolab/pimkolab/freebusy/freebusy.cpp +++ b/resources/kolab/pimkolab/freebusy/freebusy.cpp @@ -1,312 +1,312 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "freebusy.h" #include "pimkolab_debug.h" #include "conversion/kcalconversion.h" #include "conversion/commonconversion.h" #include "libkolab-version.h" #include -#include +#include #include #include // namespace KCalendarCore { // struct KCalFreebusy // { // // void init( const Event::List &eventList, const KDateTime &start, const KDateTime &end ) // { // mDtStart = start.toUtc(); // mDtEnd = end.toUtc(); // // // Loops through every event in the calendar // Event::List::ConstIterator it; // for ( it = eventList.constBegin(); it != eventList.constEnd(); ++it ) { // Event::Ptr event = *it; // // // If this event is transparent it shouldn't be in the freebusy list. // if ( event->transparency() == Event::Transparent ) { // continue; // } // // if ( event->hasRecurrenceId() ) { // continue; //TODO apply special period exception (duration could be different) // } // // const KDateTime eventStart = event->dtStart().toUtc(); // const KDateTime eventEnd = event->dtEnd().toUtc(); // // if ( event->recurs() ) { // const KCalendarCore::Duration duration( eventStart, eventEnd ); // const KCalendarCore::DateTimeList list = event->recurrence()->timesInInterval(start, end); // foreach (const KDateTime &dt, list) { // const KDateTime utc = dt.toUtc(); // addLocalPeriod(utc, duration.end(utc) ); // } // } else { // addLocalPeriod( eventStart, eventEnd ); // } // } // // // q->sortList(); // } // // bool addLocalPeriod( // const KDateTime &eventStart, // const KDateTime &eventEnd ) // { // KDateTime tmpStart; // KDateTime tmpEnd; // // //Check to see if the start *or* end of the event is // //between the start and end of the freebusy dates. // if ( !( ( ( mDtStart.secsTo( eventStart ) >= 0 ) && // ( eventStart.secsTo( mDtEnd ) >= 0 ) ) || // ( ( mDtStart.secsTo( eventEnd ) >= 0 ) && // ( eventEnd.secsTo( mDtEnd ) >= 0 ) ) ) ) { // qCDebug(PIMKOLAB_LOG) << "out of scope"; // return false; // } // // // qCDebug(PIMKOLAB_LOG) << eventStart.date().toString() << eventStart.time().toString() << mDtStart.toString(); // if ( eventStart < mDtStart ) { //eventStart is before start // // qCDebug(PIMKOLAB_LOG) << "use start"; // tmpStart = mDtStart; // } else { // tmpStart = eventStart; // } // // qCDebug(PIMKOLAB_LOG) << eventEnd.date().toString() << eventEnd.time().toString() << mDtEnd.toString(); // if ( eventEnd > mDtEnd ) { //event end is after dtEnd // // qCDebug(PIMKOLAB_LOG) << "use end"; // tmpEnd = mDtEnd; // } else { // tmpEnd = eventEnd; // } // // // qCDebug(PIMKOLAB_LOG) << "########## " << tmpStart.isValid(); // Q_ASSERT(tmpStart.isValid()); // Q_ASSERT(tmpEnd.isValid()); // // qCDebug(PIMKOLAB_LOG) << tmpStart.date().toString() << tmpStart.time().toString() << tmpStart.toString(); // // FreeBusyPeriod p( tmpStart, tmpEnd ); // mBusyPeriods.append( p ); // // return true; // } // // KDateTime mDtStart; // KDateTime mDtEnd; // end datetime // FreeBusyPeriod::List mBusyPeriods; // list of periods // // }; // // } // Namespace namespace Kolab { namespace FreebusyUtils { static QString createUuid() { const QString uuid = QUuid::createUuid().toString(); return uuid.mid(1, uuid.size()-2); } Kolab::Period addLocalPeriod(const QDateTime &eventStart, const QDateTime &eventEnd, const QDateTime &mDtStart, const QDateTime &mDtEnd, bool allDay) { QDateTime tmpStart; QDateTime tmpEnd; //Check to see if the start *or* end of the event is //between the start and end of the freebusy dates. if (!(((mDtStart <= eventStart) && (eventStart <= mDtEnd)) || ((mDtStart <= eventEnd) && (eventEnd <= mDtEnd)))) { qCDebug(PIMKOLAB_LOG) << "event is not within the fb range, skipping"; return Kolab::Period(); } if (eventStart < mDtStart) { //eventStart is before start tmpStart = mDtStart; } else { tmpStart = eventStart; } // qCDebug(PIMKOLAB_LOG) << eventEnd.date().toString() << eventEnd.time().toString() << mDtEnd.toString(); if (eventEnd > mDtEnd) { //event end is after dtEnd tmpEnd = mDtEnd; } else { tmpEnd = eventEnd; } Q_ASSERT(tmpStart.isValid()); Q_ASSERT(tmpEnd.isValid()); if (allDay) { tmpStart.setTime(QTime(0, 0, 0, 0)); tmpEnd.setTime(QTime(23, 59, 59, 999)); //The window is inclusive } return Kolab::Period(Kolab::Conversion::fromDate(tmpStart, allDay), Kolab::Conversion::fromDate(tmpEnd, allDay)); } Freebusy generateFreeBusy(const std::vector< Event > &events, const cDateTime &startDate, const cDateTime &endDate) { QList list; list.reserve(events.size()); for (const Kolab::Event &e : events) { list.append(Kolab::Conversion::toKCalendarCore(e)); } KCalendarCore::Person person(QStringLiteral("dummyname"), QStringLiteral("dummyemail")); return generateFreeBusy(list, Kolab::Conversion::toDate(startDate), Kolab::Conversion::toDate(endDate), person, startDate.isDateOnly()); } Freebusy generateFreeBusy(const QList &events, const QDateTime &startDate, const QDateTime &endDate, const KCalendarCore::Person &organizer, bool allDay) { /* * TODO the conversion of date-only values to date-time is only necessary because xCal doesn't allow date only. iCalendar doesn't seem to make this restriction so it looks like a bug. */ QDateTime start = startDate.toUTC(); if (allDay) { start.setTime(QTime(0, 0, 0, 0)); } QDateTime end = endDate.toUTC(); if (allDay) { end = end.addDays(1); end.setTime(QTime(0, 0, 0, 0)); //The window is inclusive } //TODO try to merge that with KCalendarCore::Freebusy std::vector freebusyPeriods; Q_FOREACH (const KCalendarCore::Event::Ptr &event, events) { // If this event is transparent it shouldn't be in the freebusy list. if (event->transparency() == KCalendarCore::Event::Transparent) { continue; } if (event->hasRecurrenceId()) { continue; //TODO apply special period exception (duration could be different) } const QDateTime eventStart = event->dtStart().toUTC(); const QDateTime eventEnd = event->dtEnd().toUTC(); std::vector periods; if (event->recurs()) { const KCalendarCore::Duration duration(eventStart, eventEnd); const auto list = event->recurrence()->timesInInterval(start, end); for (const auto &dt : list) { const auto utc = dt.toUTC(); const Kolab::Period &period = addLocalPeriod(utc, duration.end(utc), start, end, allDay); if (period.isValid()) { periods.push_back(period); } } } else { const Kolab::Period &period = addLocalPeriod(eventStart, eventEnd, start, end, allDay); if (period.isValid()) { periods.push_back(period); } } if (!periods.empty()) { Kolab::FreebusyPeriod period; period.setPeriods(periods); //TODO get busy type from event (out-of-office, tentative) period.setType(Kolab::FreebusyPeriod::Busy); period.setEvent(Kolab::Conversion::toStdString(event->uid()), Kolab::Conversion::toStdString(event->summary()), Kolab::Conversion::toStdString(event->location())); freebusyPeriods.push_back(period); } } Kolab::Freebusy freebusy; freebusy.setStart(Kolab::Conversion::fromDate(start, allDay)); freebusy.setEnd(Kolab::Conversion::fromDate(end, allDay)); freebusy.setPeriods(freebusyPeriods); freebusy.setUid(createUuid().toStdString()); freebusy.setTimestamp(Kolab::Conversion::fromDate(QDateTime::currentDateTimeUtc(), false)); if (!organizer.isEmpty()) { freebusy.setOrganizer(ContactReference(Kolab::ContactReference::EmailReference, Kolab::Conversion::toStdString(organizer.email()), Kolab::Conversion::toStdString(organizer.name()))); } return freebusy; } Freebusy aggregateFreeBusy(const std::vector< Freebusy > &fbList, const std::string &organizerEmail, const std::string &organizerName, bool simple) { std::vector periods; QDateTime start; QDateTime end; bool allDay = false; Q_FOREACH (const Freebusy &fb, fbList) { const QDateTime &tmpStart = Kolab::Conversion::toDate(fb.start()); if (!start.isValid() || tmpStart < start) { start = tmpStart; allDay |= fb.start().isDateOnly(); } const QDateTime &tmpEnd = Kolab::Conversion::toDate(fb.end()); if (!end.isValid() || tmpEnd > end) { end = tmpEnd; allDay |= fb.start().isDateOnly(); } Q_FOREACH (const Kolab::FreebusyPeriod &period, fb.periods()) { Kolab::FreebusyPeriod simplifiedPeriod; simplifiedPeriod.setPeriods(period.periods()); simplifiedPeriod.setType(period.type()); if (!simple) { //Don't copy and reset to avoid unintentional information leaking into simple lists simplifiedPeriod.setEvent(period.eventSummary(), period.eventUid(), period.eventLocation()); } periods.push_back(simplifiedPeriod); } } Freebusy aggregateFB; aggregateFB.setStart(Kolab::Conversion::fromDate(start, allDay)); aggregateFB.setEnd(Kolab::Conversion::fromDate(end, allDay)); aggregateFB.setPeriods(periods); aggregateFB.setUid(createUuid().toStdString()); aggregateFB.setTimestamp(Kolab::Conversion::fromDate(QDateTime::currentDateTimeUtc(), false)); aggregateFB.setOrganizer(ContactReference(Kolab::ContactReference::EmailReference, organizerEmail, organizerName)); return aggregateFB; } std::string toIFB(const Kolab::Freebusy &freebusy) { KCalendarCore::FreeBusy::Ptr fb(new KCalendarCore::FreeBusy(Kolab::Conversion::toDate(freebusy.start()), Kolab::Conversion::toDate(freebusy.end()))); KCalendarCore::FreeBusyPeriod::List list; Q_FOREACH (const Kolab::FreebusyPeriod &fbPeriod, freebusy.periods()) { Q_FOREACH (const Kolab::Period &p, fbPeriod.periods()) { KCalendarCore::FreeBusyPeriod period(Kolab::Conversion::toDate(p.start), Kolab::Conversion::toDate(p.end)); // period.setSummary("summary"); Doesn't even work. X-SUMMARY is read though (just not written out)s //TODO list.append(period); } } fb->addPeriods(list); fb->setUid(QString::fromStdString(freebusy.uid())); fb->setOrganizer(KCalendarCore::Person(Conversion::fromStdString(freebusy.organizer().name()), Conversion::fromStdString(freebusy.organizer().email()))); fb->setLastModified(Kolab::Conversion::toDate(freebusy.timestamp())); KCalendarCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); QString data = format.createScheduleMessage(fb, KCalendarCore::iTIPPublish); return Conversion::toStdString(data); } } } diff --git a/resources/kolab/pimkolab/icalendar/icalendar.cpp b/resources/kolab/pimkolab/icalendar/icalendar.cpp index 60423bcfd..229c6f317 100644 --- a/resources/kolab/pimkolab/icalendar/icalendar.cpp +++ b/resources/kolab/pimkolab/icalendar/icalendar.cpp @@ -1,159 +1,159 @@ /* Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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 "icalendar.h" #include "imip.h" #include "pimkolab_debug.h" #include "libkolab-version.h" #include #include #include #include -#include -#include +#include +#include #include #include #include #include namespace Kolab { std::string toICal(const std::vector &events) { KCalendarCore::Calendar::Ptr calendar(new KCalendarCore::MemoryCalendar(Kolab::Conversion::getTimeSpec(true, std::string()))); for (const Event &event : events) { KCalendarCore::Event::Ptr kcalEvent = Conversion::toKCalendarCore(event); kcalEvent->setCreated(QDateTime::currentDateTimeUtc()); //sets dtstamp calendar->addEvent(kcalEvent); } KCalendarCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); // qCDebug(PIMKOLAB_LOG) << format.createScheduleMessage(calendar->events().first(), KCalendarCore::iTIPRequest); return Conversion::toStdString(format.toString(calendar)); } std::vector< Event > fromICalEvents(const std::string &input) { KCalendarCore::Calendar::Ptr calendar(new KCalendarCore::MemoryCalendar(Kolab::Conversion::getTimeSpec(true, std::string()))); KCalendarCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); format.fromString(calendar, Conversion::fromStdString(input)); std::vector events; foreach (const KCalendarCore::Event::Ptr &event, calendar->events()) { events.push_back(Conversion::fromKCalendarCore(*event)); } return events; } ITipHandler::ITipHandler() : mMethod(iTIPNoMethod) { } ITipHandler::ITipMethod mapFromKCalendarCore(KCalendarCore::iTIPMethod method) { Q_ASSERT((int)KCalendarCore::iTIPPublish == (int)ITipHandler::iTIPPublish); Q_ASSERT((int)KCalendarCore::iTIPNoMethod == (int)ITipHandler::iTIPNoMethod); return static_cast(method); } KCalendarCore::iTIPMethod mapToKCalendarCore(ITipHandler::ITipMethod method) { Q_ASSERT((int)KCalendarCore::iTIPPublish == (int)ITipHandler::iTIPPublish); Q_ASSERT((int)KCalendarCore::iTIPNoMethod == (int)ITipHandler::iTIPNoMethod); return static_cast(method); } std::string ITipHandler::toITip(const Event &event, ITipHandler::ITipMethod method) const { KCalendarCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); KCalendarCore::iTIPMethod m = mapToKCalendarCore(method); if (m == KCalendarCore::iTIPNoMethod) { return std::string(); } // qCDebug(PIMKOLAB_LOG) << event.start(). /* TODO * DTSTAMP is created * CREATED is current timestamp * LASTMODIFIED is lastModified * * Double check if that is correct. * * I think DTSTAMP should be the current timestamp, and CREATED should be the creation date. */ KCalendarCore::Event::Ptr e = Conversion::toKCalendarCore(event); return Conversion::toStdString(format.createScheduleMessage(e, m)); } std::vector< Event > ITipHandler::fromITip(const std::string &string) { KCalendarCore::Calendar::Ptr calendar(new KCalendarCore::MemoryCalendar(QTimeZone::utc())); KCalendarCore::ICalFormat format; KCalendarCore::ScheduleMessage::Ptr msg = format.parseScheduleMessage(calendar, Conversion::fromStdString(string)); KCalendarCore::Event::Ptr event = msg->event().dynamicCast(); std::vector< Event > events; events.push_back(Conversion::fromKCalendarCore(*event)); mMethod = mapFromKCalendarCore(msg->method()); return events; } ITipHandler::ITipMethod ITipHandler::method() const { return mMethod; } std::string ITipHandler::toIMip(const Event &event, ITipHandler::ITipMethod m, const std::string &from, bool bccMe) const { KCalendarCore::Event::Ptr e = Conversion::toKCalendarCore(event); // e->recurrence()->addRDateTime(e->dtStart()); //FIXME The createScheduleMessage converts everything to utc without a recurrence. KCalendarCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); KCalendarCore::iTIPMethod method = mapToKCalendarCore(m); const QString &messageText = format.createScheduleMessage(e, method); //This code is mostly from MailScheduler::performTransaction if (method == KCalendarCore::iTIPRequest || method == KCalendarCore::iTIPCancel || method == KCalendarCore::iTIPAdd || method == KCalendarCore::iTIPDeclineCounter) { return Conversion::toStdString(QString::fromUtf8(mailAttendees(e, bccMe, messageText))); } else { QString subject; if (e && method == KCalendarCore::iTIPCounter) { subject = i18n("Counter proposal: %1", e->summary()); } return Conversion::toStdString(QString::fromUtf8(mailOrganizer(e, Conversion::fromStdString(from), bccMe, messageText, subject))); } } std::vector< Event > ITipHandler::fromIMip(const std::string &input) { KMime::Message::Ptr msg = KMime::Message::Ptr(new KMime::Message); msg->setContent(Conversion::fromStdString(input).toUtf8()); msg->parse(); msg->content(KMime::ContentIndex()); KMime::Content *c = Kolab::Mime::findContentByType(msg, "text/calendar"); if (!c) { qCWarning(PIMKOLAB_LOG) << "could not find text/calendar part"; return std::vector< Event >(); } return fromITip(Conversion::toStdString(QString::fromUtf8(c->decodedContent()))); } } diff --git a/resources/kolab/pimkolab/icalendar/imip.cpp b/resources/kolab/pimkolab/icalendar/imip.cpp index 63a320c52..75b4a83f1 100644 --- a/resources/kolab/pimkolab/icalendar/imip.cpp +++ b/resources/kolab/pimkolab/icalendar/imip.cpp @@ -1,228 +1,228 @@ /* Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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 "imip.h" #include "pimkolab_debug.h" -#include +#include #include #include /* * The code in here is copy paste work from kdepim/calendarsupport. * * We need to refactor the code there and move the relevant parts to kdepimlibs to make it reusable. * * */ //From MailClient::send KMime::Message::Ptr createMessage(const QString &from, const QString &_to, const QString &cc, const QString &subject, const QString &body, bool hidden, bool bccMe, const QString &attachment /*, const QString &mailTransport */) { Q_UNUSED(hidden); const bool outlookConformInvitation = false; QString userAgent = QStringLiteral("libkolab"); // We must have a recipients list for most MUAs. Thus, if the 'to' list // is empty simply use the 'from' address as the recipient. QString to = _to; if (to.isEmpty()) { to = from; } qCDebug(PIMKOLAB_LOG) << "\nFrom:" << from << "\nTo:" << to << "\nCC:" << cc << "\nSubject:" << subject << "\nBody: \n" << body << "\nAttachment:\n" << attachment /*<< "\nmailTransport: " << mailTransport*/; // Now build the message we like to send. The message KMime::Message::Ptr instance // will be the root message that has 2 additional message. The body itself and // the attached cal.ics calendar file. KMime::Message::Ptr message = KMime::Message::Ptr(new KMime::Message); message->contentTransferEncoding()->clear(); // 7Bit, decoded. // Set the headers message->userAgent()->fromUnicodeString(userAgent, "utf-8"); message->from()->fromUnicodeString(from, "utf-8"); message->to()->fromUnicodeString(to, "utf-8"); message->cc()->fromUnicodeString(cc, "utf-8"); if (bccMe) { message->bcc()->fromUnicodeString(from, "utf-8"); //from==me, right? } message->date()->setDateTime(QDateTime::currentDateTime()); message->subject()->fromUnicodeString(subject, "utf-8"); if (outlookConformInvitation) { message->contentType()->setMimeType("text/calendar"); message->contentType()->setCharset("utf-8"); message->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); message->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); if (!attachment.isEmpty()) { KMime::Headers::ContentDisposition *disposition = new KMime::Headers::ContentDisposition(); disposition->setDisposition(KMime::Headers::CDinline); message->setHeader(disposition); message->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); message->setBody(KMime::CRLFtoLF(attachment.toUtf8())); } } else { // We need to set following 4 lines by hand else KMime::Content::addContent // will create a new Content instance for us to attach the main message // what we don't need cause we already have the main message instance where // 2 additional messages are attached. KMime::Headers::ContentType *ct = message->contentType(); ct->setMimeType("multipart/mixed"); ct->setBoundary(KMime::multiPartBoundary()); ct->setCategory(KMime::Headers::CCcontainer); // Set the first multipart, the body message. KMime::Content *bodyMessage = new KMime::Content; KMime::Headers::ContentDisposition *bodyDisposition = new KMime::Headers::ContentDisposition(); bodyDisposition->setDisposition(KMime::Headers::CDinline); bodyMessage->contentType()->setMimeType("text/plain"); bodyMessage->contentType()->setCharset("utf-8"); bodyMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); bodyMessage->setBody(KMime::CRLFtoLF(body.toUtf8())); message->addContent(bodyMessage); // Set the sedcond multipart, the attachment. if (!attachment.isEmpty()) { KMime::Content *attachMessage = new KMime::Content; KMime::Headers::ContentDisposition *attachDisposition = new KMime::Headers::ContentDisposition(); attachDisposition->setDisposition(KMime::Headers::CDattachment); attachMessage->contentType()->setMimeType("text/calendar"); attachMessage->contentType()->setCharset("utf-8"); attachMessage->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); attachMessage->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); attachMessage->setHeader(attachDisposition); attachMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); attachMessage->setBody(KMime::CRLFtoLF(attachment.toUtf8())); message->addContent(attachMessage); } } // Job done, attach the both multiparts and assemble the message. message->assemble(); return message; } //From MailClient::mailAttendees QByteArray mailAttendees(const KCalendarCore::IncidenceBase::Ptr &incidence, // const KPIMIdentities::Identity &identity, bool bccMe, const QString &attachment /*const QString &mailTransport */) { KCalendarCore::Attendee::List attendees = incidence->attendees(); if (attendees.isEmpty()) { qCWarning(PIMKOLAB_LOG) << "There are no attendees to e-mail"; return QByteArray(); } const QString from = incidence->organizer().fullName(); const QString organizerEmail = incidence->organizer().email(); QStringList toList; QStringList ccList; const int numberOfAttendees(attendees.count()); for (int i = 0; i < numberOfAttendees; ++i) { KCalendarCore::Attendee a = attendees.at(i); const QString email = a.email(); if (email.isEmpty()) { continue; } // In case we (as one of our identities) are the organizer we are sending // this mail. We could also have added ourselves as an attendee, in which // case we don't want to send ourselves a notification mail. if (organizerEmail == email) { continue; } // Build a nice address for this attendee including the CN. QString tname, temail; const QString username = KEmailAddress::quoteNameIfNecessary(a.name()); // ignore the return value from extractEmailAddressAndName() because // it will always be false since tusername does not contain "@domain". KEmailAddress::extractEmailAddressAndName(username, temail /*byref*/, tname /*byref*/); tname += QLatin1String(" <") + email + QLatin1Char('>'); // Optional Participants and Non-Participants are copied on the email if (a.role() == KCalendarCore::Attendee::OptParticipant || a.role() == KCalendarCore::Attendee::NonParticipant) { ccList << tname; } else { toList << tname; } } if (toList.isEmpty() && ccList.isEmpty()) { // Not really to be called a groupware meeting, eh qCWarning(PIMKOLAB_LOG) << "There are really no attendees to e-mail"; return QByteArray(); } QString to; if (!toList.isEmpty()) { to = toList.join(QLatin1String(", ")); } QString cc; if (!ccList.isEmpty()) { cc = ccList.join(QLatin1String(", ")); } QString subject; if (incidence->type() != KCalendarCore::Incidence::TypeFreeBusy) { KCalendarCore::Incidence::Ptr inc = incidence.staticCast(); subject = inc->summary(); } else { subject = QStringLiteral("Free Busy Object"); } const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); return createMessage(/* identity, */ from, to, cc, subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); } QByteArray mailOrganizer(const KCalendarCore::IncidenceBase::Ptr &incidence, // const KPIMIdentities::Identity &identity, const QString &from, bool bccMe, const QString &attachment, const QString &sub /*, const QString &mailTransport*/) { const QString to = incidence->organizer().fullName(); QString subject = sub; if (incidence->type() != KCalendarCore::Incidence::TypeFreeBusy) { KCalendarCore::Incidence::Ptr inc = incidence.staticCast(); if (subject.isEmpty()) { subject = inc->summary(); } } else { subject = QStringLiteral("Free Busy Message"); } QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); return createMessage(/*identity, */ from, to, QString(), subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); } diff --git a/resources/kolab/pimkolab/icalendar/imip.h b/resources/kolab/pimkolab/icalendar/imip.h index a4abdb105..7de64bece 100644 --- a/resources/kolab/pimkolab/icalendar/imip.h +++ b/resources/kolab/pimkolab/icalendar/imip.h @@ -1,29 +1,29 @@ /* Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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 */ #ifndef IMIP_H #define IMIP_H #include -#include +#include QByteArray mailAttendees(const KCalendarCore::IncidenceBase::Ptr &incidence, bool bccMe, const QString &attachment); QByteArray mailOrganizer(const KCalendarCore::IncidenceBase::Ptr &incidence, const QString &from, bool bccMe, const QString &attachment, const QString &sub); #endif // IMIP_H diff --git a/resources/kolab/pimkolab/kolabformat/kolabobject.h b/resources/kolab/pimkolab/kolabformat/kolabobject.h index daab9a244..28e7af785 100644 --- a/resources/kolab/pimkolab/kolabformat/kolabobject.h +++ b/resources/kolab/pimkolab/kolabformat/kolabobject.h @@ -1,141 +1,141 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef KOLABOBJECT_H #define KOLABOBJECT_H #include #include #include #include #include #include -#include +#include #include #include #include #include #include "kolabdefinitions.h" namespace Kolab { class Freebusy; KOLAB_EXPORT KCalendarCore::Event::Ptr readV2EventXML(const QByteArray &xmlData, QStringList &attachments); struct KOLAB_EXPORT RelationMember { QString messageId; QString subject; QString date; QList mailbox; QString user; qint64 uid; QString gid; }; KOLAB_EXPORT RelationMember parseMemberUrl(const QString &url); KOLAB_EXPORT QString generateMemberUrl(const RelationMember &url); /** * Class to read Kolab Mime files * * It implements the Kolab specifics of Mime message handling. * This class is not reusable and only meant to read a single object. * Parse the mime message and then call the correct getter, based on the type * */ class KOLAB_EXPORT KolabObjectReader { public: KolabObjectReader(); explicit KolabObjectReader(const KMime::Message::Ptr &msg); ~KolabObjectReader(); ObjectType parseMimeMessage(const KMime::Message::Ptr &msg); /** * Set to override the autodetected object type, before parsing the message. */ void setObjectType(ObjectType); /** * Set to override the autodetected version, before parsing the message. */ void setVersion(Version); /** * Returns the Object type of the parsed kolab object. */ ObjectType getType() const; /** * Returns the kolab-format version of the parsed kolab object. */ Version getVersion() const; /** * Getter to get the retrieved object. * Only the correct one will return a valid object. * * Use getType() to determine the correct one to call. */ KCalendarCore::Event::Ptr getEvent() const; KCalendarCore::Todo::Ptr getTodo() const; KCalendarCore::Journal::Ptr getJournal() const; KCalendarCore::Incidence::Ptr getIncidence() const; KContacts::Addressee getContact() const; KContacts::ContactGroup getDistlist() const; KMime::Message::Ptr getNote() const; QStringList getDictionary(QString &lang) const; Freebusy getFreebusy() const; bool isTag() const; Akonadi::Tag getTag() const; QStringList getTagMembers() const; bool isRelation() const; Akonadi::Relation getRelation() const; private: //@cond PRIVATE KolabObjectReader(const KolabObjectReader &other); KolabObjectReader &operator=(const KolabObjectReader &rhs); class Private; Private *const d; //@endcond }; /** * Class to write Kolab Mime files * */ class KOLAB_EXPORT KolabObjectWriter { public: static KMime::Message::Ptr writeEvent(const KCalendarCore::Event::Ptr &, Version v = KolabV3, const QString &productId = QString(), const QString &tz = QString()); static KMime::Message::Ptr writeTodo(const KCalendarCore::Todo::Ptr &, Version v = KolabV3, const QString &productId = QString(), const QString &tz = QString()); static KMime::Message::Ptr writeJournal(const KCalendarCore::Journal::Ptr &, Version v = KolabV3, const QString &productId = QString(), const QString &tz = QString()); static KMime::Message::Ptr writeIncidence(const KCalendarCore::Incidence::Ptr &, Version v = KolabV3, const QString &productId = QString(), const QString &tz = QString()); static KMime::Message::Ptr writeContact(const KContacts::Addressee &, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeDistlist(const KContacts::ContactGroup &, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeNote(const KMime::Message::Ptr &, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeDictionary(const QStringList &, const QString &lang, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeFreebusy(const Kolab::Freebusy &, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeTag(const Akonadi::Tag &, const QStringList &items, Version v = KolabV3, const QString &productId = QString()); static KMime::Message::Ptr writeRelation(const Akonadi::Relation &, const QStringList &items, Version v = KolabV3, const QString &productId = QString()); }; } //Namespace #endif // KOLABOBJECT_H diff --git a/resources/kolab/pimkolab/kolabformat/v2helpers.h b/resources/kolab/pimkolab/kolabformat/v2helpers.h index 3ef5a9dc1..b61714492 100644 --- a/resources/kolab/pimkolab/kolabformat/v2helpers.h +++ b/resources/kolab/pimkolab/kolabformat/v2helpers.h @@ -1,98 +1,98 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef V2HELPERS_H #define V2HELPERS_H #include "kolabdefinitions.h" #include "kolabformatV2/kolabbase.h" #include "kolabformatV2/journal.h" #include "kolabformatV2/task.h" #include "kolabformatV2/event.h" #include "kolabformatV2/contact.h" #include "kolabformatV2/distributionlist.h" #include "kolabformatV2/note.h" #include "mime/mimeutils.h" #include "kolabformat/errorhandler.h" #include #include -#include +#include #include #include #include #include namespace Kolab { /* * Parse XML, create KCalendarCore container and extract attachments */ template static KCalPtr fromXML(const QByteArray &xmlData, QStringList &attachments) { const QDomDocument xmlDoc = KolabV2::KolabBase::loadDocument(QString::fromUtf8(xmlData)); //TODO extract function from V2 format if (xmlDoc.isNull()) { Critical() << "Failed to read the xml document"; return KCalPtr(); } const KCalPtr i = Container::fromXml(xmlDoc, QString()); //For parsing we don't need the timezone, so we don't set one Q_ASSERT(i); const QDomNodeList nodes = xmlDoc.elementsByTagName(QStringLiteral("inline-attachment")); for (int i = 0; i < nodes.size(); i++) { attachments.append(nodes.at(i).toElement().text()); } return i; } void getAttachments(KCalendarCore::Incidence::Ptr incidence, const QStringList &attachments, const KMime::Message::Ptr &mimeData); template static inline IncidencePtr incidenceFromKolabImpl(const KMime::Message::Ptr &data, const QByteArray &mimetype, const QString &timezoneId) { KMime::Content *xmlContent = Mime::findContentByType(data, mimetype); if (!xmlContent) { Critical() << "couldn't find part"; return IncidencePtr(); } const QByteArray &xmlData = xmlContent->decodedContent(); QStringList attachments; IncidencePtr ptr = fromXML(xmlData, attachments); //TODO do we care about timezone? getAttachments(ptr, attachments, data); return ptr; } KContacts::Addressee addresseeFromKolab(const QByteArray &xmlData, const KMime::Message::Ptr &data); KContacts::Addressee addresseeFromKolab(const QByteArray &xmlData, QString &pictureAttachmentName, QString &logoAttachmentName, QString &soundAttachmentName); KMime::Message::Ptr contactToKolabFormat(const KolabV2::Contact &contact, const QString &productId); KContacts::ContactGroup contactGroupFromKolab(const QByteArray &xmlData); KMime::Message::Ptr distListToKolabFormat(const KolabV2::DistributionList &distList, const QString &productId); KMime::Message::Ptr noteFromKolab(const QByteArray &xmlData, const QDateTime &creationDate); KMime::Message::Ptr noteToKolab(const KMime::Message::Ptr &msg, const QString &productId); QByteArray noteToKolabXML(const KMime::Message::Ptr &msg); QStringList readLegacyDictionaryConfiguration(const QByteArray &xmlData, QString &language); } #endif diff --git a/resources/kolab/pimkolab/kolabformatV2/incidence.h b/resources/kolab/pimkolab/kolabformatV2/incidence.h index 0060a206c..0bc55250c 100644 --- a/resources/kolab/pimkolab/kolabformatV2/incidence.h +++ b/resources/kolab/pimkolab/kolabformatV2/incidence.h @@ -1,180 +1,180 @@ /* This file is part of the kolab resource - the implementation of the Kolab storage format. See www.kolab.org for documentation on this. Copyright (c) 2004 Bo Thorsen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef KOLABV2_INCIDENCE_H #define KOLABV2_INCIDENCE_H -#include +#include #include "kolabbase.h" class QDomElement; namespace KolabV2 { /** * This abstract class represents an incidence which has the shared * fields, of events and tasks and knows how to load/save these * from/to XML, and from/to a KCalendarCore::Incidence. */ class Incidence : public KolabBase { public: struct Recurrence { QString cycle; QString type; int interval; QStringList days; // list of days-of-the-week QString dayNumber; QString month; QString rangeType; QString range; // date or number or nothing QList exclusions; }; struct Attendee : Email { Attendee() : requestResponse(true) , invitationSent(false) { } QString status; bool requestResponse; bool invitationSent; QString role; QString delegate; QString delegator; }; explicit Incidence(const QString &tz, const KCalendarCore::Incidence::Ptr &incidence = KCalendarCore::Incidence::Ptr()); public: ~Incidence() override; void saveTo(const KCalendarCore::Incidence::Ptr &incidence); virtual void setPriority(int priority); virtual int priority() const; virtual void setSummary(const QString &summary); virtual QString summary() const; virtual void setLocation(const QString &location); virtual QString location() const; virtual void setOrganizer(const Email &organizer); virtual Email organizer() const; virtual void setStartDate(const QDateTime &startDate); virtual void setStartDate(const QDate &startDate); virtual void setStartDate(const QString &startDate); virtual QDateTime startDate() const; virtual void setAlarm(float alarm); virtual float alarm() const; virtual void setRecurrence(KCalendarCore::Recurrence *recur); virtual Recurrence recurrence() const; virtual void addAttendee(const Attendee &attendee); QList &attendees(); const QList &attendees() const; QString type() const override { return QStringLiteral("Incidence"); } /** * The internal uid is used as the uid inside KOrganizer whenever * two or more events with the same uid appear, which KOrganizer * can't handle. To avoid keep that internal uid from changing all the * time, it is persisted in the XML between a save and the next load. */ void setInternalUID(const QString &iuid); QString internalUID() const; // Load the attributes of this class bool loadAttribute(QDomElement &) override; // Save the attributes of this class bool saveAttributes(QDomElement &) const override; protected: enum FloatingStatus { Unset, AllDay, HasTime }; // Read all known fields from this ical incidence void setFields(const KCalendarCore::Incidence::Ptr &); bool loadAttendeeAttribute(QDomElement &, Attendee &); void saveAttendeeAttribute(QDomElement &element, const Attendee &attendee) const; void saveAttendees(QDomElement &element) const; void saveAttachments(QDomElement &element) const; void loadAlarms(const QDomElement &element); void saveAlarms(QDomElement &element) const; void loadRecurrence(const QDomElement &element); void saveRecurrence(QDomElement &element) const; void saveCustomAttributes(QDomElement &element) const; void loadCustomAttributes(QDomElement &element); QString productID() const override; QString mSummary; QString mLocation; Email mOrganizer; QDateTime mStartDate; FloatingStatus mFloatingStatus; float mAlarm = 0.0; bool mHasAlarm; Recurrence mRecurrence; QList mAttendees; QList mAlarms; QList mAttachments; QString mInternalUID; struct Custom { QByteArray key; QString value; }; QList mCustomList; // This is the KCal priority, not the Kolab priority. // See kcalPriorityToKolab() and kolabPrioritytoKCal(). int mPriority; }; } #endif // KOLAB_INCIDENCE_H diff --git a/resources/kolab/pimkolab/kolabformatV2/kolabbase.cpp b/resources/kolab/pimkolab/kolabformatV2/kolabbase.cpp index 2d0162152..5b521603f 100644 --- a/resources/kolab/pimkolab/kolabformatV2/kolabbase.cpp +++ b/resources/kolab/pimkolab/kolabformatV2/kolabbase.cpp @@ -1,523 +1,523 @@ /* This file is part of the kolab resource - the implementation of the Kolab storage format. See www.kolab.org for documentation on this. Copyright (c) 2004 Bo Thorsen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "kolabbase.h" #include "pimkolab_debug.h" #include #include -#include +#include #include using namespace KolabV2; KolabBase::KolabBase(const QString &tz) : mCreationDate(QDateTime::currentDateTime()) , mLastModified(QDateTime::currentDateTimeUtc()) , mSensitivity(Public) , mHasPilotSyncId(false) , mHasPilotSyncStatus(false) { // unlike the previously used KTimeZone here, QTimeZone defaults to local time zone if tz.isEmpty() // we therefore force it to invalid, which however is unsafe to use (unlike KTimeZone) // therefore all usage of mTimeZone must be preceded by a validity check mTimeZone = tz.isEmpty() ? QTimeZone() : QTimeZone(tz.toUtf8()); } KolabBase::~KolabBase() { } void KolabBase::setFields(const KCalendarCore::Incidence::Ptr &incidence) { // So far unhandled KCalendarCore::IncidenceBase fields: // mPilotID, mSyncStatus, mFloats setUid(incidence->uid()); setBody(incidence->description()); setCategories(incidence->categoriesStr()); setCreationDate(localToUTC(incidence->created())); setLastModified(incidence->lastModified()); setSensitivity(static_cast(incidence->secrecy())); // TODO: Attachments } void KolabBase::saveTo(const KCalendarCore::Incidence::Ptr &incidence) const { incidence->setUid(uid()); incidence->setDescription(body()); incidence->setCategories(categories()); incidence->setCreated(utcToLocal(creationDate())); incidence->setLastModified(lastModified()); switch (sensitivity()) { case 1: incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPrivate); break; case 2: incidence->setSecrecy(KCalendarCore::Incidence::SecrecyConfidential); break; default: incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPublic); break; } // TODO: Attachments } void KolabBase::setFields(const KContacts::Addressee *addressee) { // An addressee does not have a creation date, so somehow we should // make one, if this is a new entry setUid(addressee->uid()); setBody(addressee->note()); setCategories(addressee->categories().join(QLatin1Char(','))); // Set creation-time and last-modification-time const QString creationString = addressee->custom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate")); qCDebug(PIMKOLAB_LOG) <<"Creation time string:" << creationString; QDateTime creationDate; if (creationString.isEmpty() && mTimeZone.isValid()) { creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone); qCDebug(PIMKOLAB_LOG) <<"Creation date set to current time" << mTimeZone; } else { creationDate = stringToDateTime(creationString); qCDebug(PIMKOLAB_LOG) <<"Creation date loaded"; } QDateTime modified; if (mTimeZone.isValid()) { modified = addressee->revision().toTimeZone(mTimeZone); } if (!modified.isValid()) { modified = QDateTime::currentDateTimeUtc(); } setLastModified(modified); if (modified < creationDate) { // It's not possible that the modification date is earlier than creation creationDate = modified; qCDebug(PIMKOLAB_LOG) <<"Creation date set to modification date"; } setCreationDate(creationDate); const QString newCreationDate = dateTimeToString(creationDate); if (creationString != newCreationDate) { // We modified the creation date, so store it for future reference const_cast(addressee) ->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), newCreationDate); qCDebug(PIMKOLAB_LOG) <<"Creation date modified. New one:" << newCreationDate; } switch (addressee->secrecy().type()) { case KContacts::Secrecy::Private: setSensitivity(Private); break; case KContacts::Secrecy::Confidential: setSensitivity(Confidential); break; default: setSensitivity(Public); } // TODO: Attachments } void KolabBase::saveTo(KContacts::Addressee *addressee) const { addressee->setUid(uid()); addressee->setNote(body()); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) addressee->setCategories(categories().split(QLatin1Char(','), QString::SkipEmptyParts)); #else addressee->setCategories(categories().split(QLatin1Char(','), Qt::SkipEmptyParts)); #endif if (mTimeZone.isValid()) { addressee->setRevision(lastModified().toTimeZone(mTimeZone)); } addressee->insertCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate"), dateTimeToString(creationDate())); switch (sensitivity()) { case Private: addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Private)); break; case Confidential: addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Confidential)); break; default: addressee->setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public)); break; } // TODO: Attachments } void KolabBase::setFields(const KContacts::ContactGroup *contactGroup) { // A contactgroup does not have a creation date, so somehow we should // make one, if this is a new entry setUid(contactGroup->id()); // Set creation-time and last-modification-time QDateTime creationDate; if (mTimeZone.isValid()) { creationDate = QDateTime::currentDateTime().toTimeZone(mTimeZone); } qCDebug(PIMKOLAB_LOG) <<"Creation date set to current time"; QDateTime modified = QDateTime::currentDateTimeUtc(); setLastModified(modified); if (modified < creationDate) { // It's not possible that the modification date is earlier than creation creationDate = modified; qCDebug(PIMKOLAB_LOG) <<"Creation date set to modification date"; } setCreationDate(creationDate); } void KolabBase::saveTo(KContacts::ContactGroup *contactGroup) const { contactGroup->setId(uid()); } void KolabBase::setUid(const QString &uid) { mUid = uid; } QString KolabBase::uid() const { return mUid; } void KolabBase::setBody(const QString &body) { mBody = body; } QString KolabBase::body() const { return mBody; } void KolabBase::setCategories(const QString &categories) { mCategories = categories; } QString KolabBase::categories() const { return mCategories; } void KolabBase::setCreationDate(const QDateTime &date) { mCreationDate = date; } QDateTime KolabBase::creationDate() const { return mCreationDate; } void KolabBase::setLastModified(const QDateTime &date) { mLastModified = date; } QDateTime KolabBase::lastModified() const { return mLastModified; } void KolabBase::setSensitivity(Sensitivity sensitivity) { mSensitivity = sensitivity; } KolabBase::Sensitivity KolabBase::sensitivity() const { return mSensitivity; } void KolabBase::setPilotSyncId(unsigned long id) { mHasPilotSyncId = true; mPilotSyncId = id; } bool KolabBase::hasPilotSyncId() const { return mHasPilotSyncId; } unsigned long KolabBase::pilotSyncId() const { return mPilotSyncId; } void KolabBase::setPilotSyncStatus(int status) { mHasPilotSyncStatus = true; mPilotSyncStatus = status; } bool KolabBase::hasPilotSyncStatus() const { return mHasPilotSyncStatus; } int KolabBase::pilotSyncStatus() const { return mPilotSyncStatus; } bool KolabBase::loadEmailAttribute(QDomElement &element, Email &email) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); const QString tagName = e.tagName(); if (tagName == QLatin1String("display-name")) { email.displayName = e.text(); } else if (tagName == QLatin1String("smtp-address")) { email.smtpAddress = e.text(); } else { // TODO: Unhandled tag - save for later storage qCDebug(PIMKOLAB_LOG) <<"Warning: Unhandled tag" << e.tagName(); } } else { qCDebug(PIMKOLAB_LOG) <<"Node is not a comment or an element???"; } } return true; } void KolabBase::saveEmailAttribute(QDomElement &element, const Email &email, const QString &tagName) const { QDomElement e = element.ownerDocument().createElement(tagName); element.appendChild(e); writeString(e, QStringLiteral("display-name"), email.displayName); writeString(e, QStringLiteral("smtp-address"), email.smtpAddress); } bool KolabBase::loadAttribute(QDomElement &element) { const QString tagName = element.tagName(); switch (tagName[0].toLatin1()) { case 'u': if (tagName == QLatin1String("uid")) { setUid(element.text()); return true; } break; case 'b': if (tagName == QLatin1String("body")) { setBody(element.text()); return true; } break; case 'c': if (tagName == QLatin1String("categories")) { setCategories(element.text()); return true; } if (tagName == QLatin1String("creation-date")) { setCreationDate(stringToDateTime(element.text())); return true; } break; case 'l': if (tagName == QLatin1String("last-modification-date")) { setLastModified(stringToDateTime(element.text())); return true; } break; case 's': if (tagName == QLatin1String("sensitivity")) { setSensitivity(stringToSensitivity(element.text())); return true; } break; case 'p': if (tagName == QLatin1String("product-id")) { return true; // ignore this field } if (tagName == QLatin1String("pilot-sync-id")) { setPilotSyncId(element.text().toULong()); return true; } if (tagName == QLatin1String("pilot-sync-status")) { setPilotSyncStatus(element.text().toInt()); return true; } break; default: break; } return false; } bool KolabBase::saveAttributes(QDomElement &element) const { writeString(element, QStringLiteral("product-id"), productID()); writeString(element, QStringLiteral("uid"), uid()); writeString(element, QStringLiteral("body"), body()); writeString(element, QStringLiteral("categories"), categories()); writeString(element, QStringLiteral("creation-date"), dateTimeToString(creationDate().toUTC())); writeString(element, QStringLiteral("last-modification-date"), dateTimeToString(lastModified().toUTC())); writeString(element, QStringLiteral("sensitivity"), sensitivityToString(sensitivity())); if (hasPilotSyncId()) { writeString(element, QStringLiteral("pilot-sync-id"), QString::number(pilotSyncId())); } if (hasPilotSyncStatus()) { writeString(element, QStringLiteral("pilot-sync-status"), QString::number(pilotSyncStatus())); } return true; } bool KolabBase::load(const QString &xml) { const QDomDocument document = loadDocument(xml); if (document.isNull()) { return false; } // XML file loaded into tree. Now parse it return loadXML(document); } QDomDocument KolabBase::loadDocument(const QString &xmlData) { QString errorMsg; int errorLine, errorColumn; QDomDocument document; bool ok = document.setContent(xmlData, &errorMsg, &errorLine, &errorColumn); if (!ok) { qWarning("Error loading document: %s, line %d, column %d", qPrintable(errorMsg), errorLine, errorColumn); return QDomDocument(); } return document; } QDomDocument KolabBase::domTree() { QDomDocument document; const QString p = QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""); document.appendChild(document.createProcessingInstruction(QStringLiteral("xml"), p)); return document; } QString KolabBase::dateTimeToString(const QDateTime &time) { return time.toString(Qt::ISODate); } QString KolabBase::dateToString(const QDate &date) { return date.toString(Qt::ISODate); } QDateTime KolabBase::stringToDateTime(const QString &time) { return QDateTime::fromString(time, Qt::ISODate); } QDate KolabBase::stringToDate(const QString &date) { return QDate::fromString(date, Qt::ISODate); } QString KolabBase::sensitivityToString(Sensitivity s) { switch (s) { case Private: return QStringLiteral("private"); case Confidential: return QStringLiteral("confidential"); case Public: return QStringLiteral("public"); } return QStringLiteral("What what what???"); } KolabBase::Sensitivity KolabBase::stringToSensitivity(const QString &s) { if (s == QLatin1String("private")) { return Private; } if (s == QLatin1String("confidential")) { return Confidential; } return Public; } QString KolabBase::colorToString(const QColor &color) { // Color is in the format "#RRGGBB" return color.name(); } QColor KolabBase::stringToColor(const QString &s) { return QColor(s); } void KolabBase::writeString(QDomElement &element, const QString &tag, const QString &tagString) { if (!tagString.isEmpty()) { QDomElement e = element.ownerDocument().createElement(tag); QDomText t = element.ownerDocument().createTextNode(tagString); e.appendChild(t); element.appendChild(e); } } QDateTime KolabBase::localToUTC(const QDateTime &time) const { return time.toUTC(); } QDateTime KolabBase::utcToLocal(const QDateTime &time) const { QDateTime dt = time; dt.setTimeSpec(Qt::UTC); return dt; } diff --git a/resources/kolab/pimkolab/kolabformatV2/kolabbase.h b/resources/kolab/pimkolab/kolabformatV2/kolabbase.h index f35851161..2a5eb0c95 100644 --- a/resources/kolab/pimkolab/kolabformatV2/kolabbase.h +++ b/resources/kolab/pimkolab/kolabformatV2/kolabbase.h @@ -1,181 +1,181 @@ /* This file is part of the kolab resource - the implementation of the Kolab storage format. See www.kolab.org for documentation on this. Copyright (c) 2004 Bo Thorsen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef KOLABV2_KOLABBASE_H #define KOLABV2_KOLABBASE_H -#include +#include #include #include #include #include namespace KContacts { class Addressee; class ContactGroup; } namespace KolabV2 { class KolabBase { public: struct Email { public: Email(const QString &name = QString(), const QString &email = QString()) : displayName(name) , smtpAddress(email) { } QString displayName; QString smtpAddress; }; enum Sensitivity { Public = 0, Private = 1, Confidential = 2 }; explicit KolabBase(const QString &time_zone = QString()); virtual ~KolabBase(); // Return a string identifying this type virtual QString type() const = 0; virtual void setUid(const QString &uid); virtual QString uid() const; virtual void setBody(const QString &body); virtual QString body() const; virtual void setCategories(const QString &categories); virtual QString categories() const; virtual void setCreationDate(const QDateTime &date); virtual QDateTime creationDate() const; virtual void setLastModified(const QDateTime &date); virtual QDateTime lastModified() const; virtual void setSensitivity(Sensitivity sensitivity); virtual Sensitivity sensitivity() const; virtual void setPilotSyncId(unsigned long id); virtual bool hasPilotSyncId() const; virtual unsigned long pilotSyncId() const; virtual void setPilotSyncStatus(int status); virtual bool hasPilotSyncStatus() const; virtual int pilotSyncStatus() const; // String - Date conversion methods static QString dateTimeToString(const QDateTime &time); static QString dateToString(const QDate &date); static QDateTime stringToDateTime(const QString &time); static QDate stringToDate(const QString &date); // String - Sensitivity conversion methods static QString sensitivityToString(Sensitivity); static Sensitivity stringToSensitivity(const QString &); // String - Color conversion methods static QString colorToString(const QColor &); static QColor stringToColor(const QString &); // Load this object by reading the XML file bool load(const QString &xml); static QDomDocument loadDocument(const QString &xmlData); // Load this QDomDocument virtual bool loadXML(const QDomDocument &xml) = 0; // Serialize this object to an XML string virtual QString saveXML() const = 0; protected: /// Read all known fields from this ical incidence void setFields(const KCalendarCore::Incidence::Ptr &); /// Save all known fields into this ical incidence void saveTo(const KCalendarCore::Incidence::Ptr &) const; /// Read all known fields from this contact void setFields(const KContacts::Addressee *); /// Save all known fields into this contact void saveTo(KContacts::Addressee *) const; /// Read all known fields from this contact group void setFields(const KContacts::ContactGroup *); /// Save all known fields into this contact groupd void saveTo(KContacts::ContactGroup *) const; // This just makes the initial dom tree with version and doctype static QDomDocument domTree(); bool loadEmailAttribute(QDomElement &element, Email &email); void saveEmailAttribute(QDomElement &element, const Email &email, const QString &tagName = QStringLiteral("email")) const; // Load the attributes of this class virtual bool loadAttribute(QDomElement &); // Save the attributes of this class virtual bool saveAttributes(QDomElement &) const; // Return the product ID virtual QString productID() const = 0; // Write a string tag static void writeString(QDomElement &, const QString &, const QString &); QDateTime localToUTC(const QDateTime &time) const; QDateTime utcToLocal(const QDateTime &time) const; QString mUid; QString mBody; QString mCategories; QDateTime mCreationDate; QDateTime mLastModified; Sensitivity mSensitivity; QTimeZone mTimeZone; // KPilot synchronization stuff bool mHasPilotSyncId, mHasPilotSyncStatus; unsigned long mPilotSyncId; int mPilotSyncStatus; }; } #endif // KOLABV2_KOLABBASE_H diff --git a/resources/kolab/pimkolab/kolabformatV2/task.h b/resources/kolab/pimkolab/kolabformatV2/task.h index 3240ae796..dc4fca76c 100644 --- a/resources/kolab/pimkolab/kolabformatV2/task.h +++ b/resources/kolab/pimkolab/kolabformatV2/task.h @@ -1,129 +1,129 @@ /* This file is part of the kolab resource - the implementation of the Kolab storage format. See www.kolab.org for documentation on this. Copyright (c) 2004 Bo Thorsen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef KOLABV2_TASK_H #define KOLABV2_TASK_H #include "incidence.h" #include -#include +#include class QDomElement; namespace KCal { class ResourceKolab; } namespace KolabV2 { /** * This class represents a task, and knows how to load/save it * from/to XML, and from/to a KCalendarCore::Todo. * The instances of this class are temporary, only used to convert * one to the other. */ class Task : public Incidence { public: /// Use this to parse an xml string to a task entry /// The caller is responsible for deleting the returned task static KCalendarCore::Todo::Ptr fromXml(const QDomDocument &xmlDoc, const QString &tz /*, KCalendarCore::ResourceKolab *res = 0, const QString& subResource = QString(), quint32 sernum = 0 */); /// Use this to get an xml string describing this task entry static QString taskToXML(const KCalendarCore::Todo::Ptr &, const QString &tz); explicit Task( /*KCalendarCore::ResourceKolab *res, const QString& subResource, quint32 sernum,*/ const QString &tz, const KCalendarCore::Todo::Ptr &todo = KCalendarCore::Todo::Ptr()); ~Task() override; QString type() const override { return QStringLiteral("Task"); } void saveTo(const KCalendarCore::Todo::Ptr &todo); virtual void setPercentCompleted(int percent); virtual int percentCompleted() const; virtual void setStatus(KCalendarCore::Incidence::Status status); virtual KCalendarCore::Incidence::Status status() const; virtual void setParent(const QString &parentUid); virtual QString parent() const; virtual void setHasStartDate(bool); virtual bool hasStartDate() const; virtual void setDueDate(const QDateTime &date); virtual void setDueDate(const QString &date); virtual void setDueDate(const QDate &date); virtual QDateTime dueDate() const; virtual bool hasDueDate() const; virtual void setCompletedDate(const QDateTime &date); virtual QDateTime completedDate() const; virtual bool hasCompletedDate() const; // Load the attributes of this class bool loadAttribute(QDomElement &) override; // Save the attributes of this class bool saveAttributes(QDomElement &) const override; // Load this task by reading the XML file bool loadXML(const QDomDocument &xml) override; // Serialize this task to an XML string QString saveXML() const override; protected: // Read all known fields from this ical todo void setFields(const KCalendarCore::Todo::Ptr &); int mPercentCompleted; KCalendarCore::Incidence::Status mStatus; QString mParent; bool mHasStartDate; bool mHasDueDate; QDateTime mDueDate; bool mHasCompletedDate; QDateTime mCompletedDate; }; } #endif // KOLAB_TASK_H diff --git a/resources/mbox/deleteditemsattribute.h b/resources/mbox/deleteditemsattribute.h index 858f60157..37ed06c40 100644 --- a/resources/mbox/deleteditemsattribute.h +++ b/resources/mbox/deleteditemsattribute.h @@ -1,64 +1,64 @@ /* Copyright (c) 2009 Bertjan Broeksema This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DELETEDITEMSATTRIBUTE_H #define DELETEDITEMSATTRIBUTE_H #include -#include +#include #include /** * This attribute stores a list of offsets in the mbox file of mails which are * deleted but not yet actually removed from the file yet. */ class DeletedItemsAttribute : public Akonadi::Attribute { public: DeletedItemsAttribute(); DeletedItemsAttribute(const DeletedItemsAttribute &other); ~DeletedItemsAttribute(); void addDeletedItemOffset(quint64); DeletedItemsAttribute *clone() const override; QSet deletedItemOffsets() const; KMBox::MBoxEntry::List deletedItemEntries() const; void deserialize(const QByteArray &data) override; /** * Returns the number of offsets stored in this attribute. */ int offsetCount() const; QByteArray serialized() const override; QByteArray type() const override; bool operator ==(const DeletedItemsAttribute &other) const; private: QSet mDeletedItemOffsets; }; #endif