diff --git a/resources/ews/abchperson/ewsabchpersonhandler.cpp b/resources/ews/abchperson/ewsabchpersonhandler.cpp index 39557f640..f6fd461d9 100644 --- a/resources/ews/abchperson/ewsabchpersonhandler.cpp +++ b/resources/ews/abchperson/ewsabchpersonhandler.cpp @@ -1,81 +1,77 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsabchpersonhandler.h" #include #include "ewsfetchabchpersondetailjob.h" #include "ewsmodifyabchpersonjob.h" #include "ewscreateabchpersonjob.h" using namespace Akonadi; EwsAbchPersonHandler::EwsAbchPersonHandler() { } EwsAbchPersonHandler::~EwsAbchPersonHandler() { } EwsItemHandler *EwsAbchPersonHandler::factory() { return new EwsAbchPersonHandler(); } -EwsFetchItemDetailJob *EwsAbchPersonHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) +EwsFetchItemDetailJob *EwsAbchPersonHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) { return new EwsFetchAbchContactDetailsJob(client, parent, collection); } void EwsAbchPersonHandler::setSeenFlag(Item &item, bool value) { Q_UNUSED(item) Q_UNUSED(value) } QString EwsAbchPersonHandler::mimeType() { return KCalCore::Todo::todoMimeType(); } bool EwsAbchPersonHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) { Q_UNUSED(item) Q_UNUSED(ewsItem) return true; } -EwsModifyItemJob *EwsAbchPersonHandler::modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) +EwsModifyItemJob *EwsAbchPersonHandler::modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) { return new EwsModifyAbchPersonJob(client, items, parts, parent); } -EwsCreateItemJob *EwsAbchPersonHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateItemJob *EwsAbchPersonHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) { return new EwsCreateAbchPersonJob(client, item, collection, tagStore, parent); } EWS_DECLARE_ITEM_HANDLER(EwsAbchPersonHandler, EwsItemTypeAbchPerson) diff --git a/resources/ews/abchperson/ewsabchpersonhandler.h b/resources/ews/abchperson/ewsabchpersonhandler.h index d0225bb4d..0c079065e 100644 --- a/resources/ews/abchperson/ewsabchpersonhandler.h +++ b/resources/ews/abchperson/ewsabchpersonhandler.h @@ -1,44 +1,40 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSABCHPERSONHANDLER_H #define EWSABCHPERSONHANDLER_H #include "ewsitemhandler.h" class EwsAbchPersonHandler : public EwsItemHandler { public: EwsAbchPersonHandler(); ~EwsAbchPersonHandler() override; - EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) override; + EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) override; void setSeenFlag(Akonadi::Item &item, bool value) override; QString mimeType() override; bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) override; - EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) override; - EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) override; + EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) override; + EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) override; static EwsItemHandler *factory(); }; #endif diff --git a/resources/ews/abchperson/ewscreateabchpersonjob.cpp b/resources/ews/abchperson/ewscreateabchpersonjob.cpp index 624f7ef7a..b77ac33b3 100644 --- a/resources/ews/abchperson/ewscreateabchpersonjob.cpp +++ b/resources/ews/abchperson/ewscreateabchpersonjob.cpp @@ -1,46 +1,45 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreateabchpersonjob.h" #include "ewsresource_debug.h" -EwsCreateAbchPersonJob::EwsCreateAbchPersonJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, - EwsResource *parent) +EwsCreateAbchPersonJob::EwsCreateAbchPersonJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) : EwsCreateItemJob(client, item, collection, tagStore, parent) { } + EwsCreateAbchPersonJob::~EwsCreateAbchPersonJob() { } void EwsCreateAbchPersonJob::doStart() { qCWarning(EWSRES_LOG) << QStringLiteral("Abch person item creation not implemented!"); emitResult(); } bool EwsCreateAbchPersonJob::setSend(bool send) { Q_UNUSED(send) qCWarning(EWSRES_LOG) << QStringLiteral("Sending abch person items is not supported!"); return false; } diff --git a/resources/ews/abchperson/ewscreateabchpersonjob.h b/resources/ews/abchperson/ewscreateabchpersonjob.h index 359c52b7f..1bd245a84 100644 --- a/resources/ews/abchperson/ewscreateabchpersonjob.h +++ b/resources/ews/abchperson/ewscreateabchpersonjob.h @@ -1,37 +1,36 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATEABCHPERSONJOB_H #define EWSCREATEABCHPERSONJOB_H #include "ewscreateitemjob.h" class EwsCreateAbchPersonJob : public EwsCreateItemJob { Q_OBJECT public: - EwsCreateAbchPersonJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateAbchPersonJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateAbchPersonJob() override; bool setSend(bool send = true) override; protected: void doStart() override; }; #endif diff --git a/resources/ews/abchperson/ewsfetchabchpersondetailjob.cpp b/resources/ews/abchperson/ewsfetchabchpersondetailjob.cpp index 0d482c2ec..b3616e23c 100644 --- a/resources/ews/abchperson/ewsfetchabchpersondetailjob.cpp +++ b/resources/ews/abchperson/ewsfetchabchpersondetailjob.cpp @@ -1,62 +1,60 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchabchpersondetailjob.h" #include "ewsitemshape.h" #include "ewsgetitemrequest.h" #include "ewsmailbox.h" #include "ewsresource_debug.h" using namespace Akonadi; EwsFetchAbchContactDetailsJob::EwsFetchAbchContactDetailsJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) : EwsFetchItemDetailJob(client, parent, collection) { EwsItemShape shape(EwsShapeIdOnly); mRequest->setItemShape(shape); } - EwsFetchAbchContactDetailsJob::~EwsFetchAbchContactDetailsJob() { } void EwsFetchAbchContactDetailsJob::processItems(const QList &responses) { Item::List::iterator it = mChangedItems.begin(); for (const EwsGetItemRequest::Response &resp : responses) { Item &item = *it; if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item %1").arg(item.remoteId()); continue; } //const EwsItem &ewsItem = resp.item(); // TODO: Implement ++it; } emitResult(); } - diff --git a/resources/ews/abchperson/ewsmodifyabchpersonjob.cpp b/resources/ews/abchperson/ewsmodifyabchpersonjob.cpp index 443bfd099..8fd856673 100644 --- a/resources/ews/abchperson/ewsmodifyabchpersonjob.cpp +++ b/resources/ews/abchperson/ewsmodifyabchpersonjob.cpp @@ -1,37 +1,37 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifyabchpersonjob.h" #include "ewsresource_debug.h" -EwsModifyAbchPersonJob::EwsModifyAbchPersonJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) +EwsModifyAbchPersonJob::EwsModifyAbchPersonJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) : EwsModifyItemJob(client, items, parts, parent) { } + EwsModifyAbchPersonJob::~EwsModifyAbchPersonJob() { } void EwsModifyAbchPersonJob::start() { qCWarning(EWSRES_LOG) << QStringLiteral("Abch person item modification not implemented!"); emitResult(); } diff --git a/resources/ews/abchperson/ewsmodifyabchpersonjob.h b/resources/ews/abchperson/ewsmodifyabchpersonjob.h index 817343db7..3c71f576a 100644 --- a/resources/ews/abchperson/ewsmodifyabchpersonjob.h +++ b/resources/ews/abchperson/ewsmodifyabchpersonjob.h @@ -1,35 +1,34 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYABCHPERSONJOB_H #define EWSMODIFYABCHPERSONJOB_H #include "ewsmodifyitemjob.h" class EwsModifyAbchPersonJob : public EwsModifyItemJob { Q_OBJECT public: - EwsModifyAbchPersonJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyAbchPersonJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyAbchPersonJob() override; void start() override; }; #endif diff --git a/resources/ews/calendar/ewscalendarhandler.cpp b/resources/ews/calendar/ewscalendarhandler.cpp index 5ed52993e..e18d6981c 100644 --- a/resources/ews/calendar/ewscalendarhandler.cpp +++ b/resources/ews/calendar/ewscalendarhandler.cpp @@ -1,81 +1,77 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscalendarhandler.h" #include #include "ewsfetchcalendardetailjob.h" #include "ewsmodifycalendarjob.h" #include "ewscreatecalendarjob.h" using namespace Akonadi; EwsCalendarHandler::EwsCalendarHandler() { } EwsCalendarHandler::~EwsCalendarHandler() { } EwsItemHandler *EwsCalendarHandler::factory() { return new EwsCalendarHandler(); } -EwsFetchItemDetailJob *EwsCalendarHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) +EwsFetchItemDetailJob *EwsCalendarHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) { return new EwsFetchCalendarDetailJob(client, parent, collection); } void EwsCalendarHandler::setSeenFlag(Item &item, bool value) { Q_UNUSED(item) Q_UNUSED(value) } QString EwsCalendarHandler::mimeType() { return KCalCore::Event::eventMimeType(); } bool EwsCalendarHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) { Q_UNUSED(item) Q_UNUSED(ewsItem) return true; } -EwsModifyItemJob *EwsCalendarHandler::modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) +EwsModifyItemJob *EwsCalendarHandler::modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) { return new EwsModifyCalendarJob(client, items, parts, parent); } -EwsCreateItemJob *EwsCalendarHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateItemJob *EwsCalendarHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) { return new EwsCreateCalendarJob(client, item, collection, tagStore, parent); } EWS_DECLARE_ITEM_HANDLER(EwsCalendarHandler, EwsItemTypeCalendarItem) diff --git a/resources/ews/calendar/ewscalendarhandler.h b/resources/ews/calendar/ewscalendarhandler.h index ce640808a..8ce0d4c60 100644 --- a/resources/ews/calendar/ewscalendarhandler.h +++ b/resources/ews/calendar/ewscalendarhandler.h @@ -1,44 +1,40 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCALENDARHANDLER_H #define EWSCALENDARHANDLER_H #include "ewsitemhandler.h" class EwsCalendarHandler : public EwsItemHandler { public: EwsCalendarHandler(); ~EwsCalendarHandler() override; - EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) override; + EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) override; void setSeenFlag(Akonadi::Item &item, bool value) override; QString mimeType() override; bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) override; - EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) override; - EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) override; + EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) override; + EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) override; static EwsItemHandler *factory(); }; #endif diff --git a/resources/ews/calendar/ewscreatecalendarjob.cpp b/resources/ews/calendar/ewscreatecalendarjob.cpp index 042bfe594..a145631ed 100644 --- a/resources/ews/calendar/ewscreatecalendarjob.cpp +++ b/resources/ews/calendar/ewscreatecalendarjob.cpp @@ -1,46 +1,45 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreatecalendarjob.h" #include "ewsresource_debug.h" -EwsCreateCalendarJob::EwsCreateCalendarJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateCalendarJob::EwsCreateCalendarJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) : EwsCreateItemJob(client, item, collection, tagStore, parent) { } + EwsCreateCalendarJob::~EwsCreateCalendarJob() { } void EwsCreateCalendarJob::doStart() { qCWarning(EWSRES_LOG) << QStringLiteral("Calendar item creation not implemented!"); emitResult(); } bool EwsCreateCalendarJob::setSend(bool send) { Q_UNUSED(send) qCWarning(EWSRES_LOG) << QStringLiteral("Sending calendar items is not supported!"); return false; } diff --git a/resources/ews/calendar/ewscreatecalendarjob.h b/resources/ews/calendar/ewscreatecalendarjob.h index 311e16f12..b46d61eb4 100644 --- a/resources/ews/calendar/ewscreatecalendarjob.h +++ b/resources/ews/calendar/ewscreatecalendarjob.h @@ -1,37 +1,36 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATECALENDARJOB_H #define EWSCREATECALENDARJOB_H #include "ewscreateitemjob.h" class EwsCreateCalendarJob : public EwsCreateItemJob { Q_OBJECT public: - EwsCreateCalendarJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateCalendarJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateCalendarJob() override; bool setSend(bool send = true) override; protected: void doStart() override; }; #endif diff --git a/resources/ews/calendar/ewsfetchcalendardetailjob.cpp b/resources/ews/calendar/ewsfetchcalendardetailjob.cpp index 8e12c9cc7..c24ebd637 100644 --- a/resources/ews/calendar/ewsfetchcalendardetailjob.cpp +++ b/resources/ews/calendar/ewsfetchcalendardetailjob.cpp @@ -1,219 +1,217 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchcalendardetailjob.h" #include #include #include #include #include "ewsresource_debug.h" #include "ewsgetitemrequest.h" #include "ewsitemshape.h" #include "ewsmailbox.h" #include "ewsoccurrence.h" using namespace Akonadi; -EwsFetchCalendarDetailJob::EwsFetchCalendarDetailJob(EwsClient &client, QObject *parent, - const Collection &collection) +EwsFetchCalendarDetailJob::EwsFetchCalendarDetailJob(EwsClient &client, QObject *parent, const Collection &collection) : EwsFetchItemDetailJob(client, parent, collection) { EwsItemShape shape(EwsShapeIdOnly); /* shape << EwsPropertyField(QStringLiteral("calendar:UID")); shape << EwsPropertyField(QStringLiteral("item:Subject")); shape << EwsPropertyField(QStringLiteral("item:Body")); shape << EwsPropertyField(QStringLiteral("calendar:Organizer")); shape << EwsPropertyField(QStringLiteral("calendar:RequiredAttendees")); shape << EwsPropertyField(QStringLiteral("calendar:OptionalAttendees")); shape << EwsPropertyField(QStringLiteral("calendar:Resources")); shape << EwsPropertyField(QStringLiteral("calendar:Start")); shape << EwsPropertyField(QStringLiteral("calendar:End")); shape << EwsPropertyField(QStringLiteral("calendar:IsAllDayEvent")); shape << EwsPropertyField(QStringLiteral("calendar:LegacyFreeBusyStatus")); shape << EwsPropertyField(QStringLiteral("calendar:AppointmentSequenceNumber")); shape << EwsPropertyField(QStringLiteral("calendar:IsRecurring")); shape << EwsPropertyField(QStringLiteral("calendar:Recurrence")); shape << EwsPropertyField(QStringLiteral("calendar:FirstOccurrence")); shape << EwsPropertyField(QStringLiteral("calendar:LastOccurrence")); shape << EwsPropertyField(QStringLiteral("calendar:ModifiedOccurrences")); shape << EwsPropertyField(QStringLiteral("calendar:DeletedOccurrences")); shape << EwsPropertyField(QStringLiteral("calendar:StartTimeZone")); shape << EwsPropertyField(QStringLiteral("calendar:EndTimeZone")); shape << EwsPropertyField(QStringLiteral("calendar:MyResponseType")); shape << EwsPropertyField(QStringLiteral("item:HasAttachments")); shape << EwsPropertyField(QStringLiteral("item:Attachments"));*/ // shape << EwsPropertyField(QStringLiteral("item:Attachments")); shape << EwsPropertyField(QStringLiteral("calendar:ModifiedOccurrences")); shape << EwsPropertyField(QStringLiteral("calendar:DeletedOccurrences")); shape << EwsPropertyField(QStringLiteral("item:Body")); // shape << EwsPropertyField(QStringLiteral("item:Culture")); shape << EwsPropertyField(QStringLiteral("item:MimeContent")); shape << EwsPropertyField(QStringLiteral("item:Subject")); // shape << EwsPropertyField(QStringLiteral("calendar:TimeZone")); mRequest->setItemShape(shape); } - EwsFetchCalendarDetailJob::~EwsFetchCalendarDetailJob() { } void EwsFetchCalendarDetailJob::processItems(const QList &responses) { Item::List::iterator it = mChangedItems.begin(); KCalCore::ICalFormat format; EwsId::List addItems; Q_FOREACH (const EwsGetItemRequest::Response &resp, responses) { Item &item = *it; if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item %1").arg(item.remoteId()); continue; } const EwsItem &ewsItem = resp.item(); QString mimeContent = ewsItem[EwsItemFieldMimeContent].toString(); KCalCore::Calendar::Ptr memcal(new KCalCore::MemoryCalendar(QTimeZone::utc())); format.fromString(memcal, mimeContent); qCDebugNC(EWSRES_LOG) << QStringLiteral("Found %1 events").arg(memcal->events().count()); KCalCore::Incidence::Ptr incidence; if (memcal->events().count() > 1) { Q_FOREACH (const KCalCore::Event::Ptr &event, memcal->events()) { qCDebugNC(EWSRES_LOG) << QString::number(event->recurrence()->recurrenceType(), 16) << event->recurrenceId() << event->recurrenceId().isValid(); if (!event->recurrenceId().isValid()) { incidence = event; } } const EwsOccurrence::List excList = ewsItem[EwsItemFieldModifiedOccurrences].value(); for (const EwsOccurrence &exc : excList) { addItems.append(exc.itemId()); } } else if (memcal->events().count() == 1) { incidence = memcal->events()[0]; } //KCalCore::Incidence::Ptr incidence(format.fromString(mimeContent)); if (incidence) { QDateTime dt(incidence->dtStart()); if (dt.isValid()) { incidence->setDtStart(dt); } if (incidence->type() == KCalCore::Incidence::TypeEvent) { - KCalCore::Event *event = reinterpret_cast(incidence.data()); + KCalCore::Event *event = reinterpret_cast(incidence.data()); dt = event->dtEnd(); if (dt.isValid()) { event->setDtEnd(dt); } } dt = incidence->recurrenceId(); if (dt.isValid()) { incidence->setRecurrenceId(dt); } item.setPayload(incidence); } ++it; } if (addItems.isEmpty()) { emitResult(); } else { EwsGetItemRequest *req = new EwsGetItemRequest(mClient, this); EwsItemShape shape(EwsShapeIdOnly); // shape << EwsPropertyField(QStringLiteral("item:Attachments")); shape << EwsPropertyField(QStringLiteral("item:Body")); shape << EwsPropertyField(QStringLiteral("item:MimeContent")); // shape << EwsPropertyField(QStringLiteral("calendar:TimeZone")); // shape << EwsPropertyField(QStringLiteral("item:Culture")); req->setItemShape(shape); req->setItemIds(addItems); connect(req, &KJob::result, this, &EwsFetchCalendarDetailJob::exceptionItemsFetched); req->start(); } } void EwsFetchCalendarDetailJob::exceptionItemsFetched(KJob *job) { if (job->error()) { setError(job->error()); setErrorText(job->errorText()); emitResult(); return; } - EwsGetItemRequest *req = qobject_cast(job); + EwsGetItemRequest *req = qobject_cast(job); if (!req) { setError(1); setErrorText(QStringLiteral("Job is not an instance of EwsGetItemRequest")); emitResult(); return; } KCalCore::ICalFormat format; Q_FOREACH (const EwsGetItemRequest::Response &resp, req->responses()) { if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item."); continue; } const EwsItem &ewsItem = resp.item(); Item item(KCalCore::Event::eventMimeType()); item.setParentCollection(mCollection); EwsId id = ewsItem[EwsItemFieldItemId].value(); item.setRemoteId(id.id()); item.setRemoteRevision(id.changeKey()); QString mimeContent = ewsItem[EwsItemFieldMimeContent].toString(); KCalCore::Calendar::Ptr memcal(new KCalCore::MemoryCalendar(QTimeZone::utc())); format.fromString(memcal, mimeContent); KCalCore::Incidence::Ptr incidence(memcal->events().last()); incidence->clearRecurrence(); QDateTime dt(incidence->dtStart()); if (dt.isValid()) { incidence->setDtStart(dt); } if (incidence->type() == KCalCore::Incidence::TypeEvent) { - KCalCore::Event *event = reinterpret_cast(incidence.data()); + KCalCore::Event *event = reinterpret_cast(incidence.data()); dt = event->dtEnd(); if (dt.isValid()) { event->setDtEnd(dt); } } dt = incidence->recurrenceId(); if (dt.isValid()) { incidence->setRecurrenceId(dt); } item.setPayload(incidence); mChangedItems.append(item); } emitResult(); } diff --git a/resources/ews/calendar/ewsmodifycalendarjob.cpp b/resources/ews/calendar/ewsmodifycalendarjob.cpp index c25579899..24735f369 100644 --- a/resources/ews/calendar/ewsmodifycalendarjob.cpp +++ b/resources/ews/calendar/ewsmodifycalendarjob.cpp @@ -1,37 +1,37 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifycalendarjob.h" #include "ewsresource_debug.h" -EwsModifyCalendarJob::EwsModifyCalendarJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) +EwsModifyCalendarJob::EwsModifyCalendarJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) : EwsModifyItemJob(client, items, parts, parent) { } + EwsModifyCalendarJob::~EwsModifyCalendarJob() { } void EwsModifyCalendarJob::start() { qCWarning(EWSRES_LOG) << QStringLiteral("Calendar item modification not implemented!"); emitResult(); } diff --git a/resources/ews/calendar/ewsmodifycalendarjob.h b/resources/ews/calendar/ewsmodifycalendarjob.h index 5b154fb38..55fb82cda 100644 --- a/resources/ews/calendar/ewsmodifycalendarjob.h +++ b/resources/ews/calendar/ewsmodifycalendarjob.h @@ -1,35 +1,34 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYCALENDARJOB_H #define EWSMODIFYCALENDARJOB_H #include "ewsmodifyitemjob.h" class EwsModifyCalendarJob : public EwsModifyItemJob { Q_OBJECT public: - EwsModifyCalendarJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyCalendarJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyCalendarJob() override; void start() override; }; #endif diff --git a/resources/ews/contact/ewscontacthandler.cpp b/resources/ews/contact/ewscontacthandler.cpp index fdd665c13..55e9dbde1 100644 --- a/resources/ews/contact/ewscontacthandler.cpp +++ b/resources/ews/contact/ewscontacthandler.cpp @@ -1,82 +1,78 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscontacthandler.h" #include #include #include "ewsfetchcontactdetailjob.h" #include "ewsmodifycontactjob.h" #include "ewscreatecontactjob.h" using namespace Akonadi; EwsContactHandler::EwsContactHandler() { } EwsContactHandler::~EwsContactHandler() { } EwsItemHandler *EwsContactHandler::factory() { return new EwsContactHandler(); } -EwsFetchItemDetailJob *EwsContactHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) +EwsFetchItemDetailJob *EwsContactHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) { return new EwsFetchContactDetailJob(client, parent, collection); } void EwsContactHandler::setSeenFlag(Item &item, bool value) { Q_UNUSED(item) Q_UNUSED(value) } QString EwsContactHandler::mimeType() { return KContacts::Addressee::mimeType(); } bool EwsContactHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) { Q_UNUSED(item) Q_UNUSED(ewsItem) return true; } -EwsModifyItemJob *EwsContactHandler::modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) +EwsModifyItemJob *EwsContactHandler::modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) { return new EwsModifyContactJob(client, items, parts, parent); } -EwsCreateItemJob *EwsContactHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateItemJob *EwsContactHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) { return new EwsCreateContactJob(client, item, collection, tagStore, parent); } EWS_DECLARE_ITEM_HANDLER(EwsContactHandler, EwsItemTypeContact) diff --git a/resources/ews/contact/ewscontacthandler.h b/resources/ews/contact/ewscontacthandler.h index aa091125e..293f51437 100644 --- a/resources/ews/contact/ewscontacthandler.h +++ b/resources/ews/contact/ewscontacthandler.h @@ -1,44 +1,40 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCONTACTHANDLER_H #define EWSCONTACTHANDLER_H #include "ewsitemhandler.h" class EwsContactHandler : public EwsItemHandler { public: EwsContactHandler(); ~EwsContactHandler() override; - EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) override; + EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) override; void setSeenFlag(Akonadi::Item &item, bool value) override; QString mimeType() override; bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) override; - EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) override; - EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) override; + EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) override; + EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) override; static EwsItemHandler *factory(); }; #endif diff --git a/resources/ews/contact/ewscreatecontactjob.cpp b/resources/ews/contact/ewscreatecontactjob.cpp index 224513f14..2756f5303 100644 --- a/resources/ews/contact/ewscreatecontactjob.cpp +++ b/resources/ews/contact/ewscreatecontactjob.cpp @@ -1,46 +1,45 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreatecontactjob.h" #include "ewsresource_debug.h" -EwsCreateContactJob::EwsCreateContactJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateContactJob::EwsCreateContactJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) : EwsCreateItemJob(client, item, collection, tagStore, parent) { } + EwsCreateContactJob::~EwsCreateContactJob() { } void EwsCreateContactJob::doStart() { qCWarning(EWSRES_LOG) << QStringLiteral("Contact item creation not implemented!"); emitResult(); } bool EwsCreateContactJob::setSend(bool send) { Q_UNUSED(send) qCWarning(EWSRES_LOG) << QStringLiteral("Sending contact items is not supported!"); return false; } diff --git a/resources/ews/contact/ewscreatecontactjob.h b/resources/ews/contact/ewscreatecontactjob.h index bbea020c0..e049e0133 100644 --- a/resources/ews/contact/ewscreatecontactjob.h +++ b/resources/ews/contact/ewscreatecontactjob.h @@ -1,37 +1,36 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATECONTACTJOB_H #define EWSCREATECONTACTJOB_H #include "ewscreateitemjob.h" class EwsCreateContactJob : public EwsCreateItemJob { Q_OBJECT public: - EwsCreateContactJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateContactJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateContactJob() override; bool setSend(bool send = true) override; protected: void doStart() override; }; #endif diff --git a/resources/ews/contact/ewsfetchcontactdetailjob.cpp b/resources/ews/contact/ewsfetchcontactdetailjob.cpp index 5f3ac57c6..b7e614071 100644 --- a/resources/ews/contact/ewsfetchcontactdetailjob.cpp +++ b/resources/ews/contact/ewsfetchcontactdetailjob.cpp @@ -1,60 +1,59 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchcontactdetailjob.h" #include "ewsitemshape.h" #include "ewsgetitemrequest.h" #include "ewsmailbox.h" #include "ewsresource_debug.h" using namespace Akonadi; EwsFetchContactDetailJob::EwsFetchContactDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) : EwsFetchItemDetailJob(client, parent, collection) { EwsItemShape shape(EwsShapeIdOnly); mRequest->setItemShape(shape); } - EwsFetchContactDetailJob::~EwsFetchContactDetailJob() { } void EwsFetchContactDetailJob::processItems(const QList &responses) { Item::List::iterator it = mChangedItems.begin(); for (const EwsGetItemRequest::Response &resp : responses) { Item &item = *it; if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item %1").arg(item.remoteId()); continue; } //const EwsItem &ewsItem = resp.item(); // TODO: Implement ++it; } emitResult(); } diff --git a/resources/ews/contact/ewsmodifycontactjob.cpp b/resources/ews/contact/ewsmodifycontactjob.cpp index 5383c06c2..0000b5094 100644 --- a/resources/ews/contact/ewsmodifycontactjob.cpp +++ b/resources/ews/contact/ewsmodifycontactjob.cpp @@ -1,37 +1,37 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifycontactjob.h" #include "ewsresource_debug.h" -EwsModifyContactJob::EwsModifyContactJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) +EwsModifyContactJob::EwsModifyContactJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) : EwsModifyItemJob(client, items, parts, parent) { } + EwsModifyContactJob::~EwsModifyContactJob() { } void EwsModifyContactJob::start() { qCWarning(EWSRES_LOG) << QStringLiteral("Contact item modification not implemented!"); emitResult(); } diff --git a/resources/ews/contact/ewsmodifycontactjob.h b/resources/ews/contact/ewsmodifycontactjob.h index dbcdbcd3e..af9939940 100644 --- a/resources/ews/contact/ewsmodifycontactjob.h +++ b/resources/ews/contact/ewsmodifycontactjob.h @@ -1,35 +1,34 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYCONTACTJOB_H #define EWSMODIFYCONTACTJOB_H #include "ewsmodifyitemjob.h" class EwsModifyContactJob : public EwsModifyItemJob { Q_OBJECT public: - EwsModifyContactJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyContactJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyContactJob() override; void start() override; }; #endif diff --git a/resources/ews/ewsautodiscoveryjob.cpp b/resources/ews/ewsautodiscoveryjob.cpp index d41f2d307..687c86854 100644 --- a/resources/ews/ewsautodiscoveryjob.cpp +++ b/resources/ews/ewsautodiscoveryjob.cpp @@ -1,151 +1,154 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsautodiscoveryjob.h" #include "ewspoxautodiscoverrequest.h" #include "ewsresource_debug.h" #include -EwsAutodiscoveryJob::EwsAutodiscoveryJob(const QString &email, const QString &username, const QString &password, - const QString &userAgent, bool enableNTLMv2, QObject *parent) - : EwsJob(parent), mEmail(email), mUsername(username), mPassword(password), mUserAgent(userAgent), - mEnableNTLMv2(enableNTLMv2), mUsedCreds(false) +EwsAutodiscoveryJob::EwsAutodiscoveryJob(const QString &email, const QString &username, const QString &password, const QString &userAgent, bool enableNTLMv2, QObject *parent) + : EwsJob(parent) + , mEmail(email) + , mUsername(username) + , mPassword(password) + , mUserAgent(userAgent) + , mEnableNTLMv2(enableNTLMv2) + , mUsedCreds(false) { } EwsAutodiscoveryJob::~EwsAutodiscoveryJob() { } void EwsAutodiscoveryJob::start() { parseEmail(); if (!mUrlQueue.isEmpty()) { sendNextRequest(false); } } void EwsAutodiscoveryJob::parseEmail() { const int atIndex = mEmail.indexOf(QLatin1Char('@')); if (atIndex < 0) { setErrorMsg(i18n("Incorrect email address")); emitResult(); return; } const QString domain = mEmail.mid(atIndex + 1); if (domain.isEmpty()) { setErrorMsg(i18n("Incorrect email address")); emitResult(); return; } addUrls(domain); } void EwsAutodiscoveryJob::addUrls(const QString &domain) { mUrlQueue.enqueue(QStringLiteral("https://") + domain + QStringLiteral("/autodiscover/autodiscover.xml")); mUrlQueue.enqueue(QStringLiteral("https://autodiscover.") + domain + QStringLiteral("/autodiscover/autodiscover.xml")); mUrlQueue.enqueue(QStringLiteral("http://") + domain + QStringLiteral("/autodiscover/autodiscover.xml")); mUrlQueue.enqueue(QStringLiteral("http://autodiscover.") + domain + QStringLiteral("/autodiscover/autodiscover.xml")); } void EwsAutodiscoveryJob::sendNextRequest(bool useCreds) { QUrl url(mUrlQueue.head()); if (useCreds) { url.setUserName(mUsername); url.setPassword(mPassword); } mUsedCreds = useCreds; EwsPoxAutodiscoverRequest *req = new EwsPoxAutodiscoverRequest(url, mEmail, mUserAgent, - mEnableNTLMv2, this); + mEnableNTLMv2, this); connect(req, &EwsPoxAutodiscoverRequest::result, this, &EwsAutodiscoveryJob::autodiscoveryRequestFinished); req->start(); } void EwsAutodiscoveryJob::autodiscoveryRequestFinished(KJob *job) { - EwsPoxAutodiscoverRequest *req = qobject_cast(job); + EwsPoxAutodiscoverRequest *req = qobject_cast(job); if (!req) { setErrorMsg(QStringLiteral("Invalid EwsPoxAutodiscoverRequest job object")); emitResult(); return; } if (req->error()) { - - if (req->error() == 401 && !mUsedCreds && - req->lastHttpUrl().scheme() != QStringLiteral("http")) { // Don't try authentication over HTTP - + if (req->error() == 401 && !mUsedCreds + && req->lastHttpUrl().scheme() != QStringLiteral("http")) { // Don't try authentication over HTTP /* The 401 error may have come from an URL different to the original one (due to * redirections). When the original URL is retried with credentials KIO HTTP will issue * a warning that an authenticated request is made to a host that never asked for it. * To fix this restart the request using the last URL that resulted in the 401 code. */ mUrlQueue.head() = req->lastHttpUrl().toString(); sendNextRequest(true); return; } else { mUrlQueue.removeFirst(); } if (mUrlQueue.isEmpty()) { setErrorText(job->errorText()); setError(job->error()); emitResult(); } else { sendNextRequest(false); } } else { switch (req->action()) { - case EwsPoxAutodiscoverRequest::Settings: { + case EwsPoxAutodiscoverRequest::Settings: + { EwsPoxAutodiscoverRequest::Protocol proto = req->protocol(EwsPoxAutodiscoverRequest::ExchangeProto); if (!proto.isValid()) { setErrorMsg(i18n("Exchange protocol information not found")); } else { mEwsUrl = proto.ewsUrl(); mOabUrl = proto.oabUrl(); } emitResult(); break; } case EwsPoxAutodiscoverRequest::RedirectAddr: qCDebug(EWSRES_LOG) << "Redirected to e-mail addr" << req->redirectAddr(); mEmail = req->redirectAddr(); mUrlQueue.clear(); parseEmail(); if (!mUrlQueue.isEmpty()) { sendNextRequest(false); } break; case EwsPoxAutodiscoverRequest::RedirectUrl: qCDebug(EWSRES_LOG) << "Redirected to URL" << req->redirectUrl(); mUrlQueue.clear(); mUrlQueue.enqueue(req->redirectUrl()); sendNextRequest(false); break; } } } diff --git a/resources/ews/ewsautodiscoveryjob.h b/resources/ews/ewsautodiscoveryjob.h index 052162c10..4d614ffd7 100644 --- a/resources/ews/ewsautodiscoveryjob.h +++ b/resources/ews/ewsautodiscoveryjob.h @@ -1,67 +1,67 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSAUTODISCOVERYJOB_H #define EWSAUTODISCOVERYJOB_H #include #include "ewsjob.h" class EwsAutodiscoveryJob : public EwsJob { Q_OBJECT public: - EwsAutodiscoveryJob(const QString &email, const QString &username, const QString &password, const QString &userAgent, - bool enableNTLMv2, QObject *parent); + EwsAutodiscoveryJob(const QString &email, const QString &username, const QString &password, const QString &userAgent, bool enableNTLMv2, QObject *parent); ~EwsAutodiscoveryJob() override; void start() override; const QString &ewsUrl() const { return mEwsUrl; } + const QString &oabUrl() const { return mOabUrl; } private Q_SLOTS: void autodiscoveryRequestFinished(KJob *job); private: void addUrls(const QString &domain); void sendNextRequest(bool useCreds); void parseEmail(); QString mEmail; QString mUsername; QString mPassword; QString mUserAgent; bool mEnableNTLMv2; QQueue mUrlQueue; QString mEwsUrl; QString mOabUrl; bool mUsedCreds; }; #endif diff --git a/resources/ews/ewsclient/auth/ewsoauth.cpp b/resources/ews/ewsclient/auth/ewsoauth.cpp index 003374eb8..e8565e7cf 100644 --- a/resources/ews/ewsclient/auth/ewsoauth.cpp +++ b/resources/ews/ewsclient/auth/ewsoauth.cpp @@ -1,493 +1,513 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 "ewsoauth.h" #include #ifdef EWSOAUTH_UNITTEST #include "ewsoauth_ut_mock.h" using namespace Mock; #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_QCA #include "ewspkeyauthjob.h" #endif #endif #include "ewsclient_debug.h" static const auto o365AuthorizationUrl = QUrl(QStringLiteral("https://login.microsoftonline.com/common/oauth2/authorize")); static const auto o365AccessTokenUrl = QUrl(QStringLiteral("https://login.microsoftonline.com/common/oauth2/token")); static const auto o365FakeUserAgent = QStringLiteral("Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36"); static const auto o365Resource = QStringLiteral("https%3A%2F%2Foutlook.office365.com%2F"); #ifdef HAVE_QCA static const auto pkeyAuthSuffix = QStringLiteral(" PKeyAuth/1.0"); static const auto pkeyRedirectUri = QStringLiteral("urn:http-auth:PKeyAuth"); static const QString pkeyPasswordMapKey = QStringLiteral("pkey-password"); #endif static const QString accessTokenMapKey = QStringLiteral("access-token"); static const QString refreshTokenMapKey = QStringLiteral("refresh-token"); class EwsOAuthUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { Q_OBJECT public: - EwsOAuthUrlSchemeHandler(QObject *parent = nullptr) : QWebEngineUrlSchemeHandler(parent) {} + EwsOAuthUrlSchemeHandler(QObject *parent = nullptr) : QWebEngineUrlSchemeHandler(parent) + { + } + ~EwsOAuthUrlSchemeHandler() override = default; void requestStarted(QWebEngineUrlRequestJob *request) override; Q_SIGNALS: void returnUriReceived(const QUrl &url); }; class EwsOAuthReplyHandler final : public QAbstractOAuthReplyHandler { Q_OBJECT public: EwsOAuthReplyHandler(QObject *parent, const QString &returnUri) - : QAbstractOAuthReplyHandler(parent), mReturnUri(returnUri) {} + : QAbstractOAuthReplyHandler(parent) + , mReturnUri(returnUri) + { + } + ~EwsOAuthReplyHandler() override = default; - QString callback() const override { return mReturnUri; } + QString callback() const override + { + return mReturnUri; + } + void networkReplyFinished(QNetworkReply *reply) override; Q_SIGNALS: void replyError(const QString &error); private: const QString mReturnUri; }; class EwsOAuthRequestInterceptor final : public QWebEngineUrlRequestInterceptor { Q_OBJECT public: EwsOAuthRequestInterceptor(QObject *parent, const QString &redirectUri) - : QWebEngineUrlRequestInterceptor(parent), mRedirectUri(redirectUri) + : QWebEngineUrlRequestInterceptor(parent) + , mRedirectUri(redirectUri) { } ~EwsOAuthRequestInterceptor() override = default; void interceptRequest(QWebEngineUrlRequestInfo &info) override; Q_SIGNALS: void redirectUriIntercepted(const QUrl &url); private: const QString mRedirectUri; }; class EwsOAuthPrivate final : public QObject { Q_OBJECT public: EwsOAuthPrivate(EwsOAuth *parent, const QString &email, const QString &appId, const QString &redirectUri); ~EwsOAuthPrivate() override = default; bool authenticate(bool interactive); void modifyParametersFunction(QAbstractOAuth::Stage stage, QVariantMap *parameters); void authorizeWithBrowser(const QUrl &url); void redirectUriIntercepted(const QUrl &url); void granted(); void error(const QString &error, const QString &errorDescription, const QUrl &uri); QVariantMap queryToVarmap(const QUrl &url); #ifdef HAVE_QCA void pkeyAuthResult(KJob *job); #endif QWebEngineView mWebView; QWebEngineProfile mWebProfile; QWebEnginePage mWebPage; QOAuth2AuthorizationCodeFlow mOAuth2; EwsOAuthReplyHandler mReplyHandler; EwsOAuthRequestInterceptor mRequestInterceptor; EwsOAuthUrlSchemeHandler mSchemeHandler; QString mToken; const QString mEmail; const QString mRedirectUri; bool mAuthenticated; QPointer mWebDialog; #ifdef HAVE_QCA QString mPKeyPassword; #endif EwsOAuth *q_ptr = nullptr; Q_DECLARE_PUBLIC(EwsOAuth) }; - void EwsOAuthUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) { returnUriReceived(request->requestUrl()); } void EwsOAuthReplyHandler::networkReplyFinished(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { Q_EMIT replyError(reply->errorString()); return; } else if (reply->header(QNetworkRequest::ContentTypeHeader).isNull()) { Q_EMIT replyError(QStringLiteral("Empty or no Content-type header")); return; } const auto cth = reply->header(QNetworkRequest::ContentTypeHeader); const auto ct = cth.isNull() ? QStringLiteral("text/html") : cth.toString(); const auto data = reply->readAll(); if (data.isEmpty()) { Q_EMIT replyError(QStringLiteral("No data received")); return; } Q_EMIT replyDataReceived(data); QVariantMap tokens; - if (ct.startsWith(QStringLiteral("text/html")) || - ct.startsWith(QStringLiteral("application/x-www-form-urlencoded"))) { + if (ct.startsWith(QStringLiteral("text/html")) + || ct.startsWith(QStringLiteral("application/x-www-form-urlencoded"))) { QUrlQuery q(QString::fromUtf8(data)); const auto items = q.queryItems(QUrl::FullyDecoded); for (const auto &it : items) { tokens.insert(it.first, it.second); } } else if (ct.startsWith(QStringLiteral("application/json")) || ct.startsWith(QStringLiteral("text/javascript"))) { const auto document = QJsonDocument::fromJson(data); if (!document.isObject()) { Q_EMIT replyError(QStringLiteral("Invalid JSON data received")); return; } const auto object = document.object(); if (object.isEmpty()) { Q_EMIT replyError(QStringLiteral("Empty JSON data received")); return; } tokens = object.toVariantMap(); } else { Q_EMIT replyError(QStringLiteral("Unknown content type")); return; } const auto error = tokens.value(QStringLiteral("error")); if (error.isValid()) { Q_EMIT replyError(QStringLiteral("Received error response: ") + error.toString()); return; } const auto accessToken = tokens.value(QStringLiteral("access_token")); if (!accessToken.isValid() || accessToken.toString().isEmpty()) { Q_EMIT replyError(QStringLiteral("Received empty or no access token")); return; } Q_EMIT tokensReceived(tokens); } void EwsOAuthRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { const auto url = info.requestUrl(); qCDebugNC(EWSCLI_LOG) << QStringLiteral("Intercepted browser navigation to ") << url; if ((url.toString(QUrl::RemoveQuery) == mRedirectUri) #ifdef HAVE_QCA || (url.toString(QUrl::RemoveQuery) == pkeyRedirectUri) #endif ) { qCDebug(EWSCLI_LOG) << QStringLiteral("Found redirect URI - blocking request"); redirectUriIntercepted(url); info.block(true); } } EwsOAuthPrivate::EwsOAuthPrivate(EwsOAuth *parent, const QString &email, const QString &appId, const QString &redirectUri) - : QObject(nullptr), mWebView(nullptr), mWebProfile(), mWebPage(&mWebProfile), mReplyHandler(this, redirectUri), - mRequestInterceptor(this, redirectUri), mEmail(email), mRedirectUri(redirectUri), mAuthenticated(false), - q_ptr(parent) + : QObject(nullptr) + , mWebView(nullptr) + , mWebProfile() + , mWebPage(&mWebProfile) + , mReplyHandler(this, redirectUri) + , mRequestInterceptor(this, redirectUri) + , mEmail(email) + , mRedirectUri(redirectUri) + , mAuthenticated(false) + , q_ptr(parent) { mOAuth2.setReplyHandler(&mReplyHandler); mOAuth2.setAuthorizationUrl(o365AuthorizationUrl); mOAuth2.setAccessTokenUrl(o365AccessTokenUrl); mOAuth2.setClientIdentifier(appId); mWebProfile.setRequestInterceptor(&mRequestInterceptor); mWebProfile.installUrlSchemeHandler("urn", &mSchemeHandler); mWebView.setPage(&mWebPage); mOAuth2.setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QVariantMap *parameters) { modifyParametersFunction(stage, parameters); }); connect(&mOAuth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &EwsOAuthPrivate::authorizeWithBrowser); connect(&mOAuth2, &QOAuth2AuthorizationCodeFlow::granted, this, &EwsOAuthPrivate::granted); connect(&mOAuth2, &QOAuth2AuthorizationCodeFlow::error, this, &EwsOAuthPrivate::error); connect(&mRequestInterceptor, &EwsOAuthRequestInterceptor::redirectUriIntercepted, this, - &EwsOAuthPrivate::redirectUriIntercepted, Qt::QueuedConnection); + &EwsOAuthPrivate::redirectUriIntercepted, Qt::QueuedConnection); connect(&mReplyHandler, &EwsOAuthReplyHandler::replyError, this, [this](const QString &err) { - error(QStringLiteral("Network reply error"), err, QUrl()); - }); + error(QStringLiteral("Network reply error"), err, QUrl()); + }); } bool EwsOAuthPrivate::authenticate(bool interactive) { Q_Q(EwsOAuth); qCInfoNC(EWSCLI_LOG) << QStringLiteral("Starting OAuth2 authentication"); if (!mOAuth2.refreshToken().isEmpty()) { mOAuth2.refreshAccessToken(); return true; } else if (interactive) { mOAuth2.grant(); return true; } else { return false; } } void EwsOAuthPrivate::modifyParametersFunction(QAbstractOAuth::Stage stage, QVariantMap *parameters) { switch (stage) { case QAbstractOAuth::Stage::RequestingAccessToken: parameters->insert(QStringLiteral("resource"), o365Resource); break; case QAbstractOAuth::Stage::RequestingAuthorization: parameters->insert(QStringLiteral("prompt"), QStringLiteral("login")); parameters->insert(QStringLiteral("login_hint"), mEmail); parameters->insert(QStringLiteral("resource"), o365Resource); break; default: break; } } void EwsOAuthPrivate::authorizeWithBrowser(const QUrl &url) { Q_Q(EwsOAuth); qCInfoNC(EWSCLI_LOG) << QStringLiteral("Launching browser for authentication"); /* Bad bad Microsoft... * When Conditional Access is enabled on the server the OAuth2 authentication server only supports Windows, * MacOSX, Android and iOS. No option to include Linux. Support (i.e. guarantee that it works) * is one thing, but blocking unsupported browsers completely is just wrong. * Fortunately enough this can be worked around by faking the user agent to something "supported". */ auto userAgent = o365FakeUserAgent; #ifdef HAVE_QCA if (!q->mPKeyCertFile.isNull() && !q->mPKeyKeyFile.isNull()) { qCInfoNC(EWSCLI_LOG) << QStringLiteral("Found PKeyAuth certificates"); userAgent += pkeyAuthSuffix; } else { qCInfoNC(EWSCLI_LOG) << QStringLiteral("PKeyAuth certificates not found"); } #endif mWebProfile.setHttpUserAgent(userAgent); mWebDialog = new QDialog(q->mAuthParentWidget); mWebDialog->setObjectName(QStringLiteral("Akonadi EWS Resource - Authentication")); mWebDialog->setWindowIcon(QIcon(QStringLiteral("akonadi-ews"))); mWebDialog->resize(400, 500); auto layout = new QHBoxLayout(mWebDialog); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(&mWebView); mWebView.show(); connect(mWebDialog.data(), &QDialog::rejected, this, [this]() { - error(QStringLiteral("User cancellation"), QStringLiteral("The authentication browser was closed"), QUrl()); - }); + error(QStringLiteral("User cancellation"), QStringLiteral("The authentication browser was closed"), QUrl()); + }); mWebView.load(url); mWebDialog->show(); } QVariantMap EwsOAuthPrivate::queryToVarmap(const QUrl &url) { QUrlQuery query(url); QVariantMap varmap; for (const auto &item : query.queryItems()) { varmap[item.first] = item.second; } return varmap; } void EwsOAuthPrivate::redirectUriIntercepted(const QUrl &url) { qCDebugNC(EWSCLI_LOG) << QStringLiteral("Intercepted redirect URI from browser: ") << url; mWebView.stop(); mWebDialog->hide(); #ifdef HAVE_QCA Q_Q(EwsOAuth); if (url.toString(QUrl::RemoveQuery) == pkeyRedirectUri) { qCDebugNC(EWSCLI_LOG) << QStringLiteral("Found PKeyAuth URI"); auto pkeyAuthJob = new EwsPKeyAuthJob(url, q->mPKeyCertFile, q->mPKeyKeyFile, mPKeyPassword, this); connect(pkeyAuthJob, &KJob::result, this, &EwsOAuthPrivate::pkeyAuthResult); pkeyAuthJob->start(); return; } #endif mOAuth2.authorizationCallbackReceived(queryToVarmap(url)); } #ifdef HAVE_QCA void EwsOAuthPrivate::pkeyAuthResult(KJob *j) { - EwsPKeyAuthJob *job = qobject_cast(j); + EwsPKeyAuthJob *job = qobject_cast(j); qCDebugNC(EWSCLI_LOG) << QStringLiteral("PKeyAuth result: %1").arg(job->error()); QVariantMap varmap; if (job->error() == 0) { varmap = queryToVarmap(job->resultUri()); } else { varmap[QStringLiteral("error")] = job->errorString(); } mOAuth2.authorizationCallbackReceived(varmap); } + #endif void EwsOAuthPrivate::granted() { Q_Q(EwsOAuth); qCInfoNC(EWSCLI_LOG) << QStringLiteral("Authentication succeeded"); mAuthenticated = true; QMap map; map[accessTokenMapKey] = mOAuth2.token(); map[refreshTokenMapKey] = mOAuth2.refreshToken(); Q_EMIT q->setWalletMap(map); Q_EMIT q->authSucceeded(); } void EwsOAuthPrivate::error(const QString &error, const QString &errorDescription, const QUrl &uri) { Q_Q(EwsOAuth); Q_UNUSED(uri); mAuthenticated = false; mOAuth2.setRefreshToken(QString()); qCInfoNC(EWSCLI_LOG) << QStringLiteral("Authentication failed: ") << error << errorDescription; Q_EMIT q->authFailed(error); } EwsOAuth::EwsOAuth(QObject *parent, const QString &email, const QString &appId, const QString &redirectUri) - : EwsAbstractAuth(parent), d_ptr(new EwsOAuthPrivate(this, email, appId, redirectUri)) + : EwsAbstractAuth(parent) + , d_ptr(new EwsOAuthPrivate(this, email, appId, redirectUri)) { } EwsOAuth::~EwsOAuth() { } void EwsOAuth::init() { requestWalletMap(); } bool EwsOAuth::getAuthData(QString &username, QString &password, QStringList &customHeaders) { Q_D(const EwsOAuth); Q_UNUSED(username); Q_UNUSED(password); if (d->mAuthenticated) { customHeaders.append(QStringLiteral("Authorization: Bearer ") + d->mOAuth2.token()); return true; } else { return false; } } void EwsOAuth::notifyRequestAuthFailed() { Q_D(EwsOAuth); d->mOAuth2.setToken(QString()); d->mAuthenticated = false; EwsAbstractAuth::notifyRequestAuthFailed(); } bool EwsOAuth::authenticate(bool interactive) { Q_D(EwsOAuth); return d->authenticate(interactive); } const QString &EwsOAuth::reauthPrompt() const { static const QString prompt = i18nc("@info", "Microsoft Exchange credentials for the account %1 are no longer valid. You need to authenticate in order to continue using it."); return prompt; } const QString &EwsOAuth::authFailedPrompt() const { static const QString prompt = i18nc("@info", "Failed to obtain credentials for Microsoft Exchange account %1. Please update it in the account settings page."); return prompt; } void EwsOAuth::walletPasswordRequestFinished(const QString &password) { Q_UNUSED(password); } void EwsOAuth::walletMapRequestFinished(const QMap &map) { Q_D(EwsOAuth); #ifdef HAVE_QCA if (map.contains(pkeyPasswordMapKey)) { d->mPKeyPassword = map[pkeyPasswordMapKey]; } #endif if (map.contains(refreshTokenMapKey)) { d->mOAuth2.setRefreshToken(map[refreshTokenMapKey]); } if (map.contains(accessTokenMapKey)) { d->mOAuth2.setToken(map[accessTokenMapKey]); d->mAuthenticated = true; Q_EMIT authSucceeded(); } else { Q_EMIT authFailed(QStringLiteral("Access token request failed")); } } #include "ewsoauth.moc" diff --git a/resources/ews/ewsclient/auth/ewspasswordauth.cpp b/resources/ews/ewsclient/auth/ewspasswordauth.cpp index 2a9d5cd00..e688f7a47 100644 --- a/resources/ews/ewsclient/auth/ewspasswordauth.cpp +++ b/resources/ews/ewsclient/auth/ewspasswordauth.cpp @@ -1,92 +1,92 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 "ewspasswordauth.h" #include #include "ewsclient_debug.h" - EwsPasswordAuth::EwsPasswordAuth(const QString &username, QObject *parent) - : EwsAbstractAuth(parent), mUsername(username) + : EwsAbstractAuth(parent) + , mUsername(username) { } void EwsPasswordAuth::init() { Q_EMIT requestWalletPassword(false); } bool EwsPasswordAuth::getAuthData(QString &username, QString &password, QStringList &customHeaders) { Q_UNUSED(customHeaders); if (!mPassword.isNull()) { username = mUsername; password = mPassword; return true; } return false; } void EwsPasswordAuth::notifyRequestAuthFailed() { EwsAbstractAuth::notifyRequestAuthFailed(); } bool EwsPasswordAuth::authenticate(bool interactive) { Q_UNUSED(interactive); return false; } const QString &EwsPasswordAuth::reauthPrompt() const { static const QString prompt; return prompt; } const QString &EwsPasswordAuth::authFailedPrompt() const { static const QString prompt = i18nc("@info", "The username/password for the Microsoft Exchange EWS account %1 is not valid. Please update it in the account settings page."); return prompt; } void EwsPasswordAuth::walletPasswordRequestFinished(const QString &password) { mPassword = password; if (!password.isNull()) { Q_EMIT authSucceeded(); } else { Q_EMIT authFailed(QStringLiteral("Password request failed")); } } void EwsPasswordAuth::walletMapRequestFinished(const QMap &map) { Q_UNUSED(map); } void EwsPasswordAuth::setUsername(const QString &username) { mUsername = username; } diff --git a/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp b/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp index 3409f5d38..3dfc27227 100644 --- a/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp +++ b/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp @@ -1,186 +1,187 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 "ewspkeyauthjob.h" #include #include #include #include #include static const QMap stringToKnownCertInfoType = { {QStringLiteral("CN"), QCA::CommonName}, {QStringLiteral("L"), QCA::Locality}, {QStringLiteral("ST"), QCA::State}, {QStringLiteral("O"), QCA::Organization}, {QStringLiteral("OU"), QCA::OrganizationalUnit}, {QStringLiteral("C"), QCA::Country}, {QStringLiteral("emailAddress"), QCA::EmailLegacy} }; static QMultiMap parseCertSubjectInfo(const QString &info) { QMultiMap map; for (const auto &token : info.split(QLatin1Char(','), QString::SkipEmptyParts)) { const auto keyval = token.trimmed().split(QLatin1Char('=')); if (keyval.count() == 2) { if (stringToKnownCertInfoType.contains(keyval[0])) { map.insert(stringToKnownCertInfoType[keyval[0]], keyval[1]); } } } return map; } static QString escapeSlashes(const QString &str) { QString result = str; return result.replace(QLatin1Char('/'), QStringLiteral("\\/")); } -EwsPKeyAuthJob::EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, - QObject *parent) - : EwsJob(parent), mPKeyUri(pkeyUri), mCertFile(certFile), mKeyFile(keyFile), mKeyPassword(keyPassword), - mNetworkAccessManager(new QNetworkAccessManager(this)) +EwsPKeyAuthJob::EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, QObject *parent) + : EwsJob(parent) + , mPKeyUri(pkeyUri) + , mCertFile(certFile) + , mKeyFile(keyFile) + , mKeyPassword(keyPassword) + , mNetworkAccessManager(new QNetworkAccessManager(this)) { } EwsPKeyAuthJob::~EwsPKeyAuthJob() { } void EwsPKeyAuthJob::start() { const QUrlQuery query(mPKeyUri); QMap params; for (const auto &it : query.queryItems()) { params[it.first.toLower()] = QUrl::fromPercentEncoding(it.second.toLatin1()); } - if (params.contains(QStringLiteral("submiturl")) && params.contains(QStringLiteral("nonce")) && - params.contains(QStringLiteral("certauthorities")) && params.contains(QStringLiteral("context")) && - params.contains(QStringLiteral("version"))) { - + if (params.contains(QStringLiteral("submiturl")) && params.contains(QStringLiteral("nonce")) + && params.contains(QStringLiteral("certauthorities")) && params.contains(QStringLiteral("context")) + && params.contains(QStringLiteral("version"))) { const auto respToken = buildAuthResponse(params); if (!respToken.isEmpty()) { sendAuthRequest(respToken, QUrl(params[QStringLiteral("submiturl")]), params[QStringLiteral("context")]); } else { emitResult(); } } else { setErrorMsg(QStringLiteral("Missing one or more input parameters")); emitResult(); } - } void EwsPKeyAuthJob::sendAuthRequest(const QByteArray &respToken, const QUrl &submitUrl, const QString &context) { QNetworkRequest req(submitUrl); req.setRawHeader( "Authorization", QStringLiteral("PKeyAuth AuthToken=\"%1\",Context=\"%2\",Version=\"1.0\"").arg(QString::fromLatin1(respToken), context).toLatin1()); mAuthReply.reset(mNetworkAccessManager->get(req)); connect(mAuthReply.data(), &QNetworkReply::finished, this, &EwsPKeyAuthJob::authRequestFinished); } void EwsPKeyAuthJob::authRequestFinished() { if (mAuthReply->error() == QNetworkReply::NoError) { mResultUri = mAuthReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!mResultUri.isValid()) { setErrorMsg(QStringLiteral("Incorect or missing redirect URI in PKeyAuth response")); } } else { setErrorMsg(QStringLiteral("Failed to process PKeyAuth request: %1").arg(mAuthReply->errorString())); } emitResult(); } QByteArray EwsPKeyAuthJob::buildAuthResponse(const QMap ¶ms) { QCA::Initializer init; if (!QCA::isSupported("cert")) { setErrorMsg(QStringLiteral("QCA was not built with PKI certificate support")); - return QByteArray(); + return QByteArray(); } if (params[QStringLiteral("version")] != QStringLiteral("1.0")) { setErrorMsg(QStringLiteral("Unknown version of PKey Authentication: %1").arg(params[QStringLiteral("version")])); return QByteArray(); } const auto authoritiesInfo = parseCertSubjectInfo(params[QStringLiteral("certauthorities")]); QCA::ConvertResult importResult; const QCA::CertificateCollection certs = QCA::CertificateCollection::fromFlatTextFile(mCertFile, &importResult); if (importResult != QCA::ConvertGood) { setErrorMsg(QStringLiteral("Certificate import failed")); return QByteArray(); } QCA::Certificate cert; for (const auto &c : certs.certificates()) { if (c.issuerInfo() == authoritiesInfo) { cert = c; break; } } if (cert.isNull()) { setErrorMsg(QStringLiteral("No suitable certificate found")); return QByteArray(); } QCA::PrivateKey privateKey = QCA::PrivateKey::fromPEMFile(mKeyFile, mKeyPassword.toUtf8(), &importResult); if (importResult != QCA::ConvertGood) { setErrorMsg(QStringLiteral("Private key import failed")); return QByteArray(); } const QString certStr = escapeSlashes(QString::fromLatin1(cert.toDER().toBase64())); const QString header = QStringLiteral("{\"x5c\":[\"%1\"],\"typ\":\"JWT\",\"alg\":\"RS256\"}").arg(certStr); const QString payload = QStringLiteral("{\"nonce\":\"%1\",\"iat\":\"%2\",\"aud\":\"%3\"}") - .arg(params[QStringLiteral("nonce")]).arg(QDateTime::currentSecsSinceEpoch()) - .arg(escapeSlashes(params[QStringLiteral("submiturl")])); + .arg(params[QStringLiteral("nonce")]).arg(QDateTime::currentSecsSinceEpoch()) + .arg(escapeSlashes(params[QStringLiteral("submiturl")])); const auto headerB64 = header.toUtf8().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); const auto payloadB64 = payload.toUtf8().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QCA::SecureArray data(headerB64 + '.' + payloadB64); QByteArray sig = privateKey.signMessage(data, QCA::EMSA3_SHA256, QCA::IEEE_1363); return headerB64 + '.' + payloadB64 + '.' + sig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); } const QUrl &EwsPKeyAuthJob::resultUri() const { return mResultUri; } diff --git a/resources/ews/ewsclient/auth/ewspkeyauthjob.h b/resources/ews/ewsclient/auth/ewspkeyauthjob.h index 5e29d416f..66ecb4fb1 100644 --- a/resources/ews/ewsclient/auth/ewspkeyauthjob.h +++ b/resources/ews/ewsclient/auth/ewspkeyauthjob.h @@ -1,57 +1,56 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 EWSPKEYAUTHJOB_H #define EWSPKEYAUTHJOB_H #include "ewsjob.h" #include #include class QNetworkAccessManager; class QNetworkReply; class EwsPKeyAuthJob : public EwsJob { Q_OBJECT public: - explicit EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, - QObject *parent); + explicit EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, QObject *parent); ~EwsPKeyAuthJob() override; const QUrl &resultUri() const; void start() override; private: QByteArray buildAuthResponse(const QMap ¶ms); void sendAuthRequest(const QByteArray &respToken, const QUrl &submitUrl, const QString &context); void authRequestFinished(); const QUrl mPKeyUri; const QString mCertFile; const QString mKeyFile; const QString mKeyPassword; QScopedPointer mNetworkAccessManager; QScopedPointer mAuthReply; QUrl mResultUri; }; #endif /* EWSPKEYAUTHJOB_H */ diff --git a/resources/ews/ewsclient/ewsattachment.cpp b/resources/ews/ewsclient/ewsattachment.cpp index f2189003b..86b252ee7 100644 --- a/resources/ews/ewsclient/ewsattachment.cpp +++ b/resources/ews/ewsclient/ewsattachment.cpp @@ -1,533 +1,534 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 "ewsattachment.h" #include #include "ewsclient_debug.h" #include "ewsxml.h" class EwsAttachmentPrivate : public QSharedData { public: EwsAttachmentPrivate(); ~EwsAttachmentPrivate(); enum Field { Id = 0, Name, ContentType, ContentId, ContentLocation, Size, LastModifiedTime, IsInline, IsContactPhoto, Content, Item, NumFields }; - EwsAttachment::Type mType; QString mId; QString mName; QString mContentType; QString mContentId; QString mContentLocation; long mSize; QDateTime mLastModifiedTime; bool mIsInline; bool mIsContactPhoto; QByteArray mContent; EwsItem mItem; bool mValid; QBitArray mValidFields; }; EwsAttachmentPrivate::EwsAttachmentPrivate() - : mType(EwsAttachment::UnknownAttachment), mSize(0), mIsInline(false), mIsContactPhoto(false), mValid(false), - mValidFields(NumFields) + : mType(EwsAttachment::UnknownAttachment) + , mSize(0) + , mIsInline(false) + , mIsContactPhoto(false) + , mValid(false) + , mValidFields(NumFields) { } EwsAttachmentPrivate::~EwsAttachmentPrivate() { } EwsAttachment::EwsAttachment() : d(new EwsAttachmentPrivate()) { } EwsAttachment::~EwsAttachment() { } EwsAttachment::EwsAttachment(QXmlStreamReader &reader) : d(new EwsAttachmentPrivate()) { bool ok = true; if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in Attachment element:") << reader.namespaceUri(); reader.skipCurrentElement(); return; } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("ItemAttachment")) { d->mType = ItemAttachment; } else if (readerName == QStringLiteral("FileAttachment")) { d->mType = FileAttachment; } else if (readerName == QStringLiteral("ReferenceAttachment")) { d->mType = ReferenceAttachment; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unknown attachment type %1").arg(readerName.toString()); ok = false; } // Skip this attachment type as it's not clearly documented. if (d->mType == ReferenceAttachment) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Attachment type ReferenceAttachment not fully supported"); reader.skipCurrentElement(); d->mValid = true; return; } while (ok && reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in Attachment element:") << reader.namespaceUri(); reader.skipCurrentElement(); ok = false; break; } const QString elmName = reader.name().toString(); if (elmName == QStringLiteral("AttachmentId")) { QXmlStreamAttributes attrs = reader.attributes(); if (!attrs.hasAttribute(QStringLiteral("Id"))) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - missing Id in AttachmentId element.") - .arg(QStringLiteral("Attachment")); + .arg(QStringLiteral("Attachment")); reader.skipCurrentElement(); ok = false; } else { d->mId = attrs.value(QStringLiteral("Id")).toString(); d->mValidFields.setBit(EwsAttachmentPrivate::Id); } reader.skipCurrentElement(); } else if (elmName == QStringLiteral("Name")) { d->mName = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::Name, ok); } else if (elmName == QStringLiteral("ContentType")) { d->mContentType = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::ContentType, ok); } else if (elmName == QStringLiteral("ContentId")) { d->mContentId = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::ContentId, ok); } else if (elmName == QStringLiteral("ContentLocation")) { d->mContentLocation = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::ContentLocation, ok); } else if (elmName == QStringLiteral("AttachmentOriginalUrl")) { // Ignore reader.skipCurrentElement(); } else if (elmName == QStringLiteral("Size")) { d->mSize = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::Size, ok); } else if (elmName == QStringLiteral("LastModifiedTime")) { d->mLastModifiedTime = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::LastModifiedTime, ok); } else if (elmName == QStringLiteral("IsInline")) { d->mIsInline = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::IsInline, ok); } else if (d->mType == FileAttachment && elmName == QStringLiteral("IsContactPhoto")) { d->mIsContactPhoto = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::IsContactPhoto, ok); } else if (d->mType == FileAttachment && elmName == QStringLiteral("Content")) { d->mContent = readXmlElementValue(reader, ok, QStringLiteral("Attachment")); d->mValidFields.setBit(EwsAttachmentPrivate::Content, ok); - } else if (d->mType == ItemAttachment && (elmName == QStringLiteral("Item") || elmName == QStringLiteral("Message") || - elmName == QStringLiteral("CalendarItem") || elmName == QStringLiteral("Contact") || - elmName == QStringLiteral("MeetingMessage") || elmName == QStringLiteral("MeetingRequest") || - elmName == QStringLiteral("MeetingResponse") || elmName == QStringLiteral("MeetingCancellation") || - elmName == QStringLiteral("Task"))) { + } else if (d->mType == ItemAttachment && (elmName == QStringLiteral("Item") || elmName == QStringLiteral("Message") + || elmName == QStringLiteral("CalendarItem") || elmName == QStringLiteral("Contact") + || elmName == QStringLiteral("MeetingMessage") || elmName == QStringLiteral("MeetingRequest") + || elmName == QStringLiteral("MeetingResponse") || elmName == QStringLiteral("MeetingCancellation") + || elmName == QStringLiteral("Task"))) { d->mItem = EwsItem(reader); if (!d->mItem.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(QStringLiteral("Attachment"), QStringLiteral("Item")); + .arg(QStringLiteral("Attachment"), QStringLiteral("Item")); reader.skipCurrentElement(); ok = false; } else { d->mValidFields.setBit(EwsAttachmentPrivate::Item); } } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown %2 element.") - .arg(QStringLiteral("Attachment"), elmName); + .arg(QStringLiteral("Attachment"), elmName); reader.skipCurrentElement(); ok = false; } } if (!ok) { reader.skipCurrentElement(); } d->mValid = ok; } EwsAttachment::EwsAttachment(const EwsAttachment &other) { d = other.d; } EwsAttachment::EwsAttachment(EwsAttachment &&other) { d = std::move(other.d); } EwsAttachment &EwsAttachment::operator=(EwsAttachment &&other) { d = std::move(other.d); return *this; } EwsAttachment &EwsAttachment::operator=(const EwsAttachment &other) { d = other.d; return *this; } void EwsAttachment::write(QXmlStreamWriter &writer) const { QString elmName; switch (d->mType) { case ItemAttachment: elmName = QStringLiteral("ItemAttachment"); break; case FileAttachment: elmName = QStringLiteral("FileAttachment"); break; case ReferenceAttachment: elmName = QStringLiteral("ReferenceAttachment"); break; default: qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to write Attachment element - invalid attachment type."); return; } writer.writeStartElement(ewsTypeNsUri, elmName); if (d->mType == ReferenceAttachment) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Attachment type ReferenceAttachment not fully supported"); writer.writeEndElement(); return; } if (d->mValidFields[EwsAttachmentPrivate::Id]) { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("AttachmentId")); writer.writeAttribute(QStringLiteral("Id"), d->mId); writer.writeEndElement(); } if (d->mValidFields[EwsAttachmentPrivate::Name]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Name"), d->mName); } if (d->mValidFields[EwsAttachmentPrivate::ContentType]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentType"), d->mContentType); } if (d->mValidFields[EwsAttachmentPrivate::ContentId]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentId"), d->mContentId); } if (d->mValidFields[EwsAttachmentPrivate::ContentLocation]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentLocation"), d->mContentLocation); } if (d->mValidFields[EwsAttachmentPrivate::Size]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Size"), QString::number(d->mSize)); } if (d->mValidFields[EwsAttachmentPrivate::LastModifiedTime]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("LastModifiedTime"), d->mLastModifiedTime.toString(Qt::ISODate)); } if (d->mValidFields[EwsAttachmentPrivate::IsInline]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("IsInline"), d->mIsInline ? QStringLiteral("true") : QStringLiteral("false")); } if (d->mType == FileAttachment) { if (d->mValidFields[EwsAttachmentPrivate::IsContactPhoto]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("IsContactPhoto"), d->mIsContactPhoto ? QStringLiteral("true") : QStringLiteral("false")); } if (d->mValidFields[EwsAttachmentPrivate::Content]) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Content"), QString::fromLatin1(d->mContent.toBase64())); } } else if (d->mType == ItemAttachment) { if (d->mValidFields[EwsAttachmentPrivate::Item]) { d->mItem.write(writer); } } writer.writeEndElement(); } bool EwsAttachment::isValid() const { return d->mValid; } EwsAttachment::Type EwsAttachment::type() const { return d->mType; } void EwsAttachment::setType(Type type) { d->mType = type; } QString EwsAttachment::id() const { return d->mId; } void EwsAttachment::setId(const QString &id) { d->mId = id; d->mValidFields.setBit(EwsAttachmentPrivate::Id); } void EwsAttachment::resetId() { d->mValidFields.clearBit(EwsAttachmentPrivate::Id); } bool EwsAttachment::hasId() const { return d->mValidFields[EwsAttachmentPrivate::Id]; } QString EwsAttachment::name() const { return d->mName; } void EwsAttachment::setName(const QString &name) { d->mName = name; d->mValidFields.setBit(EwsAttachmentPrivate::Name); } void EwsAttachment::resetName() { d->mValidFields.clearBit(EwsAttachmentPrivate::Name); } bool EwsAttachment::hasName() const { return d->mValidFields[EwsAttachmentPrivate::Name]; } QString EwsAttachment::contentType() const { return d->mContentType; } void EwsAttachment::setContentType(const QString &contentType) { d->mContentType = contentType; d->mValidFields.setBit(EwsAttachmentPrivate::ContentType); } void EwsAttachment::resetContentType() { d->mValidFields.clearBit(EwsAttachmentPrivate::ContentType); } bool EwsAttachment::hasContentType() const { return d->mValidFields[EwsAttachmentPrivate::ContentType]; } QString EwsAttachment::contentId() const { return d->mContentId; } void EwsAttachment::setContentId(const QString &contentId) { d->mContentId = contentId; d->mValidFields.setBit(EwsAttachmentPrivate::ContentId); } void EwsAttachment::resetContentId() { d->mValidFields.clearBit(EwsAttachmentPrivate::ContentId); } bool EwsAttachment::hasContentId() const { return d->mValidFields[EwsAttachmentPrivate::ContentId]; } QString EwsAttachment::contentLocation() const { return d->mContentLocation; } void EwsAttachment::setContentLocation(const QString &contentLocation) { d->mContentLocation = contentLocation; d->mValidFields.setBit(EwsAttachmentPrivate::ContentLocation); } void EwsAttachment::resetContentLocation() { d->mValidFields.clearBit(EwsAttachmentPrivate::ContentLocation); } bool EwsAttachment::hasContentLocation() const { return d->mValidFields[EwsAttachmentPrivate::ContentLocation]; } long EwsAttachment::size() const { return d->mSize; } void EwsAttachment::setSize(long size) { d->mSize = size; d->mValidFields.setBit(EwsAttachmentPrivate::Size); } void EwsAttachment::resetSize() { d->mValidFields.clearBit(EwsAttachmentPrivate::Size); } bool EwsAttachment::hasSize() const { return d->mValidFields[EwsAttachmentPrivate::Size]; } QDateTime EwsAttachment::lastModifiedTime() const { return d->mLastModifiedTime; } void EwsAttachment::setLastModifiedTime(const QDateTime &time) { d->mLastModifiedTime = time; d->mValidFields.setBit(EwsAttachmentPrivate::LastModifiedTime); } void EwsAttachment::resetLastModifiedTime() { d->mValidFields.clearBit(EwsAttachmentPrivate::LastModifiedTime); } bool EwsAttachment::hasLastModifiedTime() const { return d->mValidFields[EwsAttachmentPrivate::LastModifiedTime]; } bool EwsAttachment::isInline() const { return d->mIsInline; } void EwsAttachment::setIsInline(bool isInline) { d->mIsInline = isInline; d->mValidFields.setBit(EwsAttachmentPrivate::IsInline); } void EwsAttachment::resetIsInline() { d->mValidFields.clearBit(EwsAttachmentPrivate::IsInline); } bool EwsAttachment::hasIsInline() const { return d->mValidFields[EwsAttachmentPrivate::IsInline]; } bool EwsAttachment::isContactPhoto() const { return d->mIsContactPhoto; } void EwsAttachment::setIsContactPhoto(bool isContactPhoto) { d->mIsContactPhoto = isContactPhoto; d->mValidFields.setBit(EwsAttachmentPrivate::IsContactPhoto); } void EwsAttachment::resetIsContactPhoto() { d->mValidFields.clearBit(EwsAttachmentPrivate::IsContactPhoto); } bool EwsAttachment::hasIsContactPhoto() const { return d->mValidFields[EwsAttachmentPrivate::IsContactPhoto]; } QByteArray EwsAttachment::content() const { return d->mContent; } void EwsAttachment::setContent(const QByteArray &content) { d->mContent = content; d->mValidFields.setBit(EwsAttachmentPrivate::Content); } void EwsAttachment::resetContent() { d->mValidFields.clearBit(EwsAttachmentPrivate::Content); } bool EwsAttachment::hasContent() const { return d->mValidFields[EwsAttachmentPrivate::Content]; } const EwsItem &EwsAttachment::item() const { return d->mItem; } void EwsAttachment::setItem(const EwsItem &item) { d->mItem = item; d->mValidFields.setBit(EwsAttachmentPrivate::Item); } void EwsAttachment::resetItem() { d->mValidFields.clearBit(EwsAttachmentPrivate::Item); } bool EwsAttachment::hasItem() const { return d->mValidFields[EwsAttachmentPrivate::Item]; } - - diff --git a/resources/ews/ewsclient/ewsattendee.cpp b/resources/ews/ewsclient/ewsattendee.cpp index a9eb42461..22eae52e8 100644 --- a/resources/ews/ewsclient/ewsattendee.cpp +++ b/resources/ews/ewsclient/ewsattendee.cpp @@ -1,146 +1,148 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsattendee.h" #include #include #include "ewsclient_debug.h" #include "ewsmailbox.h" #include "ewstypes.h" class EwsAttendeePrivate : public QSharedData { public: EwsAttendeePrivate(); virtual ~EwsAttendeePrivate(); bool mValid; EwsMailbox mMailbox; EwsEventResponseType mResponse; QDateTime mResponseTime; }; static const QString responseTypeNames[] = { QStringLiteral("Unknown"), QStringLiteral("Organizer"), QStringLiteral("Tentative"), QStringLiteral("Accept"), QStringLiteral("Decline"), QStringLiteral("NoResponseReceived") }; Q_CONSTEXPR unsigned responseTypeNameCount = sizeof(responseTypeNames) / sizeof(responseTypeNames[0]); EwsAttendeePrivate::EwsAttendeePrivate() - : mValid(false), mResponse(EwsEventResponseNotReceived) + : mValid(false) + , mResponse(EwsEventResponseNotReceived) { } EwsAttendee::EwsAttendee() : d(new EwsAttendeePrivate()) { } EwsAttendee::EwsAttendee(QXmlStreamReader &reader) : d(new EwsAttendeePrivate()) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in mailbox element:") << reader.namespaceUri(); return; } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("Mailbox")) { d->mMailbox = EwsMailbox(reader); if (!d->mMailbox.isValid()) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid attendee %1 element.") - .arg(readerName.toString()); + .arg(readerName.toString()); return; } } else if (readerName == QStringLiteral("ResponseType")) { bool ok; d->mResponse = decodeEnumString(reader.readElementText(), - responseTypeNames, responseTypeNameCount, &ok); + responseTypeNames, responseTypeNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid attendee %1 element.") - .arg(readerName.toString()); + .arg(readerName.toString()); return; } } else if (readerName == QStringLiteral("LastResponseTime")) { // Unsupported - ignore //qCWarningNC(EWSCLIENT_LOG) << QStringLiteral("Unsupported mailbox element %1").arg(reader.name().toString()); reader.skipCurrentElement(); } } d->mValid = true; } EwsAttendeePrivate::~EwsAttendeePrivate() { } EwsAttendee::EwsAttendee(const EwsAttendee &other) : d(other.d) { } EwsAttendee::EwsAttendee(EwsAttendee &&other) : d(std::move(other.d)) { } EwsAttendee::~EwsAttendee() { } EwsAttendee &EwsAttendee::operator=(const EwsAttendee &other) { d = other.d; return *this; } + EwsAttendee &EwsAttendee::operator=(EwsAttendee &&other) { d = std::move(other.d); return *this; } bool EwsAttendee::isValid() const { return d->mValid; } const EwsMailbox &EwsAttendee::mailbox() const { return d->mMailbox; } EwsEventResponseType EwsAttendee::response() const { return d->mResponse; } QDateTime EwsAttendee::responseTime() const { return d->mResponseTime; } diff --git a/resources/ews/ewsclient/ewsclient.cpp b/resources/ews/ewsclient/ewsclient.cpp index d5f5104ab..292fd1d7e 100644 --- a/resources/ews/ewsclient/ewsclient.cpp +++ b/resources/ews/ewsclient/ewsclient.cpp @@ -1,60 +1,60 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 "ewsclient.h" #include #include "auth/ewsabstractauth.h" #include "ewsclient_debug.h" QHash EwsClient::folderHash; EwsClient::EwsClient(QObject *parent) - : QObject(parent), mEnableNTLMv2(true) + : QObject(parent) + , mEnableNTLMv2(true) { - } EwsClient::~EwsClient() { } void EwsClient::setServerVersion(const EwsServerVersion &version) { if (mServerVersion.isValid() && mServerVersion != version) { qCWarning(EWSCLI_LOG) << "Warning - server version changed." << mServerVersion << version; } mServerVersion = version; } QUrl EwsClient::url() const { return mUrl; } void EwsClient::setAuth(EwsAbstractAuth *auth) { mAuth = auth; } EwsAbstractAuth *EwsClient::auth() const { return mAuth.data(); } diff --git a/resources/ews/ewsclient/ewsclient.h b/resources/ews/ewsclient/ewsclient.h index 613dbcaef..4eed57b3c 100644 --- a/resources/ews/ewsclient/ewsclient.h +++ b/resources/ews/ewsclient/ewsclient.h @@ -1,102 +1,104 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 EWSCLIENT_H #define EWSCLIENT_H #include #include #include #include #include "ewsserverversion.h" class EwsAbstractAuth; class EwsClient : public QObject { Q_OBJECT public: explicit EwsClient(QObject *parent = nullptr); ~EwsClient(); void setUrl(const QString &url) { mUrl.setUrl(url); } void setAuth(EwsAbstractAuth *auth); EwsAbstractAuth *auth() const; enum RequestedConfiguration { MailTips = 0, UnifiedMessagingConfiguration, ProtectionRules }; QUrl url() const; bool isConfigured() const { return !mUrl.isEmpty(); } void setServerVersion(const EwsServerVersion &version); const EwsServerVersion &serverVersion() const { return mServerVersion; } void setUserAgent(const QString &userAgent) { mUserAgent = userAgent; } + const QString &userAgent() const { return mUserAgent; } void setEnableNTLMv2(bool enable) { mEnableNTLMv2 = enable; } + bool isNTLMv2Enabled() const { return mEnableNTLMv2; } static QHash folderHash; Q_SIGNALS: void oAuthTokensChanged(const QString &accessToken, const QString &refreshToken); void oAuthBrowserDisplayRequest(); private: QUrl mUrl; QPointer mAuth; QString mUserAgent; bool mEnableNTLMv2; EwsServerVersion mServerVersion; friend class EwsRequest; }; #endif diff --git a/resources/ews/ewsclient/ewscreatefolderrequest.cpp b/resources/ews/ewsclient/ewscreatefolderrequest.cpp index 61fe9d820..434f7373c 100644 --- a/resources/ews/ewsclient/ewscreatefolderrequest.cpp +++ b/resources/ews/ewsclient/ewscreatefolderrequest.cpp @@ -1,121 +1,123 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewscreatefolderrequest.h" #include "ewsclient_debug.h" EwsCreateFolderRequest::EwsCreateFolderRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsCreateFolderRequest::~EwsCreateFolderRequest() { } void EwsCreateFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("CreateFolder")); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ParentFolderId")); mParentFolderId.writeFolderIds(writer); writer.writeEndElement(); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("Folders")); for (const EwsFolder &folder : qAsConst(mFolders)) { folder.write(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting CreateFolder request (%1 folders, parent %2)") - .arg(mFolders.size()).arg(mParentFolderId.id()); + .arg(mFolders.size()).arg(mParentFolderId.id()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsCreateFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("CreateFolder"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsCreateFolderRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got CreateFolder response - OK"); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got CreateFolder response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsCreateFolderRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Folders")) { if (reader.readNextStartElement()) { EwsFolder folder(reader); if (!folder.isValid()) { return; } mId = folder[EwsFolderFieldFolderId].value(); // Finish the Folders element. reader.skipCurrentElement(); } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewscreatefolderrequest.h b/resources/ews/ewsclient/ewscreatefolderrequest.h index 726f408b6..e5a7934a1 100644 --- a/resources/ews/ewsclient/ewscreatefolderrequest.h +++ b/resources/ews/ewsclient/ewscreatefolderrequest.h @@ -1,79 +1,81 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSCREATEFOLDERREQUEST_H #define EWSCREATEFOLDERREQUEST_H #include #include #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsCreateFolderRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: const EwsId &folderId() const { return mId; } + protected: Response(QXmlStreamReader &reader); EwsId mId; friend class EwsCreateFolderRequest; }; EwsCreateFolderRequest(EwsClient &client, QObject *parent); ~EwsCreateFolderRequest() override; void setFolders(const EwsFolder::List &folders) { mFolders = folders; } void setParentFolderId(const EwsId &id) { mParentFolderId = id; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsFolder::List mFolders; EwsId mParentFolderId; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewscreateitemrequest.cpp b/resources/ews/ewsclient/ewscreateitemrequest.cpp index b9071669e..b30d5f262 100644 --- a/resources/ews/ewsclient/ewscreateitemrequest.cpp +++ b/resources/ews/ewsclient/ewscreateitemrequest.cpp @@ -1,144 +1,148 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreateitemrequest.h" #include "ewsclient_debug.h" static const QVector messageDispositionNames = { QStringLiteral("SaveOnly"), QStringLiteral("SendOnly"), QStringLiteral("SendAndSaveCopy") }; static const QVector meetingDispositionNames = { QStringLiteral("SendToNone"), QStringLiteral("SendOnlyToAll"), QStringLiteral("SendOnlyToChanged"), QStringLiteral("SendToAllAndSaveCopy"), QStringLiteral("SendToChangedAndSaveCopy") }; EwsCreateItemRequest::EwsCreateItemRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mMessageDisp(EwsDispSaveOnly), mMeetingDisp(EwsMeetingDispUnspecified) + : EwsRequest(client, parent) + , mMessageDisp(EwsDispSaveOnly) + , mMeetingDisp(EwsMeetingDispUnspecified) { } EwsCreateItemRequest::~EwsCreateItemRequest() { } void EwsCreateItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("CreateItem")); writer.writeAttribute(QStringLiteral("MessageDisposition"), messageDispositionNames[mMessageDisp]); if (mMeetingDisp != EwsMeetingDispUnspecified) { writer.writeAttribute(QStringLiteral("SendMeetingInvitations"), meetingDispositionNames[mMeetingDisp]); } if (mMessageDisp == EwsDispSaveOnly || mMessageDisp == EwsDispSendAndSaveCopy) { writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SavedItemFolderId")); mSavedFolderId.writeFolderIds(writer); writer.writeEndElement(); } writer.writeStartElement(ewsMsgNsUri, QStringLiteral("Items")); for (const EwsItem &item : qAsConst(mItems)) { item.write(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting CreateItem request (%1 items, parent %2)") - .arg(mItems.size()).arg(mSavedFolderId.id()); + .arg(mItems.size()).arg(mSavedFolderId.id()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsCreateItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("CreateItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsCreateItemRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got CreateItem response - OK"); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got CreateItem response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsCreateItemRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Items")) { if (reader.readNextStartElement()) { EwsItem item(reader); if (!item.isValid()) { return; } mId = item[EwsItemFieldItemId].value(); // Finish the Items element. reader.skipCurrentElement(); } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewscreateitemrequest.h b/resources/ews/ewsclient/ewscreateitemrequest.h index 033bfac65..335fa8f7f 100644 --- a/resources/ews/ewsclient/ewscreateitemrequest.h +++ b/resources/ews/ewsclient/ewscreateitemrequest.h @@ -1,91 +1,93 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSCREATEITEMREQUEST_H #define EWSCREATEITEMREQUEST_H #include #include #include "ewsitem.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsCreateItemRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: const EwsId &itemId() const { return mId; } + protected: Response(QXmlStreamReader &reader); EwsId mId; friend class EwsCreateItemRequest; }; EwsCreateItemRequest(EwsClient &client, QObject *parent); ~EwsCreateItemRequest() override; void setItems(const EwsItem::List &items) { mItems = items; } void setMessageDisposition(EwsMessageDisposition disp) { mMessageDisp = disp; } void setMeetingDisposition(EwsMeetingDisposition disp) { mMeetingDisp = disp; } void setSavedFolderId(const EwsId &id) { mSavedFolderId = id; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsItem::List mItems; EwsId mSavedFolderId; EwsMessageDisposition mMessageDisp; EwsMeetingDisposition mMeetingDisp; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsdeletefolderrequest.cpp b/resources/ews/ewsclient/ewsdeletefolderrequest.cpp index 836b92386..f9e3150d1 100644 --- a/resources/ews/ewsclient/ewsdeletefolderrequest.cpp +++ b/resources/ews/ewsclient/ewsdeletefolderrequest.cpp @@ -1,114 +1,117 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsdeletefolderrequest.h" #include "ewsclient_debug.h" static const QVector deleteTypes = { QStringLiteral("HardDelete"), QStringLiteral("SoftDelete"), QStringLiteral("MoveToDeletedItems") }; EwsDeleteFolderRequest::EwsDeleteFolderRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mType(SoftDelete) + : EwsRequest(client, parent) + , mType(SoftDelete) { } EwsDeleteFolderRequest::~EwsDeleteFolderRequest() { } void EwsDeleteFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("DeleteFolder")); writer.writeAttribute(QStringLiteral("DeleteType"), deleteTypes[mType]); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FolderIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeFolderIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting DeleteFolder request (%1 folders)") - .arg(mIds.size()); + .arg(mIds.size()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsDeleteFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("DeleteFolder"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsDeleteFolderRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got DeleteFolder response - OK"); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got DeleteFolder response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsDeleteFolderRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewsdeletefolderrequest.h b/resources/ews/ewsclient/ewsdeletefolderrequest.h index e6ef12ad3..41844dd1d 100644 --- a/resources/ews/ewsclient/ewsdeletefolderrequest.h +++ b/resources/ews/ewsclient/ewsdeletefolderrequest.h @@ -1,78 +1,79 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSDELETEFOLDERREQUEST_H #define EWSDELETEFOLDERREQUEST_H #include #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsDeleteFolderRequest : public EwsRequest { Q_OBJECT public: enum Type { HardDelete = 0, SoftDelete, MoveToDeletedItems }; class Response : public EwsRequest::Response { public: protected: Response(QXmlStreamReader &reader); friend class EwsDeleteFolderRequest; }; EwsDeleteFolderRequest(EwsClient &client, QObject *parent); ~EwsDeleteFolderRequest() override; void setFolderIds(const EwsId::List &ids) { mIds = ids; } void setType(Type type) { mType = type; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId::List mIds; Type mType; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsdeleteitemrequest.cpp b/resources/ews/ewsclient/ewsdeleteitemrequest.cpp index 19ce7de1d..4c46d0da4 100644 --- a/resources/ews/ewsclient/ewsdeleteitemrequest.cpp +++ b/resources/ews/ewsclient/ewsdeleteitemrequest.cpp @@ -1,113 +1,116 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsdeleteitemrequest.h" #include "ewsclient_debug.h" static const QVector deleteTypes = { QStringLiteral("HardDelete"), QStringLiteral("SoftDelete"), QStringLiteral("MoveToDeletedItems") }; EwsDeleteItemRequest::EwsDeleteItemRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mType(SoftDelete) + : EwsRequest(client, parent) + , mType(SoftDelete) { } EwsDeleteItemRequest::~EwsDeleteItemRequest() { } void EwsDeleteItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("DeleteItem")); writer.writeAttribute(QStringLiteral("DeleteType"), deleteTypes[mType]); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ItemIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeItemIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting DeleteItem request (") << mIds << ")"; qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsDeleteItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("DeleteItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsDeleteItemRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got DeleteItem response - OK, deleted items") << mIds; } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got DeleteItem response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsDeleteItemRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewsdeleteitemrequest.h b/resources/ews/ewsclient/ewsdeleteitemrequest.h index 78016cfb2..bf4946feb 100644 --- a/resources/ews/ewsclient/ewsdeleteitemrequest.h +++ b/resources/ews/ewsclient/ewsdeleteitemrequest.h @@ -1,78 +1,79 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSDELETEITEMREQUEST_H #define EWSDELETEITEMREQUEST_H #include #include "ewsitem.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsDeleteItemRequest : public EwsRequest { Q_OBJECT public: enum Type { HardDelete = 0, SoftDelete, MoveToDeletedItems }; class Response : public EwsRequest::Response { public: protected: Response(QXmlStreamReader &reader); friend class EwsDeleteItemRequest; }; EwsDeleteItemRequest(EwsClient &client, QObject *parent); ~EwsDeleteItemRequest() override; void setItemIds(const EwsId::List &ids) { mIds = ids; } void setType(Type type) { mType = type; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId::List mIds; Type mType; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewseffectiverights.cpp b/resources/ews/ewsclient/ewseffectiverights.cpp index 07d873ff4..81b085921 100644 --- a/resources/ews/ewsclient/ewseffectiverights.cpp +++ b/resources/ews/ewsclient/ewseffectiverights.cpp @@ -1,201 +1,202 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewseffectiverights.h" #include #include #include #include "ewsclient_debug.h" #include "ewstypes.h" class EwsEffectiveRightsPrivate : public QSharedData { public: enum Right { CreateAssociated = 0, CreateContents, CreateHierarchy, Delete, Modify, Read, ViewPrivateItems }; EwsEffectiveRightsPrivate(); virtual ~EwsEffectiveRightsPrivate(); bool readRight(QXmlStreamReader &reader, Right right); bool mValid; QBitArray mRights; }; EwsEffectiveRightsPrivate::EwsEffectiveRightsPrivate() - : mValid(false), mRights(7) + : mValid(false) + , mRights(7) { } EwsEffectiveRightsPrivate::~EwsEffectiveRightsPrivate() { } bool EwsEffectiveRightsPrivate::readRight(QXmlStreamReader &reader, Right right) { QString elm = reader.name().toString(); if (reader.error() != QXmlStreamReader::NoError) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(QStringLiteral("EffectiveRights"), elm); + .arg(QStringLiteral("EffectiveRights"), elm); return false; } const QString text = reader.readElementText(); if (text == QStringLiteral("true")) { mRights.setBit(right); } else if (text == QStringLiteral("false")) { mRights.clearBit(right); } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element value: %3.") - .arg(QStringLiteral("EffectiveRights"), elm, text); + .arg(QStringLiteral("EffectiveRights"), elm, text); return false; } return true; } EwsEffectiveRights::EwsEffectiveRights() : d(new EwsEffectiveRightsPrivate()) { } EwsEffectiveRights::EwsEffectiveRights(QXmlStreamReader &reader) : d(new EwsEffectiveRightsPrivate()) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in mailbox element:") << reader.namespaceUri(); return; } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("CreateAssociated")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::CreateAssociated)) { return; } } else if (readerName == QStringLiteral("CreateContents")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::CreateContents)) { return; } } else if (readerName == QStringLiteral("CreateHierarchy")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::CreateHierarchy)) { return; } } else if (readerName == QStringLiteral("Delete")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::Delete)) { return; } } else if (readerName == QStringLiteral("Modify")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::Modify)) { return; } } else if (readerName == QStringLiteral("Read")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::Read)) { return; } } else if (readerName == QStringLiteral("ViewPrivateItems")) { if (!d->readRight(reader, EwsEffectiveRightsPrivate::ViewPrivateItems)) { return; } } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("EffectiveRights"), readerName.toString()); + .arg(QStringLiteral("EffectiveRights"), readerName.toString()); return; } } d->mValid = true; } EwsEffectiveRights::EwsEffectiveRights(const EwsEffectiveRights &other) : d(other.d) { } EwsEffectiveRights::EwsEffectiveRights(EwsEffectiveRights &&other) : d(std::move(other.d)) { } EwsEffectiveRights::~EwsEffectiveRights() { } EwsEffectiveRights &EwsEffectiveRights::operator=(const EwsEffectiveRights &other) { d = other.d; return *this; } + EwsEffectiveRights &EwsEffectiveRights::operator=(EwsEffectiveRights &&other) { d = std::move(other.d); return *this; } bool EwsEffectiveRights::isValid() const { return d->mValid; } bool EwsEffectiveRights::canCreateAssociated() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::CreateAssociated); } bool EwsEffectiveRights::canCreateContents() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::CreateContents); } bool EwsEffectiveRights::canCreateHierarchy() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::CreateHierarchy); } bool EwsEffectiveRights::canDelete() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::Delete); } bool EwsEffectiveRights::canModify() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::Modify); } bool EwsEffectiveRights::canRead() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::Read); } bool EwsEffectiveRights::canViewPrivateItems() const { return d->mRights.testBit(EwsEffectiveRightsPrivate::ViewPrivateItems); } - diff --git a/resources/ews/ewsclient/ewseventrequestbase.cpp b/resources/ews/ewsclient/ewseventrequestbase.cpp index 55e542c90..94cfe2786 100644 --- a/resources/ews/ewsclient/ewseventrequestbase.cpp +++ b/resources/ews/ewsclient/ewseventrequestbase.cpp @@ -1,269 +1,272 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewseventrequestbase.h" #include "ewsxml.h" #include "ewsclient_debug.h" enum NotificationElementType { SubscriptionId, PreviousWatermark, MoreEvents, Events }; typedef EwsXml NotificationReader; enum EventElementType { Watermark, Timestamp, ItemId, FolderId, ParentFolderId, OldItemId, OldFolderId, OldParentFolderId, UnreadCount }; typedef EwsXml EventReader; EwsEventRequestBase::EwsEventRequestBase(EwsClient &client, const QString &reqName, QObject *parent) - : EwsRequest(client, parent), mReqName(reqName) + : EwsRequest(client, parent) + , mReqName(reqName) { qRegisterMetaType(); } EwsEventRequestBase::~EwsEventRequestBase() { } bool EwsEventRequestBase::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, mReqName, - [this](QXmlStreamReader &reader) {return parseNotificationsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseNotificationsResponse(reader); + }); } bool EwsEventRequestBase::parseNotificationsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { int numEv = 0; Q_FOREACH (const Notification &nfy, resp.notifications()) { numEv += nfy.events().size(); } qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got %1 response (%2 notifications, %3 events)") - .arg(mReqName).arg(resp.notifications().size()).arg(numEv); + .arg(mReqName).arg(resp.notifications().size()).arg(numEv); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got %1 response - %2") - .arg(mReqName, resp.responseMessage()); + .arg(mReqName, resp.responseMessage()); } } mResponses.append(resp); return true; } EwsEventRequestBase::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Notification")) { Notification nfy(reader); if (!nfy.isValid()) { setErrorMsg(QStringLiteral("Failed to process notification.")); reader.skipCurrentElement(); return; } mNotifications.append(nfy); } else if (reader.name() == QStringLiteral("Notifications")) { while (reader.readNextStartElement()) { if (reader.name() == QStringLiteral("Notification")) { Notification nfy(reader); if (!nfy.isValid()) { setErrorMsg(QStringLiteral("Failed to process notification.")); reader.skipCurrentElement(); return; } mNotifications.append(nfy); } else { setErrorMsg(QStringLiteral("Failed to read EWS request - expected Notification inside Notifications")); } } } else if (reader.name() == QStringLiteral("ConnectionStatus")) { reader.skipCurrentElement(); } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element '%1'") .arg(reader.name().toString())); return; } } } EwsEventRequestBase::Notification::Notification(QXmlStreamReader &reader) { static const QVector items = { {SubscriptionId, QStringLiteral("SubscriptionId"), &ewsXmlTextReader}, {PreviousWatermark, QStringLiteral("PreviousWatermark"), &ewsXmlTextReader}, {MoreEvents, QStringLiteral("MoreEvents"), &ewsXmlBoolReader}, {Events, QStringLiteral("CopiedEvent"), &eventsReader}, {Events, QStringLiteral("CreatedEvent"), &eventsReader}, {Events, QStringLiteral("DeletedEvent"), &eventsReader}, {Events, QStringLiteral("ModifiedEvent"), &eventsReader}, {Events, QStringLiteral("MovedEvent"), &eventsReader}, {Events, QStringLiteral("NewMailEvent"), &eventsReader}, {Events, QStringLiteral("FreeBusyChangeEvent"), &eventsReader}, {Events, QStringLiteral("StatusEvent"), &eventsReader} }; static const NotificationReader staticReader(items); NotificationReader ewsreader(staticReader); if (!ewsreader.readItems(reader, ewsTypeNsUri)) { return; } QHash values = ewsreader.values(); mSubscriptionId = values[SubscriptionId].toString(); mWatermark = values[PreviousWatermark].toString(); mMoreEvents = values[MoreEvents].toBool(); mEvents = values[Events].value(); } bool EwsEventRequestBase::Notification::eventsReader(QXmlStreamReader &reader, QVariant &val) { Event::List events = val.value(); const QString elmName(reader.name().toString()); Event event(reader); if (!event.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element").arg(elmName); return false; } events.append(event); val = QVariant::fromValue(events); return true; } EwsEventRequestBase::Event::Event(QXmlStreamReader &reader) : mType(EwsUnknownEvent) { static const QVector items = { {Watermark, QStringLiteral("Watermark"), &ewsXmlTextReader}, {Timestamp, QStringLiteral("TimeStamp"), &ewsXmlDateTimeReader}, {FolderId, QStringLiteral("FolderId"), &ewsXmlIdReader}, {ItemId, QStringLiteral("ItemId"), &ewsXmlIdReader}, {ParentFolderId, QStringLiteral("ParentFolderId"), &ewsXmlIdReader}, {OldFolderId, QStringLiteral("OldFolderId"), &ewsXmlIdReader}, {OldItemId, QStringLiteral("OldItemId"), &ewsXmlIdReader}, {OldParentFolderId, QStringLiteral("OldParentFolderId"), &ewsXmlIdReader}, {UnreadCount, QStringLiteral("UnreadCount"), &ewsXmlUIntReader}, }; static const EventReader staticReader(items); EventReader ewsreader(staticReader); QStringRef elmName = reader.name(); if (elmName == QStringLiteral("CopiedEvent")) { mType = EwsCopiedEvent; } else if (elmName == QStringLiteral("CreatedEvent")) { mType = EwsCreatedEvent; } else if (elmName == QStringLiteral("DeletedEvent")) { mType = EwsDeletedEvent; } else if (elmName == QStringLiteral("ModifiedEvent")) { mType = EwsModifiedEvent; } else if (elmName == QStringLiteral("MovedEvent")) { mType = EwsMovedEvent; } else if (elmName == QStringLiteral("NewMailEvent")) { mType = EwsNewMailEvent; } else if (elmName == QStringLiteral("StatusEvent")) { mType = EwsStatusEvent; } else if (elmName == QStringLiteral("FreeBusyChangedEvent")) { mType = EwsFreeBusyChangedEvent; } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Unknown notification event type: %1") - .arg(elmName.toString()); + .arg(elmName.toString()); return; } if (!ewsreader.readItems(reader, ewsTypeNsUri)) { mType = EwsUnknownEvent; return; } QHash values = ewsreader.values(); mWatermark = values[Watermark].toString(); mTimestamp = values[Timestamp].toDateTime(); if (values.contains(ItemId)) { mId = values[ItemId].value(); mOldId = values[OldItemId].value(); mIsFolder = false; } else { mId = values[FolderId].value(); mOldId = values[OldFolderId].value(); mIsFolder = true; } mParentFolderId = values[ParentFolderId].value(); mOldParentFolderId = values[OldParentFolderId].value(); mUnreadCount = values[UnreadCount].toUInt(); if (mType == EwsStatusEvent) { qCDebugNCS(EWSCLI_LOG) << QStringLiteral(" %1").arg(elmName.toString()); } else { qCDebugNCS(EWSCLI_LOG) << QStringLiteral(" %1, %2, parent: ").arg(elmName.toString()).arg(mIsFolder ? 'F' : 'I') << mParentFolderId << QStringLiteral(", id: ") << mId; } } bool EwsEventRequestBase::Response::operator==(const Response &other) const { return mNotifications == other.mNotifications; } bool EwsEventRequestBase::Notification::operator==(const Notification &other) const { - return (mSubscriptionId == other.mSubscriptionId) && (mWatermark == other.mWatermark) && - (mMoreEvents == other.mMoreEvents) && (mEvents == other.mEvents); + return (mSubscriptionId == other.mSubscriptionId) && (mWatermark == other.mWatermark) + && (mMoreEvents == other.mMoreEvents) && (mEvents == other.mEvents); } bool EwsEventRequestBase::Event::operator==(const Event &other) const { - return (mType == other.mType) && (mWatermark == other.mWatermark) && - (mTimestamp == other.mTimestamp) && (mId == other.mId) && - (mParentFolderId == other.mParentFolderId) && (mUnreadCount == other.mUnreadCount) && - (mOldId == other.mOldId) && (mOldParentFolderId == other.mOldParentFolderId) && - (mIsFolder == other.mIsFolder); + return (mType == other.mType) && (mWatermark == other.mWatermark) + && (mTimestamp == other.mTimestamp) && (mId == other.mId) + && (mParentFolderId == other.mParentFolderId) && (mUnreadCount == other.mUnreadCount) + && (mOldId == other.mOldId) && (mOldParentFolderId == other.mOldParentFolderId) + && (mIsFolder == other.mIsFolder); } diff --git a/resources/ews/ewsclient/ewseventrequestbase.h b/resources/ews/ewsclient/ewseventrequestbase.h index 4d6a4d9a8..8b7418ac2 100644 --- a/resources/ews/ewsclient/ewseventrequestbase.h +++ b/resources/ews/ewsclient/ewseventrequestbase.h @@ -1,195 +1,196 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSEVENTREQUESTBASE_H #define EWSEVENTREQUESTBASE_H #include #include #include #include "ewsid.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsEventRequestBase : public EwsRequest { Q_OBJECT public: class Notification; class Response; class Event { public: typedef QList List; EwsEventType type() const { return mType; } const QString &watermark() const { return mWatermark; } const QDateTime ×tamp() const { return mTimestamp; } const EwsId &itemId() const { return mId; } const EwsId &parentFolderId() const { return mParentFolderId; } uint unreadCount() const { return mUnreadCount; } const EwsId &oldItemId() const { return mOldId; } const EwsId &oldParentFolderId() const { return mOldParentFolderId; } bool itemIsFolder() const { return mIsFolder; } bool operator==(const Event &other) const; protected: Event(QXmlStreamReader &reader); bool isValid() const { return mType != EwsUnknownEvent; } EwsEventType mType; QString mWatermark; QDateTime mTimestamp; EwsId mId; EwsId mParentFolderId; uint mUnreadCount = 0; EwsId mOldId; EwsId mOldParentFolderId; bool mIsFolder; friend class EwsEventRequestBase::Notification; }; class Notification { public: typedef QList List; const QString &subscriptionId() const { return mSubscriptionId; } const QString &previousWatermark() const { return mWatermark; } bool hasMoreEvents() const { return mMoreEvents; } const Event::List &events() const { return mEvents; } bool operator==(const Notification &other) const; protected: Notification(QXmlStreamReader &reader); bool isValid() const { return !mSubscriptionId.isNull(); } static bool eventsReader(QXmlStreamReader &reader, QVariant &val); QString mSubscriptionId; QString mWatermark; bool mMoreEvents; Event::List mEvents; friend class EwsEventRequestBase::Response; }; class Response : public EwsRequest::Response { public: const Notification::List ¬ifications() const { return mNotifications; } bool operator==(const Response &other) const; protected: Response(QXmlStreamReader &reader); Notification::List mNotifications; friend class EwsEventRequestBase; }; ~EwsEventRequestBase() override; void setSubscriptionId(const QString &id) { mSubscriptionId = id; } const QList &responses() const { return mResponses; } + protected: EwsEventRequestBase(EwsClient &client, const QString &reqName, QObject *parent); bool parseResult(QXmlStreamReader &reader) override; bool parseNotificationsResponse(QXmlStreamReader &reader); QString mSubscriptionId; QList mResponses; const QString mReqName; }; Q_DECLARE_METATYPE(EwsEventRequestBase::Event::List) #endif diff --git a/resources/ews/ewsclient/ewsfindfolderrequest.cpp b/resources/ews/ewsclient/ewsfindfolderrequest.cpp index c0b11c2de..7aeb6260b 100644 --- a/resources/ews/ewsclient/ewsfindfolderrequest.cpp +++ b/resources/ews/ewsclient/ewsfindfolderrequest.cpp @@ -1,244 +1,248 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsfindfolderrequest.h" #include #include #include "ewsclient_debug.h" static const QString traversalTypeNames[] = { QStringLiteral("Shallow"), QStringLiteral("Deep"), QStringLiteral("SoftDeleted") }; class EwsFindFolderResponse : public EwsRequest::Response { public: EwsFindFolderResponse(QXmlStreamReader &reader); bool parseRootFolder(QXmlStreamReader &reader); - EwsFolder* readFolder(QXmlStreamReader &reader); + EwsFolder *readFolder(QXmlStreamReader &reader); unsigned readChildFolders(EwsFolder &parent, unsigned count, QXmlStreamReader &reader); QList mFolders; }; EwsFindFolderRequest::EwsFindFolderRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mTraversal(EwsTraversalDeep) + : EwsRequest(client, parent) + , mTraversal(EwsTraversalDeep) { } EwsFindFolderRequest::~EwsFindFolderRequest() { } void EwsFindFolderRequest::setParentFolderId(const EwsId &id) { mParentId = id; } void EwsFindFolderRequest::setFolderShape(const EwsFolderShape &shape) { mShape = shape; } void EwsFindFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FindFolder")); writer.writeAttribute(QStringLiteral("Traversal"), traversalTypeNames[mTraversal]); mShape.write(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ParentFolderIds")); mParentId.writeFolderIds(writer); writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsFindFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("FindFolder"), - [this](QXmlStreamReader &reader) {return parseFoldersResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseFoldersResponse(reader); + }); } bool EwsFindFolderRequest::parseFoldersResponse(QXmlStreamReader &reader) { EwsFindFolderResponse *resp = new EwsFindFolderResponse(reader); if (resp->responseClass() == EwsResponseUnknown) { return false; } mFolders = resp->mFolders; return true; } EwsFindFolderResponse::EwsFindFolderResponse(QXmlStreamReader &reader) : EwsRequest::Response(reader) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("RootFolder")) { if (!parseRootFolder(reader)) { return; } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } bool EwsFindFolderResponse::parseRootFolder(QXmlStreamReader &reader) { if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QStringLiteral("RootFolder")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element (got %2).") .arg(QStringLiteral("RootFolder"), reader.qualifiedName().toString())); } if (!reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView")) || !reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView"))) { return setErrorMsg(QStringLiteral("Failed to read EWS request - missing attributes of %1 element.") .arg(QStringLiteral("RootFolder"))); } bool ok; unsigned totalItems = reader.attributes().value(QStringLiteral("TotalItemsInView")).toUInt(&ok); if (!ok) { return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.") .arg(QStringLiteral("TotalItemsInView"))); } if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in %1 element.") .arg(QStringLiteral("RootFolder"))); } if (reader.namespaceUri() != ewsTypeNsUri || reader.name() != QStringLiteral("Folders")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element (got %2).") .arg(QStringLiteral("Folders"), reader.qualifiedName().toString())); } if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in %1 element.") .arg(QStringLiteral("Folders"))); } if (reader.namespaceUri() != ewsTypeNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected child element from types namespace.")); } unsigned i = 0; for (i = 0; i < totalItems; ++i) { EwsFolder *folder = readFolder(reader); reader.readNextStartElement(); if (folder) { bool ok; int childCount = (*folder)[EwsFolderFieldChildFolderCount].toUInt(&ok); if (childCount > 0) { unsigned readCount = readChildFolders(*folder, childCount, reader); - if (readCount == 0) + if (readCount == 0) { return false; + } i += readCount; } mFolders.append(*folder); delete folder; } } // Finish the Folders element reader.skipCurrentElement(); // Finish the RootFolder element reader.skipCurrentElement(); return true; } -EwsFolder* EwsFindFolderResponse::readFolder(QXmlStreamReader &reader) +EwsFolder *EwsFindFolderResponse::readFolder(QXmlStreamReader &reader) { EwsFolder *folder = nullptr; const QStringRef readerName = reader.name(); - if (readerName == QStringLiteral("Folder") || - readerName == QStringLiteral("CalendarFolder") || - readerName == QStringLiteral("ContactsFolder") || - readerName == QStringLiteral("TasksFolder") || - readerName == QStringLiteral("SearchFolder")) { + if (readerName == QStringLiteral("Folder") + || readerName == QStringLiteral("CalendarFolder") + || readerName == QStringLiteral("ContactsFolder") + || readerName == QStringLiteral("TasksFolder") + || readerName == QStringLiteral("SearchFolder")) { folder = new EwsFolder(reader); if (!folder->isValid()) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.") .arg(QStringLiteral("Folder"))); delete folder; return nullptr; } QVariant dn = (*folder)[EwsFolderFieldDisplayName]; if (!dn.isNull()) { EwsClient::folderHash[(*folder)[EwsFolderFieldFolderId].value().id()] = dn.toString(); } } else { qCWarning(EWSCLI_LOG).noquote() << QStringLiteral("Unsupported folder type %1").arg(readerName.toString()); reader.skipCurrentElement(); } return folder; } unsigned EwsFindFolderResponse::readChildFolders(EwsFolder &parent, unsigned count, QXmlStreamReader &reader) { unsigned readCount = 0; for (unsigned i = 0; i < count; ++i) { EwsFolder *folder = readFolder(reader); reader.readNextStartElement(); if (folder) { bool ok; int childCount = (*folder)[EwsFolderFieldChildFolderCount].toUInt(&ok); if (ok && childCount > 0) { unsigned readCount2 = readChildFolders(*folder, childCount, reader); - if (readCount2 == 0) + if (readCount2 == 0) { return false; + } readCount += readCount2; } parent.addChild(*folder); } ++readCount; } return readCount; } - diff --git a/resources/ews/ewsclient/ewsfindfolderrequest.h b/resources/ews/ewsclient/ewsfindfolderrequest.h index b2b686ee2..a526fdcbb 100644 --- a/resources/ews/ewsclient/ewsfindfolderrequest.h +++ b/resources/ews/ewsclient/ewsfindfolderrequest.h @@ -1,58 +1,59 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFINDFOLDERREQUEST_H #define EWSFINDFOLDERREQUEST_H #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" #include "ewsfoldershape.h" class EwsFindFolderRequest : public EwsRequest { Q_OBJECT public: explicit EwsFindFolderRequest(EwsClient &client, QObject *parent); ~EwsFindFolderRequest() override; void setParentFolderId(const EwsId &id); void setFolderShape(const EwsFolderShape &shape); void setTraversal(EwsTraversalType traversal) { mTraversal = traversal; } void start() override; const QList folders() const { return mFolders; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseFoldersResponse(QXmlStreamReader &reader); private: EwsId mParentId; EwsFolderShape mShape; EwsTraversalType mTraversal; QList mFolders; }; #endif diff --git a/resources/ews/ewsclient/ewsfinditemrequest.cpp b/resources/ews/ewsclient/ewsfinditemrequest.cpp index c386c71b5..6c6ca4625 100644 --- a/resources/ews/ewsclient/ewsfinditemrequest.cpp +++ b/resources/ews/ewsclient/ewsfinditemrequest.cpp @@ -1,281 +1,296 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsfinditemrequest.h" #include #include #include "ewsclient_debug.h" static const QString traversalTypeNames[] = { QStringLiteral("Shallow"), QStringLiteral("Deep"), QStringLiteral("SoftDeleted"), QStringLiteral("Associated") }; class EwsFindItemResponse : public EwsRequest::Response { public: EwsFindItemResponse(QXmlStreamReader &reader); bool parseRootFolder(QXmlStreamReader &reader); - EwsItem* readItem(QXmlStreamReader &reader); + EwsItem *readItem(QXmlStreamReader &reader); QList mItems; unsigned mTotalItems; int mNextOffset; int mNextNumerator; int mNextDenominator; bool mIncludesLastItem; }; EwsFindItemRequest::EwsFindItemRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mTraversal(EwsTraversalShallow), mPagination(false), - mPageBasePoint(EwsBasePointBeginning), mPageOffset(0), mFractional(false), mMaxItems(-1), - mFracNumerator(0), mFracDenominator(0), mTotalItems(0), mNextOffset(-1), mNextNumerator(-1), - mNextDenominator(-1), mIncludesLastItem(false) + : EwsRequest(client, parent) + , mTraversal(EwsTraversalShallow) + , mPagination(false) + , mPageBasePoint(EwsBasePointBeginning) + , mPageOffset(0) + , mFractional(false) + , mMaxItems(-1) + , mFracNumerator(0) + , mFracDenominator(0) + , mTotalItems(0) + , mNextOffset(-1) + , mNextNumerator(-1) + , mNextDenominator(-1) + , mIncludesLastItem(false) { } EwsFindItemRequest::~EwsFindItemRequest() { } void EwsFindItemRequest::setFolderId(const EwsId &id) { mFolderId = id; } void EwsFindItemRequest::setItemShape(const EwsItemShape &shape) { mShape = shape; } void EwsFindItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FindItem")); writer.writeAttribute(QStringLiteral("Traversal"), traversalTypeNames[mTraversal]); mShape.write(writer); if (mPagination) { writer.writeStartElement(ewsMsgNsUri, QStringLiteral("IndexedPageItemView")); if (mMaxItems > 0) { writer.writeAttribute(QStringLiteral("MaxEntriesReturned"), QString::number(mMaxItems)); } writer.writeAttribute(QStringLiteral("Offset"), QString::number(mPageOffset)); writer.writeAttribute(QStringLiteral("BasePoint"), (mPageBasePoint == EwsBasePointEnd) ? QStringLiteral("End") : QStringLiteral("Beginning")); writer.writeEndElement(); } else if (mFractional) { writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FractionalPageItemView")); if (mMaxItems > 0) { writer.writeAttribute(QStringLiteral("MaxEntriesReturned"), QString::number(mMaxItems)); } writer.writeAttribute(QStringLiteral("Numerator"), QString::number(mFracNumerator)); writer.writeAttribute(QStringLiteral("Denominator"), QString::number(mFracDenominator)); writer.writeEndElement(); } writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ParentFolderIds")); mFolderId.writeFolderIds(writer); writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting FindItems request (folder: ") << mFolderId << QStringLiteral(")"); prepare(reqString); doSend(); } bool EwsFindItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("FindItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsFindItemRequest::parseItemsResponse(QXmlStreamReader &reader) { EwsFindItemResponse *resp = new EwsFindItemResponse(reader); if (resp->responseClass() == EwsResponseUnknown) { return false; } mItems = resp->mItems; mTotalItems = resp->mTotalItems; mNextOffset = resp->mNextOffset; mNextNumerator = resp->mNextNumerator; mNextDenominator = resp->mNextDenominator; mIncludesLastItem = resp->mIncludesLastItem; if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp->isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got FindItems response (%1 items, last included: %2)") - .arg(mItems.size()).arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false")); + .arg(mItems.size()).arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false")); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got FindItems response - %1") - .arg(resp->responseMessage()); + .arg(resp->responseMessage()); } } return true; } EwsFindItemResponse::EwsFindItemResponse(QXmlStreamReader &reader) : EwsRequest::Response(reader) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("RootFolder")) { if (!parseRootFolder(reader)) { return; } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } bool EwsFindItemResponse::parseRootFolder(QXmlStreamReader &reader) { - if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QStringLiteral("RootFolder")) + if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QStringLiteral("RootFolder")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element (got %2).") .arg(QStringLiteral("RootFolder")).arg(reader.qualifiedName().toString())); + } if (!reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView")) || !reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView"))) { return setErrorMsg(QStringLiteral("Failed to read EWS request - missing attributes of %1 element.") .arg(QStringLiteral("RootFolder"))); } bool ok; QXmlStreamAttributes attrs = reader.attributes(); mTotalItems = attrs.value(QStringLiteral("TotalItemsInView")).toUInt(&ok); if (!ok) { return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.") .arg(QStringLiteral("TotalItemsInView"))); } mIncludesLastItem = attrs.value(QStringLiteral("IncludesLastItemInRange")) == QStringLiteral("true"); if (attrs.hasAttribute(QStringLiteral("IndexedPagingOffset"))) { mNextOffset = attrs.value(QStringLiteral("IndexedPagingOffset")).toInt(&ok); if (!ok) { return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.") .arg(QStringLiteral("IndexedPagingOffset"))); } } if (attrs.hasAttribute(QStringLiteral("NumeratorOffset"))) { mNextNumerator = attrs.value(QStringLiteral("NumeratorOffset")).toInt(&ok); if (!ok) { return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.") .arg(QStringLiteral("NumeratorOffset"))); } } if (attrs.hasAttribute(QStringLiteral("AbsoluteDenominator"))) { mNextDenominator = attrs.value(QStringLiteral("AbsoluteDenominator")).toInt(&ok); if (!ok) { return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.") .arg(QStringLiteral("AbsoluteDenominator"))); } } - if (!reader.readNextStartElement()) + if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in %1 element.") .arg(QStringLiteral("RootFolder"))); + } - if (reader.namespaceUri() != ewsTypeNsUri || reader.name() != QStringLiteral("Items")) + if (reader.namespaceUri() != ewsTypeNsUri || reader.name() != QStringLiteral("Items")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element (got %2).") .arg(QStringLiteral("Items")).arg(reader.qualifiedName().toString())); + } if (!reader.readNextStartElement()) { // An empty Items element means no items. reader.skipCurrentElement(); return true; } - if (reader.namespaceUri() != ewsTypeNsUri) + if (reader.namespaceUri() != ewsTypeNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected child element from types namespace.")); + } do { - EwsItem*item = readItem(reader); + EwsItem *item = readItem(reader); if (item) { mItems.append(*item); } } while (reader.readNextStartElement()); // Finish the Items element reader.skipCurrentElement(); // Finish the RootFolder element reader.skipCurrentElement(); return true; } -EwsItem* EwsFindItemResponse::readItem(QXmlStreamReader &reader) +EwsItem *EwsFindItemResponse::readItem(QXmlStreamReader &reader) { EwsItem *item = nullptr; const QStringRef readerName = reader.name(); - if (readerName == QStringLiteral("Item") || - readerName == QStringLiteral("Message") || - readerName == QStringLiteral("CalendarItem") || - readerName == QStringLiteral("Contact") || - readerName == QStringLiteral("DistributionList") || - readerName == QStringLiteral("MeetingMessage") || - readerName == QStringLiteral("MeetingRequest") || - readerName == QStringLiteral("MeetingResponse") || - readerName == QStringLiteral("MeetingCancellation") || - readerName == QStringLiteral("Task")) { + if (readerName == QStringLiteral("Item") + || readerName == QStringLiteral("Message") + || readerName == QStringLiteral("CalendarItem") + || readerName == QStringLiteral("Contact") + || readerName == QStringLiteral("DistributionList") + || readerName == QStringLiteral("MeetingMessage") + || readerName == QStringLiteral("MeetingRequest") + || readerName == QStringLiteral("MeetingResponse") + || readerName == QStringLiteral("MeetingCancellation") + || readerName == QStringLiteral("Task")) { qCDebug(EWSCLI_LOG).noquote() << QStringLiteral("Processing %1").arg(readerName.toString()); item = new EwsItem(reader); if (!item->isValid()) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.") .arg(readerName.toString())); delete item; return nullptr; } } else { qCWarning(EWSCLI_LOG).noquote() << QStringLiteral("Unsupported folder type %1").arg(readerName.toString()); reader.skipCurrentElement(); } return item; } - diff --git a/resources/ews/ewsclient/ewsfinditemrequest.h b/resources/ews/ewsclient/ewsfinditemrequest.h index 02f760a76..18d075ce9 100644 --- a/resources/ews/ewsclient/ewsfinditemrequest.h +++ b/resources/ews/ewsclient/ewsfinditemrequest.h @@ -1,108 +1,109 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFINDITEMREQUEST_H #define EWSFINDITEMREQUEST_H #include "ewsitem.h" #include "ewsrequest.h" #include "ewstypes.h" #include "ewsitemshape.h" class EwsFindItemRequest : public EwsRequest { Q_OBJECT public: explicit EwsFindItemRequest(EwsClient &client, QObject *parent); ~EwsFindItemRequest() override; void setFolderId(const EwsId &id); void setItemShape(const EwsItemShape &shape); void setTraversal(EwsTraversalType traversal) { mTraversal = traversal; } void setPagination(EwsIndexedViewBasePoint basePoint, unsigned offset, int maxItems = -1) { mFractional = false; mMaxItems = maxItems; mPageBasePoint = basePoint; mPageOffset = offset; mPagination = true; } void setFractional(unsigned numerator, unsigned denominator, int maxItems = -1) { mPagination = false; mMaxItems = maxItems; mFracNumerator = numerator; mFracDenominator = denominator; mFractional = true; } void start() override; bool includesLastItem() const { return mIncludesLastItem; } int nextOffset() const { return mNextOffset; } int nextNumerator() const { return mNextNumerator; } int nextDenominator() const { return mNextDenominator; } const QList items() const { return mItems; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId mFolderId; EwsItemShape mShape; EwsTraversalType mTraversal; bool mPagination; EwsIndexedViewBasePoint mPageBasePoint; unsigned mPageOffset; bool mFractional; int mMaxItems; unsigned mFracNumerator; unsigned mFracDenominator; QList mItems; unsigned mTotalItems; int mNextOffset; int mNextNumerator; int mNextDenominator; bool mIncludesLastItem; }; #endif diff --git a/resources/ews/ewsclient/ewsfolder.cpp b/resources/ews/ewsclient/ewsfolder.cpp index 26885d401..06cf29a7a 100644 --- a/resources/ews/ewsclient/ewsfolder.cpp +++ b/resources/ews/ewsclient/ewsfolder.cpp @@ -1,233 +1,234 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfolder.h" #include #include #include "ewsitembase_p.h" #include "ewsxml.h" #include "ewseffectiverights.h" #include "ewsclient_debug.h" -#define D_PTR EwsFolderPrivate *d = reinterpret_cast(this->d.data()); -#define D_CPTR const EwsFolderPrivate *d = reinterpret_cast(this->d.data()); +#define D_PTR EwsFolderPrivate *d = reinterpret_cast(this->d.data()); +#define D_CPTR const EwsFolderPrivate *d = reinterpret_cast(this->d.data()); static const QVector folderTypeNames = { QStringLiteral("Folder"), QStringLiteral("CalendarFolder"), QStringLiteral("ContactsFolder"), QStringLiteral("SearchFolder"), QStringLiteral("TasksFolder") }; class EwsFolderPrivate : public EwsItemBasePrivate { public: typedef EwsXml XmlProc; EwsFolderPrivate(); EwsFolderPrivate(const EwsItemBasePrivate &other); EwsItemBasePrivate *clone() const override { return new EwsFolderPrivate(*this); } static bool effectiveRightsReader(QXmlStreamReader &reader, QVariant &val); EwsFolderType mType; EwsFolder *mParent; QVector mChildren; static const XmlProc mStaticEwsXml; XmlProc mEwsXml; }; typedef EwsXml ItemFieldsReader; static const QVector ewsFolderItems = { {EwsFolderFieldFolderId, QStringLiteral("FolderId"), &ewsXmlIdReader, &ewsXmlIdWriter}, {EwsFolderFieldParentFolderId, QStringLiteral("ParentFolderId"), &ewsXmlIdReader}, {EwsFolderFieldFolderClass, QStringLiteral("FolderClass"), &ewsXmlTextReader, &ewsXmlTextWriter}, {EwsFolderFieldDisplayName, QStringLiteral("DisplayName"), &ewsXmlTextReader, &ewsXmlTextWriter}, {EwsFolderFieldTotalCount, QStringLiteral("TotalCount"), &ewsXmlUIntReader, &ewsXmlUIntWriter}, {EwsFolderFieldChildFolderCount, QStringLiteral("ChildFolderCount"), &ewsXmlUIntReader}, {EwsItemFieldExtendedProperties, QStringLiteral("ExtendedProperty"), - &EwsItemBasePrivate::extendedPropertyReader, &EwsItemBasePrivate::extendedPropertyWriter}, + &EwsItemBasePrivate::extendedPropertyReader, &EwsItemBasePrivate::extendedPropertyWriter}, {EwsFolderFieldUnreadCount, QStringLiteral("UnreadCount"), &ewsXmlUIntReader}, {EwsFolderPrivate::XmlProc::Ignore, QStringLiteral("SearchParameters")}, {EwsFolderFieldEffectiveRights, QStringLiteral("EffectiveRights"), - &EwsFolderPrivate::effectiveRightsReader}, + &EwsFolderPrivate::effectiveRightsReader}, {EwsFolderPrivate::XmlProc::Ignore, QStringLiteral("ManagedFolderInformation")}, }; const EwsFolderPrivate::XmlProc EwsFolderPrivate::mStaticEwsXml(ewsFolderItems); EwsFolderPrivate::EwsFolderPrivate() - : EwsItemBasePrivate(), mType(EwsFolderTypeUnknown), mParent(nullptr), mEwsXml(mStaticEwsXml) + : EwsItemBasePrivate() + , mType(EwsFolderTypeUnknown) + , mParent(nullptr) + , mEwsXml(mStaticEwsXml) { } bool EwsFolderPrivate::effectiveRightsReader(QXmlStreamReader &reader, QVariant &val) { EwsEffectiveRights rights(reader); if (!rights.isValid()) { reader.skipCurrentElement(); return false; } val = QVariant::fromValue(rights); return true; } EwsFolder::EwsFolder() : EwsItemBase(QSharedDataPointer(new EwsFolderPrivate())) { } EwsFolder::EwsFolder(QXmlStreamReader &reader) : EwsItemBase(QSharedDataPointer(new EwsFolderPrivate())) { D_PTR // Check what item type are we uint i = 0; d->mType = EwsFolderTypeUnknown; Q_FOREACH (const QString &name, folderTypeNames) { if (name == reader.name()) { d->mType = static_cast(i); break; } i++; } if (d->mType == EwsFolderTypeUnknown) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unknown folder type %1").arg(reader.name().toString()); } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << "Unexpected namespace in folder element:" << reader.namespaceUri(); return; } if (!readBaseFolderElement(reader)) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Invalid folder child: %1").arg(reader.qualifiedName().toString()); return; } } d->mValid = true; - } EwsFolder::EwsFolder(const EwsFolder &other) : EwsItemBase(other.d) { qRegisterMetaType(); } EwsFolder::EwsFolder(EwsFolder &&other) : EwsItemBase(other.d) { } EwsFolder::~EwsFolder() { } EwsFolderType EwsFolder::type() const { D_CPTR return d->mType; } void EwsFolder::setType(EwsFolderType type) { D_PTR d->mType = type; } bool EwsFolder::readBaseFolderElement(QXmlStreamReader &reader) { D_PTR if (!d->mEwsXml.readItem(reader, QStringLiteral("Folder"), ewsTypeNsUri)) { return false; } d->mFields = d->mEwsXml.values(); return true; } const QVector EwsFolder::childFolders() const { D_CPTR return d->mChildren; } void EwsFolder::addChild(EwsFolder &child) { D_PTR if (child.parentFolder() != nullptr) { qCWarning(EWSCLI_LOG).noquote() - << QStringLiteral("Attempt to add child folder which already has a parent (child: %1)"). - arg(child[EwsFolderFieldFolderId].value().id()); + << QStringLiteral("Attempt to add child folder which already has a parent (child: %1)"). + arg(child[EwsFolderFieldFolderId].value().id()); } d->mChildren.append(child); child.setParentFolder(this); } -EwsFolder* EwsFolder::parentFolder() const +EwsFolder *EwsFolder::parentFolder() const { D_CPTR return d->mParent; } void EwsFolder::setParentFolder(EwsFolder *parent) { D_PTR d->mParent = parent; } EwsFolder &EwsFolder::operator=(const EwsFolder &other) { d = other.d; return *this; } EwsFolder &EwsFolder::operator=(EwsFolder &&other) { d = std::move(other.d); return *this; } bool EwsFolder::write(QXmlStreamWriter &writer) const { D_CPTR writer.writeStartElement(ewsTypeNsUri, folderTypeNames[d->mType]); bool status = d->mEwsXml.writeItems(writer, folderTypeNames[d->mType], ewsTypeNsUri, d->mFields); writer.writeEndElement(); return status; } - diff --git a/resources/ews/ewsclient/ewsfolder.h b/resources/ews/ewsclient/ewsfolder.h index b5111821d..f55719993 100644 --- a/resources/ews/ewsclient/ewsfolder.h +++ b/resources/ews/ewsclient/ewsfolder.h @@ -1,59 +1,59 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFOLDER_H #define EWSFOLDER_H #include "ewsitembase.h" #include "ewstypes.h" class QXmlStreamReader; class QXmlStreamWriter; class EwsFolderPrivate; class EwsFolder : public EwsItemBase { public: typedef QList List; EwsFolder(); explicit EwsFolder(QXmlStreamReader &reader); EwsFolder(const EwsFolder &other); EwsFolder(EwsFolder &&other); ~EwsFolder() override; EwsFolder &operator=(const EwsFolder &other); EwsFolder &operator=(EwsFolder &&other); EwsFolderType type() const; void setType(EwsFolderType type); const QVector childFolders() const; void addChild(EwsFolder &child); - EwsFolder* parentFolder() const; + EwsFolder *parentFolder() const; void setParentFolder(EwsFolder *parent); bool write(QXmlStreamWriter &writer) const; protected: bool readBaseFolderElement(QXmlStreamReader &reader); }; Q_DECLARE_METATYPE(EwsFolder) #endif diff --git a/resources/ews/ewsclient/ewsfoldershape.h b/resources/ews/ewsclient/ewsfoldershape.h index 8370e67ff..f27b4e8c5 100644 --- a/resources/ews/ewsclient/ewsfoldershape.h +++ b/resources/ews/ewsclient/ewsfoldershape.h @@ -1,68 +1,83 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSFOLDERSHAPE_H #define EWSFOLDERSHAPE_H #include #include #include "ewspropertyfield.h" #include "ewstypes.h" class EwsFolderShape { public: - explicit EwsFolderShape(EwsBaseShape shape = EwsShapeDefault) : mBaseShape(shape) {} - ~EwsFolderShape() {} + explicit EwsFolderShape(EwsBaseShape shape = EwsShapeDefault) : mBaseShape(shape) + { + } + + ~EwsFolderShape() + { + } + EwsFolderShape(const EwsFolderShape &other) - : mBaseShape(other.mBaseShape), mProps(other.mProps) {} + : mBaseShape(other.mBaseShape) + , mProps(other.mProps) + { + } + EwsFolderShape(EwsFolderShape &&other) - : mBaseShape(other.mBaseShape), mProps(other.mProps) {} + : mBaseShape(other.mBaseShape) + , mProps(other.mProps) + { + } + EwsFolderShape &operator=(EwsFolderShape &&other) { mBaseShape = other.mBaseShape; mProps = std::move(other.mProps); return *this; } + EwsFolderShape &operator=(const EwsFolderShape &other) { mBaseShape = other.mBaseShape; mProps = other.mProps; return *this; } void write(QXmlStreamWriter &writer) const; friend EwsFolderShape &operator<<(EwsFolderShape &shape, const EwsPropertyField &prop); protected: void writeBaseShape(QXmlStreamWriter &writer) const; void writeProperties(QXmlStreamWriter &writer) const; EwsBaseShape mBaseShape; QVector mProps; }; inline EwsFolderShape &operator<<(EwsFolderShape &shape, const EwsPropertyField &prop) { shape.mProps.append(prop); return shape; } #endif diff --git a/resources/ews/ewsclient/ewsgeteventsrequest.cpp b/resources/ews/ewsclient/ewsgeteventsrequest.cpp index a065cdcaa..5a590c5ce 100644 --- a/resources/ews/ewsclient/ewsgeteventsrequest.cpp +++ b/resources/ews/ewsclient/ewsgeteventsrequest.cpp @@ -1,58 +1,58 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsgeteventsrequest.h" #include "ewsxml.h" #include "ewsclient_debug.h" EwsGetEventsRequest::EwsGetEventsRequest(EwsClient &client, QObject *parent) : EwsEventRequestBase(client, QStringLiteral("GetEvents"), parent) { } EwsGetEventsRequest::~EwsGetEventsRequest() { } void EwsGetEventsRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("GetEvents")); writer.writeTextElement(ewsMsgNsUri, QStringLiteral("SubscriptionId"), mSubscriptionId); writer.writeTextElement(ewsMsgNsUri, QStringLiteral("Watermark"), mWatermark); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting GetEvents request (subId: %1, wmark: %2)") - .arg(mSubscriptionId, mWatermark); + .arg(mSubscriptionId, mWatermark); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } diff --git a/resources/ews/ewsclient/ewsgeteventsrequest.h b/resources/ews/ewsclient/ewsgeteventsrequest.h index 403cb9510..53109effe 100644 --- a/resources/ews/ewsclient/ewsgeteventsrequest.h +++ b/resources/ews/ewsclient/ewsgeteventsrequest.h @@ -1,48 +1,47 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSGETEVENTSREQUEST_H #define EWSGETEVENTSREQUEST_H #include #include "ewsid.h" #include "ewseventrequestbase.h" #include "ewstypes.h" - class EwsGetEventsRequest : public EwsEventRequestBase { Q_OBJECT public: EwsGetEventsRequest(EwsClient &client, QObject *parent); ~EwsGetEventsRequest() override; void setWatermark(const QString &watermark) { mWatermark = watermark; } void start() override; protected: QString mWatermark; }; #endif diff --git a/resources/ews/ewsclient/ewsgetfolderrequest.cpp b/resources/ews/ewsclient/ewsgetfolderrequest.cpp index c50f8076e..871173fc9 100644 --- a/resources/ews/ewsclient/ewsgetfolderrequest.cpp +++ b/resources/ews/ewsclient/ewsgetfolderrequest.cpp @@ -1,159 +1,160 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsgetfolderrequest.h" #include #include "ewsclient_debug.h" EwsGetFolderRequest::EwsGetFolderRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsGetFolderRequest::~EwsGetFolderRequest() { } void EwsGetFolderRequest::setFolderIds(const EwsId::List &ids) { mIds = ids; } void EwsGetFolderRequest::setFolderShape(const EwsFolderShape &shape) { mShape = shape; } void EwsGetFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("GetFolder")); mShape.write(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FolderIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeFolderIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting GetFolder request (") << mIds << ")"; prepare(reqString); doSend(); } bool EwsGetFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("GetFolder"), - [this](QXmlStreamReader &reader) {return parseFoldersResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseFoldersResponse(reader); + }); } bool EwsGetFolderRequest::parseFoldersResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } mResponses.append(resp); if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { const EwsFolder &folder = resp.folder(); const EwsId &id = folder[EwsFolderFieldFolderId].value(); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got GetFolder response (id: %1, name: %2)") - .arg(ewsHash(id.id()), folder[EwsFolderFieldDisplayName].toString()); + .arg(ewsHash(id.id()), folder[EwsFolderFieldDisplayName].toString()); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got GetFolder response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } QVariant dn = resp.folder()[EwsFolderFieldDisplayName]; if (!dn.isNull()) { EwsClient::folderHash[resp.folder()[EwsFolderFieldFolderId].value().id()] = dn.toString(); } return true; } EwsGetFolderRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Folders")) { if (responseClass() == EwsResponseError) { // Skip empty folders element reader.skipCurrentElement(); - } - else if (!parseFolders(reader)) { + } else if (!parseFolders(reader)) { return; } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } bool EwsGetFolderRequest::Response::parseFolders(QXmlStreamReader &reader) { if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QStringLiteral("Folders")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected Folders element (got %1).") .arg(reader.qualifiedName().toString())); } if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in Folders element.")); } if (reader.namespaceUri() != ewsTypeNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected child element from types namespace.")); } EwsFolder folder(reader); if (!folder.isValid()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - invalid Folder element.")); } mFolder = folder; // Finish the Folders element reader.skipCurrentElement(); return true; } diff --git a/resources/ews/ewsclient/ewsgetfolderrequest.h b/resources/ews/ewsclient/ewsgetfolderrequest.h index 4c7be4998..b59facc75 100644 --- a/resources/ews/ewsclient/ewsgetfolderrequest.h +++ b/resources/ews/ewsclient/ewsgetfolderrequest.h @@ -1,67 +1,69 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSGETFOLDERREQUEST_H #define EWSGETFOLDERREQUEST_H #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" #include "ewsfoldershape.h" class EwsGetFolderRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: explicit Response(QXmlStreamReader &reader); bool parseFolders(QXmlStreamReader &reader); const EwsFolder &folder() const { return mFolder; } + private: EwsFolder mFolder; }; EwsGetFolderRequest(EwsClient &client, QObject *parent); ~EwsGetFolderRequest() override; void setFolderIds(const EwsId::List &ids); void setFolderShape(const EwsFolderShape &shape); void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseFoldersResponse(QXmlStreamReader &reader); private: EwsId::List mIds; EwsFolderShape mShape; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsgetitemrequest.cpp b/resources/ews/ewsclient/ewsgetitemrequest.cpp index 385808b02..52efa67a0 100644 --- a/resources/ews/ewsclient/ewsgetitemrequest.cpp +++ b/resources/ews/ewsclient/ewsgetitemrequest.cpp @@ -1,148 +1,151 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsgetitemrequest.h" #include "ewsclient_debug.h" EwsGetItemRequest::EwsGetItemRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsGetItemRequest::~EwsGetItemRequest() { } void EwsGetItemRequest::setItemIds(const EwsId::List &ids) { mIds = ids; } void EwsGetItemRequest::setItemShape(const EwsItemShape &shape) { mShape = shape; } void EwsGetItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("GetItem")); mShape.write(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ItemIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeItemIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting GetItem request (") << mIds << ")"; prepare(reqString); doSend(); } bool EwsGetItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("GetItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsGetItemRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { const EwsItem &item = resp.item(); const EwsId &id = item[EwsItemFieldItemId].value(); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got GetItem response (id: %1, subject: %2)") - .arg(ewsHash(id.id()), item[EwsItemFieldSubject].toString()); + .arg(ewsHash(id.id()), item[EwsItemFieldSubject].toString()); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got GetItem response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsGetItemRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Items")) { if (!parseItems(reader)) { return; } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } bool EwsGetItemRequest::Response::parseItems(QXmlStreamReader &reader) { if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QStringLiteral("Items")) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected Items element (got %1).") .arg(reader.qualifiedName().toString())); } if (reader.readNextStartElement()) { - if (reader.namespaceUri() != ewsTypeNsUri) + if (reader.namespaceUri() != ewsTypeNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected child element from types namespace.")); + } EwsItem item(reader); if (!item.isValid()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - invalid Item element.")); } mItem = item; // Finish the Items element. reader.skipCurrentElement(); } return true; } diff --git a/resources/ews/ewsclient/ewsgetitemrequest.h b/resources/ews/ewsclient/ewsgetitemrequest.h index b3c480bc2..34476514f 100644 --- a/resources/ews/ewsclient/ewsgetitemrequest.h +++ b/resources/ews/ewsclient/ewsgetitemrequest.h @@ -1,68 +1,70 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSGETITEMREQUEST_H #define EWSGETITEMREQUEST_H #include #include "ewsitem.h" #include "ewsitemshape.h" #include "ewsrequest.h" #include "ewstypes.h" class EwsGetItemRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: explicit Response(QXmlStreamReader &reader); bool parseItems(QXmlStreamReader &reader); const EwsItem &item() const { return mItem; } + private: EwsItem mItem; }; EwsGetItemRequest(EwsClient &client, QObject *parent); ~EwsGetItemRequest() override; void setItemIds(const EwsId::List &ids); void setItemShape(const EwsItemShape &shape); void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId::List mIds; EwsItemShape mShape; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsgetstreamingeventsrequest.cpp b/resources/ews/ewsclient/ewsgetstreamingeventsrequest.cpp index f456e847a..9e58385d6 100644 --- a/resources/ews/ewsclient/ewsgetstreamingeventsrequest.cpp +++ b/resources/ews/ewsclient/ewsgetstreamingeventsrequest.cpp @@ -1,118 +1,119 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsgetstreamingeventsrequest.h" #include #include "ewsclient_debug.h" #include "ewsxml.h" static Q_CONSTEXPR uint respChunkTimeout = 250; /* ms */ EwsGetStreamingEventsRequest::EwsGetStreamingEventsRequest(EwsClient &client, QObject *parent) - : EwsEventRequestBase(client, QStringLiteral("GetStreamingEvents"), parent), mTimeout(30), - mRespTimer(this) + : EwsEventRequestBase(client, QStringLiteral("GetStreamingEvents"), parent) + , mTimeout(30) + , mRespTimer(this) { mRespTimer.setInterval(respChunkTimeout); connect(&mRespTimer, &QTimer::timeout, this, &EwsGetStreamingEventsRequest::requestDataTimeout); } EwsGetStreamingEventsRequest::~EwsGetStreamingEventsRequest() { } void EwsGetStreamingEventsRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); if (!serverVersion().supports(EwsServerVersion::StreamingSubscription)) { setServerVersion(EwsServerVersion::minSupporting(EwsServerVersion::StreamingSubscription)); } startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("GetStreamingEvents")); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SubscriptionIds")); writer.writeTextElement(ewsTypeNsUri, QStringLiteral("SubscriptionId"), mSubscriptionId); writer.writeEndElement(); writer.writeTextElement(ewsMsgNsUri, QStringLiteral("ConnectionTimeout"), QString::number(mTimeout)); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting GetStreamingEvents request (subId: %1, timeout: %2)") - .arg(ewsHash(mSubscriptionId)).arg(mTimeout); + .arg(ewsHash(mSubscriptionId)).arg(mTimeout); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } void EwsGetStreamingEventsRequest::requestData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); mRespTimer.stop(); qCDebug(EWSCLI_PROTO_LOG) << "data" << job << data; mResponseData += QString::fromUtf8(data); mRespTimer.start(); } void EwsGetStreamingEventsRequest::requestDataTimeout() { if (mResponseData.isEmpty()) { return; } if (EWSCLI_PROTO_LOG().isDebugEnabled()) { ewsLogDir.setAutoRemove(false); if (ewsLogDir.isValid()) { QTemporaryFile dumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmldump_XXXXXXX.xml")); dumpFile.open(); dumpFile.setAutoRemove(false); dumpFile.write(mResponseData.toUtf8()); qCDebug(EWSCLI_PROTO_LOG) << "response dumped to" << dumpFile.fileName(); dumpFile.close(); } } QXmlStreamReader reader(mResponseData); if (!readResponse(reader)) { Q_FOREACH (KJob *job, subjobs()) { removeSubjob(job); job->kill(); } emitResult(); } else { Q_EMIT eventsReceived(this); } mResponseData.clear(); } void EwsGetStreamingEventsRequest::eventsProcessed(const Response &resp) { mResponses.removeOne(resp); } diff --git a/resources/ews/ewsclient/ewsgetstreamingeventsrequest.h b/resources/ews/ewsclient/ewsgetstreamingeventsrequest.h index 934db4a60..a7dbddafc 100644 --- a/resources/ews/ewsclient/ewsgetstreamingeventsrequest.h +++ b/resources/ews/ewsclient/ewsgetstreamingeventsrequest.h @@ -1,56 +1,55 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSGETSTREAMINGEVENTSREQUEST_H #define EWSGETSTREAMINGEVENTSREQUEST_H #include #include #include "ewseventrequestbase.h" #include "ewsid.h" #include "ewstypes.h" - class EwsGetStreamingEventsRequest : public EwsEventRequestBase { Q_OBJECT public: EwsGetStreamingEventsRequest(EwsClient &client, QObject *parent); ~EwsGetStreamingEventsRequest() override; void setTimeout(uint timeout) { mTimeout = timeout; } void start() override; public Q_SLOTS: void eventsProcessed(const Response &response); Q_SIGNALS: void eventsReceived(KJob *job); protected Q_SLOTS: void requestData(KIO::Job *job, const QByteArray &data) override; void requestDataTimeout(); protected: uint mTimeout; QTimer mRespTimer; }; #endif diff --git a/resources/ews/ewsclient/ewsid.cpp b/resources/ews/ewsclient/ewsid.cpp index 36df983fc..002e7a746 100644 --- a/resources/ews/ewsclient/ewsid.cpp +++ b/resources/ews/ewsclient/ewsid.cpp @@ -1,205 +1,213 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsid.h" #include #include #include #include #include "ewsclient.h" #include "ewsclient_debug.h" static const QString distinguishedIdNames[] = { QStringLiteral("calendar"), QStringLiteral("contacts"), QStringLiteral("deleteditems"), QStringLiteral("drafts"), QStringLiteral("inbox"), QStringLiteral("journal"), QStringLiteral("notes"), QStringLiteral("outbox"), QStringLiteral("sentitems"), QStringLiteral("tasks"), QStringLiteral("msgfolderroot"), QStringLiteral("root"), QStringLiteral("junkemail"), QStringLiteral("searchfolders"), QStringLiteral("voicemail"), QStringLiteral("recoverableitemsroot"), QStringLiteral("recoverableitemsdeletions"), QStringLiteral("recoverableitemsversions"), QStringLiteral("recoverableitemspurges"), QStringLiteral("archiveroot"), QStringLiteral("archivemsgfolderroot"), QStringLiteral("archivedeleteditems"), QStringLiteral("archiverecoverableitemsroot"), QStringLiteral("archiverecoverableitemsdeletions"), QStringLiteral("archiverecoverableitemsversions"), QStringLiteral("archiverecoverableitemspurges") }; class EwsIdComparatorRegistrar { public: EwsIdComparatorRegistrar() { QMetaType::registerComparators(); } }; const EwsIdComparatorRegistrar ewsIdComparatorRegistrar; EwsId::EwsId(QXmlStreamReader &reader) : mDid(EwsDIdCalendar) { // Don't check for this element's name as a folder id may be contained in several elements // such as "FolderId" or "ParentFolderId". const QXmlStreamAttributes &attrs = reader.attributes(); QStringRef idRef = attrs.value(QStringLiteral("Id")); QStringRef changeKeyRef = attrs.value(QStringLiteral("ChangeKey")); - if (idRef.isNull()) + if (idRef.isNull()) { return; + } mId = idRef.toString(); - if (!changeKeyRef.isNull()) + if (!changeKeyRef.isNull()) { mChangeKey = changeKeyRef.toString(); + } mType = Real; } EwsId::EwsId(const QString &id, const QString &changeKey) - : mType(Real), mId(id), mChangeKey(changeKey), mDid(EwsDIdCalendar) + : mType(Real) + , mId(id) + , mChangeKey(changeKey) + , mDid(EwsDIdCalendar) { } EwsId &EwsId::operator=(const EwsId &other) { mType = other.mType; if (mType == Distinguished) { mDid = other.mDid; } else if (mType == Real) { mId = other.mId; mChangeKey = other.mChangeKey; } return *this; } EwsId &EwsId::operator=(EwsId &&other) { mType = other.mType; if (mType == Distinguished) { mDid = other.mDid; } else if (mType == Real) { mId = std::move(other.mId); mChangeKey = std::move(other.mChangeKey); } return *this; } bool EwsId::operator==(const EwsId &other) const { - if (mType != other.mType) + if (mType != other.mType) { return false; + } if (mType == Distinguished) { - return (mDid == other.mDid); + return mDid == other.mDid; } else if (mType == Real) { - return (mId == other.mId && mChangeKey == other.mChangeKey); + return mId == other.mId && mChangeKey == other.mChangeKey; } return true; } bool EwsId::operator<(const EwsId &other) const { - if (mType != other.mType) + if (mType != other.mType) { return mType < other.mType; + } if (mType == Distinguished) { - return (mDid < other.mDid); + return mDid < other.mDid; } else if (mType == Real) { - return (mId < other.mId && mChangeKey < other.mChangeKey); + return mId < other.mId && mChangeKey < other.mChangeKey; } return false; } void EwsId::writeFolderIds(QXmlStreamWriter &writer) const { if (mType == Distinguished) { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("DistinguishedFolderId")); writer.writeAttribute(QStringLiteral("Id"), distinguishedIdNames[mDid]); writer.writeEndElement(); } else if (mType == Real) { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("FolderId")); writer.writeAttribute(QStringLiteral("Id"), mId); if (!mChangeKey.isEmpty()) { writer.writeAttribute(QStringLiteral("ChangeKey"), mChangeKey); } writer.writeEndElement(); } } void EwsId::writeItemIds(QXmlStreamWriter &writer) const { if (mType == Real) { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("ItemId")); writer.writeAttribute(QStringLiteral("Id"), mId); if (!mChangeKey.isEmpty()) { writer.writeAttribute(QStringLiteral("ChangeKey"), mChangeKey); } writer.writeEndElement(); } } void EwsId::writeAttributes(QXmlStreamWriter &writer) const { if (mType == Real) { writer.writeAttribute(QStringLiteral("Id"), mId); if (!mChangeKey.isEmpty()) { writer.writeAttribute(QStringLiteral("ChangeKey"), mChangeKey); } } } QDebug operator<<(QDebug debug, const EwsId &id) { QDebugStateSaver saver(debug); QDebug d = debug.nospace().noquote(); d << QStringLiteral("EwsId("); switch (id.mType) { case EwsId::Distinguished: d << QStringLiteral("Distinguished: ") << distinguishedIdNames[id.mDid]; break; - case EwsId::Real: { + case EwsId::Real: + { QString name = EwsClient::folderHash.value(id.mId, ewsHash(id.mId)); d << name << QStringLiteral(", ") << ewsHash(id.mChangeKey); break; } default: break; } d << ')'; return debug; } uint qHash(const EwsId &id, uint seed) { return qHash(id.id(), seed) ^ qHash(id.changeKey(), seed) ^ static_cast(id.type()); } diff --git a/resources/ews/ewsclient/ewsid.h b/resources/ews/ewsclient/ewsid.h index 55afd1e20..2dcc23fdd 100644 --- a/resources/ews/ewsclient/ewsid.h +++ b/resources/ews/ewsclient/ewsid.h @@ -1,117 +1,125 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSID_H #define EWSID_H #include #include #include #include "ewstypes.h" class QXmlStreamWriter; class QXmlStreamReader; /** * @brief EWS Id wrapper class * * This class wraps an EWS folder or item identifier. * * In the EWS world an id can come in two forms: * - An "actual" id identified by a unique, server-generated string (actually it's a * base64-encoded internal server structure). Optionally this id is accompanied by a change * key, which acts as a version number of the item. Each time something changes with the * item (either the item itself or folder content - not sure if this applies to subfolders) * the change key is updated. This gives you access to an older version of the item and * allows to quickly find out if the item needs synchronizing. * - A "distinguished" folder id which is a string identifying a list of known root folders * such as 'inbox'. This is necessary for the initial query as there is no way to know the * real folder ids beforehand. This applies only to folder identifiers. */ class EwsId { public: enum Type { Distinguished, Real, Unspecified }; typedef QList List; - explicit EwsId(EwsDistinguishedId did) : mType(Distinguished), mDid(did) {} + explicit EwsId(EwsDistinguishedId did) : mType(Distinguished) + , mDid(did) + { + } + explicit EwsId(const QString &id, const QString &changeKey = QString()); EwsId(const EwsId &id) { *this = id; } EwsId(EwsId &&id) { *this = std::move(id); } - EwsId() : mType(Unspecified), mDid(EwsDIdCalendar) {} + EwsId() : mType(Unspecified) + , mDid(EwsDIdCalendar) + { + } + explicit EwsId(QXmlStreamReader &reader); Type type() const { return mType; } QString id() const { return mId; } QString changeKey() const { return mChangeKey; } EwsDistinguishedId distinguishedId() const { return mDid; } EwsId &operator=(const EwsId &other); EwsId &operator=(EwsId &&other); bool operator==(const EwsId &other) const; bool operator<(const EwsId &other) const; void writeFolderIds(QXmlStreamWriter &writer) const; void writeItemIds(QXmlStreamWriter &writer) const; void writeAttributes(QXmlStreamWriter &writer) const; friend QDebug operator<<(QDebug debug, const EwsId &id); private: Type mType; QString mId; QString mChangeKey; EwsDistinguishedId mDid; }; uint qHash(const EwsId &id, uint seed); QDebug operator<<(QDebug debug, const EwsId &id); Q_DECLARE_METATYPE(EwsId) Q_DECLARE_METATYPE(EwsId::List) #endif diff --git a/resources/ews/ewsclient/ewsitem.cpp b/resources/ews/ewsclient/ewsitem.cpp index 5e1f64c90..e53320bd9 100644 --- a/resources/ews/ewsclient/ewsitem.cpp +++ b/resources/ews/ewsclient/ewsitem.cpp @@ -1,568 +1,568 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsitem.h" #include #include - #include "ewsattachment.h" #include "ewsattendee.h" #include "ewsclient_debug.h" #include "ewsitembase_p.h" #include "ewsmailbox.h" #include "ewsoccurrence.h" #include "ewsrecurrence.h" #include "ewsxml.h" -#define D_PTR EwsItemPrivate *d = reinterpret_cast(this->d.data()); -#define D_CPTR const EwsItemPrivate *d = reinterpret_cast(this->d.data()); +#define D_PTR EwsItemPrivate *d = reinterpret_cast(this->d.data()); +#define D_CPTR const EwsItemPrivate *d = reinterpret_cast(this->d.data()); class EwsItemPrivate : public EwsItemBasePrivate { public: typedef EwsXml Reader; EwsItemPrivate(); EwsItemPrivate(const EwsItemBasePrivate &other); EwsItemBasePrivate *clone() const override { return new EwsItemPrivate(*this); } static bool bodyReader(QXmlStreamReader &reader, QVariant &val); static bool messageHeadersReader(QXmlStreamReader &reader, QVariant &val); static bool mailboxReader(QXmlStreamReader &reader, QVariant &val); static bool recipientsReader(QXmlStreamReader &reader, QVariant &val); static bool timezoneReader(QXmlStreamReader &reader, QVariant &val); static bool attendeesReader(QXmlStreamReader &reader, QVariant &val); static bool occurrenceReader(QXmlStreamReader &reader, QVariant &val); static bool occurrencesReader(QXmlStreamReader &reader, QVariant &val); static bool recurrenceReader(QXmlStreamReader &reader, QVariant &val); static bool categoriesReader(QXmlStreamReader &reader, QVariant &val); static bool categoriesWriter(QXmlStreamWriter &writer, const QVariant &val); static bool attachmentsReader(QXmlStreamReader &reader, QVariant &val); bool operator==(const EwsItemPrivate &other) const; EwsItemType mType; static const Reader mStaticEwsXml; Reader mEwsXml; }; static const QVector ewsItemItems = { // Item fields {EwsItemFieldMimeContent, QStringLiteral("MimeContent"), &ewsXmlBase64Reader, &ewsXmlBase64Writer}, {EwsItemFieldItemId, QStringLiteral("ItemId"), &ewsXmlIdReader, &ewsXmlIdWriter}, {EwsItemFieldParentFolderId, QStringLiteral("ParentFolderId"), &ewsXmlIdReader, &ewsXmlIdWriter}, {EwsItemFieldItemClass, QStringLiteral("ItemClass"), &ewsXmlTextReader, &ewsXmlTextWriter}, {EwsItemFieldSubject, QStringLiteral("Subject"), &ewsXmlTextReader, &ewsXmlTextWriter}, {EwsItemFieldSensitivity, QStringLiteral("Sensitivity"), &ewsXmlSensitivityReader}, {EwsItemFieldBody, QStringLiteral("Body"), &EwsItemPrivate::bodyReader}, {EwsItemFieldAttachments, QStringLiteral("Attachments"), &EwsItemPrivate::attachmentsReader}, {EwsItemFieldDateTimeReceived, QStringLiteral("DateTimeReceived"), &ewsXmlDateTimeReader}, {EwsItemFieldSize, QStringLiteral("Size"), &ewsXmlUIntReader}, {EwsItemFieldCategories, QStringLiteral("Categories"), &EwsItemPrivate::categoriesReader, - &EwsItemPrivate::categoriesWriter}, + &EwsItemPrivate::categoriesWriter}, {EwsItemFieldImportance, QStringLiteral("Importance"), &ewsXmlImportanceReader}, {EwsItemFieldInReplyTo, QStringLiteral("InReplyTo"), &ewsXmlTextReader}, {EwsItemFieldIsDraft, QStringLiteral("IsDraft"), &ewsXmlBoolReader, &ewsXmlBoolWriter}, {EwsItemFieldIsFromMe, QStringLiteral("IsFromMe"), &ewsXmlBoolReader}, {EwsItemFieldInternetMessageHeaders, QStringLiteral("InternetMessageHeaders"), - &EwsItemPrivate::messageHeadersReader}, + &EwsItemPrivate::messageHeadersReader}, {EwsItemFieldExtendedProperties, QStringLiteral("ExtendedProperty"), - &EwsItemBasePrivate::extendedPropertyReader, &EwsItemBasePrivate::extendedPropertyWriter}, + &EwsItemBasePrivate::extendedPropertyReader, &EwsItemBasePrivate::extendedPropertyWriter}, {EwsItemFieldHasAttachments, QStringLiteral("HasAttachments"), &ewsXmlBoolReader}, // Message fields {EwsItemFieldToRecipients, QStringLiteral("ToRecipients"), &EwsItemPrivate::recipientsReader}, {EwsItemFieldCcRecipients, QStringLiteral("CcRecipients"), &EwsItemPrivate::recipientsReader}, {EwsItemFieldBccRecipients, QStringLiteral("BccRecipients"), &EwsItemPrivate::recipientsReader}, {EwsItemFieldFrom, QStringLiteral("From"), &EwsItemPrivate::mailboxReader}, {EwsItemFieldInternetMessageId, QStringLiteral("InternetMessageId"), &ewsXmlTextReader}, {EwsItemFieldIsRead, QStringLiteral("IsRead"), &ewsXmlBoolReader, &ewsXmlBoolWriter}, {EwsItemFieldReferences, QStringLiteral("References"), &ewsXmlTextReader}, {EwsItemFieldReplyTo, QStringLiteral("ReplyTo"), &EwsItemPrivate::mailboxReader}, // CalendarItem fields {EwsItemFieldCalendarItemType, QStringLiteral("CalendarItemType"), - &ewsXmlCalendarItemTypeReader}, + &ewsXmlCalendarItemTypeReader}, {EwsItemFieldUID, QStringLiteral("UID"), &ewsXmlTextReader}, {EwsItemFieldCulture, QStringLiteral("Culture"), &ewsXmlTextReader}, {EwsItemFieldStartTimeZone, QStringLiteral("StartTimeZone"), &EwsItemPrivate::timezoneReader}, {EwsItemFieldOrganizer, QStringLiteral("Organizer"), &EwsItemPrivate::mailboxReader}, {EwsItemFieldRequiredAttendees, QStringLiteral("RequiredAttendees"), - &EwsItemPrivate::attendeesReader}, + &EwsItemPrivate::attendeesReader}, {EwsItemFieldOptionalAttendees, QStringLiteral("OptionalAttendees"), - &EwsItemPrivate::attendeesReader}, + &EwsItemPrivate::attendeesReader}, {EwsItemFieldResources, QStringLiteral("Resources"), &EwsItemPrivate::attendeesReader}, {EwsItemFieldStart, QStringLiteral("Start"), &ewsXmlDateTimeReader}, {EwsItemFieldEnd, QStringLiteral("End"), &ewsXmlDateTimeReader}, {EwsItemFieldRecurrenceId, QStringLiteral("RecurrenceId"), &ewsXmlDateTimeReader}, {EwsItemFieldIsAllDayEvent, QStringLiteral("IsAllDayEvent"), &ewsXmlBoolReader}, {EwsItemFieldLegacyFreeBusyStatus, QStringLiteral("LegacyFreeBusyStatus"), - &ewsXmlLegacyFreeBusyStatusReader}, + &ewsXmlLegacyFreeBusyStatusReader}, {EwsItemFieldMyResponseType, QStringLiteral("MyResponseType"), &ewsXmlResponseTypeReader}, {EwsItemFieldAppointmentSequenceNumber, QStringLiteral("AppointmentSequenceNumber"), - &ewsXmlUIntReader}, + &ewsXmlUIntReader}, {EwsItemFieldRecurrence, QStringLiteral("Recurrence"), &EwsItemPrivate::recurrenceReader}, {EwsItemFieldFirstOccurrence, QStringLiteral("FirstOccurrence"), - &EwsItemPrivate::occurrenceReader}, + &EwsItemPrivate::occurrenceReader}, {EwsItemFieldLastOccurrence, QStringLiteral("LastOccurrence"), - &EwsItemPrivate::occurrenceReader}, + &EwsItemPrivate::occurrenceReader}, {EwsItemFieldModifiedOccurrences, QStringLiteral("ModifiedOccurrences"), - &EwsItemPrivate::occurrencesReader}, + &EwsItemPrivate::occurrencesReader}, {EwsItemFieldDeletedOccurrences, QStringLiteral("DeletedOccurrences"), - &EwsItemPrivate::occurrencesReader}, + &EwsItemPrivate::occurrencesReader}, {EwsItemFieldTimeZone, QStringLiteral("TimeZone"), &ewsXmlTextReader}, {EwsItemFieldExchangePersonIdGuid, QStringLiteral("ExchangePersonIdGuid"), &ewsXmlTextReader}, {EwsItemFieldDoNotForwardMeeting, QStringLiteral("DoNotForwardMeeting"), &ewsXmlBoolReader}, }; const EwsItemPrivate::Reader EwsItemPrivate::mStaticEwsXml(ewsItemItems); EwsItemPrivate::EwsItemPrivate() - : EwsItemBasePrivate(), mType(EwsItemTypeUnknown), mEwsXml(mStaticEwsXml) + : EwsItemBasePrivate() + , mType(EwsItemTypeUnknown) + , mEwsXml(mStaticEwsXml) { } bool EwsItemPrivate::bodyReader(QXmlStreamReader &reader, QVariant &val) { QVariantList vl; QStringRef bodyType = reader.attributes().value(QStringLiteral("BodyType")); if (bodyType.isNull()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - missing %2 attribute") - .arg(QStringLiteral("Body"), QStringLiteral("BodyType")); + .arg(QStringLiteral("Body"), QStringLiteral("BodyType")); return false; } bool isHtml; if (bodyType == QStringLiteral("HTML")) { isHtml = true; } else if (bodyType == QStringLiteral("Text")) { isHtml = false; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Body element- unknown body type"); return false; } vl.append(reader.readElementText()); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(QStringLiteral("Body")); + .arg(QStringLiteral("Body")); return false; } vl.append(isHtml); val = vl; return true; } bool EwsItemPrivate::messageHeadersReader(QXmlStreamReader &reader, QVariant &val) { EwsItem::HeaderMap map = val.value(); while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in InternetMessageHeaders element:") << reader.namespaceUri(); return false; } if (reader.name() == QStringLiteral("InternetMessageHeader")) { QStringRef nameRef = reader.attributes().value(QStringLiteral("HeaderName")); if (nameRef.isNull()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Missing HeaderName attribute in InternetMessageHeader element."); return false; } QString name = nameRef.toString(); QString value = reader.readElementText(); map.insert(name, value); if (reader.error() != QXmlStreamReader::NoError) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element.") - .arg(QStringLiteral("InternetMessageHeader")); + .arg(QStringLiteral("InternetMessageHeader")); return false; } } } val = QVariant::fromValue(map); return true; } bool EwsItemPrivate::recipientsReader(QXmlStreamReader &reader, QVariant &val) { EwsMailbox::List mboxList; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(reader.name().toString()) << reader.namespaceUri(); return false; } EwsMailbox mbox(reader); if (!mbox.isValid()) { return false; } mboxList.append(mbox); } val = QVariant::fromValue(mboxList); return true; } bool EwsItemPrivate::mailboxReader(QXmlStreamReader &reader, QVariant &val) { if (!reader.readNextStartElement()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Expected mailbox in %1 element:").arg(reader.name().toString()); return false; } if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(reader.name().toString()) << reader.namespaceUri(); reader.skipCurrentElement(); return false; } EwsMailbox mbox(reader); if (!mbox.isValid()) { reader.skipCurrentElement(); return false; } val = QVariant::fromValue(mbox); // Leave the element reader.skipCurrentElement(); return true; } bool EwsItemPrivate::timezoneReader(QXmlStreamReader &reader, QVariant &val) { // TODO: This only reads the timezone identifier. QStringRef idRef = reader.attributes().value(QStringLiteral("Id")); if (idRef.isNull()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading %1 element - missing %2 attribute") - .arg(reader.name().toString()).arg(QStringLiteral("Id")); + .arg(reader.name().toString()).arg(QStringLiteral("Id")); reader.skipCurrentElement(); return false; } else { reader.skipCurrentElement(); val = idRef.toString(); return true; } } bool EwsItemPrivate::attendeesReader(QXmlStreamReader &reader, QVariant &val) { EwsAttendee::List attList; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(reader.name().toString()) << reader.namespaceUri(); return false; } EwsAttendee att(reader); if (!att.isValid()) { return false; } attList.append(att); } val = QVariant::fromValue(attList); return true; } bool EwsItemPrivate::occurrenceReader(QXmlStreamReader &reader, QVariant &val) { EwsOccurrence occ(reader); if (!occ.isValid()) { return false; } val = QVariant::fromValue(occ); return true; } bool EwsItemPrivate::occurrencesReader(QXmlStreamReader &reader, QVariant &val) { EwsOccurrence::List occList; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:") - .arg(reader.name().toString()) + .arg(reader.name().toString()) << reader.namespaceUri(); return false; } EwsOccurrence occ(reader); if (!occ.isValid()) { return false; } occList.append(occ); } val = QVariant::fromValue(occList); return true; } bool EwsItemPrivate::categoriesReader(QXmlStreamReader &reader, QVariant &val) { QStringList categories; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:") - .arg(reader.name().toString()) + .arg(reader.name().toString()) << reader.namespaceUri(); return false; } if (reader.name() == QStringLiteral("String")) { categories.append(reader.readElementText()); - if (reader.error() != QXmlStreamReader::NoError) { + if (reader.error() != QXmlStreamReader::NoError) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element.") - .arg(QStringLiteral("Categories/Value")); + .arg(QStringLiteral("Categories/Value")); return false; } } } val = categories; return true; } bool EwsItemPrivate::categoriesWriter(QXmlStreamWriter &writer, const QVariant &val) { const QStringList categories = val.toStringList(); for (const QString &cat : categories) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("String"), cat); } return true; } bool EwsItemPrivate::recurrenceReader(QXmlStreamReader &reader, QVariant &val) { EwsRecurrence recurrence(reader); val = QVariant::fromValue(recurrence); return true; } bool EwsItemPrivate::attachmentsReader(QXmlStreamReader &reader, QVariant &val) { EwsAttachment::List attList; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(reader.name().toString()) << reader.namespaceUri(); return false; } EwsAttachment att(reader); if (!att.isValid()) { return false; } attList.append(att); } val = QVariant::fromValue(attList); return true; } bool EwsItemPrivate::operator==(const EwsItemPrivate &other) const { if (!EwsItemBasePrivate::operator==(other)) { return false; } return mType == other.mType; } EwsItem::EwsItem() : EwsItemBase(QSharedDataPointer(new EwsItemPrivate())) { } EwsItem::EwsItem(QXmlStreamReader &reader) : EwsItemBase(QSharedDataPointer(new EwsItemPrivate())) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); - EwsItemPrivate *d = reinterpret_cast(this->d.data()); + EwsItemPrivate *d = reinterpret_cast(this->d.data()); // Check what item type are we const QStringRef elmName = reader.name(); if (elmName == QStringLiteral("Item")) { d->mType = EwsItemTypeItem; const QStringRef subtype = reader.attributes().value(QStringLiteral("xsi:type")); if (!subtype.isEmpty()) { auto tokens = subtype.split(QLatin1Char(':')); const QStringRef type = tokens.size() == 1 ? tokens[0] : tokens[1]; if (type == QStringLiteral("AbchPersonItemType")) { d->mType = EwsItemTypeAbchPerson; } } } else if (elmName == QStringLiteral("Message")) { d->mType = EwsItemTypeMessage; } else if (elmName == QStringLiteral("CalendarItem")) { d->mType = EwsItemTypeCalendarItem; } else if (elmName == QStringLiteral("Contact")) { d->mType = EwsItemTypeContact; } else if (elmName == QStringLiteral("DistributionList")) { d->mType = EwsItemTypeDistributionList; } else if (elmName == QStringLiteral("MeetingMessage")) { d->mType = EwsItemTypeMeetingMessage; } else if (elmName == QStringLiteral("MeetingRequest")) { d->mType = EwsItemTypeMeetingRequest; } else if (elmName == QStringLiteral("MeetingResponse")) { d->mType = EwsItemTypeMeetingResponse; } else if (elmName == QStringLiteral("MeetingCancellation")) { d->mType = EwsItemTypeMeetingCancellation; } else if (elmName == QStringLiteral("PostItem")) { d->mType = EwsItemTypePostItem; } else if (elmName == QStringLiteral("Task")) { d->mType = EwsItemTypeTask; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << "Unexpected namespace in Item element:" << reader.namespaceUri(); return; } if (!readBaseItemElement(reader)) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Invalid Item child: %1").arg(reader.qualifiedName().toString()); return; } } d->mValid = true; - } EwsItem::EwsItem(const EwsItem &other) : EwsItemBase(other.d) { } EwsItem::EwsItem(EwsItem &&other) : EwsItemBase(other.d) { } EwsItem::~EwsItem() { } EwsItem &EwsItem::operator=(const EwsItem &other) { d = other.d; return *this; } EwsItem &EwsItem::operator=(EwsItem &&other) { d = std::move(other.d); return *this; } EwsItemType EwsItem::type() const { - const EwsItemPrivate *d = reinterpret_cast(this->d.data()); + const EwsItemPrivate *d = reinterpret_cast(this->d.data()); return d->mType; } void EwsItem::setType(EwsItemType type) { D_PTR d->mType = type; d->mValid = true; } EwsItemType EwsItem::internalType() const { D_CPTR EwsItemType type = d->mType; switch (type) { case EwsItemTypeMeetingMessage: case EwsItemTypeMeetingRequest: case EwsItemTypeMeetingResponse: case EwsItemTypeMeetingCancellation: type = EwsItemTypeMessage; break; case EwsItemTypeDistributionList: type = EwsItemTypeContact; break; default: break; } return type; } bool EwsItem::readBaseItemElement(QXmlStreamReader &reader) { D_PTR if (!d->mEwsXml.readItem(reader, QStringLiteral("Item"), ewsTypeNsUri)) { return false; } d->mFields = d->mEwsXml.values(); // The body item is special as it hold two values in one. Need to separate them into their // proper places. if (d->mFields.contains(EwsItemFieldBody)) { QVariantList vl = d->mFields[EwsItemFieldBody].value(); QVariantList::const_iterator it = vl.cbegin(); d->mFields[EwsItemFieldBody] = *it++; d->mFields[EwsItemFieldBodyIsHtml] = *it; } return true; } bool EwsItem::write(QXmlStreamWriter &writer) const { D_CPTR writer.writeStartElement(ewsTypeNsUri, ewsItemTypeNames[d->mType]); bool status = d->mEwsXml.writeItems(writer, ewsItemTypeNames[d->mType], ewsTypeNsUri, d->mFields); writer.writeEndElement(); return status; } bool EwsItem::operator==(const EwsItem &other) const { return *d == *other.d; } diff --git a/resources/ews/ewsclient/ewsitembase.cpp b/resources/ews/ewsclient/ewsitembase.cpp index 96255cc22..6f09fbbfc 100644 --- a/resources/ews/ewsclient/ewsitembase.cpp +++ b/resources/ews/ewsclient/ewsitembase.cpp @@ -1,182 +1,182 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsitembase.h" #include "ewsitembase_p.h" #include "ewsclient_debug.h" EwsItemBasePrivate::EwsItemBasePrivate() : mValid(false) { qRegisterMetaType(); } EwsItemBasePrivate::~EwsItemBasePrivate() { } EwsItemBase::EwsItemBase(const QSharedDataPointer &priv) : d(priv) { } EwsItemBase::EwsItemBase(const EwsItemBase &other) : d(other.d) { } EwsItemBase::EwsItemBase(EwsItemBase &&other) : d(std::move(other.d)) { } EwsItemBase::~EwsItemBase() { } bool EwsItemBase::isValid() const { return d->mValid; } bool EwsItemBasePrivate::extendedPropertyReader(QXmlStreamReader &reader, QVariant &val) { EwsPropertyField prop; QVariant value; PropertyHash propHash = val.value(); QString elmName = reader.name().toString(); while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid namespace.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } if (reader.name() == QStringLiteral("FieldURI") || reader.name() == QStringLiteral("IndexedFieldURI") || reader.name() == QStringLiteral("ExtendedFieldURI")) { if (!prop.read(reader)) { reader.skipCurrentElement(); return false; } reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("Value")) { value = reader.readElementText(); } else if (reader.name() == QStringLiteral("Values")) { QStringList values; while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid namespace.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); reader.skipCurrentElement(); return false; } if (reader.name() == QStringLiteral("Value")) { values.append(reader.readElementText()); } } value = values; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unexpected child element %2") - .arg(elmName, reader.qualifiedName().toString()); + .arg(elmName, reader.qualifiedName().toString()); reader.skipCurrentElement(); return false; } } propHash.insert(prop, value); val = QVariant::fromValue(propHash); return true; } bool EwsItemBasePrivate::extendedPropertyWriter(QXmlStreamWriter &writer, const QVariant &val) { PropertyHash propHash = val.value(); PropertyHash::const_iterator it; for (it = propHash.cbegin(); it != propHash.cend();) { /* EwsXml will already start the ExtendedProperty element expecting a single value. * This is not exactly true for extended properties as there may be many. Work around this * by avoiding writing the first element start tag and the last element end tag. */ if (it != propHash.cbegin()) { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("ExtendedProperty")); } it.key().write(writer); it.key().writeExtendedValue(writer, it.value()); it++; if (it != propHash.cend()) { writer.writeEndElement(); } } return true; } bool EwsItemBasePrivate::operator==(const EwsItemBasePrivate &other) const { if (mValid != other.mValid) { return false; } return mFields == other.mFields; } QVariant EwsItemBase::operator[](const EwsPropertyField &prop) const { - EwsItemBasePrivate::PropertyHash propHash = - d->mFields[EwsItemFieldExtendedProperties].value(); + EwsItemBasePrivate::PropertyHash propHash + = d->mFields[EwsItemFieldExtendedProperties].value(); EwsItemBasePrivate::PropertyHash::iterator it = propHash.find(prop); if (it != propHash.end()) { return it.value(); } else { return QVariant(); } } bool EwsItemBase::hasField(EwsItemFields f) const { return d->mFields.contains(f); } QVariant EwsItemBase::operator[](EwsItemFields f) const { if (hasField(f)) { return d->mFields[f]; } else { return QVariant(); } } void EwsItemBase::setField(EwsItemFields f, const QVariant &value) { d->mFields[f] = value; } void EwsItemBase::setProperty(const EwsPropertyField &prop, const QVariant &value) { - EwsItemBasePrivate::PropertyHash propHash = - d->mFields[EwsItemFieldExtendedProperties].value(); + EwsItemBasePrivate::PropertyHash propHash + = d->mFields[EwsItemFieldExtendedProperties].value(); propHash[prop] = value; d->mFields[EwsItemFieldExtendedProperties] = QVariant::fromValue(propHash); } diff --git a/resources/ews/ewsclient/ewsitembase.h b/resources/ews/ewsclient/ewsitembase.h index 91b28816b..66c20fd27 100644 --- a/resources/ews/ewsclient/ewsitembase.h +++ b/resources/ews/ewsclient/ewsitembase.h @@ -1,58 +1,57 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSITEMBASE_H #define EWSITEMBASE_H #include #include "ewsid.h" #include "ewspropertyfield.h" class EwsItemBasePrivate; class EwsItemBase { public: EwsItemBase(const EwsItemBase &other); EwsItemBase(EwsItemBase &&other); virtual ~EwsItemBase(); EwsItemBase &operator=(const EwsItemBase &other); EwsItemBase &operator=(EwsItemBase &&other); bool isValid() const; bool hasField(EwsItemFields f) const; QVariant operator[](EwsItemFields f) const; QVariant operator[](const EwsPropertyField &prop) const; void setField(EwsItemFields f, const QVariant &value); void setProperty(const EwsPropertyField &prop, const QVariant &value); protected: EwsItemBase(const QSharedDataPointer &priv); void resetFields(); QSharedDataPointer d; }; #endif - diff --git a/resources/ews/ewsclient/ewsitembase_p.h b/resources/ews/ewsclient/ewsitembase_p.h index b036766a6..b8c00090c 100644 --- a/resources/ews/ewsclient/ewsitembase_p.h +++ b/resources/ews/ewsclient/ewsitembase_p.h @@ -1,58 +1,58 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSITEMBASE_P_H #define EWSITEMBASE_P_H #include #include "ewsid.h" class EwsItemBasePrivate : public QSharedData { public: typedef QHash PropertyHash; EwsItemBasePrivate(); virtual ~EwsItemBasePrivate(); virtual EwsItemBasePrivate *clone() const = 0; static bool extendedPropertyReader(QXmlStreamReader &reader, QVariant &val); static bool extendedPropertyWriter(QXmlStreamWriter &writer, const QVariant &val); // When the item, is first constructed it will only contain the id and will therefore be // invalid. Once updated through EWS the remaining data will be populated and the item will // be valid. bool mValid; QHash mFields; bool operator==(const EwsItemBasePrivate &other) const; }; -template <> +template<> Q_INLINE_TEMPLATE EwsItemBasePrivate *QSharedDataPointer::clone() { return d->clone(); } Q_DECLARE_METATYPE(EwsItemBasePrivate::PropertyHash) #endif diff --git a/resources/ews/ewsclient/ewsitemshape.cpp b/resources/ews/ewsclient/ewsitemshape.cpp index 4eea34c90..8d5639539 100644 --- a/resources/ews/ewsclient/ewsitemshape.cpp +++ b/resources/ews/ewsclient/ewsitemshape.cpp @@ -1,68 +1,67 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsitemshape.h" void EwsItemShape::write(QXmlStreamWriter &writer) const { writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ItemShape")); // Write the base shape writeBaseShape(writer); // Write the IncludeMimeContent element (if applicable) if (mFlags.testFlag(IncludeMimeContent)) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("IncludeMimeContent"), QStringLiteral("true")); } // Write the BodyType element if (mBodyType != BodyNone) { QString bodyTypeText; switch (mBodyType) { case BodyHtml: bodyTypeText = QStringLiteral("HTML"); break; case BodyText: bodyTypeText = QStringLiteral("Text"); break; default: - //case BodyBest: + //case BodyBest: bodyTypeText = QStringLiteral("Best"); break; } writer.writeTextElement(ewsTypeNsUri, QStringLiteral("BodyType"), bodyTypeText); } // Write the FilterHtmlContent element (if applicable) if (mBodyType == BodyHtml && mFlags.testFlag(FilterHtmlContent)) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("FilterHtmlContent"), QStringLiteral("true")); } // Write the ConvertHtmlCodePageToUTF8 element (if applicable) if (mBodyType == BodyHtml && mFlags.testFlag(ConvertHtmlToUtf8)) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ConvertHtmlCodePageToUTF8"), QStringLiteral("true")); } // Write properties (if any) writeProperties(writer); writer.writeEndElement(); } - diff --git a/resources/ews/ewsclient/ewsitemshape.h b/resources/ews/ewsclient/ewsitemshape.h index 33740c044..a47bf6bf3 100644 --- a/resources/ews/ewsclient/ewsitemshape.h +++ b/resources/ews/ewsclient/ewsitemshape.h @@ -1,84 +1,95 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSITEMSHAPE_H #define EWSITEMSHAPE_H - #include "ewsfoldershape.h" class EwsItemShape : public EwsFolderShape { public: enum Flag { IncludeMimeContent = 0x01, FilterHtmlContent = 0x02, ConvertHtmlToUtf8 = 0x04 }; enum BodyType { BodyNone, BodyBest, BodyHtml, BodyText }; Q_DECLARE_FLAGS(Flags, Flag) - explicit EwsItemShape(EwsBaseShape shape = EwsShapeDefault) : EwsFolderShape(shape), mBodyType(BodyNone) {} + explicit EwsItemShape(EwsBaseShape shape = EwsShapeDefault) : EwsFolderShape(shape) + , mBodyType(BodyNone) + { + } + EwsItemShape(const EwsItemShape &other) - : EwsFolderShape(other), mBodyType(BodyNone) {} + : EwsFolderShape(other) + , mBodyType(BodyNone) + { + } + explicit EwsItemShape(EwsFolderShape &&other) - : EwsFolderShape(other), mBodyType(BodyNone) {} + : EwsFolderShape(other) + , mBodyType(BodyNone) + { + } + EwsItemShape &operator=(EwsItemShape &&other) { mBaseShape = other.mBaseShape; mProps = std::move(other.mProps); mFlags = other.mFlags; mBodyType = other.mBodyType; return *this; } + EwsItemShape &operator=(const EwsItemShape &other) { mBaseShape = other.mBaseShape; mProps = other.mProps; mFlags = other.mFlags; mBodyType = other.mBodyType; return *this; } void setFlags(Flags flags) { mFlags = flags; } void setBodyType(BodyType type) { mBodyType = type; } void write(QXmlStreamWriter &writer) const; protected: Flags mFlags; BodyType mBodyType; - }; Q_DECLARE_OPERATORS_FOR_FLAGS(EwsItemShape::Flags) #endif diff --git a/resources/ews/ewsclient/ewsjob.cpp b/resources/ews/ewsclient/ewsjob.cpp index 6ec23cbd2..4d55e7bc3 100644 --- a/resources/ews/ewsclient/ewsjob.cpp +++ b/resources/ews/ewsclient/ewsjob.cpp @@ -1,49 +1,48 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsjob.h" #include "ewsclient_debug.h" EwsJob::EwsJob(QObject *parent) : KCompositeJob(parent) { } EwsJob::~EwsJob() { } bool EwsJob::doKill() { Q_FOREACH (KJob *job, subjobs()) { job->kill(KJob::Quietly); } clearSubjobs(); return true; } bool EwsJob::setErrorMsg(const QString &msg, int code) { setError(code); setErrorText(msg); qCWarningNC(EWSCLI_LOG) << msg; return false; } - diff --git a/resources/ews/ewsclient/ewsmailbox.cpp b/resources/ews/ewsclient/ewsmailbox.cpp index 7feaf4160..3c0eed295 100644 --- a/resources/ews/ewsclient/ewsmailbox.cpp +++ b/resources/ews/ewsclient/ewsmailbox.cpp @@ -1,145 +1,146 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsmailbox.h" #include #include #include "ewsclient_debug.h" class EwsMailboxPrivate : public QSharedData { public: EwsMailboxPrivate(); virtual ~EwsMailboxPrivate(); bool mValid; QString mName; QString mEmail; }; EwsMailboxPrivate::EwsMailboxPrivate() : mValid(false) { } EwsMailbox::EwsMailbox() : d(new EwsMailboxPrivate()) { } EwsMailbox::EwsMailbox(QXmlStreamReader &reader) : d(new EwsMailboxPrivate()) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in mailbox element:") << reader.namespaceUri(); return; } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("Name")) { d->mName = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid mailbox Name element."); return; } } else if (readerName == QStringLiteral("EmailAddress")) { d->mEmail = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid mailbox EmailAddress element."); return; } - } else if (readerName == QStringLiteral("RoutingType") || - readerName == QStringLiteral("MailboxType") || - readerName == QStringLiteral("ItemId")) { + } else if (readerName == QStringLiteral("RoutingType") + || readerName == QStringLiteral("MailboxType") + || readerName == QStringLiteral("ItemId")) { // Unsupported - ignore //qCWarningNC(EWSCLIENT_LOG) << QStringLiteral("Unsupported mailbox element %1").arg(reader.name().toString()); reader.skipCurrentElement(); } } d->mValid = true; } EwsMailboxPrivate::~EwsMailboxPrivate() { } EwsMailbox::EwsMailbox(const EwsMailbox &other) : d(other.d) { } EwsMailbox::EwsMailbox(EwsMailbox &&other) : d(std::move(other.d)) { } EwsMailbox::~EwsMailbox() { } EwsMailbox &EwsMailbox::operator=(const EwsMailbox &other) { d = other.d; return *this; } + EwsMailbox &EwsMailbox::operator=(EwsMailbox &&other) { d = std::move(other.d); return *this; } bool EwsMailbox::isValid() const { return d->mValid; } QString EwsMailbox::name() const { return d->mName; } QString EwsMailbox::email() const { return d->mEmail; } QString EwsMailbox::emailWithName() const { if (d->mName.isEmpty()) { return d->mEmail; } else { return QStringLiteral("%1 <%2>").arg(d->mName).arg(d->mEmail); } } EwsMailbox::operator KMime::Types::Mailbox() const { KMime::Types::Mailbox mbox; mbox.setAddress(d->mEmail.toLatin1()); if (!d->mName.isEmpty()) { mbox.setName(d->mName); } return mbox; } diff --git a/resources/ews/ewsclient/ewsmailbox.h b/resources/ews/ewsclient/ewsmailbox.h index 8badd5374..a666f679a 100644 --- a/resources/ews/ewsclient/ewsmailbox.h +++ b/resources/ews/ewsclient/ewsmailbox.h @@ -1,64 +1,62 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSMAILBOX_H #define EWSMAILBOX_H #include #include #include "ewsitembase.h" class EwsMailboxPrivate; -namespace KMime -{ -namespace Types -{ +namespace KMime { +namespace Types { class Mailbox; } } class EwsMailbox { public: typedef QList List; EwsMailbox(); explicit EwsMailbox(QXmlStreamReader &reader); EwsMailbox(const EwsMailbox &other); EwsMailbox(EwsMailbox &&other); virtual ~EwsMailbox(); EwsMailbox &operator=(const EwsMailbox &other); EwsMailbox &operator=(EwsMailbox &&other); bool isValid() const; QString name() const; QString email() const; QString emailWithName() const; operator KMime::Types::Mailbox() const; protected: QSharedDataPointer d; }; Q_DECLARE_METATYPE(EwsMailbox) Q_DECLARE_METATYPE(EwsMailbox::List) #endif diff --git a/resources/ews/ewsclient/ewsmovefolderrequest.cpp b/resources/ews/ewsclient/ewsmovefolderrequest.cpp index 9b3a9856c..f6ca84e86 100644 --- a/resources/ews/ewsclient/ewsmovefolderrequest.cpp +++ b/resources/ews/ewsclient/ewsmovefolderrequest.cpp @@ -1,120 +1,122 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmovefolderrequest.h" #include "ewsclient_debug.h" EwsMoveFolderRequest::EwsMoveFolderRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsMoveFolderRequest::~EwsMoveFolderRequest() { } void EwsMoveFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("MoveFolder")); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ToFolderId")); mDestFolderId.writeFolderIds(writer); writer.writeEndElement(); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FolderIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeFolderIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting MoveFolder request (%1 folders, to %2)") - .arg(mIds.size()).arg(mDestFolderId.id()); + .arg(mIds.size()).arg(mDestFolderId.id()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsMoveFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("MoveFolder"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsMoveFolderRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got MoveFolder response - OK"); } else { qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got MoveFolder response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsMoveFolderRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Folders")) { if (reader.readNextStartElement()) { EwsFolder folder(reader); if (!folder.isValid()) { return; } mId = folder[EwsFolderFieldFolderId].value(); // Finish the Folders element. reader.skipCurrentElement(); } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewsmovefolderrequest.h b/resources/ews/ewsclient/ewsmovefolderrequest.h index ce08369b3..a62fce3fa 100644 --- a/resources/ews/ewsclient/ewsmovefolderrequest.h +++ b/resources/ews/ewsclient/ewsmovefolderrequest.h @@ -1,79 +1,81 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSMOVEFOLDERREQUEST_H #define EWSMOVEFOLDERREQUEST_H #include #include #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsMoveFolderRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: const EwsId &folderId() const { return mId; } + protected: Response(QXmlStreamReader &reader); EwsId mId; friend class EwsMoveFolderRequest; }; EwsMoveFolderRequest(EwsClient &client, QObject *parent); ~EwsMoveFolderRequest() override; void setFolderIds(const EwsId::List &ids) { mIds = ids; } void setDestinationFolderId(const EwsId &id) { mDestFolderId = id; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId::List mIds; EwsId mDestFolderId; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsmoveitemrequest.cpp b/resources/ews/ewsclient/ewsmoveitemrequest.cpp index b3078dbad..a56d6806a 100644 --- a/resources/ews/ewsclient/ewsmoveitemrequest.cpp +++ b/resources/ews/ewsclient/ewsmoveitemrequest.cpp @@ -1,119 +1,121 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmoveitemrequest.h" #include "ewsclient_debug.h" EwsMoveItemRequest::EwsMoveItemRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsMoveItemRequest::~EwsMoveItemRequest() { } void EwsMoveItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("MoveItem")); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ToFolderId")); mDestFolderId.writeFolderIds(writer); writer.writeEndElement(); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ItemIds")); for (const EwsId &id : qAsConst(mIds)) { id.writeItemIds(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting MoveItem request (") << mIds << "to" << mDestFolderId << ")"; qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsMoveItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("MoveItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsMoveItemRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got MoveItem response - OK"); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got MoveItem response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsMoveItemRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Items")) { if (reader.readNextStartElement()) { EwsItem item(reader); if (!item.isValid()) { return; } mId = item[EwsItemFieldItemId].value(); // Finish the Items element. reader.skipCurrentElement(); } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewsmoveitemrequest.h b/resources/ews/ewsclient/ewsmoveitemrequest.h index 6523fdcd7..f428b0e3a 100644 --- a/resources/ews/ewsclient/ewsmoveitemrequest.h +++ b/resources/ews/ewsclient/ewsmoveitemrequest.h @@ -1,79 +1,81 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSMOVEITEMREQUEST_H #define EWSMOVEITEMREQUEST_H #include #include #include "ewsitem.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class EwsMoveItemRequest : public EwsRequest { Q_OBJECT public: class Response : public EwsRequest::Response { public: const EwsId &itemId() const { return mId; } + protected: Response(QXmlStreamReader &reader); EwsId mId; friend class EwsMoveItemRequest; }; EwsMoveItemRequest(EwsClient &client, QObject *parent); ~EwsMoveItemRequest() override; void setItemIds(const EwsId::List &ids) { mIds = ids; } void setDestinationFolderId(const EwsId &id) { mDestFolderId = id; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId::List mIds; EwsId mDestFolderId; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsoccurrence.cpp b/resources/ews/ewsclient/ewsoccurrence.cpp index 78626b4c0..64f6253e0 100644 --- a/resources/ews/ewsclient/ewsoccurrence.cpp +++ b/resources/ews/ewsclient/ewsoccurrence.cpp @@ -1,149 +1,150 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsoccurrence.h" #include #include #include "ewsclient_debug.h" #include "ewsid.h" #include "ewstypes.h" class EwsOccurrencePrivate : public QSharedData { public: EwsOccurrencePrivate(); virtual ~EwsOccurrencePrivate(); bool mValid; EwsId mItemId; QDateTime mStart; QDateTime mEnd; QDateTime mOriginalStart; }; EwsOccurrencePrivate::EwsOccurrencePrivate() : mValid(false) { } EwsOccurrence::EwsOccurrence() : d(new EwsOccurrencePrivate()) { } EwsOccurrence::EwsOccurrence(QXmlStreamReader &reader) : d(new EwsOccurrencePrivate()) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in mailbox element:") << reader.namespaceUri(); return; } if (reader.name() == QStringLiteral("ItemId")) { d->mItemId = EwsId(reader); reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("Start")) { d->mStart = QDateTime::fromString(reader.readElementText(), Qt::ISODate); if (reader.error() != QXmlStreamReader::NoError || !d->mStart.isValid()) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("Start")); + .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("Start")); return; } } else if (reader.name() == QStringLiteral("End")) { d->mEnd = QDateTime::fromString(reader.readElementText(), Qt::ISODate); if (reader.error() != QXmlStreamReader::NoError || !d->mStart.isValid()) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("End")); + .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("End")); return; } } else if (reader.name() == QStringLiteral("OriginalStart")) { d->mStart = QDateTime::fromString(reader.readElementText(), Qt::ISODate); if (reader.error() != QXmlStreamReader::NoError || !d->mStart.isValid()) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("OriginalStart")); + .arg(QStringLiteral("Occurrence")).arg(QStringLiteral("OriginalStart")); return; } } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("Occurrence")).arg(reader.name().toString()); + .arg(QStringLiteral("Occurrence")).arg(reader.name().toString()); return; } } d->mValid = true; } EwsOccurrencePrivate::~EwsOccurrencePrivate() { } EwsOccurrence::EwsOccurrence(const EwsOccurrence &other) : d(other.d) { } EwsOccurrence::EwsOccurrence(EwsOccurrence &&other) : d(std::move(other.d)) { } EwsOccurrence::~EwsOccurrence() { } EwsOccurrence &EwsOccurrence::operator=(const EwsOccurrence &other) { d = other.d; return *this; } + EwsOccurrence &EwsOccurrence::operator=(EwsOccurrence &&other) { d = std::move(other.d); return *this; } bool EwsOccurrence::isValid() const { return d->mValid; } const EwsId &EwsOccurrence::itemId() const { return d->mItemId; } QDateTime EwsOccurrence::start() const { return d->mStart; } QDateTime EwsOccurrence::end() const { return d->mEnd; } QDateTime EwsOccurrence::originalStart() const { return d->mOriginalStart; } diff --git a/resources/ews/ewsclient/ewspoxautodiscoverrequest.cpp b/resources/ews/ewsclient/ewspoxautodiscoverrequest.cpp index 38aab2b13..143fed0d5 100644 --- a/resources/ews/ewsclient/ewspoxautodiscoverrequest.cpp +++ b/resources/ews/ewsclient/ewspoxautodiscoverrequest.cpp @@ -1,285 +1,288 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewspoxautodiscoverrequest.h" #include #include #include #include #include "ewsclient_debug.h" static const QString poxAdOuReqNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"); static const QString poxAdRespNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"); static const QString poxAdOuRespNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"); -EwsPoxAutodiscoverRequest::EwsPoxAutodiscoverRequest(const QUrl &url, const QString &email, - const QString &userAgent, bool useNTLMv2, QObject *parent) - : EwsJob(parent), mUrl(url), mEmail(email), mUserAgent(userAgent), mUseNTLMv2(useNTLMv2), - mServerVersion(EwsServerVersion::ewsVersion2007Sp1), mAction(Settings) +EwsPoxAutodiscoverRequest::EwsPoxAutodiscoverRequest(const QUrl &url, const QString &email, const QString &userAgent, bool useNTLMv2, QObject *parent) + : EwsJob(parent) + , mUrl(url) + , mEmail(email) + , mUserAgent(userAgent) + , mUseNTLMv2(useNTLMv2) + , mServerVersion(EwsServerVersion::ewsVersion2007Sp1) + , mAction(Settings) { } EwsPoxAutodiscoverRequest::~EwsPoxAutodiscoverRequest() { } void EwsPoxAutodiscoverRequest::doSend() { Q_FOREACH (KJob *job, subjobs()) { job->start(); } } void EwsPoxAutodiscoverRequest::prepare(const QString body) { mBody = body; mLastUrl = mUrl; KIO::TransferJob *job = KIO::http_post(mUrl, body.toUtf8(), KIO::HideProgressInfo); job->addMetaData(QStringLiteral("content-type"), QStringLiteral("text/xml")); job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); if (mUseNTLMv2) { job->addMetaData(QStringLiteral("EnableNTLMv2Auth"), QStringLiteral("true")); } if (!mUserAgent.isEmpty()) { job->addMetaData(QStringLiteral("UserAgent"), mUserAgent); } //config->readEntry("no-spoof-check", false) connect(job, &KIO::TransferJob::result, this, &EwsPoxAutodiscoverRequest::requestResult); connect(job, &KIO::TransferJob::data, this, &EwsPoxAutodiscoverRequest::requestData); connect(job, &KIO::TransferJob::redirection, this, &EwsPoxAutodiscoverRequest::requestRedirect); addSubjob(job); } void EwsPoxAutodiscoverRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); writer.writeStartDocument(); writer.writeDefaultNamespace(poxAdOuReqNsUri); writer.writeStartElement(poxAdOuReqNsUri, QStringLiteral("Autodiscover")); writer.writeStartElement(poxAdOuReqNsUri, QStringLiteral("Request")); writer.writeTextElement(poxAdOuReqNsUri, QStringLiteral("EMailAddress"), mEmail); writer.writeTextElement(poxAdOuReqNsUri, QStringLiteral("AcceptableResponseSchema"), poxAdOuRespNsUri); writer.writeEndElement(); // Request writer.writeEndElement(); // Autodiscover writer.writeEndDocument(); qCDebug(EWSCLI_PROTO_LOG) << reqString; qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting POX Autodiscovery request (url: ") << mUrl << QStringLiteral(", email: ") << mEmail; prepare(reqString); doSend(); } - void EwsPoxAutodiscoverRequest::requestData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); qCDebug(EWSCLI_PROTO_LOG) << "data" << job << data; mResponseData += QString::fromUtf8(data); } void EwsPoxAutodiscoverRequest::requestResult(KJob *job) { if (EWSCLI_PROTO_LOG().isDebugEnabled()) { ewsLogDir.setAutoRemove(false); if (ewsLogDir.isValid()) { QTemporaryFile dumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmldump_XXXXXXX.xml")); dumpFile.open(); dumpFile.setAutoRemove(false); dumpFile.write(mResponseData.toUtf8()); qCDebug(EWSCLI_PROTO_LOG) << "response dumped to" << dumpFile.fileName(); dumpFile.close(); } } - KIO::TransferJob *trJob = qobject_cast(job); + KIO::TransferJob *trJob = qobject_cast(job); int resp = trJob->metaData()[QStringLiteral("responsecode")].toUInt(); if (job->error() != 0) { setErrorMsg(QStringLiteral("Failed to process EWS request: ") + job->errorString()); setError(job->error()); } else if (resp >= 300) { setErrorMsg(QStringLiteral("Failed to process EWS request - HTTP code %1").arg(resp)); setError(resp); } else { QXmlStreamReader reader(mResponseData); readResponse(reader); } emitResult(); } bool EwsPoxAutodiscoverRequest::readResponse(QXmlStreamReader &reader) { if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read POX response XML")); } if ((reader.name() != QStringLiteral("Autodiscover")) || (reader.namespaceUri() != poxAdRespNsUri)) { return setErrorMsg(QStringLiteral("Failed to read POX response - not an Autodiscover response")); } if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read POX response - expected %1 element") .arg(QStringLiteral("Response"))); } if ((reader.name() != QStringLiteral("Response")) || (reader.namespaceUri() != poxAdOuRespNsUri)) { return setErrorMsg(QStringLiteral("Failed to read POX response - expected %1 element, found %2") .arg(QStringLiteral("Response").arg(reader.name().toString()))); } while (reader.readNextStartElement()) { if (reader.namespaceUri() != poxAdOuRespNsUri) { return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); } if (reader.name() == QStringLiteral("User")) { reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("Account")) { if (!readAccount(reader)) { return false; } } else { return setErrorMsg(QStringLiteral("Failed to read POX response - unknown element '%1' inside '%2'") .arg(reader.name().toString()).arg(QStringLiteral("Response"))); } } return true; } bool EwsPoxAutodiscoverRequest::readAccount(QXmlStreamReader &reader) { while (reader.readNextStartElement()) { if (reader.namespaceUri() != poxAdOuRespNsUri) { return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("Action")) { QString action = reader.readElementText(); if (action == QStringLiteral("settings")) { mAction = Settings; } else if (action == QStringLiteral("redirectUrl")) { mAction = RedirectUrl; } else if (action == QStringLiteral("redirectAddr")) { mAction = RedirectAddr; } else { return setErrorMsg(QStringLiteral("Failed to read POX response - unknown action '%1'") .arg(action)); } } else if (readerName == QStringLiteral("RedirectUrl")) { mRedirectUrl = reader.readElementText(); } else if (readerName == QStringLiteral("RedirectAddr")) { mRedirectAddr = reader.readElementText(); } else if (readerName == QStringLiteral("RedirectAddr")) { mRedirectAddr = reader.readElementText(); } else if (readerName == QStringLiteral("Protocol")) { if (!readProtocol(reader)) { return false; } } else { reader.skipCurrentElement(); } } return true; } bool EwsPoxAutodiscoverRequest::readProtocol(QXmlStreamReader &reader) { Protocol proto; while (reader.readNextStartElement()) { if (reader.namespaceUri() != poxAdOuRespNsUri) { return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); } const QStringRef readerName = reader.name(); if (readerName == QStringLiteral("Type")) { QString type = reader.readElementText(); if (type == QStringLiteral("EXCH")) { proto.mType = ExchangeProto; } else if (type == QStringLiteral("EXPR")) { proto.mType = ExchangeProxyProto; } else if (type == QStringLiteral("WEB")) { proto.mType = ExchangeWebProto; } else { return setErrorMsg(QStringLiteral("Failed to read POX response - unknown protocol '%1'") .arg(type)); } } else if (readerName == QStringLiteral("EwsUrl")) { proto.mEwsUrl = reader.readElementText(); } else if (readerName == QStringLiteral("OabUrl")) { proto.mOabUrl = reader.readElementText(); } else { reader.skipCurrentElement(); } } qCDebug(EWSCLI_LOG) << "Adding proto type" << proto.mType << proto.isValid(); mProtocols[proto.mType] = proto; return true; } void EwsPoxAutodiscoverRequest::requestRedirect(KIO::Job *job, const QUrl &url) { Q_UNUSED(job); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got HTTP redirect to: ") << mUrl; mLastUrl = url; } void EwsPoxAutodiscoverRequest::dump() const { ewsLogDir.setAutoRemove(false); if (ewsLogDir.isValid()) { QTemporaryFile reqDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlreqdump_XXXXXXX.xml")); reqDumpFile.open(); reqDumpFile.setAutoRemove(false); reqDumpFile.write(mBody.toUtf8()); reqDumpFile.close(); QTemporaryFile resDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlresdump_XXXXXXX.xml")); resDumpFile.open(); resDumpFile.setAutoRemove(false); resDumpFile.write(mResponseData.toUtf8()); resDumpFile.close(); qCDebug(EWSCLI_LOG) << "request dumped to" << reqDumpFile.fileName(); qCDebug(EWSCLI_LOG) << "response dumped to" << resDumpFile.fileName(); } else { qCWarning(EWSCLI_LOG) << "failed to dump request and response"; } } diff --git a/resources/ews/ewsclient/ewspoxautodiscoverrequest.h b/resources/ews/ewsclient/ewspoxautodiscoverrequest.h index d9e23c60c..04d9d7bc2 100644 --- a/resources/ews/ewsclient/ewspoxautodiscoverrequest.h +++ b/resources/ews/ewsclient/ewspoxautodiscoverrequest.h @@ -1,149 +1,150 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSPOXAUTODISCOVERREQUEST_H #define EWSPOXAUTODISCOVERREQUEST_H #include #include #include #include "ewsjob.h" #include "ewsserverversion.h" #include "ewstypes.h" -namespace KIO -{ +namespace KIO { class Job; } class EwsPoxAutodiscoverRequest : public EwsJob { Q_OBJECT public: enum Action { RedirectUrl = 0, RedirectAddr, Settings, }; enum ProtocolType { ExchangeProto, ExchangeProxyProto, ExchangeWebProto, UnknownProto }; class Protocol { public: - Protocol() : mType(UnknownProto) {} + Protocol() : mType(UnknownProto) + { + } bool isValid() const { return mType != UnknownProto; } ProtocolType type() const { return mType; } const QString &ewsUrl() const { return mEwsUrl; } const QString &oabUrl() const { return mOabUrl; } + private: ProtocolType mType; QString mEwsUrl; QString mOabUrl; friend class EwsPoxAutodiscoverRequest; }; - EwsPoxAutodiscoverRequest(const QUrl &url, const QString &email, const QString &userAgent, - bool useNTLMv2, QObject *parent); + EwsPoxAutodiscoverRequest(const QUrl &url, const QString &email, const QString &userAgent, bool useNTLMv2, QObject *parent); ~EwsPoxAutodiscoverRequest() override; const EwsServerVersion &serverVersion() const { return mServerVersion; } void dump() const; void start() override; Action action() const { return mAction; } const Protocol protocol(ProtocolType type) const { return mProtocols.value(type); } const QString &redirectAddr() const { return mRedirectAddr; } const QString &redirectUrl() const { return mRedirectUrl; } const QUrl &lastHttpUrl() const { return mLastUrl; } protected: void doSend(); void prepare(const QString body); bool readResponse(QXmlStreamReader &reader); protected Q_SLOTS: void requestResult(KJob *job); void requestData(KIO::Job *job, const QByteArray &data); void requestRedirect(KIO::Job *job, const QUrl &url); private: bool readAccount(QXmlStreamReader &reader); bool readProtocol(QXmlStreamReader &reader); QString mResponseData; QString mBody; QUrl mUrl; QString mEmail; QString mUserAgent; bool mUseNTLMv2; EwsServerVersion mServerVersion; Action mAction; QString mRedirectUrl; QString mRedirectAddr; QHash mProtocols; QUrl mLastUrl; }; #endif diff --git a/resources/ews/ewsclient/ewspropertyfield.cpp b/resources/ews/ewsclient/ewspropertyfield.cpp index 21ca2f15b..9dd5eebf4 100644 --- a/resources/ews/ewsclient/ewspropertyfield.cpp +++ b/resources/ews/ewsclient/ewspropertyfield.cpp @@ -1,636 +1,654 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewspropertyfield.h" #include #include "ewsclient_debug.h" static const QString distinguishedPropSetIdNames[] = { QStringLiteral("Meeting"), QStringLiteral("Appointment"), QStringLiteral("Common"), QStringLiteral("PublicStrings"), QStringLiteral("Address"), QStringLiteral("InternetHeaders"), QStringLiteral("CalendarAssistant"), QStringLiteral("UnifiedMessaging") }; static const QString propertyTypeNames[] = { QStringLiteral("ApplicationTime"), QStringLiteral("ApplicationTimeArray"), QStringLiteral("Binary"), QStringLiteral("BinaryArray"), QStringLiteral("Boolean"), QStringLiteral("CLSID"), QStringLiteral("CLSIDArray"), QStringLiteral("Currency"), QStringLiteral("CurrencyArray"), QStringLiteral("Double"), QStringLiteral("DoubleArray"), QStringLiteral("Error"), QStringLiteral("Float"), QStringLiteral("FloatArray"), QStringLiteral("Integer"), QStringLiteral("IntegerArray"), QStringLiteral("Long"), QStringLiteral("LongArray"), QStringLiteral("Null"), QStringLiteral("Object"), QStringLiteral("ObjectArray"), QStringLiteral("Short"), QStringLiteral("ShortArray"), QStringLiteral("SystemTime"), QStringLiteral("SystemTimeArray"), QStringLiteral("String"), QStringLiteral("StringArray") }; class EwsPropertyFieldPrivate : public QSharedData { public: EwsPropertyFieldPrivate() - : mPropType(EwsPropertyField::UnknownField), mIndex(0), mPsIdType(DistinguishedPropSet), - mPsDid(EwsPropSetMeeting), mIdType(PropName), mId(0), mHasTag(false), mTag(0), - mType(EwsPropTypeNull), mHash(0) - {} + : mPropType(EwsPropertyField::UnknownField) + , mIndex(0) + , mPsIdType(DistinguishedPropSet) + , mPsDid(EwsPropSetMeeting) + , mIdType(PropName) + , mId(0) + , mHasTag(false) + , mTag(0) + , mType(EwsPropTypeNull) + , mHash(0) + { + } enum PropSetIdType { DistinguishedPropSet, RealPropSet }; enum PropIdType { PropName, PropId }; EwsPropertyField::Type mPropType; QString mUri; unsigned mIndex; PropSetIdType mPsIdType; EwsDistinguishedPropSetId mPsDid; QString mPsId; PropIdType mIdType; unsigned mId; QString mName; bool mHasTag; unsigned mTag; EwsPropertyType mType; uint mHash; // Precalculated hash for the qHash() function. void recalcHash(); }; void EwsPropertyFieldPrivate::recalcHash() { mHash = 0; switch (mPropType) { case EwsPropertyField::Field: mHash = 0x00000000 | (qHash(mUri) & 0x3FFFFFFF); break; case EwsPropertyField::IndexedField: mHash = 0x80000000 | ((qHash(mUri) ^ mIndex) & 0x3FFFFFFF); break; case EwsPropertyField::ExtendedField: if (mHasTag) { mHash = 0x40000000 | mTag; } else { if (mPsIdType == DistinguishedPropSet) { mHash |= mPsDid << 16; } else { mHash |= (qHash(mPsId) & 0x1FFF) << 16; } if (mIdType == PropId) { mHash |= mId & 0xFFFF; } else { mHash |= (qHash(mName) & 0xFFFF); } mHash |= 0xC0000000; } break; default: break; } } EwsPropertyField::EwsPropertyField() : d(new EwsPropertyFieldPrivate()) { } EwsPropertyField::EwsPropertyField(const QString &uri) : d(new EwsPropertyFieldPrivate()) { d->mPropType = Field; d->mUri = uri; d->recalcHash(); } EwsPropertyField::EwsPropertyField(const QString &uri, unsigned index) : d(new EwsPropertyFieldPrivate()) { d->mPropType = IndexedField; d->mUri = uri; d->mIndex = index; d->recalcHash(); } EwsPropertyField::EwsPropertyField(EwsDistinguishedPropSetId psid, unsigned id, EwsPropertyType type) : d(new EwsPropertyFieldPrivate()) { d->mPropType = ExtendedField; d->mPsIdType = EwsPropertyFieldPrivate::DistinguishedPropSet; d->mPsDid = psid; d->mIdType = EwsPropertyFieldPrivate::PropId; d->mId = id; d->mType = type; d->recalcHash(); } EwsPropertyField::EwsPropertyField(EwsDistinguishedPropSetId psid, const QString &name, EwsPropertyType type) : d(new EwsPropertyFieldPrivate()) { d->mPropType = ExtendedField; d->mPsIdType = EwsPropertyFieldPrivate::DistinguishedPropSet; d->mPsDid = psid; d->mIdType = EwsPropertyFieldPrivate::PropName; d->mName = name; d->mType = type; d->recalcHash(); } EwsPropertyField::EwsPropertyField(const QString &psid, unsigned id, EwsPropertyType type) : d(new EwsPropertyFieldPrivate()) { d->mPropType = ExtendedField; d->mPsIdType = EwsPropertyFieldPrivate::RealPropSet; d->mPsId = psid; d->mIdType = EwsPropertyFieldPrivate::PropId; d->mId = id; d->mType = type; d->recalcHash(); } EwsPropertyField::EwsPropertyField(const QString &psid, const QString &name, EwsPropertyType type) : d(new EwsPropertyFieldPrivate()) { d->mPropType = ExtendedField; d->mPsIdType = EwsPropertyFieldPrivate::RealPropSet; d->mPsId = psid; d->mIdType = EwsPropertyFieldPrivate::PropName; d->mName = name; d->mType = type; d->recalcHash(); } EwsPropertyField::EwsPropertyField(unsigned tag, EwsPropertyType type) : d(new EwsPropertyFieldPrivate()) { d->mPropType = ExtendedField; d->mHasTag = true; d->mTag = tag; d->mType = type; d->recalcHash(); } EwsPropertyField::EwsPropertyField(const EwsPropertyField &other) : d(other.d) { } EwsPropertyField::EwsPropertyField(EwsPropertyField &&other) : d(other.d) { } EwsPropertyField &EwsPropertyField::operator=(EwsPropertyField &&other) { d = other.d; return *this; } EwsPropertyField::~EwsPropertyField() { } EwsPropertyField &EwsPropertyField::operator=(const EwsPropertyField &other) { d = other.d; return *this; } bool EwsPropertyField::operator==(const EwsPropertyField &other) const { - if (d == other.d) + if (d == other.d) { return true; + } const EwsPropertyFieldPrivate *od = other.d; - if (d->mPropType != od->mPropType) + if (d->mPropType != od->mPropType) { return false; + } switch (d->mPropType) { case UnknownField: return true; case Field: - return (d->mUri == od->mUri); + return d->mUri == od->mUri; case IndexedField: return (d->mUri == od->mUri) && (d->mIndex == od->mIndex); case ExtendedField: - if (d->mType != od->mType) + if (d->mType != od->mType) { return false; + } - if (d->mHasTag != od->mHasTag) + if (d->mHasTag != od->mHasTag) { return false; - else if (d->mHasTag) + } else if (d->mHasTag) { return d->mTag == od->mTag; + } - if (d->mPsIdType != od->mPsIdType) + if (d->mPsIdType != od->mPsIdType) { return false; - else if ((d->mPsIdType == EwsPropertyFieldPrivate::DistinguishedPropSet) - && (d->mPsDid != od->mPsDid)) + } else if ((d->mPsIdType == EwsPropertyFieldPrivate::DistinguishedPropSet) + && (d->mPsDid != od->mPsDid)) { return false; - else if ((d->mPsIdType == EwsPropertyFieldPrivate::RealPropSet) && (d->mPsId != od->mPsId)) + } else if ((d->mPsIdType == EwsPropertyFieldPrivate::RealPropSet) && (d->mPsId != od->mPsId)) { return false; + } - if (d->mIdType != od->mIdType) + if (d->mIdType != od->mIdType) { return false; - else if ((d->mIdType == EwsPropertyFieldPrivate::PropId) && (d->mId != od->mId)) + } else if ((d->mIdType == EwsPropertyFieldPrivate::PropId) && (d->mId != od->mId)) { return false; - else if ((d->mIdType == EwsPropertyFieldPrivate::PropName) && (d->mName != od->mName)) + } else if ((d->mIdType == EwsPropertyFieldPrivate::PropName) && (d->mName != od->mName)) { return false; + } return true; default: return false; } // Shouldn't get here. return false; } void EwsPropertyField::write(QXmlStreamWriter &writer) const { switch (d->mPropType) { case Field: writer.writeStartElement(ewsTypeNsUri, QStringLiteral("FieldURI")); writer.writeAttribute(QStringLiteral("FieldURI"), d->mUri); writer.writeEndElement(); break; - case IndexedField: { + case IndexedField: + { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("IndexedFieldURI")); writer.writeAttribute(QStringLiteral("FieldURI"), d->mUri); QStringList tokens = d->mUri.split(QLatin1Char(':')); writer.writeAttribute(QStringLiteral("FieldIndex"), tokens[1] + QString::number(d->mIndex)); writer.writeEndElement(); break; } case ExtendedField: writer.writeStartElement(ewsTypeNsUri, QStringLiteral("ExtendedFieldURI")); if (d->mHasTag) { writer.writeAttribute(QStringLiteral("PropertyTag"), QStringLiteral("0x") + QString::number(d->mTag, 16)); } else { if (d->mPsIdType == EwsPropertyFieldPrivate::DistinguishedPropSet) { writer.writeAttribute(QStringLiteral("DistinguishedPropertySetId"), distinguishedPropSetIdNames[d->mPsDid]); } else { writer.writeAttribute(QStringLiteral("PropertySetId"), d->mPsId); } if (d->mIdType == EwsPropertyFieldPrivate::PropId) { writer.writeAttribute(QStringLiteral("PropertyId"), QString::number(d->mId)); } else { writer.writeAttribute(QStringLiteral("PropertyName"), d->mName); } } writer.writeAttribute(QStringLiteral("PropertyType"), propertyTypeNames[d->mType]); writer.writeEndElement(); break; case UnknownField: break; } } bool EwsPropertyField::read(QXmlStreamReader &reader) { if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - invalid namespace."); return false; } QXmlStreamAttributes attrs = reader.attributes(); bool ok; // First check the property type if (reader.name() == QStringLiteral("FieldURI")) { if (!attrs.hasAttribute(QStringLiteral("FieldURI"))) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing %1 attribute.") - .arg(QStringLiteral("FieldURI")); + .arg(QStringLiteral("FieldURI")); return false; } d->mPropType = Field; d->mUri = attrs.value(QStringLiteral("FieldURI")).toString(); } else if (reader.name() == QStringLiteral("IndexedFieldURI")) { if (!attrs.hasAttribute(QStringLiteral("FieldURI"))) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing %1 attribute.") - .arg(QStringLiteral("FieldURI")); + .arg(QStringLiteral("FieldURI")); return false; } if (!attrs.hasAttribute(QStringLiteral("FieldIndex"))) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing %1 attribute.") - .arg(QStringLiteral("FieldIndex")); + .arg(QStringLiteral("FieldIndex")); return false; } QString uri = attrs.value(QStringLiteral("FieldURI")).toString(); QStringList tokens = uri.split(QLatin1Char(':')); QString indexStr = attrs.value(QStringLiteral("FieldIndex")).toString(); if (!indexStr.startsWith(tokens[1])) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - malformed %1 attribute.") - .arg(QStringLiteral("FieldIndex")); + .arg(QStringLiteral("FieldIndex")); return false; } unsigned index = indexStr.midRef(tokens[1].size()).toUInt(&ok, 0); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - error reading %1 attribute.") - .arg(QStringLiteral("FieldIndex")); + .arg(QStringLiteral("FieldIndex")); return false; } d->mPropType = IndexedField; d->mUri = uri; d->mIndex = index; } else if (reader.name() == QStringLiteral("ExtendedFieldURI")) { if (!attrs.hasAttribute(QStringLiteral("PropertyType"))) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing %1 attribute.") - .arg(QStringLiteral("PropertyType")); + .arg(QStringLiteral("PropertyType")); return false; } QStringRef propTypeText = attrs.value(QStringLiteral("PropertyType")); unsigned i; EwsPropertyType propType; for (i = 0; i < sizeof(propertyTypeNames) / sizeof(propertyTypeNames[0]); ++i) { if (propTypeText == propertyTypeNames[i]) { propType = static_cast(i); break; } } if (i == sizeof(propertyTypeNames) / sizeof(propertyTypeNames[0])) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - error reading %1 attribute.") - .arg(QStringLiteral("PropertyType")); + .arg(QStringLiteral("PropertyType")); return false; } if (attrs.hasAttribute(QStringLiteral("PropertyTag"))) { - unsigned tag = attrs.value(QStringLiteral("PropertyTag")).toUInt(&ok, 0); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - error reading %1 attribute.") - .arg(QStringLiteral("PropertyTag")); + .arg(QStringLiteral("PropertyTag")); return false; } d->mHasTag = true; d->mTag = tag; } else { EwsPropertyFieldPrivate::PropSetIdType psIdType = EwsPropertyFieldPrivate::DistinguishedPropSet; EwsDistinguishedPropSetId psDid = EwsPropSetMeeting; QString psId; EwsPropertyFieldPrivate::PropIdType idType = EwsPropertyFieldPrivate::PropName; unsigned id = 0; QString name; if (attrs.hasAttribute(QStringLiteral("PropertyId"))) { id = attrs.value(QStringLiteral("PropertyId")).toUInt(&ok, 0); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - error reading %1 attribute.") - .arg(QStringLiteral("PropertyId")); + .arg(QStringLiteral("PropertyId")); return false; } idType = EwsPropertyFieldPrivate::PropId; } else if (attrs.hasAttribute(QStringLiteral("PropertyName"))) { name = attrs.value(QStringLiteral("PropertyName")).toString(); idType = EwsPropertyFieldPrivate::PropName; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing one of %1 or %2 attributes.") - .arg(QStringLiteral("PropertyId").arg(QStringLiteral("PropertyName"))); + .arg(QStringLiteral("PropertyId").arg(QStringLiteral("PropertyName"))); return false; } if (attrs.hasAttribute(QStringLiteral("DistinguishedPropertySetId"))) { QStringRef didText = attrs.value(QStringLiteral("DistinguishedPropertySetId")); unsigned i; for (i = 0; i < sizeof(distinguishedPropSetIdNames) / sizeof(distinguishedPropSetIdNames[0]); ++i) { if (didText == distinguishedPropSetIdNames[i]) { psDid = static_cast(i); break; } } if (i == sizeof(distinguishedPropSetIdNames) / sizeof(distinguishedPropSetIdNames[0])) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - error reading %1 attribute.") - .arg(QStringLiteral("DistinguishedPropertySetId")); + .arg(QStringLiteral("DistinguishedPropertySetId")); return false; } psIdType = EwsPropertyFieldPrivate::DistinguishedPropSet; } else if (attrs.hasAttribute(QStringLiteral("PropertySetId"))) { psId = attrs.value(QStringLiteral("PropertySetId")).toString(); psIdType = EwsPropertyFieldPrivate::RealPropSet; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading property field - missing one of %1 or %2 attributes.") - .arg(QStringLiteral("DistinguishedPropertySetId").arg(QStringLiteral("PropertySetId"))); + .arg(QStringLiteral("DistinguishedPropertySetId").arg(QStringLiteral("PropertySetId"))); return false; } d->mPsIdType = psIdType; d->mPsDid = psDid; d->mPsId = psId; d->mIdType = idType; d->mId = id; d->mName = name; } d->mType = propType; d->mPropType = ExtendedField; } d->recalcHash(); return true; } uint qHash(const EwsPropertyField &prop, uint seed) { return prop.d->mHasTag ^ seed; } QDebug operator<<(QDebug debug, const EwsPropertyField &prop) { QDebugStateSaver saver(debug); QDebug d = debug.nospace().noquote(); d << QStringLiteral("EwsPropertyField("); switch (prop.d->mPropType) { case EwsPropertyField::Field: d << QStringLiteral("FieldUri: ") << prop.d->mUri; break; case EwsPropertyField::IndexedField: d << QStringLiteral("IndexedFieldUri: ") << prop.d->mUri << '@' << prop.d->mIndex; break; case EwsPropertyField::ExtendedField: d << QStringLiteral("ExtendedFieldUri: "); if (prop.d->mHasTag) { d << QStringLiteral("tag: 0x") << QString::number(prop.d->mTag, 16); } else { if (prop.d->mPsIdType == EwsPropertyFieldPrivate::DistinguishedPropSet) { d << QStringLiteral("psdid: ") << distinguishedPropSetIdNames[prop.d->mPsDid]; } else { d << QStringLiteral("psid: ") << prop.d->mPsId; } d << QStringLiteral(", "); if (prop.d->mIdType == EwsPropertyFieldPrivate::PropId) { d << QStringLiteral("id: 0x") << QString::number(prop.d->mId, 16); } else { d << QStringLiteral("name: ") << prop.d->mName; } } d << QStringLiteral(", type: ") << propertyTypeNames[prop.d->mType]; break; case EwsPropertyField::UnknownField: d << QStringLiteral("Unknown"); break; } d << ')'; return debug; } bool EwsPropertyField::writeWithValue(QXmlStreamWriter &writer, const QVariant &value) const { switch (d->mPropType) { - case Field: { + case Field: + { QStringList tokens = d->mUri.split(QLatin1Char(':')); if (tokens.size() != 2) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Invalid field URI: %1").arg(d->mUri); return false; } writer.writeStartElement(ewsTypeNsUri, tokens[1]); writeValue(writer, value); writer.writeEndElement(); break; } - case IndexedField: { + case IndexedField: + { QStringList tokens = d->mUri.split(QLatin1Char(':')); if (tokens.size() != 2) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Invalid field URI: %1").arg(d->mUri); return false; } writer.writeStartElement(ewsTypeNsUri, tokens[1] + QStringLiteral("es")); writer.writeStartElement(ewsTypeNsUri, QStringLiteral("Entry")); writer.writeAttribute(QStringLiteral("Key"), tokens[1] + QString::number(d->mIndex)); writeValue(writer, value); writer.writeEndElement(); writer.writeEndElement(); break; } case ExtendedField: writer.writeStartElement(ewsTypeNsUri, QStringLiteral("ExtendedProperty")); write(writer); writeExtendedValue(writer, value); writer.writeEndElement(); break; default: return false; } return true; } void EwsPropertyField::writeValue(QXmlStreamWriter &writer, const QVariant &value) const { switch (value.type()) { - case QVariant::StringList: { + case QVariant::StringList: + { const QStringList list = value.toStringList(); for (const QString &str : list) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("String"), str); } break; } case QVariant::String: writer.writeCharacters(value.toString()); break; default: qCWarning(EWSCLI_LOG) << QStringLiteral("Unknown variant type to write: %1") - .arg(QString::fromLatin1(value.typeName())); + .arg(QString::fromLatin1(value.typeName())); } } void EwsPropertyField::writeExtendedValue(QXmlStreamWriter &writer, const QVariant &value) const { switch (value.type()) { - case QVariant::StringList: { + case QVariant::StringList: + { const QStringList list = value.toStringList(); writer.writeStartElement(ewsTypeNsUri, QStringLiteral("Values")); for (const QString &str : list) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Value"), str); } writer.writeEndElement(); break; } case QVariant::String: writer.writeStartElement(ewsTypeNsUri, QStringLiteral("Value")); writer.writeCharacters(value.toString()); writer.writeEndElement(); break; default: qCWarning(EWSCLI_LOG) << QStringLiteral("Unknown variant type to write: %1") - .arg(QString::fromLatin1(value.typeName())); + .arg(QString::fromLatin1(value.typeName())); } } EwsPropertyField::Type EwsPropertyField::type() const { return d->mPropType; } QString EwsPropertyField::uri() const { if (d->mPropType == Field || d->mPropType == IndexedField) { return d->mUri; } else { return QString(); } } diff --git a/resources/ews/ewsclient/ewsrecurrence.cpp b/resources/ews/ewsclient/ewsrecurrence.cpp index 5bd311d91..188c5340c 100644 --- a/resources/ews/ewsclient/ewsrecurrence.cpp +++ b/resources/ews/ewsclient/ewsrecurrence.cpp @@ -1,574 +1,572 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsrecurrence.h" #include #include #include #include "ewsclient_debug.h" #include "ewstypes.h" using namespace KCalCore; static const QString dayOfWeekNames[] = { QStringLiteral("Monday"), QStringLiteral("Tuesday"), QStringLiteral("Wednesday"), QStringLiteral("Thursday"), QStringLiteral("Friday"), QStringLiteral("Saturday"), QStringLiteral("Sunday"), QStringLiteral("Day"), QStringLiteral("Weekday"), QStringLiteral("WeekendDay"), }; Q_CONSTEXPR unsigned dayOfWeekNameCount = sizeof(dayOfWeekNames) / sizeof(dayOfWeekNames[0]); static const QString dayOfWeekIndexNames[] = { QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third"), QStringLiteral("Fourth"), QStringLiteral("Last") }; Q_CONSTEXPR unsigned dayOfWeekIndexNameCount = sizeof(dayOfWeekIndexNames) / sizeof(dayOfWeekIndexNames[0]); static const QString monthNames[] = { QStringLiteral("January"), QStringLiteral("February"), QStringLiteral("March"), QStringLiteral("April"), QStringLiteral("May"), QStringLiteral("June"), QStringLiteral("July"), QStringLiteral("August"), QStringLiteral("September"), QStringLiteral("October"), QStringLiteral("November"), QStringLiteral("December") }; Q_CONSTEXPR unsigned monthNameCount = sizeof(monthNames) / sizeof(monthNames[0]); EwsRecurrence::EwsRecurrence() : Recurrence() { } EwsRecurrence::EwsRecurrence(QXmlStreamReader &reader) { while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return; } if (elmName == QStringLiteral("RelativeYearlyRecurrence")) { if (!readRelativeYearlyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("AbsoluteYearlyRecurrence")) { if (!readAbsoluteYearlyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("RelativeMonthlyRecurrence")) { if (!readRelativeMonthlyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("AbsoluteMonthlyRecurrence")) { if (!readAbsoluteMonthlyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("WeeklyRecurrence")) { if (!readWeeklyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("DailyRecurrence")) { if (!readWeeklyRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("NoEndRecurrence")) { // Ignore - this is the default reader.skipCurrentElement(); } else if (elmName == QStringLiteral("EndDateRecurrence")) { if (!readEndDateRecurrence(reader)) { return; } } else if (elmName == QStringLiteral("NumberedRecurrence")) { if (!readNumberedRecurrence(reader)) { return; } } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("Recurrence")).arg(elmName); + .arg(QStringLiteral("Recurrence")).arg(elmName); return; } } } EwsRecurrence::EwsRecurrence(const EwsRecurrence &other) : Recurrence(other) { - } EwsRecurrence &EwsRecurrence::operator=(const EwsRecurrence &other) { - *static_cast(this) = static_cast(other); + *static_cast(this) = static_cast(other); return *this; } bool EwsRecurrence::readRelativeYearlyRecurrence(QXmlStreamReader &reader) { QBitArray dow(7); short dowWeekIndex = 0; short month = 0; bool hasDow = false; bool hasDowWeekIndex = false; bool hasMonth = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("DaysOfWeek")) { if (!readDow(reader, dow)) { return false; } hasDow = true; } else if (elmName == QStringLiteral("DayOfWeekIndex")) { bool ok; QString text = reader.readElementText(); dowWeekIndex = decodeEnumString(text, dayOfWeekIndexNames, dayOfWeekIndexNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("DayOfWeekIndex").arg(text)); + .arg(QStringLiteral("DayOfWeekIndex").arg(text)); return false; } if (dowWeekIndex == 4) { // "Last" dowWeekIndex = -1; } hasDowWeekIndex = true; } else if (elmName == QStringLiteral("Month")) { bool ok; QString text = reader.readElementText(); month = decodeEnumString(text, monthNames, monthNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Month").arg(text)); + .arg(QStringLiteral("Month").arg(text)); return false; } hasMonth = true; } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("RelativeYearlyRecurrence")).arg(elmName); + .arg(QStringLiteral("RelativeYearlyRecurrence")).arg(elmName); return false; } } if (!hasMonth || !hasDow || !hasDowWeekIndex) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Month, DaysOfWeek and DayOfWeekIndex elements."); return false; } addYearlyMonth(month); addYearlyPos(dowWeekIndex, dow); return true; } bool EwsRecurrence::readAbsoluteYearlyRecurrence(QXmlStreamReader &reader) { short dom = 0; short month = 0; bool hasDom = false; bool hasMonth = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("DayOfMonth")) { bool ok; QString text = reader.readElementText(); dom = text.toShort(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("DayOfMonth").arg(text)); + .arg(QStringLiteral("DayOfMonth").arg(text)); return false; } hasDom = true; } else if (elmName == QStringLiteral("Month")) { bool ok; QString text = reader.readElementText(); month = decodeEnumString(text, monthNames, monthNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Month").arg(text)); + .arg(QStringLiteral("Month").arg(text)); return false; } hasMonth = true; } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.") - .arg(elmName); + .arg(elmName); return false; } } if (!hasDom || !hasMonth) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - need both month and dom values."); return false; } // "If for a particular month this value is larger than the number of days in the month, // the last day of the month is assumed for this property." QDate date(2000, month, 1); if (dom > date.daysInMonth()) { dom = -1; } addYearlyMonth(month); addYearlyDay(dom); return true; } bool EwsRecurrence::readRelativeMonthlyRecurrence(QXmlStreamReader &reader) { QBitArray dow(7); short dowWeekIndex = 0; int interval = 0; bool hasDow = false; bool hasDowWeekIndex = false; bool hasInterval = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("Interval")) { bool ok; QString text = reader.readElementText(); interval = text.toInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Interval").arg(text)); + .arg(QStringLiteral("Interval").arg(text)); return false; } hasInterval = true; } else if (elmName == QStringLiteral("DaysOfWeek")) { if (!readDow(reader, dow)) { return false; } hasDow = true; } else if (elmName == QStringLiteral("DayOfWeekIndex")) { bool ok; QString text = reader.readElementText(); dowWeekIndex = decodeEnumString(text, dayOfWeekIndexNames, dayOfWeekIndexNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("DayOfWeekIndex").arg(text)); + .arg(QStringLiteral("DayOfWeekIndex").arg(text)); return false; } if (dowWeekIndex == 4) { // "Last" dowWeekIndex = -1; } hasDowWeekIndex = true; } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - unknown element: %1.") - .arg(elmName); + .arg(elmName); return false; } } if (!hasInterval || !hasDow || !hasDowWeekIndex) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Interval, DaysOfWeek and DayOfWeekIndex elements."); return false; } addMonthlyPos(dowWeekIndex, dow); setFrequency(interval); return true; } bool EwsRecurrence::readAbsoluteMonthlyRecurrence(QXmlStreamReader &reader) { short dom = 0; int interval = 0; bool hasInterval = false; bool hasDom = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("Interval")) { bool ok; QString text = reader.readElementText(); interval = text.toInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Interval").arg(text)); + .arg(QStringLiteral("Interval").arg(text)); return false; } hasInterval = true; } else if (elmName == QStringLiteral("DayOfMonth")) { bool ok; QString text = reader.readElementText(); dom = text.toShort(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("DayOfMonth").arg(text)); + .arg(QStringLiteral("DayOfMonth").arg(text)); return false; } hasDom = true; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - unknown element: %1.") - .arg(elmName); + .arg(elmName); return false; } } if (!hasInterval || !hasDom) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected both Interval and DayOfMonth."); return false; } addMonthlyDate(dom); setFrequency(interval); return true; } bool EwsRecurrence::readWeeklyRecurrence(QXmlStreamReader &reader) { int interval = 1; QBitArray dow(7); int weekStart = 0; bool hasInterval = false; bool hasDow = false; bool hasWeekStart = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("Interval")) { bool ok; QString text = reader.readElementText(); interval = text.toInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Interval").arg(text)); + .arg(QStringLiteral("Interval").arg(text)); return false; } hasInterval = true; } else if (elmName == QStringLiteral("DaysOfWeek")) { if (!readDow(reader, dow)) { return false; } hasDow = true; } else if (elmName == QStringLiteral("FirstDayOfWeek")) { bool ok; QString text = reader.readElementText(); weekStart = decodeEnumString(text, dayOfWeekNames, dayOfWeekNameCount, &ok) + 1; if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("FirstDayOfWeek").arg(text)); + .arg(QStringLiteral("FirstDayOfWeek").arg(text)); return false; } hasWeekStart = true; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("WeeklyRecurrence")).arg(elmName); + .arg(QStringLiteral("WeeklyRecurrence")).arg(elmName); return false; } } if (!hasInterval || !hasDow || !hasWeekStart) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected all of Interval, DaysOfWeek and FirstDatOfWeek elements."); return false; } setWeekly(interval, dow, weekStart); return true; } bool EwsRecurrence::readDailyRecurrence(QXmlStreamReader &reader) { int interval = 1; bool hasInterval = false; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("Interval")) { bool ok; QString text = reader.readElementText(); interval = text.toInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("Interval").arg(text)); + .arg(QStringLiteral("Interval").arg(text)); return false; } hasInterval = true; } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.") - .arg(elmName); + .arg(elmName); return false; } } if (!hasInterval) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read Recurrence element - expected an Interval element."); return false; } setDaily(interval); return true; } bool EwsRecurrence::readEndDateRecurrence(QXmlStreamReader &reader) { QDate dateEnd; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("EndDate")) { QString text = reader.readElementText(); dateEnd = QDate::fromString(text, Qt::ISODate); if (reader.error() != QXmlStreamReader::NoError || !dateEnd.isValid()) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("EndDate").arg(text)); + .arg(QStringLiteral("EndDate").arg(text)); return false; } } else if (elmName == QStringLiteral("StartDate")) { // Don't care reader.skipCurrentElement(); } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown element: %2.") - .arg(QStringLiteral("EndDateRecurrence")).arg(elmName); + .arg(QStringLiteral("EndDateRecurrence")).arg(elmName); return false; } } setEndDate(dateEnd); return true; } bool EwsRecurrence::readNumberedRecurrence(QXmlStreamReader &reader) { int numOccurrences = 0; while (reader.readNextStartElement()) { QString elmName = reader.name().toString(); if (reader.namespaceUri() != ewsTypeNsUri) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in %1 element:").arg(elmName) << reader.namespaceUri(); return false; } if (elmName == QStringLiteral("NumberOfOccurrences")) { bool ok; QString text = reader.readElementText(); numOccurrences = text.toInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("NumberOfOccurrences").arg(text)); + .arg(QStringLiteral("NumberOfOccurrences").arg(text)); return false; } } else if (elmName == QStringLiteral("StartDate")) { // Don't care reader.skipCurrentElement(); } else { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read recurrence element - unknown element: %1.") - .arg(elmName); + .arg(elmName); return false; } } setDuration(numOccurrences); return true; } bool EwsRecurrence::readDow(QXmlStreamReader &reader, QBitArray &dow) { bool ok; QString text = reader.readElementText(); const QStringList days = text.split(QLatin1Char(' ')); for (const QString &day : days) { short dowIndex = decodeEnumString(day, dayOfWeekNames, dayOfWeekNameCount, &ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - invalid %1 element (value: %2).") - .arg(QStringLiteral("DaysOfWeek").arg(day)); + .arg(QStringLiteral("DaysOfWeek").arg(day)); return false; } if (dowIndex == 7) { // "Day" dow.fill(true, 0, 7); } else if (dowIndex == 8) { // "Weekday" dow.fill(true, 0, 5); } else if (dowIndex == 9) { // "WeekendDay" dow.fill(true, 5, 7); } else { dow.setBit(dowIndex); } } return true; } - diff --git a/resources/ews/ewsclient/ewsrequest.cpp b/resources/ews/ewsclient/ewsrequest.cpp index 4d68be399..c1f55cdd7 100644 --- a/resources/ews/ewsclient/ewsrequest.cpp +++ b/resources/ews/ewsclient/ewsrequest.cpp @@ -1,372 +1,374 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 "ewsrequest.h" #include #include "ewsclient.h" #include "ewsclient_debug.h" #include "ewsserverversion.h" #include "auth/ewsabstractauth.h" EwsRequest::EwsRequest(EwsClient &client, QObject *parent) - : EwsJob(parent), mClient(client), mServerVersion(EwsServerVersion::ewsVersion2007Sp1) + : EwsJob(parent) + , mClient(client) + , mServerVersion(EwsServerVersion::ewsVersion2007Sp1) { } EwsRequest::~EwsRequest() { } void EwsRequest::doSend() { Q_FOREACH (KJob *job, subjobs()) { job->start(); } } void EwsRequest::startSoapDocument(QXmlStreamWriter &writer) { writer.setCodec("UTF-8"); writer.writeStartDocument(); writer.writeNamespace(soapEnvNsUri, QStringLiteral("soap")); writer.writeNamespace(ewsMsgNsUri, QStringLiteral("m")); writer.writeNamespace(ewsTypeNsUri, QStringLiteral("t")); // SOAP Envelope writer.writeStartElement(soapEnvNsUri, QStringLiteral("Envelope")); // SOAP Header writer.writeStartElement(soapEnvNsUri, QStringLiteral("Header")); mServerVersion.writeRequestServerVersion(writer); writer.writeEndElement(); // SOAP Body writer.writeStartElement(soapEnvNsUri, QStringLiteral("Body")); } void EwsRequest::endSoapDocument(QXmlStreamWriter &writer) { // End SOAP Body writer.writeEndElement(); // End SOAP Envelope writer.writeEndElement(); writer.writeEndDocument(); } void EwsRequest::prepare(const QString &body) { mBody = body; QString username, password; QStringList customHeaders; if (mClient.auth()) { if (!mClient.auth()->getAuthData(username, password, customHeaders)) { setErrorMsg(QStringLiteral("Failed to retrieve authentication data")); } } QUrl url = mClient.url(); url.setUserName(username); url.setPassword(password); KIO::TransferJob *job = KIO::http_post(url, body.toUtf8(), KIO::HideProgressInfo); job->addMetaData(QStringLiteral("content-type"), QStringLiteral("text/xml")); if (!mClient.userAgent().isEmpty()) { job->addMetaData(QStringLiteral("UserAgent"), mClient.userAgent()); } job->addMetaData(mMd); if (!customHeaders.isEmpty()) { job->addMetaData(QStringLiteral("customHTTPHeader"), customHeaders.join(QStringLiteral("\r\n"))); } job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); if (mClient.isNTLMv2Enabled()) { job->addMetaData(QStringLiteral("EnableNTLMv2Auth"), QStringLiteral("true")); } connect(job, &KIO::TransferJob::result, this, &EwsRequest::requestResult); connect(job, &KIO::TransferJob::data, this, &EwsRequest::requestData); addSubjob(job); } void EwsRequest::setMetaData(const KIO::MetaData &md) { mMd = md; } void EwsRequest::addMetaData(const QString &key, const QString &value) { mMd.insert(key, value); } void EwsRequest::requestResult(KJob *job) { if (EWSCLI_PROTO_LOG().isDebugEnabled()) { ewsLogDir.setAutoRemove(false); if (ewsLogDir.isValid()) { QTemporaryFile dumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmldump_XXXXXXX.xml")); dumpFile.open(); dumpFile.setAutoRemove(false); dumpFile.write(mResponseData.toUtf8()); qCDebug(EWSCLI_PROTO_LOG) << "response dumped to" << dumpFile.fileName(); dumpFile.close(); } } - KIO::TransferJob *trJob = qobject_cast(job); + KIO::TransferJob *trJob = qobject_cast(job); int resp = trJob->metaData()[QStringLiteral("responsecode")].toUInt(); if (resp == 401 && mClient.auth()) { mClient.auth()->notifyRequestAuthFailed(); } if (job->error() != 0) { setErrorMsg(QStringLiteral("Failed to process EWS request: ") + job->errorString(), job->error()); } /* Don't attempt to parse the response in case of a HTTP error. The only exception is * 500 (Bad Request) as in such case the server does provide the usual SOAP response. */ else if ((resp >= 300) && (resp != 500)) { setErrorMsg(QStringLiteral("Failed to process EWS request - HTTP code %1").arg(resp)); setError(resp); } else { QXmlStreamReader reader(mResponseData); readResponse(reader); } emitResult(); } bool EwsRequest::readResponse(QXmlStreamReader &reader) { if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request XML")); } if ((reader.name() != QStringLiteral("Envelope")) || (reader.namespaceUri() != soapEnvNsUri)) { return setErrorMsg(QStringLiteral("Failed to read EWS request - not a SOAP XML")); } while (reader.readNextStartElement()) { if (reader.namespaceUri() != soapEnvNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - not a SOAP XML")); } if (reader.name() == QStringLiteral("Body")) { - if (!readSoapBody(reader)) + if (!readSoapBody(reader)) { return false; + } } else if (reader.name() == QStringLiteral("Header")) { if (!readHeader(reader)) { return false; } } } return true; } bool EwsRequest::readSoapBody(QXmlStreamReader &reader) { while (reader.readNextStartElement()) { if ((reader.name() == QStringLiteral("Fault")) && (reader.namespaceUri() == soapEnvNsUri)) { return readSoapFault(reader); } if (!parseResult(reader)) { if (EWSCLI_FAILEDREQUEST_LOG().isDebugEnabled()) { dump(); } return false; } } return true; } bool EwsRequest::readSoapFault(QXmlStreamReader &reader) { QString faultCode; QString faultString; while (reader.readNextStartElement()) { if (reader.name() == QStringLiteral("faultcode")) { faultCode = reader.readElementText(); } else if (reader.name() == QStringLiteral("faultstring")) { faultString = reader.readElementText(); } } setErrorMsg(faultCode + QStringLiteral(": ") + faultString); if (EWSCLI_FAILEDREQUEST_LOG().isDebugEnabled()) { dump(); } return false; } void EwsRequest::requestData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); qCDebug(EWSCLI_PROTO_LOG) << "data" << job << data; mResponseData += QString::fromUtf8(data); } -bool EwsRequest::parseResponseMessage(QXmlStreamReader &reader, const QString &reqName, - ContentReaderFn contentReader) +bool EwsRequest::parseResponseMessage(QXmlStreamReader &reader, const QString &reqName, ContentReaderFn contentReader) { if (reader.name() != reqName + QStringLiteral("Response") || reader.namespaceUri() != ewsMsgNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element.") .arg(reqName + QStringLiteral("Response"))); } if (!reader.readNextStartElement()) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in %1 element.") .arg(reqName + QStringLiteral("Response"))); } if (reader.name() != QStringLiteral("ResponseMessages") || reader.namespaceUri() != ewsMsgNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element.") .arg(QStringLiteral("ResponseMessages"))); } while (reader.readNextStartElement()) { if (reader.name() != reqName + QStringLiteral("ResponseMessage") || reader.namespaceUri() != ewsMsgNsUri) { return setErrorMsg(QStringLiteral("Failed to read EWS request - expected %1 element.") .arg(reqName + QStringLiteral("ResponseMessage"))); } if (!contentReader(reader)) { return false; } } return true; } void EwsRequest::setServerVersion(const EwsServerVersion &version) { mServerVersion = version; } EwsRequest::Response::Response(QXmlStreamReader &reader) { static const QString respClasses[] = { QStringLiteral("Success"), QStringLiteral("Warning"), QStringLiteral("Error") }; QStringRef respClassRef = reader.attributes().value(QStringLiteral("ResponseClass")); if (respClassRef.isNull()) { mClass = EwsResponseParseError; qCWarning(EWSCLI_LOG) << "ResponseClass attribute not found in response element"; return; } unsigned i; for (i = 0; i < sizeof(respClasses) / sizeof(respClasses[0]); ++i) { if (respClassRef == respClasses[i]) { mClass = static_cast(i); break; } } } bool EwsRequest::Response::readResponseElement(QXmlStreamReader &reader) { if (reader.namespaceUri() != ewsMsgNsUri) { return false; } if (reader.name() == QStringLiteral("ResponseCode")) { mCode = reader.readElementText(); } else if (reader.name() == QStringLiteral("MessageText")) { mMessage = reader.readElementText(); } else if (reader.name() == QStringLiteral("DescriptiveLinkKey")) { reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("MessageXml")) { reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("ErrorSubscriptionIds")) { reader.skipCurrentElement(); } else { return false; } return true; } bool EwsRequest::readHeader(QXmlStreamReader &reader) { while (reader.readNextStartElement()) { if (reader.name() == QStringLiteral("ServerVersionInfo") && reader.namespaceUri() == ewsTypeNsUri) { EwsServerVersion version(reader); if (!version.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read EWS request - error parsing server version."); return false; } mServerVersion = version; mClient.setServerVersion(version); reader.skipCurrentElement(); } else { reader.skipCurrentElement(); } } return true; } bool EwsRequest::Response::setErrorMsg(const QString &msg) { mClass = EwsResponseParseError; mCode = QStringLiteral("ResponseParseError"); mMessage = msg; qCWarningNC(EWSCLI_LOG) << msg; return false; } void EwsRequest::dump() const { ewsLogDir.setAutoRemove(false); if (ewsLogDir.isValid()) { QTemporaryFile reqDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlreqdump_XXXXXXX.xml")); reqDumpFile.open(); reqDumpFile.setAutoRemove(false); reqDumpFile.write(mBody.toUtf8()); reqDumpFile.close(); QTemporaryFile resDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlresdump_XXXXXXX.xml")); resDumpFile.open(); resDumpFile.setAutoRemove(false); resDumpFile.write(mResponseData.toUtf8()); resDumpFile.close(); qCDebug(EWSCLI_LOG) << "request dumped to" << reqDumpFile.fileName(); qCDebug(EWSCLI_LOG) << "response dumped to" << resDumpFile.fileName(); } else { qCWarning(EWSCLI_LOG) << "failed to dump request and response"; } } diff --git a/resources/ews/ewsclient/ewsrequest.h b/resources/ews/ewsclient/ewsrequest.h index e4345b953..caed2724e 100644 --- a/resources/ews/ewsclient/ewsrequest.h +++ b/resources/ews/ewsclient/ewsrequest.h @@ -1,115 +1,118 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 EWSREQUEST_H #define EWSREQUEST_H #include #include #include #include #include #include "ewsclient.h" #include "ewsjob.h" #include "ewsserverversion.h" #include "ewstypes.h" class QWidget; class EwsRequest : public EwsJob { Q_OBJECT public: class Response { public: EwsResponseClass responseClass() const { return mClass; } + bool isSuccess() const { return mClass == EwsResponseSuccess; } + QString responseCode() const { return mCode; } + QString responseMessage() const { return mMessage; } + protected: Response(QXmlStreamReader &reader); bool readResponseElement(QXmlStreamReader &reader); bool setErrorMsg(const QString &msg); EwsResponseClass mClass; QString mCode; QString mMessage; }; EwsRequest(EwsClient &client, QObject *parent); ~EwsRequest() override; void setMetaData(const KIO::MetaData &md); void addMetaData(const QString &key, const QString &value); void setServerVersion(const EwsServerVersion &version); const EwsServerVersion &serverVersion() const { return mServerVersion; } void dump() const; protected: - typedef std::function ContentReaderFn; + typedef std::function ContentReaderFn; void doSend(); void prepare(const QString &body); virtual bool parseResult(QXmlStreamReader &reader) = 0; void startSoapDocument(QXmlStreamWriter &writer); void endSoapDocument(QXmlStreamWriter &writer); - bool parseResponseMessage(QXmlStreamReader &reader, const QString &reqName, - ContentReaderFn contentReader); + bool parseResponseMessage(QXmlStreamReader &reader, const QString &reqName, ContentReaderFn contentReader); bool readResponse(QXmlStreamReader &reader); KIO::MetaData mMd; QString mResponseData; protected Q_SLOTS: void requestResult(KJob *job); virtual void requestData(KIO::Job *job, const QByteArray &data); private: bool readSoapBody(QXmlStreamReader &reader); bool readSoapFault(QXmlStreamReader &reader); bool readHeader(QXmlStreamReader &reader); bool readResponseAttr(const QXmlStreamAttributes &attrs, EwsResponseClass &responseClass); QString getOAuthToken(); QString mBody; EwsClient &mClient; EwsServerVersion mServerVersion; QWidget *mParentWindow; }; #endif diff --git a/resources/ews/ewsclient/ewsserverversion.cpp b/resources/ews/ewsclient/ewsserverversion.cpp index fd917d2b3..8a4a964b4 100644 --- a/resources/ews/ewsclient/ewsserverversion.cpp +++ b/resources/ews/ewsclient/ewsserverversion.cpp @@ -1,157 +1,158 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsserverversion.h" #include #include #include "ewsclient_debug.h" #include "ewstypes.h" const EwsServerVersion EwsServerVersion::ewsVersion2007(8, 0, QStringLiteral("Exchange2007"), - QStringLiteral("Exchange 2007")); + QStringLiteral("Exchange 2007")); const EwsServerVersion EwsServerVersion::ewsVersion2007Sp1(8, 1, QStringLiteral("Exchange2007_SP1"), - QStringLiteral("Exchange 2007 SP1")); + QStringLiteral("Exchange 2007 SP1")); const EwsServerVersion EwsServerVersion::ewsVersion2007Sp2(8, 2, QStringLiteral("Exchange2007_SP2"), - QStringLiteral("Exchange 2007 SP2")); + QStringLiteral("Exchange 2007 SP2")); const EwsServerVersion EwsServerVersion::ewsVersion2007Sp3(8, 3, QStringLiteral("Exchange2007_SP3"), - QStringLiteral("Exchange 2007 SP3")); + QStringLiteral("Exchange 2007 SP3")); const EwsServerVersion EwsServerVersion::ewsVersion2010(14, 0, QStringLiteral("Exchange2010"), - QStringLiteral("Exchange 2010")); + QStringLiteral("Exchange 2010")); const EwsServerVersion EwsServerVersion::ewsVersion2010Sp1(14, 1, QStringLiteral("Exchange2010_SP1"), - QStringLiteral("Exchange 2010 SP1")); + QStringLiteral("Exchange 2010 SP1")); const EwsServerVersion EwsServerVersion::ewsVersion2010Sp2(14, 2, QStringLiteral("Exchange2010_SP2"), - QStringLiteral("Exchange 2010 SP2")); + QStringLiteral("Exchange 2010 SP2")); const EwsServerVersion EwsServerVersion::ewsVersion2010Sp3(14, 3, QStringLiteral("Exchange2010_SP3"), - QStringLiteral("Exchange 2010 SP3")); + QStringLiteral("Exchange 2010 SP3")); const EwsServerVersion EwsServerVersion::ewsVersion2013(15, 0, QStringLiteral("Exchange2013"), - QStringLiteral("Exchange 2013")); + QStringLiteral("Exchange 2013")); const EwsServerVersion EwsServerVersion::ewsVersion2016(15, 1, QStringLiteral("Exchange2016"), - QStringLiteral("Exchange 2016")); + QStringLiteral("Exchange 2016")); static const EwsServerVersion ewsNullVersion; EwsServerVersion::EwsServerVersion(QXmlStreamReader &reader) - : mMajor(0), mMinor(0) + : mMajor(0) + , mMinor(0) { // QXmlStreamAttributes attrs = reader.attributes(); QStringRef majorRef = attrs.value(QStringLiteral("MajorVersion")); QStringRef minorRef = attrs.value(QStringLiteral("MinorVersion")); QStringRef majorBuildRef = attrs.value(QStringLiteral("MajorBuildNumber")); QStringRef minorBuildRef = attrs.value(QStringLiteral("MinorBuildNumber")); QStringRef nameRef = attrs.value(QStringLiteral("Version")); if (majorRef.isNull() || minorRef.isNull()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading server version info - missing attribute."); return; } bool ok; uint majorVer = majorRef.toUInt(&ok); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading server version info - invalid major version number."); return; } uint minorVer = minorRef.toUInt(&ok); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading server version info - invalid minor version number."); return; } mMajor = majorVer; mMinor = minorVer; mMajorBuild = majorBuildRef.toUInt(); mMinorBuild = minorBuildRef.toUInt(); mName = nameRef.toString(); } void EwsServerVersion::writeRequestServerVersion(QXmlStreamWriter &writer) const { writer.writeStartElement(ewsTypeNsUri, QStringLiteral("RequestServerVersion")); writer.writeAttribute(QStringLiteral("Version"), mName); writer.writeEndElement(); } bool EwsServerVersion::supports(ServerFeature feature) const { const EwsServerVersion &minVer = minSupporting(feature); if (minVer.isValid()) { return *this >= minVer; } else { return false; } } const EwsServerVersion &EwsServerVersion::minSupporting(ServerFeature feature) { switch (feature) { case StreamingSubscription: case FreeBusyChangedEvent: return ewsVersion2010Sp1; default: return ewsNullVersion; } } QString EwsServerVersion::toString() const { static const QVector knownVersions = { ewsVersion2007, ewsVersion2007Sp1, ewsVersion2007Sp2, ewsVersion2007Sp3, ewsVersion2010, ewsVersion2010Sp1, ewsVersion2010Sp2, ewsVersion2010Sp3, ewsVersion2013, ewsVersion2016 }; QString version(QStringLiteral("%1.%2").arg(mMajor).arg(mMinor)); if (mMajorBuild + mMinorBuild > 0) { version.append(QStringLiteral(".%1.%2").arg(mMajorBuild).arg(mMinorBuild)); } Q_FOREACH (const EwsServerVersion &ver, knownVersions) { if (*this == ver) { version.append(QStringLiteral(" (") + ver.mFriendlyName + QStringLiteral(")")); } } return version; } QDebug operator<<(QDebug debug, const EwsServerVersion &version) { QDebugStateSaver saver(debug); QDebug d = debug.nospace().noquote(); d << QStringLiteral("EwsServerVersion("); if (version.isValid()) { d << version.majorVersion() << QStringLiteral(", ") << version.minorVersion() << QStringLiteral(", ") << version.name(); } d << QStringLiteral(")"); return debug; } diff --git a/resources/ews/ewsclient/ewsserverversion.h b/resources/ews/ewsclient/ewsserverversion.h index bc6f94388..6375bfe0b 100644 --- a/resources/ews/ewsclient/ewsserverversion.h +++ b/resources/ews/ewsclient/ewsserverversion.h @@ -1,136 +1,155 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSSERVERVERSION_H #define EWSSERVERVERSION_H #include class QXmlStreamReader; class QXmlStreamWriter; class EwsServerVersion { public: enum ServerFeature { StreamingSubscription, FreeBusyChangedEvent, }; - EwsServerVersion() : mMajor(0), mMinor(0), mMajorBuild(0), mMinorBuild(0) {} + EwsServerVersion() : mMajor(0) + , mMinor(0) + , mMajorBuild(0) + , mMinorBuild(0) + { + } + EwsServerVersion(uint major, uint minor, const QString &name, const QString &friendlyName) - : mMajor(major), mMinor(minor), mMajorBuild(0), mMinorBuild(0), mName(name), - mFriendlyName(friendlyName) {} + : mMajor(major) + , mMinor(minor) + , mMajorBuild(0) + , mMinorBuild(0) + , mName(name) + , mFriendlyName(friendlyName) + { + } + explicit EwsServerVersion(QXmlStreamReader &reader); EwsServerVersion(const EwsServerVersion &other) - : mMajor(other.mMajor), mMinor(other.mMinor), mMajorBuild(other.mMajorBuild), - mMinorBuild(other.mMinorBuild), mName(other.mName), mFriendlyName(other.mFriendlyName) {} + : mMajor(other.mMajor) + , mMinor(other.mMinor) + , mMajorBuild(other.mMajorBuild) + , mMinorBuild(other.mMinorBuild) + , mName(other.mName) + , mFriendlyName(other.mFriendlyName) + { + } EwsServerVersion &operator=(const EwsServerVersion &other) { mMajor = other.mMajor; mMinor = other.mMinor; mMajorBuild = other.mMajorBuild; mMinorBuild = other.mMinorBuild; mName = other.mName; mFriendlyName = other.mFriendlyName; return *this; } void writeRequestServerVersion(QXmlStreamWriter &writer) const; bool operator>(const EwsServerVersion &other) const { return (mMajor > other.mMajor) ? true : ((mMinor > other.mMinor) ? true : false); } bool operator<(const EwsServerVersion &other) const { return (mMajor < other.mMajor) ? true : ((mMinor < other.mMinor) ? true : false); } bool operator>=(const EwsServerVersion &other) const { return !(*this < other); } bool operator<=(const EwsServerVersion &other) const { return !(*this > other); } bool operator==(const EwsServerVersion &other) const { return (mMajor == other.mMajor) && (mMinor == other.mMinor); } bool operator!=(const EwsServerVersion &other) const { return !(*this == other); } bool supports(ServerFeature feature) const; bool isValid() const { return mMajor != 0; } uint majorVersion() const { return mMajor; } uint minorVersion() const { return mMinor; } QString name() const { return mName; } static const EwsServerVersion &minSupporting(ServerFeature feature); QString toString() const; static const EwsServerVersion ewsVersion2007; static const EwsServerVersion ewsVersion2007Sp1; static const EwsServerVersion ewsVersion2007Sp2; static const EwsServerVersion ewsVersion2007Sp3; static const EwsServerVersion ewsVersion2010; static const EwsServerVersion ewsVersion2010Sp1; static const EwsServerVersion ewsVersion2010Sp2; static const EwsServerVersion ewsVersion2010Sp3; static const EwsServerVersion ewsVersion2013; static const EwsServerVersion ewsVersion2016; private: uint mMajor; uint mMinor; uint mMajorBuild; uint mMinorBuild; QString mName; QString mFriendlyName; }; QDebug operator<<(const QDebug dbg, const EwsServerVersion &version); #endif diff --git a/resources/ews/ewsclient/ewssubscriberequest.cpp b/resources/ews/ewsclient/ewssubscriberequest.cpp index 3cca3ac4b..922e1e029 100644 --- a/resources/ews/ewsclient/ewssubscriberequest.cpp +++ b/resources/ews/ewsclient/ewssubscriberequest.cpp @@ -1,159 +1,164 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssubscriberequest.h" #include #include "ewsclient_debug.h" static const QVector subscribeTypeNames = { QStringLiteral("PullSubscriptionRequest"), QStringLiteral("PushSubscriptionRequest"), QStringLiteral("StreamingSubscriptionRequest") }; static const QVector eventTypeNames = { QStringLiteral("CopiedEvent"), QStringLiteral("CreatedEvent"), QStringLiteral("DeletedEvent"), QStringLiteral("ModifiedEvent"), QStringLiteral("MovedEvent"), QStringLiteral("NewMailEvent"), QStringLiteral("FreeBusyChangedEvent") }; EwsSubscribeRequest::EwsSubscribeRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mType(PullSubscription), mAllFolders(false), mTimeout(30) + : EwsRequest(client, parent) + , mType(PullSubscription) + , mAllFolders(false) + , mTimeout(30) { } EwsSubscribeRequest::~EwsSubscribeRequest() { } void EwsSubscribeRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); if (mType == StreamingSubscription && !serverVersion().supports(EwsServerVersion::StreamingSubscription)) { setServerVersion(EwsServerVersion::minSupporting(EwsServerVersion::StreamingSubscription)); } if (mEventTypes.contains(EwsFreeBusyChangedEvent) && !serverVersion().supports(EwsServerVersion::FreeBusyChangedEvent)) { setServerVersion(EwsServerVersion::minSupporting(EwsServerVersion::FreeBusyChangedEvent)); } startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("Subscribe")); writer.writeStartElement(ewsMsgNsUri, subscribeTypeNames[mType]); if (mAllFolders) { writer.writeAttribute(QStringLiteral("SubscribeToAllFolders"), QStringLiteral("true")); } writer.writeStartElement(ewsTypeNsUri, QStringLiteral("FolderIds")); Q_FOREACH (const EwsId &id, mFolderIds) { id.writeFolderIds(writer); } writer.writeEndElement(); writer.writeStartElement(ewsTypeNsUri, QStringLiteral("EventTypes")); Q_FOREACH (const EwsEventType type, mEventTypes) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("EventType"), eventTypeNames[type]); } writer.writeEndElement(); // EventTypes if (mType == PullSubscription) { if (!mWatermark.isNull()) { writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Watermark"), mWatermark); } writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Timeout"), QString::number(mTimeout)); } writer.writeEndElement(); // XxxSubscriptionRequest writer.writeEndElement(); // Subscribe endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsSubscribeRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("Subscribe"), - [this](QXmlStreamReader &reader) {return parseSubscribeResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseSubscribeResponse(reader); + }); } bool EwsSubscribeRequest::parseSubscribeResponse(QXmlStreamReader &reader) { QSharedPointer resp(new Response(reader)); if (resp->responseClass() == EwsResponseUnknown) { return false; } mResponse = resp; return true; } EwsSubscribeRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("SubscriptionId")) { mId = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.") .arg(QStringLiteral("SubscriptionId"))); return; } } else if (reader.name() == QStringLiteral("Watermark")) { mWatermark = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.") .arg(QStringLiteral("Watermark"))); return; } } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewssubscriberequest.h b/resources/ews/ewsclient/ewssubscriberequest.h index 76336da47..7d70585fe 100644 --- a/resources/ews/ewsclient/ewssubscriberequest.h +++ b/resources/ews/ewsclient/ewssubscriberequest.h @@ -1,116 +1,116 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSSUBSCRIBEREQUEST_H #define EWSSUBSCRIBEREQUEST_H #include #include "ewsid.h" #include "ewsrequest.h" #include "ewstypes.h" - class EwsSubscribeRequest : public EwsRequest { Q_OBJECT public: enum Type { PullSubscription = 0, PushSubscription, StreamingSubscription }; class Response : public EwsRequest::Response { public: const QString &subscriptionId() const { return mId; } const QString &watermark() const { return mWatermark; } + protected: Response(QXmlStreamReader &reader); QString mId; QString mWatermark; friend class EwsSubscribeRequest; }; EwsSubscribeRequest(EwsClient &client, QObject *parent); ~EwsSubscribeRequest() override; void setType(Type t) { mType = t; } void setFolderIds(const EwsId::List &folders) { mFolderIds = folders; } void setAllFolders(bool allFolders) { mAllFolders = allFolders; } void setEventTypes(const QList &types) { mEventTypes = types; } void setWatermark(const QString &watermark) { mWatermark = watermark; } void setTimeout(uint timeout) { mTimeout = timeout; } const Response &response() const { return *mResponse; } void start() override; protected: bool parseResult(QXmlStreamReader &reader) override; bool parseSubscribeResponse(QXmlStreamReader &reader); private: //QSharedPointer mSubscription; Type mType; EwsId::List mFolderIds; QList mEventTypes; bool mAllFolders; QString mWatermark; uint mTimeout; QSharedPointer mResponse; }; #endif diff --git a/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.cpp b/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.cpp index 6798daf48..1848d9327 100644 --- a/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.cpp +++ b/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.cpp @@ -1,239 +1,241 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssyncfolderhierarchyrequest.h" #include #include #include "ewsclient_debug.h" #include "ewsxml.h" enum SyncFolderHierarchyResponseElementType { SyncState, IncludesLastFolderInRange, Changes }; enum SyncFolderHierarchyChangeElementType { Folder, FolderId, IsRead }; class EwsSyncFolderHierarchyRequest::Response : public EwsRequest::Response { public: Response(QXmlStreamReader &reader); static bool changeReader(QXmlStreamReader &reader, QVariant &val); EwsSyncFolderHierarchyRequest::Change::List mChanges; bool mIncludesLastFolder; QString mSyncState; }; EwsSyncFolderHierarchyRequest::EwsSyncFolderHierarchyRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mIncludesLastItem(false) + : EwsRequest(client, parent) + , mIncludesLastItem(false) { qRegisterMetaType(); qRegisterMetaType(); } EwsSyncFolderHierarchyRequest::~EwsSyncFolderHierarchyRequest() { } void EwsSyncFolderHierarchyRequest::setFolderId(const EwsId &id) { mFolderId = id; } void EwsSyncFolderHierarchyRequest::setFolderShape(const EwsFolderShape &shape) { mShape = shape; } void EwsSyncFolderHierarchyRequest::setSyncState(const QString &state) { mSyncState = state; } void EwsSyncFolderHierarchyRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderHierarchy")); mShape.write(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderId")); mFolderId.writeFolderIds(writer); writer.writeEndElement(); if (!mSyncState.isNull()) { writer.writeTextElement(ewsMsgNsUri, QStringLiteral("SyncState"), mSyncState); } writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { QString st = mSyncState.isNull() ? QStringLiteral("none") : QString::number(qHash(mSyncState), 36); qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting SyncFolderHierarchy request (folder: ") << mFolderId << QStringLiteral(", state: %1").arg(st); } prepare(reqString); doSend(); } bool EwsSyncFolderHierarchyRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("SyncFolderHierarchy"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsSyncFolderHierarchyRequest::parseItemsResponse(QXmlStreamReader &reader) { EwsSyncFolderHierarchyRequest::Response *resp = new EwsSyncFolderHierarchyRequest::Response(reader); if (resp->responseClass() == EwsResponseUnknown) { return false; } mChanges = resp->mChanges; mSyncState = resp->mSyncState; mIncludesLastItem = resp->mIncludesLastFolder; if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp->isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderHierarchy response (%1 changes, state: %3)") - .arg(mChanges.size()).arg(qHash(mSyncState), 0, 36); + .arg(mChanges.size()).arg(qHash(mSyncState), 0, 36); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderHierarchy response - %1") - .arg(resp->responseMessage()); + .arg(resp->responseMessage()); } } return true; } EwsSyncFolderHierarchyRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } static const QVector::Item> items = { {SyncState, QStringLiteral("SyncState"), &ewsXmlTextReader}, {IncludesLastFolderInRange, QStringLiteral("IncludesLastFolderInRange"), &ewsXmlBoolReader}, {Changes, QStringLiteral("Changes"), &EwsSyncFolderHierarchyRequest::Response::changeReader}, }; static const EwsXml staticReader(items); EwsXml ewsReader(staticReader); if (!ewsReader.readItems(reader, ewsMsgNsUri, - [this](QXmlStreamReader &reader, const QString &) { - if (!readResponseElement(reader)) { - setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); - return false; - } - return true; - })) - { + [this](QXmlStreamReader &reader, const QString &) { + if (!readResponseElement(reader)) { + setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); + return false; + } + return true; + })) { mClass = EwsResponseParseError; return; } QHash values = ewsReader.values(); mSyncState = values[SyncState].toString(); mIncludesLastFolder = values[IncludesLastFolderInRange].toBool(); mChanges = values[Changes].value(); } bool EwsSyncFolderHierarchyRequest::Response::changeReader(QXmlStreamReader &reader, QVariant &val) { Change::List changes; QString elmName(reader.name().toString()); while (reader.readNextStartElement()) { Change change(reader); if (!change.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element").arg(elmName); return false; } changes.append(change); } val = QVariant::fromValue(changes); return true; } EwsSyncFolderHierarchyRequest::Change::Change(QXmlStreamReader &reader) { static const QVector::Item> items = { {Folder, QStringLiteral("Folder"), &ewsXmlFolderReader}, {Folder, QStringLiteral("CalendarFolder"), &ewsXmlFolderReader}, {Folder, QStringLiteral("ContactsFolder"), &ewsXmlFolderReader}, {Folder, QStringLiteral("SearchFolder"), &ewsXmlFolderReader}, {Folder, QStringLiteral("TasksFolder"), &ewsXmlFolderReader}, {FolderId, QStringLiteral("FolderId"), &ewsXmlIdReader}, {IsRead, QStringLiteral("IsRead"), &ewsXmlBoolReader} }; static const EwsXml staticReader(items); EwsXml ewsReader(staticReader); if (reader.name() == QStringLiteral("Create")) { mType = Create; } else if (reader.name() == QStringLiteral("Update")) { mType = Update; } else if (reader.name() == QStringLiteral("Delete")) { mType = Delete; } if (!ewsReader.readItems(reader, ewsTypeNsUri)) { return; } QHash values = ewsReader.values(); switch (mType) { case Create: case Update: mFolder = values[Folder].value(); mId = mFolder[EwsFolderFieldFolderId].value(); break; case Delete: mId = values[FolderId].value(); break; default: break; } } diff --git a/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.h b/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.h index eec2d87ee..992643980 100644 --- a/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.h +++ b/resources/ews/ewsclient/ewssyncfolderhierarchyrequest.h @@ -1,108 +1,113 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSSYNCFOLDERHIERARCHYREQUEST_H #define EWSSYNCFOLDERHIERARCHYREQUEST_H #include "ewsfolder.h" #include "ewsrequest.h" #include "ewstypes.h" #include "ewsfoldershape.h" class EwsSyncFolderHierarchyRequest : public EwsRequest { Q_OBJECT public: enum ChangeType { Create, Update, Delete, Unknown }; class Response; class Change { public: typedef QList List; ChangeType type() const { return mType; } + const EwsId &folderId() const { return mId; } + const EwsFolder &folder() const { return mFolder; } + protected: Change(QXmlStreamReader &reader); bool isValid() const { return mType != Unknown; } ChangeType mType; EwsId mId; EwsFolder mFolder; friend class Response; }; EwsSyncFolderHierarchyRequest(EwsClient &client, QObject *parent); ~EwsSyncFolderHierarchyRequest() override; void setFolderId(const EwsId &id); void setFolderShape(const EwsFolderShape &shape); void setSyncState(const QString &state); void start() override; bool includesLastItem() const { return mIncludesLastItem; } const Change::List &changes() const { return mChanges; } + const QString &syncState() const { return mSyncState; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId mFolderId; EwsFolderShape mShape; QString mSyncState; Change::List mChanges; bool mIncludesLastItem; }; Q_DECLARE_METATYPE(EwsSyncFolderHierarchyRequest::Change::List) #endif diff --git a/resources/ews/ewsclient/ewssyncfolderitemsrequest.cpp b/resources/ews/ewsclient/ewssyncfolderitemsrequest.cpp index c779a8059..e10648264 100644 --- a/resources/ews/ewsclient/ewssyncfolderitemsrequest.cpp +++ b/resources/ews/ewsclient/ewssyncfolderitemsrequest.cpp @@ -1,259 +1,262 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssyncfolderitemsrequest.h" #include #include #include "ewsclient_debug.h" #include "ewsxml.h" enum SyncFolderItemsResponseElementType { SyncState, IncludesLastItemInRange, Changes }; enum SyncFolderItemsChangeElementType { Item, ItemId, IsRead }; class EwsSyncFolderItemsRequest::Response : public EwsRequest::Response { public: Response(QXmlStreamReader &reader); static bool changeReader(QXmlStreamReader &reader, QVariant &val); EwsSyncFolderItemsRequest::Change::List mChanges; bool mIncludesLastItem; QString mSyncState; }; EwsSyncFolderItemsRequest::EwsSyncFolderItemsRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mMaxChanges(100), mIncludesLastItem(false) + : EwsRequest(client, parent) + , mMaxChanges(100) + , mIncludesLastItem(false) { qRegisterMetaType(); qRegisterMetaType(); } EwsSyncFolderItemsRequest::~EwsSyncFolderItemsRequest() { } void EwsSyncFolderItemsRequest::setFolderId(const EwsId &id) { mFolderId = id; } void EwsSyncFolderItemsRequest::setItemShape(const EwsItemShape &shape) { mShape = shape; } void EwsSyncFolderItemsRequest::setSyncState(const QString &state) { mSyncState = state; } void EwsSyncFolderItemsRequest::setMaxChanges(uint max) { mMaxChanges = max; } void EwsSyncFolderItemsRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderItems")); mShape.write(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderId")); mFolderId.writeFolderIds(writer); writer.writeEndElement(); if (!mSyncState.isNull()) { writer.writeTextElement(ewsMsgNsUri, QStringLiteral("SyncState"), mSyncState); } writer.writeTextElement(ewsMsgNsUri, QStringLiteral("MaxChangesReturned"), QString::number(mMaxChanges)); writer.writeEndElement(); endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { QString st = mSyncState.isNull() ? QStringLiteral("none") : ewsHash(mSyncState); qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting SyncFolderItems request (folder: ") << mFolderId << QStringLiteral(", state: %1").arg(st); } prepare(reqString); doSend(); } bool EwsSyncFolderItemsRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("SyncFolderItems"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsSyncFolderItemsRequest::parseItemsResponse(QXmlStreamReader &reader) { EwsSyncFolderItemsRequest::Response *resp = new EwsSyncFolderItemsRequest::Response(reader); if (resp->responseClass() == EwsResponseUnknown) { return false; } mChanges = resp->mChanges; mIncludesLastItem = resp->mIncludesLastItem; mSyncState = resp->mSyncState; if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp->isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderItems response (%1 changes, last included: %2, state: %3)") - .arg(mChanges.size()).arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false")) - .arg(qHash(mSyncState), 0, 36); + .arg(mChanges.size()).arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false")) + .arg(qHash(mSyncState), 0, 36); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderItems response - %1") - .arg(resp->responseMessage()); + .arg(resp->responseMessage()); } } return true; } EwsSyncFolderItemsRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } static const QVector::Item> items = { {SyncState, QStringLiteral("SyncState"), &ewsXmlTextReader}, {IncludesLastItemInRange, QStringLiteral("IncludesLastItemInRange"), &ewsXmlBoolReader}, {Changes, QStringLiteral("Changes"), &EwsSyncFolderItemsRequest::Response::changeReader}, }; static const EwsXml staticReader(items); EwsXml ewsReader(staticReader); if (!ewsReader.readItems(reader, ewsMsgNsUri, - [this](QXmlStreamReader &reader, const QString &) { - if (!readResponseElement(reader)) { - setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); - return false; - } - return true; - })) - { + [this](QXmlStreamReader &reader, const QString &) { + if (!readResponseElement(reader)) { + setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); + return false; + } + return true; + })) { mClass = EwsResponseParseError; return; } QHash values = ewsReader.values(); mSyncState = values[SyncState].toString(); mIncludesLastItem = values[IncludesLastItemInRange].toBool(); mChanges = values[Changes].value(); } bool EwsSyncFolderItemsRequest::Response::changeReader(QXmlStreamReader &reader, QVariant &val) { Change::List changes; QString elmName(reader.name().toString()); while (reader.readNextStartElement()) { Change change(reader); if (!change.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element").arg(elmName); return false; } changes.append(change); } val = QVariant::fromValue(changes); return true; } EwsSyncFolderItemsRequest::Change::Change(QXmlStreamReader &reader) { static const QVector::Item> items = { {Item, QStringLiteral("Item"), &ewsXmlItemReader}, {Item, QStringLiteral("Message"), &ewsXmlItemReader}, {Item, QStringLiteral("CalendarItem"), &ewsXmlItemReader}, {Item, QStringLiteral("Contact"), &ewsXmlItemReader}, {Item, QStringLiteral("DistributionList"), &ewsXmlItemReader}, {Item, QStringLiteral("MeetingMessage"), &ewsXmlItemReader}, {Item, QStringLiteral("MeetingRequest"), &ewsXmlItemReader}, {Item, QStringLiteral("MeetingResponse"), &ewsXmlItemReader}, {Item, QStringLiteral("MeetingCancellation"), &ewsXmlItemReader}, {Item, QStringLiteral("Task"), &ewsXmlItemReader}, {ItemId, QStringLiteral("ItemId"), &ewsXmlIdReader}, {IsRead, QStringLiteral("IsRead"), &ewsXmlBoolReader} }; static const EwsXml staticReader(items); EwsXml ewsReader(staticReader); if (reader.name() == QStringLiteral("Create")) { mType = Create; } else if (reader.name() == QStringLiteral("Update")) { mType = Update; } else if (reader.name() == QStringLiteral("Delete")) { mType = Delete; } else if (reader.name() == QStringLiteral("ReadFlagChange")) { mType = ReadFlagChange; } if (!ewsReader.readItems(reader, ewsTypeNsUri)) { return; } QHash values = ewsReader.values(); switch (mType) { case Create: case Update: mItem = values[Item].value(); mId = mItem[EwsItemFieldItemId].value(); break; case ReadFlagChange: mIsRead = values[IsRead].toBool(); - /* fall through */ + /* fall through */ Q_FALLTHROUGH(); case Delete: mId = values[ItemId].value(); break; default: break; } } diff --git a/resources/ews/ewsclient/ewssyncfolderitemsrequest.h b/resources/ews/ewsclient/ewssyncfolderitemsrequest.h index e08cec443..dfaf18464 100644 --- a/resources/ews/ewsclient/ewssyncfolderitemsrequest.h +++ b/resources/ews/ewsclient/ewssyncfolderitemsrequest.h @@ -1,116 +1,122 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSSYNCFOLDERITEMSREQUEST_H #define EWSSYNCFOLDERITEMSREQUEST_H #include "ewsitem.h" #include "ewsrequest.h" #include "ewstypes.h" #include "ewsitemshape.h" class EwsSyncFolderItemsRequest : public EwsRequest { Q_OBJECT public: enum ChangeType { Create, Update, Delete, ReadFlagChange, Unknown }; class Response; class Change { public: typedef QList List; ChangeType type() const { return mType; } + const EwsId &itemId() const { return mId; } + const EwsItem &item() const { return mItem; } + bool isRead() const { return mIsRead; } + protected: Change(QXmlStreamReader &reader); bool isValid() const { return mType != Unknown; } ChangeType mType; EwsId mId; EwsItem mItem; bool mIsRead; friend class Response; }; EwsSyncFolderItemsRequest(EwsClient &client, QObject *parent); ~EwsSyncFolderItemsRequest() override; void setFolderId(const EwsId &id); void setItemShape(const EwsItemShape &shape); void setSyncState(const QString &state); void setMaxChanges(uint max); void start() override; bool includesLastItem() const { return mIncludesLastItem; } const Change::List &changes() const { return mChanges; } + const QString &syncState() const { return mSyncState; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: EwsId mFolderId; EwsItemShape mShape; QString mSyncState; uint mMaxChanges; Change::List mChanges; bool mIncludesLastItem; }; Q_DECLARE_METATYPE(EwsSyncFolderItemsRequest::Change::List) #endif diff --git a/resources/ews/ewsclient/ewstypes.h b/resources/ews/ewsclient/ewstypes.h index 5ed5b10b4..363cb5490 100644 --- a/resources/ews/ewsclient/ewstypes.h +++ b/resources/ews/ewsclient/ewstypes.h @@ -1,524 +1,524 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSTYPES_H #define EWSTYPES_H #include #include extern const QString soapEnvNsUri; extern const QString ewsMsgNsUri; extern const QString ewsTypeNsUri; typedef enum { EwsFolderTypeMail = 0, EwsFolderTypeCalendar, EwsFolderTypeContacts, EwsFolderTypeSearch, EwsFolderTypeTasks, EwsFolderTypeUnknown, } EwsFolderType; typedef enum { EwsResponseSuccess = 0, EwsResponseWarning, EwsResponseError, EwsResponseParseError, // Internal - never returned by an Exchange server EwsResponseUnknown // Internal - never returned by an Exchange server } EwsResponseClass; typedef enum { EwsDIdCalendar = 0, EwsDIdContacts, EwsDIdDeletedItems, EwsDIdDrafts, EwsDIdInbox, EwsDIdJournal, EwsDIdNotes, EwsDIdOutbox, EwsDIdSentItems, EwsDIdTasks, EwsDIdMsgFolderRoot, EwsDIdRoot, EwsDIdJunkEmail, EwsDIdSearchFolders, EwsDIdVoiceMail, EwsDIdRecoverableItemsRoot, EwsDIdRecoverableItemsDeletions, EwsDIdRecoverableItemsVersions, EwsDIdRecoverableItemsPurges, EwsDIdArchiveRoot, EwsDIdArchiveMsgFolderRoot, EwsDIdArchiveDeletedItems, EwsDIdArchiveRecoverableItemsRoot, EwsDIdArchiveRecoverableItemsDeletions, EwsDIdArchiveRecoverableItemsVersions, EwsDIdArchiveRecoverableItemsPurges } EwsDistinguishedId; typedef enum { EwsShapeIdOnly = 0, EwsShapeDefault, EwsShapeAllProperties } EwsBaseShape; typedef enum { EwsPropSetMeeting = 0, EwsPropSetAppointment, EwsPropSetCommon, EwsPropSetPublicStrings, EwsPropSetAddress, EwsPropSetInternetHeaders, EwsPropSetCalendarAssistant, EwsPropSetUnifiedMessaging } EwsDistinguishedPropSetId; typedef enum { EwsPropTypeApplicationTime = 0, EwsPropTypeApplicationTimeArray, EwsPropTypeBinary, EwsPropTypeBinaryArray, EwsPropTypeBoolean, EwsPropTypeCLSID, EwsPropTypeCLSIDArray, EwsPropTypeCurrency, EwsPropTypeCurrencyArray, EwsPropTypeDouble, EwsPropTypeDoubleArray, EwsPropTypeError, EwsPropTypeFloat, EwsPropTypeFloatArray, EwsPropTypeInteger, EwsPropTypeTntegerArray, EwsPropTypeLong, EwsPropTypeLongArray, EwsPropTypeNull, EwsPropTypeObject, EwsPropTypeObjectArray, EwsPropTypeShort, EwsPropTypeShortArray, EwsPropTypeSystemTime, EwsPropTypeSystemTimeArray, EwsPropTypeString, EwsPropTypeStringArray } EwsPropertyType; typedef enum { EwsTraversalShallow = 0, EwsTraversalDeep, EwsTraversalSoftDeleted, EwsTraversalAssociated } EwsTraversalType; typedef enum { EwsItemTypeItem = 0, EwsItemTypeMessage, EwsItemTypeCalendarItem, EwsItemTypeContact, EwsItemTypeDistributionList, EwsItemTypeMeetingMessage, EwsItemTypeMeetingRequest, EwsItemTypeMeetingResponse, EwsItemTypeMeetingCancellation, EwsItemTypeTask, EwsItemTypeAbchPerson, EwsItemTypePostItem, EwsItemTypeUnknown } EwsItemType; typedef enum { EwsItemSensitivityNormal, EwsItemSensitivityPersonal, EwsItemSensitivityPrivate, EwsItemSensitivityConfidential } EwsItemSensitivity; /** * @brief List of fields in EWS Item and its descendants * * The list is based on the XSD schema and contains duplicates, which were commented out. */ typedef enum { // Folder EwsFolderFieldFolderId, EwsFolderFieldParentFolderId, EwsFolderFieldFolderClass, EwsFolderFieldDisplayName, EwsFolderFieldTotalCount, EwsFolderFieldChildFolderCount, EwsFolderFieldManagedFolderInformation, EwsFolderFieldEffectiveRights, // Calendar folder EwsFolderFieldPermissionSet, // Contacts folder //EwsFolderFieldPermissionSet, DUPLICATE // Mail folder EwsFolderFieldUnreadCount, //EwsFolderFieldPermissionSet, DUPLICATE // Search folder //EwsFolderFieldUnreadCount, DUPLICATE EwsFolderFieldSearchParameters, // Tasks folder //EwsFolderFieldUnreadCount, DUPLICATE // Item EwsItemFieldMimeContent, EwsItemFieldItemId, EwsItemFieldParentFolderId, EwsItemFieldItemClass, EwsItemFieldSubject, EwsItemFieldSensitivity, EwsItemFieldBody, EwsItemFieldAttachments, EwsItemFieldDateTimeReceived, EwsItemFieldSize, EwsItemFieldCategories, EwsItemFieldImportance, EwsItemFieldInReplyTo, EwsItemFieldIsSubmitted, EwsItemFieldIsDraft, EwsItemFieldIsFromMe, EwsItemFieldIsResend, EwsItemFieldIsUnmodified, EwsItemFieldInternetMessageHeaders, EwsItemFieldDateTimeSent, EwsItemFieldDateTimeCreated, EwsItemFieldResponseObjects, EwsItemFieldReminderDueBy, EwsItemFieldReminderIsSet, EwsItemFieldReminderMinutesBeforeStart, EwsItemFieldDisplayCc, EwsItemFieldDisplayTo, EwsItemFieldHasAttachments, EwsItemFieldCulture, EwsItemFieldEffectiveRights, EwsItemFieldLastModifiedName, EwsItemFieldLastModifiedTime, EwsItemFieldIsAssociated, EwsItemFieldWebClientReadFormQueryString, EwsItemFieldWebClientEditFormQueryString, EwsItemFieldConversationId, EwsItemFieldUniqueBody, EwsItemFieldFlag, EwsItemFieldStoreEntryId, EwsItemFieldInstanceKey, EwsItemFieldNormalizedBody, EwsItemFieldEntityExtractionResult, EwsItemFieldPolicyTag, EwsItemFieldArchiveTag, EwsItemFieldRetentionDate, EwsItemFieldPreview, EwsItemFieldRightsManagementLicenseData, EwsItemFieldPredictedActionReasons, EwsItemFieldIsClutter, EwsItemFieldBlockStatus, EwsItemFieldHasBlockedImages, EwsItemFieldTextBody, EwsItemFieldIconIndex, EwsItemFieldSearchKey, EwsItemFieldSortKey, EwsItemFieldHashtags, EwsItemFieldMentions, EwsItemFieldMentionedMe, EwsItemFieldMentionsPreview, EwsItemFieldMentionsEx, EwsItemFieldAppliedHashtags, EwsItemFieldAppliedHashtagsPreview, EwsItemFieldLikes, EwsItemFieldLikesPreview, EwsItemFieldPendingSocialActivityTagIds, EwsItemFieldAtAllMention, EwsItemFieldCanDelete, EwsItemFieldInferenceClassification, // Message EwsItemFieldSender, EwsItemFieldToRecipients, EwsItemFieldCcRecipients, EwsItemFieldBccRecipients, EwsItemFieldIsReadReceiptRequested, EwsItemFieldIsDeliveryReceiptRequested, EwsItemFieldConversationIndex, EwsItemFieldConversationTopic, EwsItemFieldFrom, EwsItemFieldInternetMessageId, EwsItemFieldIsRead, EwsItemFieldIsResponseRequested, EwsItemFieldReferences, EwsItemFieldReplyTo, EwsItemFieldReceivedBy, EwsItemFieldReceivedRepresenting, // Task EwsItemFieldActualWork, EwsItemFieldAssignedTime, EwsItemFieldBillingInformation, EwsItemFieldChangeCount, EwsItemFieldCompanies, EwsItemFieldCompleteDate, EwsItemFieldContacts, EwsItemFieldDelegationState, EwsItemFieldDelegator, EwsItemFieldDueDate, EwsItemFieldIsAssignmentEditable, EwsItemFieldIsComplete, EwsItemFieldIsRecurring, EwsItemFieldIsTeamTask, EwsItemFieldMileage, EwsItemFieldOwner, EwsItemFieldPercentComplete, EwsItemFieldRecurrence, EwsItemFieldStartDate, EwsItemFieldStatus, EwsItemFieldStatusDescription, EwsItemFieldTotalWork, // Calendar EwsItemFieldUID, EwsItemFieldRecurrenceId, EwsItemFieldDateTimeStamp, EwsItemFieldStart, EwsItemFieldEnd, EwsItemFieldOriginalStart, EwsItemFieldIsAllDayEvent, EwsItemFieldLegacyFreeBusyStatus, EwsItemFieldLocation, EwsItemFieldWhen, EwsItemFieldIsMeeting, EwsItemFieldIsCancelled, //EwsItemFieldIsRecurring, DUPLICATE EwsItemFieldMeetingRequestWasSent, //EwsItemFieldIsResponseRequested, DUPLICATE EwsItemFieldCalendarItemType, EwsItemFieldMyResponseType, EwsItemFieldOrganizer, EwsItemFieldRequiredAttendees, EwsItemFieldOptionalAttendees, EwsItemFieldResources, EwsItemFieldConflictingMeetingCount, EwsItemFieldAdjacentMeetingCount, EwsItemFieldConflictingMeetings, EwsItemFieldAdjacentMeetings, EwsItemFieldDuration, EwsItemFieldTimeZone, EwsItemFieldStartTimeZone, EwsItemFieldEndTimeZone, EwsItemFieldAppointmentReplyTime, EwsItemFieldAppointmentSequenceNumber, EwsItemFieldAppointmentState, //EwsItemFieldRecurrence, DUPLICATE EwsItemFieldFirstOccurrence, EwsItemFieldLastOccurrence, EwsItemFieldModifiedOccurrences, EwsItemFieldDeletedOccurrences, EwsItemFieldMeetingTimeZone, EwsItemFieldConferenceType, EwsItemFieldAllowNewTimeProposal, EwsItemFieldIsOnlineMeeting, EwsItemFieldMeetingWorkspaceUrl, EwsItemFieldNetShowUrl, EwsItemFieldEnhancedLocation, EwsItemFieldStartWallClock, EwsItemFieldEndWallClock, EwsItemFieldStartTimeZoneId, EwsItemFieldEndTimeZoneId, EwsItemFieldIntendedFreeBusyStatus, EwsItemFieldJoinOnlineMeetingUrl, EwsItemFieldOnlineMeetingSettings, EwsItemFieldIsOrganizer, EwsItemFieldCalendarActivityData, EwsItemFieldDoNotForwardMeeting, // MeetingMessage EwsItemFieldAssociatedCalendarItemId, EwsItemFieldIsDelegated, EwsItemFieldIsOutOfDate, EwsItemFieldHasBeenProcessed, EwsItemFieldResponseType, //EwsItemFieldUID, DUPLICATE //EwsItemFieldRecurrenceId, DUPLICATE //EwsItemFieldDateTimeStamp, DUPLICATE // MeetingRequestMessage EwsItemFieldMeetingRequestType, //EwsItemFieldIntendedFreeBusyStatus, DUPLICATE //EwsItemFieldStart, DUPLICATE //EwsItemFieldEnd, DUPLICATE //EwsItemFieldOriginalStart, DUPLICATE //EwsItemFieldIsAllDayEvent, DUPLICATE //EwsItemFieldLegacyFreeBusyStatus, DUPLICATE //EwsItemFieldLocation, DUPLICATE //EwsItemFieldWhen, DUPLICATE //EwsItemFieldIsMeeting, DUPLICATE //EwsItemFieldIsCancelled, DUPLICATE //EwsItemFieldIsRecurring, DUPLICATE //EwsItemFieldMeetingRequestWasSent, DUPLICATE //EwsItemFieldCalendarItemType, DUPLICATE //EwsItemFieldMyResponseType, DUPLICATE //EwsItemFieldOrganizer, DUPLICATE //EwsItemFieldRequiredAttendees, DUPLICATE //EwsItemFieldOptionalAttendees, DUPLICATE //EwsItemFieldResources, DUPLICATE //EwsItemFieldConflictingMeetingCount, DUPLICATE //EwsItemFieldAdjacentMeetingCount, DUPLICATE //EwsItemFieldConflictingMeetings, DUPLICATE //EwsItemFieldAdjacentMeetings, DUPLICATE //EwsItemFieldDuration, DUPLICATE //EwsItemFieldTimeZone, DUPLICATE //EwsItemFieldAppointmentReplyTime, DUPLICATE //EwsItemFieldAppointmentSequenceNumber,DUPLICATE //EwsItemFieldAppointmentState, DUPLICATE //EwsItemFieldRecurrence, DUPLICATE //EwsItemFieldFirstOccurrence, DUPLICATE //EwsItemFieldLastOccurrence, DUPLICATE //EwsItemFieldModifiedOccurrences, DUPLICATE //EwsItemFieldDeletedOccurrences, DUPLICATE //EwsItemFieldMeetingTimeZone, DUPLICATE //EwsItemFieldConferenceType, DUPLICATE //EwsItemFieldAllowNewTimeProposal, DUPLICATE //EwsItemFieldIsOnlineMeeting, DUPLICATE //EwsItemFieldMeetingWorkspaceUrl, DUPLICATE //EwsItemFieldNetShowUrl, DUPLICATE // Contact EwsItemFieldFileAs, EwsItemFieldFileAsMapping, EwsItemFieldDisplayName, EwsItemFieldGivenName, EwsItemFieldInitials, EwsItemFieldMiddleName, EwsItemFieldNickname, EwsItemFieldCompleteName, EwsItemFieldCompanyName, EwsItemFieldEmailAddresses, EwsItemFieldPhysicalAddresses, EwsItemFieldPhoneNumbers, EwsItemFieldAssistantName, EwsItemFieldBirthday, EwsItemFieldBusinessHomePage, EwsItemFieldChildren, //EwsItemFieldCompanies, DUPLICATE EwsItemFieldContactSource, EwsItemFieldDepartment, EwsItemFieldGeneration, EwsItemFieldImAddresses, EwsItemFieldJobTitle, EwsItemFieldManager, //EwsItemFieldMileage, DUPLICATE EwsItemFieldOfficeLocation, EwsItemFieldPostalAddressIndex, EwsItemFieldProfession, EwsItemFieldSpouseName, EwsItemFieldSurname, EwsItemFieldWeddingAnniversary, // DistributionList //EwsItemFieldDisplayName, DUPLICATE //EwsItemFieldFileAs, DUPLICATE //EwsItemFieldContactSource, DUPLICATE // Additional fields not in EWS specification EwsItemFieldBodyIsHtml, EwsItemFieldExtendedProperties, EwsItemFieldExchangePersonIdGuid, } EwsItemFields; typedef enum { EwsItemImportanceLow, EwsItemImportanceNormal, EwsItemImportanceHigh } EwsItemImportance; typedef enum { EwsBasePointBeginning, EwsBasePointEnd } EwsIndexedViewBasePoint; typedef enum { EwsCalendarItemSingle = 0, EwsCalendarItemOccurrence, EwsCalendarItemException, EwsCalendarItemRecurringMaster } EwsCalendarItemType; typedef enum { EwsEventResponseUnknown = 0, EwsEventResponseOrganizer, EwsEventResponseTentative, EwsEventResponseAccept, EwsEventResponseDecline, EwsEventResponseNotReceived } EwsEventResponseType; typedef enum { EwsLfbStatusFree = 0, EwsLfbStatusTentative, EwsLfbStatusBusy, EwsLfbOutOfOffice, EwsLfbNoData } EwsLegacyFreeBusyStatus; typedef enum { EwsDispSaveOnly = 0, EwsDispSendOnly, EwsDispSendAndSaveCopy, } EwsMessageDisposition; typedef enum { EwsResolNeverOverwrite = 0, EwsResolAutoResolve, EwsResolAlwaysOverwrite, } EwsConflictResolution; typedef enum { EwsMeetingDispSendToNone = 0, EwsMeetingDispSendOnlyToAll, EwsMeetingDispSendOnlyToChanged, EwsMeetingDispSendToAllAndSaveCopy, EwsMeetingDispSendToChangedAndSaveCopy, EwsMeetingDispUnspecified } EwsMeetingDisposition; typedef enum { EwsCopiedEvent = 0, EwsCreatedEvent, EwsDeletedEvent, EwsModifiedEvent, EwsMovedEvent, EwsNewMailEvent, EwsFreeBusyChangedEvent, EwsStatusEvent, EwsUnknownEvent } EwsEventType; -template T decodeEnumString(const QString &str, const QString* table, unsigned count, bool *ok) +template T decodeEnumString(const QString &str, const QString *table, unsigned count, bool *ok) { unsigned i; T enumVal = T(); for (i = 0; i < count; i++) { if (str == table[i]) { enumVal = static_cast(i); break; } } *ok = (i < count); return enumVal; } inline bool isEwsMessageItemType(EwsItemType type) { return (type == EwsItemTypeItem) || (type == EwsItemTypePostItem); } extern const QVector ewsItemTypeNames; #endif diff --git a/resources/ews/ewsclient/ewsunsubscriberequest.cpp b/resources/ews/ewsclient/ewsunsubscriberequest.cpp index c0359371e..0e40eaeea 100644 --- a/resources/ews/ewsclient/ewsunsubscriberequest.cpp +++ b/resources/ews/ewsclient/ewsunsubscriberequest.cpp @@ -1,93 +1,95 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsunsubscriberequest.h" #include #include "ewsclient_debug.h" EwsUnsubscribeRequest::EwsUnsubscribeRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsUnsubscribeRequest::~EwsUnsubscribeRequest() { } void EwsUnsubscribeRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("Unsubscribe")); writer.writeTextElement(ewsMsgNsUri, QStringLiteral("SubscriptionId"), mSubscriptionId); writer.writeEndElement(); // Unsubscribe endSoapDocument(writer); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsUnsubscribeRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("Unsubscribe"), - [this](QXmlStreamReader &reader) {return parseUnsubscribeResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseUnsubscribeResponse(reader); + }); } bool EwsUnsubscribeRequest::parseUnsubscribeResponse(QXmlStreamReader &reader) { QSharedPointer resp(new Response(reader)); if (resp->responseClass() == EwsResponseUnknown) { return false; } mResponse = resp; return true; } EwsUnsubscribeRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } diff --git a/resources/ews/ewsclient/ewsupdatefolderrequest.cpp b/resources/ews/ewsclient/ewsupdatefolderrequest.cpp index 29b2c32a4..f6caab75d 100644 --- a/resources/ews/ewsclient/ewsupdatefolderrequest.cpp +++ b/resources/ews/ewsclient/ewsupdatefolderrequest.cpp @@ -1,174 +1,176 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsupdatefolderrequest.h" #include "ewsclient_debug.h" static const QVector updateTypeElementNames = { QStringLiteral("AppendToFolderField"), QStringLiteral("SetFolderField"), QStringLiteral("DeleteFolderField") }; static const QVector folderTypeNames = { QStringLiteral("Folder"), QStringLiteral("CalendarFolder"), QStringLiteral("ContactsFolder"), QStringLiteral("SearchFolder"), QStringLiteral("TasksFolder") }; EwsUpdateFolderRequest::EwsUpdateFolderRequest(EwsClient &client, QObject *parent) : EwsRequest(client, parent) { } EwsUpdateFolderRequest::~EwsUpdateFolderRequest() { } void EwsUpdateFolderRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("UpdateFolder")); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FolderChanges")); Q_FOREACH (const FolderChange &ch, mChanges) { ch.write(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting UpdateFolder request (%1 changes)") - .arg(mChanges.size()); + .arg(mChanges.size()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsUpdateFolderRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("UpdateFolder"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsUpdateFolderRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got UpdateFolder response - OK"); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got UpdateFolder response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsUpdateFolderRequest::Response::Response(QXmlStreamReader &reader) : EwsRequest::Response(reader) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Folders")) { if (reader.readNextStartElement()) { EwsFolder folder(reader); if (!folder.isValid()) { return; } mId = folder[EwsFolderFieldFolderId].value(); } // Finish the Folders element. reader.skipCurrentElement(); } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element.")); return; } } } bool EwsUpdateFolderRequest::Update::write(QXmlStreamWriter &writer, EwsFolderType folderType) const { bool retVal = true; writer.writeStartElement(ewsTypeNsUri, updateTypeElementNames[mType]); mField.write(writer); if (mType != Delete) { writer.writeStartElement(ewsTypeNsUri, folderTypeNames[folderType]); retVal = mField.writeWithValue(writer, mValue); writer.writeEndElement(); } writer.writeEndElement(); return retVal; } bool EwsUpdateFolderRequest::FolderChange::write(QXmlStreamWriter &writer) const { bool retVal = true; writer.writeStartElement(ewsTypeNsUri, QStringLiteral("FolderChange")); mId.writeFolderIds(writer); writer.writeStartElement(ewsTypeNsUri, QStringLiteral("Updates")); Q_FOREACH (const QSharedPointer &upd, mUpdates) { if (!upd->write(writer, mType)) { retVal = false; break; } } writer.writeEndElement(); writer.writeEndElement(); return retVal; } diff --git a/resources/ews/ewsclient/ewsupdatefolderrequest.h b/resources/ews/ewsclient/ewsupdatefolderrequest.h index a3d7cc1f8..fb4e8e330 100644 --- a/resources/ews/ewsclient/ewsupdatefolderrequest.h +++ b/resources/ews/ewsclient/ewsupdatefolderrequest.h @@ -1,129 +1,146 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSUPDATEFOLDERREQUEST_H #define EWSUPDATEFOLDERREQUEST_H #include #include #include "ewsfolder.h" #include "ewsfoldershape.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class QXmlStreamWriter; class EwsUpdateFolderRequest : public EwsRequest { Q_OBJECT public: class Update { public: bool write(QXmlStreamWriter &writer, EwsFolderType folderType) const; protected: enum Type { Append = 0, Set, Delete, Unknown }; Update(const EwsPropertyField &field, const QVariant &val, Type type) - : mField(field), mValue(val), mType(type) {} + : mField(field) + , mValue(val) + , mType(type) + { + } EwsPropertyField mField; QVariant mValue; Type mType; }; class AppendUpdate : public Update { public: - AppendUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Append) {} + AppendUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Append) + { + } }; class SetUpdate : public Update { public: - SetUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Set) {} + SetUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Set) + { + } }; class DeleteUpdate : public Update { public: - DeleteUpdate(const EwsPropertyField &field) : Update(field, QVariant(), Delete) {} + DeleteUpdate(const EwsPropertyField &field) : Update(field, QVariant(), Delete) + { + } }; class FolderChange { public: - FolderChange(const EwsId &folderId, EwsFolderType type) : mId(folderId), mType(type) {} + FolderChange(const EwsId &folderId, EwsFolderType type) : mId(folderId) + , mType(type) + { + } + void addUpdate(const Update *upd) { mUpdates.append(QSharedPointer(upd)); } + bool write(QXmlStreamWriter &writer) const; private: EwsId mId; EwsFolderType mType; - QList> mUpdates; + QList > mUpdates; }; class Response : public EwsRequest::Response { public: const EwsId &folderId() const { return mId; } + protected: Response(QXmlStreamReader &reader); EwsId mId; friend class EwsUpdateFolderRequest; }; EwsUpdateFolderRequest(EwsClient &client, QObject *parent); ~EwsUpdateFolderRequest() override; void addFolderChange(const FolderChange &change) { mChanges.append(change); } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: QList mChanges; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsupdateitemrequest.cpp b/resources/ews/ewsclient/ewsupdateitemrequest.cpp index 88973e934..a7a1b29ea 100644 --- a/resources/ews/ewsclient/ewsupdateitemrequest.cpp +++ b/resources/ews/ewsclient/ewsupdateitemrequest.cpp @@ -1,226 +1,231 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsupdateitemrequest.h" #include "ewsclient_debug.h" static const QVector conflictResolutionNames = { QStringLiteral("NeverOverwrite"), QStringLiteral("AutoResolve"), QStringLiteral("AlwaysOverwrite") }; static const QVector messageDispositionNames = { QStringLiteral("SaveOnly"), QStringLiteral("SendOnly"), QStringLiteral("SendAndSaveCopy") }; static const QVector meetingDispositionNames = { QStringLiteral("SendToNone"), QStringLiteral("SendOnlyToAll"), QStringLiteral("SendOnlyToChanged"), QStringLiteral("SendToAllAndSaveCopy"), QStringLiteral("SendToChangedAndSaveCopy") }; static const QVector updateTypeElementNames = { QStringLiteral("AppendToItemField"), QStringLiteral("SetItemField"), QStringLiteral("DeleteItemField") }; EwsUpdateItemRequest::EwsUpdateItemRequest(EwsClient &client, QObject *parent) - : EwsRequest(client, parent), mMessageDisp(EwsDispSaveOnly), - mConflictResol(EwsResolAlwaysOverwrite), mMeetingDisp(EwsMeetingDispUnspecified) + : EwsRequest(client, parent) + , mMessageDisp(EwsDispSaveOnly) + , mConflictResol(EwsResolAlwaysOverwrite) + , mMeetingDisp(EwsMeetingDispUnspecified) { } EwsUpdateItemRequest::~EwsUpdateItemRequest() { } void EwsUpdateItemRequest::start() { QString reqString; QXmlStreamWriter writer(&reqString); startSoapDocument(writer); writer.writeStartElement(ewsMsgNsUri, QStringLiteral("UpdateItem")); writer.writeAttribute(QStringLiteral("ConflictResolution"), conflictResolutionNames[mConflictResol]); writer.writeAttribute(QStringLiteral("MessageDisposition"), messageDispositionNames[mMessageDisp]); if (mMeetingDisp != EwsMeetingDispUnspecified) { writer.writeAttribute(QStringLiteral("SendMeetingInvitationsOrCancellations"), meetingDispositionNames[mMeetingDisp]); } if (mSavedFolderId.type() != EwsId::Unspecified) { writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SavedItemFolderId")); mSavedFolderId.writeFolderIds(writer); writer.writeEndElement(); } writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ItemChanges")); Q_FOREACH (const ItemChange &ch, mChanges) { ch.write(writer); } writer.writeEndElement(); writer.writeEndElement(); endSoapDocument(writer); qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting UpdateItem request (%1 changes)") - .arg(mChanges.size()); + .arg(mChanges.size()); qCDebug(EWSCLI_PROTO_LOG) << reqString; prepare(reqString); doSend(); } bool EwsUpdateItemRequest::parseResult(QXmlStreamReader &reader) { return parseResponseMessage(reader, QStringLiteral("UpdateItem"), - [this](QXmlStreamReader &reader) {return parseItemsResponse(reader);}); + [this](QXmlStreamReader &reader) { + return parseItemsResponse(reader); + }); } bool EwsUpdateItemRequest::parseItemsResponse(QXmlStreamReader &reader) { Response resp(reader); if (resp.responseClass() == EwsResponseUnknown) { return false; } if (EWSCLI_REQUEST_LOG().isDebugEnabled()) { if (resp.isSuccess()) { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got UpdateItem response - OK"); } else { qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got UpdateItem response - %1") - .arg(resp.responseMessage()); + .arg(resp.responseMessage()); } } mResponses.append(resp); return true; } EwsUpdateItemRequest::Response::Response(QXmlStreamReader &reader) - : EwsRequest::Response(reader), mConflictCount(0) + : EwsRequest::Response(reader) + , mConflictCount(0) { if (mClass == EwsResponseParseError) { return; } while (reader.readNextStartElement()) { if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) { setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2") .arg(QStringLiteral("ResponseMessage")).arg(reader.namespaceUri().toString())); return; } if (reader.name() == QStringLiteral("Items")) { if (reader.readNextStartElement()) { EwsItem item(reader); if (!item.isValid()) { return; } mId = item[EwsItemFieldItemId].value(); } // Finish the Items element. reader.skipCurrentElement(); } else if (reader.name() == QStringLiteral("ConflictResults")) { if (!reader.readNextStartElement()) { setErrorMsg(QStringLiteral("Failed to read EWS request - expected a %1 element inside %2 element.") .arg(QStringLiteral("Value")).arg(QStringLiteral("ConflictResults"))); return; } if (reader.name() != QStringLiteral("Count")) { setErrorMsg(QStringLiteral("Failed to read EWS request - expected a %1 element inside %2 element.") .arg(QStringLiteral("Count")).arg(QStringLiteral("ConflictResults"))); return; } bool ok; mConflictCount = reader.readElementText().toUInt(&ok); if (!ok) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.") .arg(QStringLiteral("ConflictResults/Value"))); } // Finish the Value element. reader.skipCurrentElement(); } else if (!readResponseElement(reader)) { setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element %1.").arg(reader.name().toString())); return; } } } bool EwsUpdateItemRequest::Update::write(QXmlStreamWriter &writer, EwsItemType itemType) const { bool retVal = true; writer.writeStartElement(ewsTypeNsUri, updateTypeElementNames[mType]); mField.write(writer); if (mType != Delete) { writer.writeStartElement(ewsTypeNsUri, ewsItemTypeNames[itemType]); retVal = mField.writeWithValue(writer, mValue); writer.writeEndElement(); } writer.writeEndElement(); return retVal; } bool EwsUpdateItemRequest::ItemChange::write(QXmlStreamWriter &writer) const { bool retVal = true; writer.writeStartElement(ewsTypeNsUri, QStringLiteral("ItemChange")); mId.writeItemIds(writer); writer.writeStartElement(ewsTypeNsUri, QStringLiteral("Updates")); Q_FOREACH (const QSharedPointer &upd, mUpdates) { if (!upd->write(writer, mType)) { retVal = false; break; } } writer.writeEndElement(); writer.writeEndElement(); return retVal; } diff --git a/resources/ews/ewsclient/ewsupdateitemrequest.h b/resources/ews/ewsclient/ewsupdateitemrequest.h index 07a12162b..d21015a31 100644 --- a/resources/ews/ewsclient/ewsupdateitemrequest.h +++ b/resources/ews/ewsclient/ewsupdateitemrequest.h @@ -1,159 +1,176 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSUPDATEITEMREQUEST_H #define EWSUPDATEITEMREQUEST_H #include #include #include "ewsitem.h" #include "ewsitemshape.h" #include "ewsrequest.h" #include "ewstypes.h" class QXmlStreamReader; class QXmlStreamWriter; class EwsUpdateItemRequest : public EwsRequest { Q_OBJECT public: class Update { public: bool write(QXmlStreamWriter &writer, EwsItemType itemType) const; protected: enum Type { Append = 0, Set, Delete, Unknown }; Update(const EwsPropertyField &field, const QVariant &val, Type type) - : mField(field), mValue(val), mType(type) {} + : mField(field) + , mValue(val) + , mType(type) + { + } EwsPropertyField mField; QVariant mValue; Type mType; }; class AppendUpdate : public Update { public: - AppendUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Append) {} + AppendUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Append) + { + } }; class SetUpdate : public Update { public: - SetUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Set) {} + SetUpdate(const EwsPropertyField &field, const QVariant &val) : Update(field, val, Set) + { + } }; class DeleteUpdate : public Update { public: - DeleteUpdate(const EwsPropertyField &field) : Update(field, QVariant(), Delete) {} + DeleteUpdate(const EwsPropertyField &field) : Update(field, QVariant(), Delete) + { + } }; class ItemChange { public: - ItemChange(const EwsId &itemId, EwsItemType type) : mId(itemId), mType(type) {} + ItemChange(const EwsId &itemId, EwsItemType type) : mId(itemId) + , mType(type) + { + } + void addUpdate(const Update *upd) { mUpdates.append(QSharedPointer(upd)); } + bool write(QXmlStreamWriter &writer) const; private: EwsId mId; EwsItemType mType; - QList> mUpdates; + QList > mUpdates; }; class Response : public EwsRequest::Response { public: const EwsId &itemId() const { return mId; } unsigned conflictCount() const { return mConflictCount; } + protected: Response(QXmlStreamReader &reader); unsigned mConflictCount; EwsId mId; friend class EwsUpdateItemRequest; }; EwsUpdateItemRequest(EwsClient &client, QObject *parent); ~EwsUpdateItemRequest() override; void addItemChange(const ItemChange &change) { mChanges.append(change); } void setMessageDisposition(EwsMessageDisposition disp) { mMessageDisp = disp; } void setConflictResolution(EwsConflictResolution resol) { mConflictResol = resol; } void setMeetingDisposition(EwsMeetingDisposition disp) { mMeetingDisp = disp; } void setSavedFolderId(const EwsId &id) { mSavedFolderId = id; } void start() override; const QList &responses() const { return mResponses; } + protected: bool parseResult(QXmlStreamReader &reader) override; bool parseItemsResponse(QXmlStreamReader &reader); private: QList mChanges; EwsMessageDisposition mMessageDisp; EwsConflictResolution mConflictResol; EwsMeetingDisposition mMeetingDisp; EwsId mSavedFolderId; QList mResponses; }; #endif diff --git a/resources/ews/ewsclient/ewsxml.cpp b/resources/ews/ewsclient/ewsxml.cpp index 75f897138..103f4a9b8 100644 --- a/resources/ews/ewsclient/ewsxml.cpp +++ b/resources/ews/ewsclient/ewsxml.cpp @@ -1,381 +1,379 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsxml.h" #include #include "ewsclient_debug.h" #include "ewsfolder.h" #include "ewsid.h" #include "ewsitem.h" static const QVector messageSensitivityNames = { QStringLiteral("Normal"), QStringLiteral("Personal"), QStringLiteral("Private"), QStringLiteral("Confidential") }; static const QVector messageImportanceNames = { QStringLiteral("Low"), QStringLiteral("Normal"), QStringLiteral("High") }; static const QVector calendarItemTypeNames = { QStringLiteral("Single"), QStringLiteral("Occurrence"), QStringLiteral("Exception"), QStringLiteral("RecurringMaster") }; static const QVector legacyFreeBusyStatusNames = { QStringLiteral("Free"), QStringLiteral("Tentative"), QStringLiteral("Busy"), QStringLiteral("OOF"), QStringLiteral("NoData") }; static const QVector responseTypeNames = { QStringLiteral("Unknown"), QStringLiteral("Organizer"), QStringLiteral("Tentative"), QStringLiteral("Accept"), QStringLiteral("Decline"), QStringLiteral("NoResponseReceived") }; bool ewsXmlBoolReader(QXmlStreamReader &reader, QVariant &val) { const QString elmText = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Error reading %1 element") - .arg(reader.name().toString()); + .arg(reader.name().toString()); reader.skipCurrentElement(); return false; } if (elmText == QStringLiteral("true")) { val = true; } else if (elmText == QStringLiteral("false")) { val = false; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected invalid boolean value in %1 element:") - .arg(reader.name().toString()) + .arg(reader.name().toString()) << elmText; return false; } return true; } bool ewsXmlBoolWriter(QXmlStreamWriter &writer, const QVariant &val) { writer.writeCharacters(val.toBool() ? QStringLiteral("true") : QStringLiteral("false")); return true; } bool ewsXmlBase64Reader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); val = QByteArray::fromBase64(reader.readElementText().toLatin1()); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } return true; } bool ewsXmlBase64Writer(QXmlStreamWriter &writer, const QVariant &val) { writer.writeCharacters(QString::fromLatin1(val.toByteArray().toBase64())); return true; } bool ewsXmlIdReader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); EwsId id = EwsId(reader); if (id.type() == EwsId::Unspecified) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } val = QVariant::fromValue(id); reader.skipCurrentElement(); return true; } bool ewsXmlIdWriter(QXmlStreamWriter &writer, const QVariant &val) { EwsId id = val.value(); if (id.type() == EwsId::Unspecified) { return false; } id.writeAttributes(writer); return true; } bool ewsXmlTextReader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); val = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } return true; } bool ewsXmlTextWriter(QXmlStreamWriter &writer, const QVariant &val) { writer.writeCharacters(val.toString()); return true; } bool ewsXmlUIntReader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); bool ok; val = reader.readElementText().toUInt(&ok); if (reader.error() != QXmlStreamReader::NoError || !ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); return false; } return true; } bool ewsXmlUIntWriter(QXmlStreamWriter &writer, const QVariant &val) { writer.writeCharacters(QString::number(val.toUInt())); return true; } bool ewsXmlDateTimeReader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); QDateTime dt = QDateTime::fromString(reader.readElementText(), Qt::ISODate); if (reader.error() != QXmlStreamReader::NoError || !dt.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); return false; } val = QVariant::fromValue(dt); return true; } bool ewsXmlItemReader(QXmlStreamReader &reader, QVariant &val) { QString elmName = reader.name().toString(); EwsItem item = EwsItem(reader); if (!item.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } val = QVariant::fromValue(item); return true; } bool ewsXmlFolderReader(QXmlStreamReader &reader, QVariant &val) { const QString elmName = reader.name().toString(); EwsFolder folder = EwsFolder(reader); if (!folder.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } val = QVariant::fromValue(folder); return true; } bool ewsXmlEnumReader(QXmlStreamReader &reader, QVariant &val, const QVector &items) { const QString elmName = reader.name().toString(); QString text = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid content.") - .arg(elmName); + .arg(elmName); reader.skipCurrentElement(); return false; } int i = 0; QVector::const_iterator it; for (it = items.cbegin(); it != items.cend(); ++it, i++) { if (text == *it) { val = i; break; } } if (it == items.cend()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown value %2.") - .arg(elmName).arg(text); + .arg(elmName).arg(text); return false; } return true; } bool ewsXmlSensitivityReader(QXmlStreamReader &reader, QVariant &val) { return ewsXmlEnumReader(reader, val, messageSensitivityNames); } bool ewsXmlImportanceReader(QXmlStreamReader &reader, QVariant &val) { return ewsXmlEnumReader(reader, val, messageImportanceNames); } bool ewsXmlCalendarItemTypeReader(QXmlStreamReader &reader, QVariant &val) { return ewsXmlEnumReader(reader, val, calendarItemTypeNames); } bool ewsXmlLegacyFreeBusyStatusReader(QXmlStreamReader &reader, QVariant &val) { return ewsXmlEnumReader(reader, val, legacyFreeBusyStatusNames); } bool ewsXmlResponseTypeReader(QXmlStreamReader &reader, QVariant &val) { return ewsXmlEnumReader(reader, val, responseTypeNames); } -template <> +template<> QString readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { ok = true; QStringRef elmName = reader.name(); QString val = reader.readElementText(); if (reader.error() != QXmlStreamReader::NoError) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElement).arg(elmName.toString()); + .arg(parentElement).arg(elmName.toString()); reader.skipCurrentElement(); val.clear(); ok = false; } return val; } -template <> +template<> int readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { QStringRef elmName = reader.name(); QString valStr = readXmlElementValue(reader, ok, parentElement); int val = 0; if (ok) { val = valStr.toInt(&ok); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElement).arg(elmName.toString()); + .arg(parentElement).arg(elmName.toString()); } } return val; } -template <> +template<> long readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { QStringRef elmName = reader.name(); QString valStr = readXmlElementValue(reader, ok, parentElement); long val = 0; if (ok) { val = valStr.toLong(&ok); if (!ok) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElement).arg(elmName.toString()); + .arg(parentElement).arg(elmName.toString()); } } return val; } -template <> +template<> QDateTime readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { QStringRef elmName = reader.name(); QString valStr = readXmlElementValue(reader, ok, parentElement); QDateTime val; if (ok) { val = QDateTime::fromString(valStr, Qt::ISODate); if (!val.isValid()) { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElement).arg(elmName.toString()); + .arg(parentElement).arg(elmName.toString()); ok = false; } } return val; } -template <> +template<> bool readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { QStringRef elmName = reader.name(); QString valStr = readXmlElementValue(reader, ok, parentElement); bool val = false; if (ok) { if (valStr == QStringLiteral("true")) { val = true; } else if (valStr == QStringLiteral("false")) { val = false; } else { qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElement).arg(elmName.toString()); + .arg(parentElement).arg(elmName.toString()); ok = false; } } return val; } -template <> +template<> QByteArray readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement) { QString valStr = readXmlElementValue(reader, ok, parentElement); QByteArray val; if (ok) { /* QByteArray::fromBase64() does not perform any input validity checks and skips invalid input characters */ val = QByteArray::fromBase64(valStr.toLatin1()); } return val; } - - diff --git a/resources/ews/ewsclient/ewsxml.h b/resources/ews/ewsclient/ewsxml.h index ff6d234ac..246bc7b6d 100644 --- a/resources/ews/ewsclient/ewsxml.h +++ b/resources/ews/ewsclient/ewsxml.h @@ -1,178 +1,191 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSXMLREADER_H #define EWSXMLREADER_H #include #include #include #include "ewsclient_debug.h" -template class EwsXml +template class EwsXml { public: - typedef std::function ReadFunction; - typedef std::function WriteFunction; - typedef std::function UnknownElementFunction; + typedef std::function ReadFunction; + typedef std::function WriteFunction; + typedef std::function UnknownElementFunction; typedef QHash ValueHash; static Q_CONSTEXPR T Ignore = static_cast(-1); struct Item { - Item() : key(Ignore) {} + Item() : key(Ignore) + { + } + Item(T k, const QString &n, const ReadFunction &rfn = ReadFunction(), const WriteFunction &wfn = WriteFunction()) - : key(k), elmName(n), readFn(rfn), writeFn(wfn) {} + : key(k) + , elmName(n) + , readFn(rfn) + , writeFn(wfn) + { + } + T key; QString elmName; ReadFunction readFn; WriteFunction writeFn; }; - EwsXml() {} + EwsXml() + { + } + EwsXml(const QVector &items) : mItems(items) { rebuildItemHash(); } EwsXml(const EwsXml &other) - : mItems(other.mItems), mValues(other.mValues), mItemHash(other.mItemHash) {} + : mItems(other.mItems) + , mValues(other.mValues) + , mItemHash(other.mItemHash) + { + } void setItems(const QVector &items) { mItems = items; rebuildItemHash(); } - bool readItem(QXmlStreamReader &reader, const QString &parentElm, const QString &nsUri, - UnknownElementFunction unknownElmFn = &defaultUnknownElmFunction) + bool readItem(QXmlStreamReader &reader, const QString &parentElm, const QString &nsUri, UnknownElementFunction unknownElmFn = &defaultUnknownElmFunction) { typename QHash::iterator it = mItemHash.find(reader.name().toString()); if (it != mItemHash.end() && nsUri == reader.namespaceUri()) { if (it->key == Ignore) { qCInfoNC(EWSCLI_LOG) << QStringLiteral("Unsupported %1 child element %2 - ignoring.") - .arg(parentElm).arg(reader.name().toString()); + .arg(parentElm).arg(reader.name().toString()); reader.skipCurrentElement(); return true; } else if (!it->readFn) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - no read support for %2 element.") - .arg(parentElm).arg(reader.name().toString()); + .arg(parentElm).arg(reader.name().toString()); return false; } else { QVariant val = mValues[it->key]; if (it->readFn(reader, val)) { mValues[it->key] = val; return true; } return false; } } return unknownElmFn(reader, parentElm); } - bool readItems(QXmlStreamReader &reader, const QString &nsUri, - const UnknownElementFunction &unknownElmFn = &defaultUnknownElmFunction) + bool readItems(QXmlStreamReader &reader, const QString &nsUri, const UnknownElementFunction &unknownElmFn = &defaultUnknownElmFunction) { QString elmName(reader.name().toString()); while (reader.readNextStartElement()) { if (!readItem(reader, elmName, nsUri, unknownElmFn)) { return false; } } return true; } - bool writeItems(QXmlStreamWriter &writer, const QString &parentElm, const QString &nsUri, - const ValueHash &values, const QList &keysToWrite = QList()) const + bool writeItems(QXmlStreamWriter &writer, const QString &parentElm, const QString &nsUri, const ValueHash &values, const QList &keysToWrite = QList()) const { bool hasKeysToWrite = !keysToWrite.isEmpty(); Q_FOREACH (const Item &item, mItems) { if (!hasKeysToWrite || keysToWrite.contains(item.key)) { typename ValueHash::const_iterator it = values.find(item.key); if (it != values.end()) { if (!item.writeFn) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to write %1 element - no write support for %2 element.") - .arg(parentElm).arg(item.elmName); + .arg(parentElm).arg(item.elmName); return false; } writer.writeStartElement(nsUri, item.elmName); bool status = item.writeFn(writer, *it); writer.writeEndElement(); if (!status) { return false; } } } } return true; } ValueHash values() const { return mValues; } private: static bool defaultUnknownElmFunction(QXmlStreamReader &reader, const QString &parentElm) { qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.") - .arg(parentElm).arg(reader.name().toString()); + .arg(parentElm).arg(reader.name().toString()); return false; } const QVector mItems; ValueHash mValues; QHash mItemHash; void rebuildItemHash() { Q_FOREACH (const Item &item, mItems) { mItemHash.insert(item.elmName, item); } } }; -template +template T readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement); extern bool ewsXmlBoolReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlBoolWriter(QXmlStreamWriter &writer, const QVariant &val); extern bool ewsXmlBase64Reader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlBase64Writer(QXmlStreamWriter &writer, const QVariant &val); extern bool ewsXmlIdReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlIdWriter(QXmlStreamWriter &writer, const QVariant &val); extern bool ewsXmlTextReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlTextWriter(QXmlStreamWriter &writer, const QVariant &val); extern bool ewsXmlUIntReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlUIntWriter(QXmlStreamWriter &writer, const QVariant &val); extern bool ewsXmlDateTimeReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlItemReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlFolderReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlEnumReader(QXmlStreamReader &reader, QVariant &val, const QVector &items); extern bool ewsXmlSensitivityReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlImportanceReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlCalendarItemTypeReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlLegacyFreeBusyStatusReader(QXmlStreamReader &reader, QVariant &val); extern bool ewsXmlResponseTypeReader(QXmlStreamReader &reader, QVariant &val); #endif diff --git a/resources/ews/ewsconfigdialog.cpp b/resources/ews/ewsconfigdialog.cpp index 2c639d09a..cafbfb041 100644 --- a/resources/ews/ewsconfigdialog.cpp +++ b/resources/ews/ewsconfigdialog.cpp @@ -1,497 +1,494 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 "ewsconfigdialog.h" #include #include #include #include #include #include - #include "ewsautodiscoveryjob.h" #include "ewsgetfolderrequest.h" #include "ewsresource.h" #include "ewssettings.h" #include "ewssubscriptionwidget.h" #include "ewsprogressdialog.h" #include "auth/ewspasswordauth.h" #include "auth/ewsoauth.h" #include "ui_ewsconfigdialog.h" typedef QPair StringPair; static const QVector userAgents = { {QStringLiteral("Microsoft Outlook 2016"), QStringLiteral("Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.6326; Pro)")}, {QStringLiteral("Microsoft Outlook 2013"), QStringLiteral("Microsoft Office/15.0 (Windows NT 6.1; Microsoft Outlook 15.0.4420; Pro)")}, {QStringLiteral("Microsoft Outlook 2010"), QStringLiteral("Microsoft Office/14.0 (Windows NT 6.1; Microsoft Outlook 14.0.5128; Pro)")}, {QStringLiteral("Microsoft Outlook 2011 for Mac"), QStringLiteral("MacOutlook/14.2.0.101115 (Intel Mac OS X 10.6.7)")}, {QStringLiteral("Mozilla Thunderbird 38 for Windows (with ExQuilla)"), QStringLiteral("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0")}, {QStringLiteral("Mozilla Thunderbird 38 for Linux (with ExQuilla)"), QStringLiteral("Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0")}, {QStringLiteral("Mozilla Thunderbird 38 for Mac (with ExQuilla)"), QStringLiteral("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:38.0) Gecko/20100101 Thunderbird/38.2.0")} }; static const QString pkeyPasswordMapKey = QStringLiteral("pkey-password"); static bool execJob(KJob *job) { QEventLoop loop; QObject::connect(job, &KJob::finished, &loop, [&](KJob *j) { - loop.exit(j->error()); - }); + loop.exit(j->error()); + }); job->start(); return loop.exec() == 0; } -EwsConfigDialog::EwsConfigDialog(EwsResource *parentResource, EwsClient &client, WId wId, - EwsSettings *settings) +EwsConfigDialog::EwsConfigDialog(EwsResource *parentResource, EwsClient &client, WId wId, EwsSettings *settings) : QDialog() , mParentResource(parentResource) , mSettings(settings) { if (wId) { KWindowSystem::setMainWindow(this, wId); } QVBoxLayout *mainLayout = new QVBoxLayout(this); QWidget *mainWidget = new QWidget(this); mainLayout->addWidget(mainWidget); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(mButtonBox, &QDialogButtonBox::accepted, this, &EwsConfigDialog::dialogAccepted); connect(mButtonBox, &QDialogButtonBox::rejected, this, &EwsConfigDialog::reject); mainLayout->addWidget(mButtonBox); setWindowTitle(i18n("Microsoft Exchange Configuration")); mUi = new Ui::SetupServerView; mUi->setupUi(mainWidget); mUi->accountName->setText(parentResource->name()); mSubWidget = new EwsSubscriptionWidget(client, mSettings.data(), this); mUi->subscriptionTabLayout->addWidget(mSubWidget); mConfigManager = new KConfigDialogManager(this, mSettings.data()); mConfigManager->updateWidgets(); switch (mSettings->retrievalMethod()) { case 0: mUi->pollRadioButton->setChecked(true); break; case 1: mUi->streamingRadioButton->setChecked(true); break; default: break; } const EwsServerVersion &serverVer = client.serverVersion(); if (serverVer.isValid()) { mUi->serverStatusText->setText(i18nc("Server status", "OK")); mUi->serverVersionText->setText(serverVer.toString()); } bool baseUrlEmpty = mUi->kcfg_BaseUrl->text().isEmpty(); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!baseUrlEmpty); mUi->tryConnectButton->setEnabled(!baseUrlEmpty); mTryConnectNeeded = baseUrlEmpty; connect(mSettings.data(), &EwsSettings::passwordRequestFinished, mUi->passwordEdit, &KPasswordLineEdit::setPassword); mSettings->requestPassword(false); mUi->authOAuth2RadioButton->setEnabled(true); const auto authMode = mSettings->authMode(); if (authMode == QStringLiteral("username-password")) { mUi->authUsernameRadioButton->setChecked(true); } else if (authMode == QStringLiteral("oauth2")) { mUi->authOAuth2RadioButton->setChecked(true); mUi->pkeyAuthGroupBox->setEnabled(true); } #ifdef HAVE_QCA mUi->pkeyAuthCert->setText(mSettings->pKeyCert()); mUi->pkeyAuthKey->setText(mSettings->pKeyKey()); connect(mSettings.data(), &EwsSettings::mapRequestFinished, this, [&](const QMap &map) { - if (map.contains(pkeyPasswordMapKey)) { - mUi->pkeyAuthPassword->setPassword(map[pkeyPasswordMapKey]); - } - }); + if (map.contains(pkeyPasswordMapKey)) { + mUi->pkeyAuthPassword->setPassword(map[pkeyPasswordMapKey]); + } + }); mSettings->requestMap(); #endif int selectedIndex = -1; int i = 0; Q_FOREACH (const StringPair &item, userAgents) { mUi->userAgentCombo->addItem(item.first, item.second); if (mSettings->userAgent() == item.second) { selectedIndex = i; } i++; } mUi->userAgentCombo->addItem(i18nc("User Agent", "Custom")); if (!mSettings->userAgent().isEmpty()) { mUi->userAgentGroupBox->setChecked(true); mUi->userAgentCombo->setCurrentIndex(selectedIndex >= 0 ? selectedIndex : mUi->userAgentCombo->count() - 1); mUi->userAgentEdit->setText(mSettings->userAgent()); } else { mUi->userAgentCombo->setCurrentIndex(mUi->userAgentCombo->count()); } QIcon ewsIcon = QIcon::fromTheme(QStringLiteral("akonadi-ews")); mUi->aboutIconLabel->setPixmap(ewsIcon.pixmap(96, 96, QIcon::Normal, QIcon::On)); mUi->aboutTextLabel->setText(i18nc("@info", "Akonadi Resource for Microsoft Exchange Web Services (EWS)")); mUi->aboutCopyrightLabel->setText(i18nc("@info", "Copyright (c) Krzysztof Nowicki 2015-2017")); mUi->aboutVersionLabel->setText(i18nc("@info", "Version %1", QStringLiteral(AKONADI_EWS_VERSION))); mUi->aboutLicenseLabel->setText(i18nc("@info", "Distributed under the GNU Library General Public License version 2.0 or later.")); mUi->aboutUrlLabel->setText(QStringLiteral("https://github.com/KrissN/akonadi-ews")); mUi->pkeyAuthCert->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly); mUi->pkeyAuthKey->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly); connect(okButton, &QPushButton::clicked, this, &EwsConfigDialog::save); connect(mUi->autodiscoverButton, &QPushButton::clicked, this, &EwsConfigDialog::performAutoDiscovery); connect(mUi->kcfg_Username, &KLineEdit::textChanged, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->passwordEdit, &KPasswordLineEdit::passwordChanged, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->kcfg_Domain, &KLineEdit::textChanged, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->kcfg_HasDomain, &QCheckBox::toggled, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->kcfg_Email, &KLineEdit::textChanged, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->authUsernameRadioButton, &QRadioButton::toggled, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->authOAuth2RadioButton, &QRadioButton::toggled, this, &EwsConfigDialog::setAutoDiscoveryNeeded); connect(mUi->kcfg_BaseUrl, &KLineEdit::textChanged, this, &EwsConfigDialog::enableTryConnect); connect(mUi->tryConnectButton, &QPushButton::clicked, this, &EwsConfigDialog::tryConnect); connect(mUi->userAgentCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &EwsConfigDialog::userAgentChanged); connect(mUi->clearFolderTreeSyncStateButton, &QPushButton::clicked, mParentResource, &EwsResource::clearFolderTreeSyncState); connect(mUi->clearFolderItemSyncStateButton, &QPushButton::clicked, mParentResource, QOverload<>::of(&EwsResource::clearFolderSyncState)); } EwsConfigDialog::~EwsConfigDialog() { delete mUi; } void EwsConfigDialog::save() { mParentResource->setName(mUi->accountName->text()); mConfigManager->updateSettings(); if (mUi->pollRadioButton->isChecked()) { mSettings->setRetrievalMethod(0); } else { mSettings->setRetrievalMethod(1); } /* Erase the subscription id in case subscription is disabled or its parameters changed. This * fill force creation of a new subscription. */ - if (!mSubWidget->subscriptionEnabled() || - (mSubWidget->subscribedList() != mSettings->serverSubscriptionList())) { + if (!mSubWidget->subscriptionEnabled() + || (mSubWidget->subscribedList() != mSettings->serverSubscriptionList())) { mSettings->setEventSubscriptionId(QString()); mSettings->setEventSubscriptionWatermark(QString()); } mSettings->setServerSubscription(mSubWidget->subscriptionEnabled()); if (mSubWidget->subscribedListValid()) { mSettings->setServerSubscriptionList(mSubWidget->subscribedList()); } if (mUi->userAgentGroupBox->isChecked()) { mSettings->setUserAgent(mUi->userAgentEdit->text()); } else { mSettings->setUserAgent(QString()); } if (mUi->authUsernameRadioButton->isChecked()) { mSettings->setAuthMode(QStringLiteral("username-password")); } if (mUi->authOAuth2RadioButton->isChecked()) { mSettings->setAuthMode(QStringLiteral("oauth2")); } #ifdef HAVE_QCA - if (mUi->pkeyAuthGroupBox->isEnabled() && - !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { + if (mUi->pkeyAuthGroupBox->isEnabled() + && !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { mSettings->setPKeyCert(mUi->pkeyAuthCert->text()); mSettings->setPKeyKey(mUi->pkeyAuthKey->text()); const QMap map = {{pkeyPasswordMapKey, mUi->pkeyAuthPassword->password()}}; mSettings->setMap(map); } #endif if (!mAuthMap.isEmpty()) { mSettings->setMap(mAuthMap); } mSettings->save(); } void EwsConfigDialog::performAutoDiscovery() { mAutoDiscoveryJob = new EwsAutodiscoveryJob(mUi->kcfg_Email->text(), - fullUsername(), mUi->passwordEdit->password(), - mUi->userAgentGroupBox->isEnabled() ? mUi->userAgentEdit->text() : QString(), - mUi->kcfg_EnableNTLMv2->isChecked(), this); + fullUsername(), mUi->passwordEdit->password(), + mUi->userAgentGroupBox->isEnabled() ? mUi->userAgentEdit->text() : QString(), + mUi->kcfg_EnableNTLMv2->isChecked(), this); connect(mAutoDiscoveryJob, &EwsAutodiscoveryJob::result, this, &EwsConfigDialog::autoDiscoveryFinished); mProgressDialog = new EwsProgressDialog(this, EwsProgressDialog::AutoDiscovery); connect(mProgressDialog, &QDialog::rejected, this, &EwsConfigDialog::autoDiscoveryCancelled); mAutoDiscoveryJob->start(); mProgressDialog->show(); } void EwsConfigDialog::autoDiscoveryFinished(KJob *job) { if (job->error() || job != mAutoDiscoveryJob) { KMessageBox::error(this, job->errorText(), i18nc("Exchange server autodiscovery", "Autodiscovery failed")); mProgressDialog->reject(); } else { mProgressDialog->accept(); mUi->kcfg_BaseUrl->setText(mAutoDiscoveryJob->ewsUrl()); } mAutoDiscoveryJob->deleteLater(); mAutoDiscoveryJob = nullptr; mProgressDialog->deleteLater(); mProgressDialog = nullptr; } void EwsConfigDialog::tryConnectFinished(KJob *job) { if (job->error() || job != mTryConnectJob) { KMessageBox::error(this, job->errorText(), i18nc("Exchange server connection", "Connection failed")); mUi->serverStatusText->setText(i18nc("Exchange server status", "Failed")); mUi->serverVersionText->setText(i18nc("Exchange server version", "Unknown")); mProgressDialog->reject(); } else { mUi->serverStatusText->setText(i18nc("Exchange server status", "OK")); mUi->serverVersionText->setText(mTryConnectJob->serverVersion().toString()); mProgressDialog->accept(); } //mTryConnectJob->deleteLater(); mTryConnectJob = nullptr; //mProgressDialog->deleteLater(); mProgressDialog = nullptr; } void EwsConfigDialog::autoDiscoveryCancelled() { if (mAutoDiscoveryJob) { mAutoDiscoveryJob->kill(); } mProgressDialog->deleteLater(); mProgressDialog = nullptr; } void EwsConfigDialog::tryConnectCancelled() { if (mTryConnectJob) { mTryConnectJob->kill(); } mTryConnectJobCancelled = true; } void EwsConfigDialog::setAutoDiscoveryNeeded() { mAutoDiscoveryNeeded = true; mTryConnectNeeded = true; mAuthMap.clear(); /* Enable the OK button when at least the e-mail and username fields are set. Additionally if * autodiscovery is disabled, enable the OK button only if the base URL is set. */ bool okEnabled = !mUi->kcfg_Username->text().isEmpty() && !mUi->kcfg_Email->text().isEmpty(); if (!mUi->kcfg_AutoDiscovery->isChecked() && mUi->kcfg_BaseUrl->text().isEmpty()) { okEnabled = false; } mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(okEnabled); mUi->pkeyAuthGroupBox->setEnabled(mUi->authOAuth2RadioButton->isChecked()); } QString EwsConfigDialog::fullUsername() const { QString username = mUi->kcfg_Username->text(); if (mUi->kcfg_HasDomain->isChecked()) { username.prepend(mUi->kcfg_Domain->text() + QStringLiteral("\\")); } return username; } void EwsConfigDialog::dialogAccepted() { if (mUi->kcfg_AutoDiscovery->isChecked() && mAutoDiscoveryNeeded) { mAutoDiscoveryJob = new EwsAutodiscoveryJob(mUi->kcfg_Email->text(), - fullUsername(), mUi->passwordEdit->password(), - mUi->userAgentGroupBox->isEnabled() ? mUi->userAgentEdit->text() : QString(), - mUi->kcfg_EnableNTLMv2->isChecked(), this); + fullUsername(), mUi->passwordEdit->password(), + mUi->userAgentGroupBox->isEnabled() ? mUi->userAgentEdit->text() : QString(), + mUi->kcfg_EnableNTLMv2->isChecked(), this); connect(mAutoDiscoveryJob, &EwsAutodiscoveryJob::result, this, &EwsConfigDialog::autoDiscoveryFinished); mProgressDialog = new EwsProgressDialog(this, EwsProgressDialog::AutoDiscovery); connect(mProgressDialog, &QDialog::rejected, this, &EwsConfigDialog::autoDiscoveryCancelled); mAutoDiscoveryJob->start(); if (!mProgressDialog->exec()) { if (KMessageBox::questionYesNo(this, i18n("Autodiscovery failed. This can be caused by incorrect parameters. Do you still want to save your settings?"), i18n("Exchange server autodiscovery")) == KMessageBox::Yes) { accept(); } return; } } if (mTryConnectNeeded) { EwsClient cli; cli.setUrl(mUi->kcfg_BaseUrl->text()); const auto auth = prepareAuth(); cli.setAuth(auth); if (mUi->userAgentGroupBox->isChecked()) { cli.setUserAgent(mUi->userAgentEdit->text()); } cli.setEnableNTLMv2(mUi->kcfg_EnableNTLMv2->isChecked()); mTryConnectJob = new EwsGetFolderRequest(cli, this); mTryConnectJob->setFolderShape(EwsFolderShape(EwsShapeIdOnly)); mTryConnectJob->setFolderIds(EwsId::List() << EwsId(EwsDIdInbox)); connect(mTryConnectJob, &EwsGetFolderRequest::result, this, &EwsConfigDialog::tryConnectFinished); mProgressDialog = new EwsProgressDialog(this, EwsProgressDialog::TryConnect); connect(mProgressDialog, &QDialog::rejected, this, &EwsConfigDialog::tryConnectCancelled); mTryConnectJob->start(); if (!execJob(mTryConnectJob)) { if (!mTryConnectJobCancelled) { if (KMessageBox::questionYesNo(this, i18n("Connecting to Exchange failed. This can be caused by incorrect parameters. Do you still want to save your settings?"), i18n("Exchange server connection")) == KMessageBox::Yes) { accept(); } } return; } else { accept(); } } if (!mTryConnectNeeded && !mAutoDiscoveryNeeded) { accept(); } } void EwsConfigDialog::enableTryConnect() { mTryConnectNeeded = true; bool baseUrlEmpty = mUi->kcfg_BaseUrl->text().isEmpty(); /* Enable the OK button when at least the e-mail and username fields are set. Additionally if * autodiscovery is disabled, enable the OK button only if the base URL is set. */ bool okEnabled = !mUi->kcfg_Username->text().isEmpty() && !mUi->kcfg_Email->text().isEmpty(); if (!mUi->kcfg_AutoDiscovery->isChecked() && baseUrlEmpty) { okEnabled = false; } mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(okEnabled); mUi->tryConnectButton->setEnabled(!baseUrlEmpty); } void EwsConfigDialog::tryConnect() { EwsClient cli; cli.setUrl(mUi->kcfg_BaseUrl->text()); const auto auth = prepareAuth(); cli.setAuth(auth); if (mUi->userAgentGroupBox->isChecked()) { cli.setUserAgent(mUi->userAgentEdit->text()); } cli.setEnableNTLMv2(mUi->kcfg_EnableNTLMv2->isChecked()); mTryConnectJob = new EwsGetFolderRequest(cli, this); mTryConnectJob->setFolderShape(EwsFolderShape(EwsShapeIdOnly)); mTryConnectJob->setFolderIds(EwsId::List() << EwsId(EwsDIdInbox)); mTryConnectJobCancelled = false; mProgressDialog = new EwsProgressDialog(this, EwsProgressDialog::TryConnect); connect(mProgressDialog, &QDialog::rejected, this, &EwsConfigDialog::tryConnectCancelled); mProgressDialog->show(); if (!execJob(mTryConnectJob)) { if (!mTryConnectJobCancelled) { mUi->serverStatusText->setText(i18nc("Exchange server status", "Failed")); mUi->serverVersionText->setText(i18nc("Exchange server version", "Unknown")); KMessageBox::error(this, mTryConnectJob->errorText(), i18n("Connection failed")); } } else { mUi->serverStatusText->setText(i18nc("Exchange server status", "OK")); mUi->serverVersionText->setText(mTryConnectJob->serverVersion().toString()); } mProgressDialog->hide(); mTryConnectJob = nullptr; } void EwsConfigDialog::userAgentChanged(int) { QString data = mUi->userAgentCombo->currentData().toString(); mUi->userAgentEdit->setEnabled(data.isEmpty()); if (!data.isEmpty()) { mUi->userAgentEdit->setText(data); } } EwsAbstractAuth *EwsConfigDialog::prepareAuth() { EwsAbstractAuth *auth = nullptr; if (mUi->authOAuth2RadioButton->isChecked()) { auth = new EwsOAuth(this, mUi->kcfg_Email->text(), mSettings->oAuth2AppId(), mSettings->oAuth2ReturnUri()); - } else - if (mUi->authUsernameRadioButton->isChecked()) { + } else if (mUi->authUsernameRadioButton->isChecked()) { auth = new EwsPasswordAuth(fullUsername(), this); } auth->setAuthParentWidget(this); #ifdef HAVE_QCA - if (mUi->pkeyAuthGroupBox->isEnabled() && - !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { + if (mUi->pkeyAuthGroupBox->isEnabled() + && !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { auth->setPKeyAuthCertificateFiles(mUi->pkeyAuthCert->text(), mUi->pkeyAuthKey->text()); mAuthMap[pkeyPasswordMapKey] = mUi->pkeyAuthPassword->password(); } #endif connect(auth, &EwsAbstractAuth::requestWalletPassword, this, [&](bool) { - auth->walletPasswordRequestFinished(mUi->passwordEdit->password()); - }); + auth->walletPasswordRequestFinished(mUi->passwordEdit->password()); + }); connect(auth, &EwsAbstractAuth::requestWalletMap, this, [&]() { - auth->walletMapRequestFinished(mAuthMap); - }); + auth->walletMapRequestFinished(mAuthMap); + }); connect(auth, &EwsAbstractAuth::setWalletMap, this, [&](const QMap &map) { - mAuthMap = map; - }); + mAuthMap = map; + }); auth->init(); QEventLoop loop; bool authFinished = false; connect(auth, &EwsAbstractAuth::authSucceeded, this, [&]() { - authFinished = true; - loop.exit(0); - }); - connect(auth, &EwsAbstractAuth::authFailed, this, [&](const QString&) { - authFinished = true; - loop.exit(0); - }); + authFinished = true; + loop.exit(0); + }); + connect(auth, &EwsAbstractAuth::authFailed, this, [&](const QString &) { + authFinished = true; + loop.exit(0); + }); if (auth->authenticate(true)) { if (!authFinished) { loop.exec(); } } return auth; } diff --git a/resources/ews/ewsconfigdialog.h b/resources/ews/ewsconfigdialog.h index 5534bb125..ed7aa634e 100644 --- a/resources/ews/ewsconfigdialog.h +++ b/resources/ews/ewsconfigdialog.h @@ -1,82 +1,79 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 EWSCONFIGDIALOG_H #define EWSCONFIGDIALOG_H #include #include #include - class QDialogButtonBox; class EwsResource; class EwsClient; class KConfigDialogManager; -namespace Ui -{ +namespace Ui { class SetupServerView; } class KJob; class EwsAbstractAuth; class EwsAutodiscoveryJob; class EwsGetFolderRequest; class EwsProgressDialog; class EwsSubscriptionWidget; class EwsSettings; class EwsConfigDialog : public QDialog { Q_OBJECT public: - explicit EwsConfigDialog(EwsResource *parentResource, EwsClient &client, WId windowId, - EwsSettings *settings); + explicit EwsConfigDialog(EwsResource *parentResource, EwsClient &client, WId windowId, EwsSettings *settings); ~EwsConfigDialog() override; private: void save(); void autoDiscoveryFinished(KJob *job); void tryConnectFinished(KJob *job); void performAutoDiscovery(); void autoDiscoveryCancelled(); void tryConnectCancelled(); void setAutoDiscoveryNeeded(); void dialogAccepted(); void enableTryConnect(); void tryConnect(); void userAgentChanged(int index); EwsAbstractAuth *prepareAuth(); QString fullUsername() const; EwsResource *mParentResource = nullptr; KConfigDialogManager *mConfigManager = nullptr; Ui::SetupServerView *mUi = nullptr; QDialogButtonBox *mButtonBox = nullptr; EwsAutodiscoveryJob *mAutoDiscoveryJob = nullptr; EwsGetFolderRequest *mTryConnectJob = nullptr; bool mTryConnectJobCancelled = false; bool mAutoDiscoveryNeeded = false; bool mTryConnectNeeded = false; EwsProgressDialog *mProgressDialog = nullptr; EwsSubscriptionWidget *mSubWidget = nullptr; QPointer mSettings; QMap mAuthMap; }; #endif diff --git a/resources/ews/ewscreateitemjob.cpp b/resources/ews/ewscreateitemjob.cpp index b3e4661bb..34a99bad5 100644 --- a/resources/ews/ewscreateitemjob.cpp +++ b/resources/ews/ewscreateitemjob.cpp @@ -1,78 +1,80 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreateitemjob.h" #include "ewsresource.h" #include "tags/ewstagstore.h" #include "tags/ewsakonaditagssyncjob.h" -EwsCreateItemJob::EwsCreateItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, - EwsResource *parent) - : EwsJob(parent), mItem(item), mCollection(collection), mClient(client), mTagStore(tagStore) +EwsCreateItemJob::EwsCreateItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) + : EwsJob(parent) + , mItem(item) + , mCollection(collection) + , mClient(client) + , mTagStore(tagStore) { } EwsCreateItemJob::~EwsCreateItemJob() { } const Akonadi::Item &EwsCreateItemJob::item() const { return mItem; } void EwsCreateItemJob::start() { /* Before starting check if all Akonadi tags are known to the tag store */ bool syncNeeded = false; Q_FOREACH (const Akonadi::Tag &tag, mItem.tags()) { if (!mTagStore->containsId(tag.id())) { syncNeeded = true; break; } } if (syncNeeded) { EwsAkonadiTagsSyncJob *job = new EwsAkonadiTagsSyncJob(mTagStore, - mClient, qobject_cast(parent())->rootCollection(), this); + mClient, qobject_cast(parent())->rootCollection(), this); connect(job, &EwsAkonadiTagsSyncJob::result, this, &EwsCreateItemJob::tagSyncFinished); job->start(); } else { doStart(); } } void EwsCreateItemJob::populateCommonProperties(EwsItem &item) { if (!mTagStore->writeEwsProperties(mItem, item)) { setErrorMsg(QStringLiteral("Failed to write tags despite an earlier sync")); } } void EwsCreateItemJob::tagSyncFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorText()); emitResult(); } else { doStart(); } } diff --git a/resources/ews/ewscreateitemjob.h b/resources/ews/ewscreateitemjob.h index acf608247..1f046220d 100644 --- a/resources/ews/ewscreateitemjob.h +++ b/resources/ews/ewscreateitemjob.h @@ -1,57 +1,56 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATEITEMJOB_H #define EWSCREATEITEMJOB_H #include #include #include "ewsjob.h" class EwsClient; class EwsItem; class EwsTagStore; class EwsResource; class EwsCreateItemJob : public EwsJob { Q_OBJECT public: - EwsCreateItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateItemJob() override; virtual bool setSend(bool send = true) = 0; const Akonadi::Item &item() const; void start() override; private Q_SLOTS: void tagSyncFinished(KJob *job); protected: void populateCommonProperties(EwsItem &item); virtual void doStart() = 0; Akonadi::Item mItem; Akonadi::Collection mCollection; EwsClient &mClient; EwsTagStore *mTagStore = nullptr; }; #endif diff --git a/resources/ews/ewsfetchfoldersincrjob.cpp b/resources/ews/ewsfetchfoldersincrjob.cpp index 8a9634151..24f30b6b3 100644 --- a/resources/ews/ewsfetchfoldersincrjob.cpp +++ b/resources/ews/ewsfetchfoldersincrjob.cpp @@ -1,584 +1,591 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchfoldersincrjob.h" #include #include #include #include #include #include #include #include #include #include "ewssyncfolderhierarchyrequest.h" #include "ewsgetfolderrequest.h" #include "ewseffectiverights.h" #include "ewsclient.h" #include "ewsresource_debug.h" using namespace Akonadi; /* * Performing an incremental folder tree update relies on the SyncFolderHierarchy EWS request, * which returns a list of changes to the folder tree. Each of the change can be a folder creation, * modification or removal notification. * * The EwsFetchFoldersIncrJob class starts by executing the SyncFolderHierarchy request in order to * retrieve the remote changes. * * Once that is completed the remoteFolderIncrFetchDone() method processes the changes. For each * one a folder descriptor (FolderDescr) is created and inserted into the folder hash (mFolderHash) * keyed by the folder EWS identifier. The folder hash contains all collections that are being * processed during the update. The flags member is used to determine the type of each collection. * For each change the corresponding local collection needs to be retrieved in order to correctly * pass the change list to Akonadi. The following rules apply: * * * For created folders the parent collection is retrieved. This is necessary to put a valid parent * collection to the newly created one. In order to handle cascaded folder creations * (i.e. two folders are created, one child of the other) the parent collection is only retrieved * for the topmost created folder. * * For updated/modified folders both the current (corresponding to the EWS updated folder) and * parent collections are retrieved. The current collection is retrieved to update only the * changed information in the collection. The parent collection is retrieved in order to detect * and handle collection moves as in such case the Akonadi-side parent will not be the same as * the EWS-side parent (the parent retrieved is the EWS-side parent and the Akonadi-side parent * will be known as part of the current collection once retrieved from Akonadi). * * For deleted folders the current (corresponding to the EWS deleted folder) is retrieved. * * After the local Akonadi collections are retrieved the objects are put into their corresponding * folder descriptors in the folder hash. * * Having information about all remote changed folders and their local Akonadi collections the main * part of the synchronization process can be started. * * In the first pass the processRemoteFolders() method looks at all folders in the hash table. * Setting the parent-child relationships is performed at this stage only when the relevant parent * collection has already been processed (the FolderDescr::Processed flag is set). This ensures * that the parent-child relationships are set in the down-the-tree order. In case this condition * is not met for a collection the need for an extra reparenting pass is flagged and the parent * collection setting is not performed. * * Two types of folders are of main interest: * * * For created folders the Akonadi collection object is created and populated with data obtained * from Exchange. If the parent collection has alredy been processed in this pass the parent is * set on the newly created collection. * * For modified folders the Akonadi collection object that was retrieved previously is updated * with data obtained from Exchange. If the folder was moved (the Akonadi parent differs from the * Exchange parent) a collection move is attempted. This needs to be done explicitly using a * CollectionMoveJob as Akonadi is unable to detect collection moves in the sync code. Similar * to the created folder case the move is only performed in case the new parent is flagged as * processed. Additionally the code checks if the new parent is a newly created folder. In such * case the whole incremental sync is aborted as handling this rare corner case would introduce * extra complexity. In case of incremental sync failure the resource will fallback to a full * sync that will handle the case. * * Regardless of collection type the first pass also builds a list of top-level collections * (i.e. ones for which the parent is not in the folder hash) and a hash containing the parent-child * relationship. Both lists will be needed in case a reparenting pass is needed. * * The optional reparenting pass follows the first pass. It is performed if processing of at least * one collection failed due to an unprocessed parent. The reparenting pass focuses on the top-level * folders and starting from each recursively goes into its children setting their parent to itself. * The pass also processes any delayed collection moves in case executing them was impossible in the * first pass. * * The final stage of the synchronization process builds a list of changed and deleted collections * for Akonadi. At this stage all collections must be processed, otherwise an error is raised. If * no collection moves have been executed the job is completed. Otherwise the completion is * singalled once all moves are done. */ static const EwsPropertyField propPidTagContainerClass(0x3613, EwsPropTypeString); class FolderDescr { public: typedef enum { RemoteCreated = 0x0001, RemoteUpdated = 0x0002, RemoteDeleted = 0x0004, Processed = 0x0008 } Flag; Q_DECLARE_FLAGS(Flags, Flag) - FolderDescr() {} + FolderDescr() + { + } Akonadi::Collection collection; Flags flags; EwsFolder ewsFolder; bool isCreated() const { return flags & RemoteCreated; } + bool isModified() const { return flags & RemoteUpdated; } + bool isRemoved() const { return flags & RemoteDeleted; } + bool isProcessed() const { return flags & Processed; } + QString parent() const { return ewsFolder.isValid() ? ewsFolder[EwsFolderFieldParentFolderId].value().id() : QString(); } }; Q_DECLARE_OPERATORS_FOR_FLAGS(FolderDescr::Flags) class EwsFetchFoldersIncrJobPrivate : public QObject { public: - EwsFetchFoldersIncrJobPrivate(EwsFetchFoldersIncrJob *parent, EwsClient &client, - const Collection &rootCollection); + EwsFetchFoldersIncrJobPrivate(EwsFetchFoldersIncrJob *parent, EwsClient &client, const Collection &rootCollection); ~EwsFetchFoldersIncrJobPrivate(); bool processRemoteFolders(); void updateFolderCollection(Collection &collection, const EwsFolder &folder); void reparentRemoteFolder(const QString &id); void moveCollection(const FolderDescr &fd); public Q_SLOTS: void remoteFolderIncrFetchDone(KJob *job); void localFolderFetchDone(KJob *job); void localFolderMoveDone(KJob *job); public: EwsClient &mClient; int mPendingMoveJobs; EwsId::List mRemoteFolderIds; const Collection &mRootCollection; QMultiHash mParentMap; QHash mFolderHash; EwsFetchFoldersIncrJob *q_ptr; Q_DECLARE_PUBLIC(EwsFetchFoldersIncrJob) }; -EwsFetchFoldersIncrJobPrivate::EwsFetchFoldersIncrJobPrivate(EwsFetchFoldersIncrJob *parent, EwsClient &client, - const Collection &rootCollection) - : QObject(parent), mClient(client), mRootCollection(rootCollection), q_ptr(parent) +EwsFetchFoldersIncrJobPrivate::EwsFetchFoldersIncrJobPrivate(EwsFetchFoldersIncrJob *parent, EwsClient &client, const Collection &rootCollection) + : QObject(parent) + , mClient(client) + , mRootCollection(rootCollection) + , q_ptr(parent) { mPendingMoveJobs = 0; } EwsFetchFoldersIncrJobPrivate::~EwsFetchFoldersIncrJobPrivate() { } void EwsFetchFoldersIncrJobPrivate::remoteFolderIncrFetchDone(KJob *job) { Q_Q(EwsFetchFoldersIncrJob); - EwsSyncFolderHierarchyRequest *req = qobject_cast(job); + EwsSyncFolderHierarchyRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsSyncFolderHierarchyRequestjob object"); q->setErrorMsg(QStringLiteral("Invalid EwsSyncFolderHierarchyRequest job object")); q->emitResult(); return; } if (req->error()) { return; } if (req->changes().isEmpty()) { /* Nothing to do. */ q->emitResult(); return; } /* Build a list of local collections to fetch in response to the remote changes. * Use a hash to auto-eliminate duplicates. */ QHash localFetchHash; Q_FOREACH (const EwsSyncFolderHierarchyRequest::Change &ch, req->changes()) { FolderDescr fd; Collection c; switch (ch.type()) { - case EwsSyncFolderHierarchyRequest::Update: { + case EwsSyncFolderHierarchyRequest::Update: + { fd.ewsFolder = ch.folder(); fd.flags |= FolderDescr::RemoteUpdated; EwsId id = fd.ewsFolder[EwsFolderFieldFolderId].value(); mFolderHash.insert(id.id(), fd); /* For updated folders fetch the collection corresponding to that folder and its parent * (the parent will be needed in case of a collection move) */ Collection c2; c2.setRemoteId(fd.parent()); localFetchHash.insert(c2.remoteId(), c2); c.setRemoteId(id.id()); localFetchHash.insert(c.remoteId(), c); break; } - case EwsSyncFolderHierarchyRequest::Create: { + case EwsSyncFolderHierarchyRequest::Create: + { fd.ewsFolder = ch.folder(); fd.flags |= FolderDescr::RemoteCreated; EwsId id = fd.ewsFolder[EwsFolderFieldFolderId].value(); mFolderHash.insert(id.id(), fd); c.setRemoteId(fd.parent()); /* For created folders fetch the parent collection on Exchange side. Don't do this * when the parent collection has also been created as it would fail. */ if (!mFolderHash.value(fd.parent()).isCreated()) { localFetchHash.insert(c.remoteId(), c); } break; } - case EwsSyncFolderHierarchyRequest::Delete: { + case EwsSyncFolderHierarchyRequest::Delete: fd.flags |= FolderDescr::RemoteDeleted; mFolderHash.insert(ch.folderId().id(), fd); /* For deleted folders fetch the collection corresponding to the deleted folder. */ c.setRemoteId(ch.folderId().id()); localFetchHash.insert(c.remoteId(), c); break; - } default: break; } } if (localFetchHash.isEmpty()) { /* In either case at least one folder is expected to be queued for fetching. */ q->setErrorMsg(QStringLiteral("Expected at least one local folder to fetch.")); q->emitResult(); return; } q->mSyncState = req->syncState(); CollectionFetchJob *fetchJob = new CollectionFetchJob(localFetchHash.values().toVector(), - CollectionFetchJob::Base); + CollectionFetchJob::Base); CollectionFetchScope scope; scope.setAncestorRetrieval(CollectionFetchScope::All); fetchJob->setFetchScope(scope); connect(fetchJob, &CollectionFetchJob::result, this, &EwsFetchFoldersIncrJobPrivate::localFolderFetchDone); q->addSubjob(fetchJob); } void EwsFetchFoldersIncrJobPrivate::localFolderFetchDone(KJob *job) { Q_Q(EwsFetchFoldersIncrJob); if (job->error()) { q->setErrorMsg(QStringLiteral("Failed to fetch local collections.")); q->emitResult(); return; } - CollectionFetchJob *fetchJob = qobject_cast(job); + CollectionFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); Q_FOREACH (const Collection &col, fetchJob->collections()) { /* Retrieve the folder descriptor for this collection. Note that a new descriptor will be * created if it does not yet exist. */ FolderDescr &fd = mFolderHash[col.remoteId()]; fd.collection = col; if (!fd.flags) { /* This collection has just been created and this means that it's a parent collection * added in response to a created folder. Since the collection is here just for reference * it will not be processed by processRemoteFolders() and can be marked accordingly. */ fd.flags |= FolderDescr::Processed; } } if (!processRemoteFolders()) { q->setErrorMsg(QStringLiteral("Failed to process remote folder list.")); q->emitResult(); } if (!mPendingMoveJobs) { q->emitResult(); } /* Otherwise wait for the move requests to finish. */ } bool EwsFetchFoldersIncrJobPrivate::processRemoteFolders() { Q_Q(EwsFetchFoldersIncrJob); /* The list of top-level collections. It contains identifiers of collections for which the * parent collection is not in the folder hash. This list is used at a later stage when * setting collections parents. Building a top-level list is necessary as those updates can * only be safely performed down the tree. */ QStringList topLevelList; bool reparentPassNeeded = false; /* Iterate over all changed folders. */ for (auto it = mFolderHash.begin(), end = mFolderHash.end(); it != end; ++it) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Processing: ") << it.key(); if (it->isModified()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Collection was modified"); updateFolderCollection(it->collection, it->ewsFolder); if (it->parent() != it->collection.parentCollection().remoteId()) { /* This collection has been moved. Since Akonadi currently cannot handle collection * moves the resource needs to manually move it. */ qCDebugNC(EWSRES_LOG) << QStringLiteral("Collection was moved"); /* Before moving check if the parent exists and has been processed. */ auto parentIt = mFolderHash.find(it->parent()); if (parentIt == mFolderHash.end()) { q->setErrorMsg(QStringLiteral("Found moved collection without new parent.")); return false; } if (parentIt->isCreated()) { /* Further workarounds could be done here to ensure that the parent is manually * created before triggering a move but this would just unnecessarily complicate * matters. Instead just surrender and retry with a full sync. */ q->setErrorMsg(QStringLiteral("Found moved collection to a just created parent.")); return false; } if (!parentIt->isProcessed()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Parent not yet processed - delaying"); /* The new parent collection is not yet processed - defer the move to make * sure all the operations are done in down-the-tree order. */ reparentPassNeeded = true; } else { moveCollection(*it); it->collection.setParentCollection(parentIt->collection); it->flags |= FolderDescr::Processed; } } else { /* No collection move happening so nothing else to for this one. */ it->flags |= FolderDescr::Processed; } } else if (it->isCreated()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Collection was created"); it->collection.setRemoteId(it.key()); updateFolderCollection(it->collection, it->ewsFolder); auto parentIt = mFolderHash.find(it->parent()); if (parentIt == mFolderHash.end()) { q->setErrorMsg(QStringLiteral("Found created collection without parent.")); return false; } /* Check if the parent has already been processed. If yes, set the parent of this * collection and mark this one as done. Otherwise a second pass will be needed later. */ if (parentIt->isProcessed()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Processing"); it->collection.setParentCollection(parentIt->collection); it->flags |= FolderDescr::Processed; } else { qCDebugNC(EWSRES_LOG) << QStringLiteral("Parent not yet processed - delaying"); reparentPassNeeded = true; } } else { qCDebugNC(EWSRES_LOG) << QStringLiteral("Collection is not remotely changed"); /* This is either a deleted folder or a parent to an added collection. No processing * needed for either of those. */ it->flags |= FolderDescr::Processed; } /* Check if this collection is a top-level collection. */ if (!mFolderHash.contains(it->parent())) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Collection is top level"); topLevelList.append(it.key()); } /* Put the collection into the parent map. This will help running the reparent pass. */ if (!it->parent().isNull()) { mParentMap.insert(it->parent(), it.key()); } } if (reparentPassNeeded) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Executing reparent pass") << topLevelList; Q_FOREACH (const QString &id, topLevelList) { reparentRemoteFolder(id); } } /* Build the resulting collection list. */ for (auto it = mFolderHash.begin(), end = mFolderHash.end(); it != end; ++it) { if (it->isRemoved()) { q->mDeletedFolders.append(it->collection); } else if (it->isProcessed()) { q->mChangedFolders.append(it->collection); } else { qCWarningNC(EWSRES_LOG) << QStringLiteral("Found unprocessed collection %1").arg(it.key()); return false; } } return true; } void EwsFetchFoldersIncrJobPrivate::reparentRemoteFolder(const QString &id) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Reparenting") << id; QStringList children = mParentMap.values(id); FolderDescr &fd = mFolderHash[id]; Q_FOREACH (const QString &childId, children) { FolderDescr &childFd = mFolderHash[childId]; - if (!childFd.isProcessed() && childFd.isModified() && - childFd.parent() != childFd.collection.parentCollection().remoteId()) { + if (!childFd.isProcessed() && childFd.isModified() + && childFd.parent() != childFd.collection.parentCollection().remoteId()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Found moved collection"); /* Found unprocessed collection move. */ moveCollection(childFd); } childFd.collection.setParentCollection(fd.collection); reparentRemoteFolder(childId); } fd.flags |= FolderDescr::Processed; } void EwsFetchFoldersIncrJobPrivate::moveCollection(const FolderDescr &fd) { - qCDebugNC(EWSRES_LOG) << QStringLiteral("Moving collection") << fd.collection.remoteId() << - QStringLiteral("from") << fd.collection.parentCollection().remoteId() << - QStringLiteral("to") << fd.parent(); + qCDebugNC(EWSRES_LOG) << QStringLiteral("Moving collection") << fd.collection.remoteId() + <start(); } void EwsFetchFoldersIncrJobPrivate::localFolderMoveDone(KJob *job) { Q_Q(EwsFetchFoldersIncrJob); if (job->error()) { q->setErrorMsg(QStringLiteral("Failed to move collection.")); q->emitResult(); return; } if (--mPendingMoveJobs == 0) { q->emitResult(); } } void EwsFetchFoldersIncrJobPrivate::updateFolderCollection(Collection &collection, const EwsFolder &folder) { collection.setName(folder[EwsFolderFieldDisplayName].toString()); QStringList mimeTypes; QString contClass = folder[propPidTagContainerClass].toString(); mimeTypes.append(Collection::mimeType()); switch (folder.type()) { case EwsFolderTypeCalendar: mimeTypes.append(KCalCore::Event::eventMimeType()); break; case EwsFolderTypeContacts: mimeTypes.append(KContacts::Addressee::mimeType()); mimeTypes.append(KContacts::ContactGroup::mimeType()); break; case EwsFolderTypeTasks: mimeTypes.append(KCalCore::Todo::todoMimeType()); break; case EwsFolderTypeMail: if (contClass == QStringLiteral("IPF.Note") || contClass.isEmpty()) { mimeTypes.append(KMime::Message::mimeType()); } break; default: break; } collection.setContentMimeTypes(mimeTypes); Collection::Rights colRights; EwsEffectiveRights ewsRights = folder[EwsFolderFieldEffectiveRights].value(); // FIXME: For now full read/write support is only implemented for e-mail. In order to avoid // potential problems block write access to all other folder types. if (folder.type() == EwsFolderTypeMail) { if (ewsRights.canDelete()) { colRights |= Collection::CanDeleteCollection | Collection::CanDeleteItem; } if (ewsRights.canModify()) { colRights |= Collection::CanChangeCollection | Collection::CanChangeItem; } if (ewsRights.canCreateContents()) { colRights |= Collection::CanCreateItem; } if (ewsRights.canCreateHierarchy()) { colRights |= Collection::CanCreateCollection; } } collection.setRights(colRights); EwsId id = folder[EwsFolderFieldFolderId].value(); collection.setRemoteRevision(id.changeKey()); } - -EwsFetchFoldersIncrJob::EwsFetchFoldersIncrJob(EwsClient &client, const QString &syncState, - const Akonadi::Collection &rootCollection, QObject *parent) - : EwsJob(parent), mSyncState(syncState), - d_ptr(new EwsFetchFoldersIncrJobPrivate(this, client, rootCollection)) +EwsFetchFoldersIncrJob::EwsFetchFoldersIncrJob(EwsClient &client, const QString &syncState, const Akonadi::Collection &rootCollection, QObject *parent) + : EwsJob(parent) + , mSyncState(syncState) + , d_ptr(new EwsFetchFoldersIncrJobPrivate(this, client, rootCollection)) { qRegisterMetaType(); } EwsFetchFoldersIncrJob::~EwsFetchFoldersIncrJob() { } void EwsFetchFoldersIncrJob::start() { Q_D(const EwsFetchFoldersIncrJob); EwsSyncFolderHierarchyRequest *syncFoldersReq = new EwsSyncFolderHierarchyRequest(d->mClient, this); syncFoldersReq->setFolderId(EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape; shape << propPidTagContainerClass; shape << EwsPropertyField(QStringLiteral("folder:EffectiveRights")); shape << EwsPropertyField(QStringLiteral("folder:ParentFolderId")); syncFoldersReq->setFolderShape(shape); if (!mSyncState.isNull()) { syncFoldersReq->setSyncState(mSyncState); } connect(syncFoldersReq, &EwsSyncFolderHierarchyRequest::result, d, &EwsFetchFoldersIncrJobPrivate::remoteFolderIncrFetchDone); // Don't add this as a subjob as the error is handled in its own way rather than throwing an // error code to the parent. syncFoldersReq->start(); } QDebug operator<<(QDebug debug, const FolderDescr &fd) { QDebugStateSaver saver(debug); QDebug d = debug.nospace().noquote(); d << QStringLiteral("FolderDescr("); d << fd.collection; d << fd.flags; d << ')'; return debug; } diff --git a/resources/ews/ewsfetchfoldersincrjob.h b/resources/ews/ewsfetchfoldersincrjob.h index 3407f6eb7..8be3018f5 100644 --- a/resources/ews/ewsfetchfoldersincrjob.h +++ b/resources/ews/ewsfetchfoldersincrjob.h @@ -1,68 +1,69 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFETCHFOLDERSINCRJOB_H #define EWSFETCHFOLDERSINCRJOB_H #include #include #include "ewsjob.h" #include "ewsfolder.h" class EwsClient; class EwsFetchFoldersIncrJobPrivate; class EwsFetchFoldersIncrJob : public EwsJob { Q_OBJECT public: - EwsFetchFoldersIncrJob(EwsClient &client, const QString &syncState, - const Akonadi::Collection &rootCollection, QObject *parent); + EwsFetchFoldersIncrJob(EwsClient &client, const QString &syncState, const Akonadi::Collection &rootCollection, QObject *parent); ~EwsFetchFoldersIncrJob() override; Akonadi::Collection::List changedFolders() const { return mChangedFolders; } + Akonadi::Collection::List deletedFolders() const { return mDeletedFolders; } + const QString &syncState() const { return mSyncState; } void start() override; Q_SIGNALS: void status(int status, const QString &message = QString()); void percent(int progress); private: Akonadi::Collection::List mChangedFolders; Akonadi::Collection::List mDeletedFolders; QString mSyncState; QScopedPointer d_ptr; Q_DECLARE_PRIVATE(EwsFetchFoldersIncrJob) }; #endif diff --git a/resources/ews/ewsfetchfoldersjob.cpp b/resources/ews/ewsfetchfoldersjob.cpp index dea9bd97a..96dc2215c 100644 --- a/resources/ews/ewsfetchfoldersjob.cpp +++ b/resources/ews/ewsfetchfoldersjob.cpp @@ -1,386 +1,383 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchfoldersjob.h" #include #include #include #include #include #include #include #include #include #include "ewssyncfolderhierarchyrequest.h" #include "ewsgetfolderrequest.h" #include "ewseffectiverights.h" #include "ewsclient.h" #include "ewsresource_debug.h" using namespace Akonadi; static const EwsPropertyField propPidTagContainerClass(0x3613, EwsPropTypeString); static Q_CONSTEXPR int fetchBatchSize = 50; class EwsFetchFoldersJobPrivate : public QObject { public: - EwsFetchFoldersJobPrivate(EwsFetchFoldersJob *parent, EwsClient &client, - const Collection &rootCollection); + EwsFetchFoldersJobPrivate(EwsFetchFoldersJob *parent, EwsClient &client, const Collection &rootCollection); ~EwsFetchFoldersJobPrivate(); void processRemoteFolders(); Collection createFolderCollection(const EwsFolder &folder); void buildCollectionList(); void buildChildCollectionList(const Collection &col); public Q_SLOTS: void remoteFolderFullFetchDone(KJob *job); void remoteFolderIdFullFetchDone(KJob *job); void remoteFolderDetailFetchDone(KJob *job); public: EwsClient &mClient; int mPendingFetchJobs; int mPendingMoveJobs; EwsId::List mRemoteFolderIds; const Collection &mRootCollection; EwsFolder::List mRemoteChangedFolders; // Contains details of folders that need update // (either created or changed) QHash mCollectionMap; QMultiHash mParentMap; EwsFetchFoldersJob *q_ptr; Q_DECLARE_PUBLIC(EwsFetchFoldersJob) }; -EwsFetchFoldersJobPrivate::EwsFetchFoldersJobPrivate(EwsFetchFoldersJob *parent, EwsClient &client, - const Collection &rootCollection) - : QObject(parent), mClient(client), mRootCollection(rootCollection), q_ptr(parent) +EwsFetchFoldersJobPrivate::EwsFetchFoldersJobPrivate(EwsFetchFoldersJob *parent, EwsClient &client, const Collection &rootCollection) + : QObject(parent) + , mClient(client) + , mRootCollection(rootCollection) + , q_ptr(parent) { mPendingFetchJobs = 0; mPendingMoveJobs = 0; } EwsFetchFoldersJobPrivate::~EwsFetchFoldersJobPrivate() { } void EwsFetchFoldersJobPrivate::remoteFolderFullFetchDone(KJob *job) { Q_Q(EwsFetchFoldersJob); - EwsSyncFolderHierarchyRequest *req = qobject_cast(job); + EwsSyncFolderHierarchyRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsSyncFolderHierarchyRequest job object"); q->setErrorMsg(QStringLiteral("Invalid EwsSyncFolderHierarchyRequest job object")); q->emitResult(); return; } if (req->error()) { /* It has been reported that the SyncFolderHierarchyRequest can fail with Internal Server * Error (possibly because of a large number of folders). In order to work around this * try to fallback to fetching just the folder identifiers and retrieve the details later. */ qCDebug(EWSRES_LOG) << QStringLiteral("Full fetch failed. Trying to fetch ids only."); EwsSyncFolderHierarchyRequest *syncFoldersReq = new EwsSyncFolderHierarchyRequest(mClient, this); syncFoldersReq->setFolderId(EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape(EwsShapeIdOnly); syncFoldersReq->setFolderShape(shape); connect(syncFoldersReq, &EwsSyncFolderHierarchyRequest::result, this, &EwsFetchFoldersJobPrivate::remoteFolderIdFullFetchDone); q->addSubjob(syncFoldersReq); syncFoldersReq->start(); return; } Q_FOREACH (const EwsSyncFolderHierarchyRequest::Change &ch, req->changes()) { if (ch.type() == EwsSyncFolderHierarchyRequest::Create) { mRemoteChangedFolders.append(ch.folder()); } else { q->setErrorMsg(QStringLiteral("Got non-create change for full sync.")); q->emitResult(); return; } } if (req->includesLastItem()) { processRemoteFolders(); buildCollectionList(); q->mSyncState = req->syncState(); q->emitResult(); } else { EwsSyncFolderHierarchyRequest *syncFoldersReq = new EwsSyncFolderHierarchyRequest(mClient, this); syncFoldersReq->setFolderId(EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape; shape << propPidTagContainerClass; shape << EwsPropertyField(QStringLiteral("folder:EffectiveRights")); shape << EwsPropertyField(QStringLiteral("folder:ParentFolderId")); syncFoldersReq->setFolderShape(shape); syncFoldersReq->setSyncState(req->syncState()); connect(syncFoldersReq, &EwsSyncFolderHierarchyRequest::result, this, &EwsFetchFoldersJobPrivate::remoteFolderFullFetchDone); syncFoldersReq->start(); } } void EwsFetchFoldersJobPrivate::remoteFolderIdFullFetchDone(KJob *job) { Q_Q(EwsFetchFoldersJob); - EwsSyncFolderHierarchyRequest *req = qobject_cast(job); + EwsSyncFolderHierarchyRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsSyncFolderHierarchyRequest job object"); q->setErrorMsg(QStringLiteral("Invalid EwsSyncFolderHierarchyRequest job object")); q->emitResult(); return; } if (req->error()) { return; } Q_FOREACH (const EwsSyncFolderHierarchyRequest::Change &ch, req->changes()) { if (ch.type() == EwsSyncFolderHierarchyRequest::Create) { mRemoteFolderIds.append(ch.folder()[EwsFolderFieldFolderId].value()); } else { q->setErrorMsg(QStringLiteral("Got non-create change for full sync.")); q->emitResult(); return; } } if (req->includesLastItem()) { EwsFolderShape shape(EwsShapeDefault); shape << propPidTagContainerClass; shape << EwsPropertyField(QStringLiteral("folder:EffectiveRights")); shape << EwsPropertyField(QStringLiteral("folder:ParentFolderId")); mPendingFetchJobs = 0; for (int i = 0, total = mRemoteFolderIds.size(); i < total; i += fetchBatchSize) { EwsGetFolderRequest *req = new EwsGetFolderRequest(mClient, this); req->setFolderIds(mRemoteFolderIds.mid(i, fetchBatchSize)); req->setFolderShape(shape); connect(req, &EwsSyncFolderHierarchyRequest::result, this, &EwsFetchFoldersJobPrivate::remoteFolderDetailFetchDone); req->start(); q->addSubjob(req); mPendingFetchJobs++; } qCDebugNC(EWSRES_LOG) << QStringLiteral("Starting %1 folder fetch jobs.").arg(mPendingFetchJobs); q->mSyncState = req->syncState(); } else { EwsSyncFolderHierarchyRequest *syncFoldersReq = new EwsSyncFolderHierarchyRequest(mClient, this); syncFoldersReq->setFolderId(EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape(EwsShapeIdOnly); syncFoldersReq->setFolderShape(shape); syncFoldersReq->setSyncState(req->syncState()); connect(syncFoldersReq, &EwsSyncFolderHierarchyRequest::result, this, &EwsFetchFoldersJobPrivate::remoteFolderIdFullFetchDone); q->addSubjob(syncFoldersReq); syncFoldersReq->start(); } } void EwsFetchFoldersJobPrivate::remoteFolderDetailFetchDone(KJob *job) { Q_Q(EwsFetchFoldersJob); - EwsGetFolderRequest *req = qobject_cast(job); + EwsGetFolderRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsGetFolderRequest job object"); q->setErrorMsg(QStringLiteral("Invalid EwsGetFolderRequest job object")); q->emitResult(); return; } if (req->error()) { return; } Q_FOREACH (const EwsGetFolderRequest::Response &resp, req->responses()) { if (resp.isSuccess()) { mRemoteChangedFolders.append(resp.folder()); } else { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch folder details."); } } mPendingFetchJobs--; qCDebugNC(EWSRES_LOG) << QStringLiteral("%1 folder fetch jobs pending").arg(mPendingFetchJobs); if (mPendingFetchJobs == 0) { qCDebugNC(EWSRES_LOG) << QStringLiteral("All folder fetch jobs complete"); processRemoteFolders(); buildCollectionList(); - q->emitResult(); } } void EwsFetchFoldersJobPrivate::processRemoteFolders() { /* mCollectionMap contains the global collection list keyed by the EWS ID. */ /* mParentMap contains the parent->child map for each collection. */ /* Iterate over all changed folders. */ Q_FOREACH (const EwsFolder &folder, mRemoteChangedFolders) { /* Create a collection for each folder. */ Collection c = createFolderCollection(folder); /* Insert it into the global collection list. */ mCollectionMap.insert(c.remoteId(), c); /* Determine the parent and insert a parent->child relationship. * Don't use Collection::setParentCollection() yet as the collection object will be updated * which will cause the parent->child relationship to be broken. This happens because the * collection object holds a copy of the parent collection object. An update to that * object in the list will not be visible in the copy inside of the child object. */ EwsId parentId = folder[EwsFolderFieldParentFolderId].value(); mParentMap.insert(parentId.id(), c.remoteId()); } } void EwsFetchFoldersJobPrivate::buildCollectionList() { Q_Q(EwsFetchFoldersJob); - q->mFolders.append(mRootCollection); buildChildCollectionList(mRootCollection); if (!mCollectionMap.isEmpty()) { q->setErrorMsg(QStringLiteral("Found orphaned collections")); } } void EwsFetchFoldersJobPrivate::buildChildCollectionList(const Collection &col) { Q_Q(EwsFetchFoldersJob); QStringList children = mParentMap.values(col.remoteId()); Q_FOREACH (const QString &childId, children) { Collection child(mCollectionMap.take(childId)); child.setParentCollection(col); q->mFolders.append(child); buildChildCollectionList(child); } } Collection EwsFetchFoldersJobPrivate::createFolderCollection(const EwsFolder &folder) { Collection collection; collection.setName(folder[EwsFolderFieldDisplayName].toString()); QStringList mimeTypes; QString contClass = folder[propPidTagContainerClass].toString(); mimeTypes.append(Collection::mimeType()); switch (folder.type()) { case EwsFolderTypeCalendar: mimeTypes.append(KCalCore::Event::eventMimeType()); break; case EwsFolderTypeContacts: mimeTypes.append(KContacts::Addressee::mimeType()); mimeTypes.append(KContacts::ContactGroup::mimeType()); break; case EwsFolderTypeTasks: mimeTypes.append(KCalCore::Todo::todoMimeType()); break; case EwsFolderTypeMail: if (contClass == QStringLiteral("IPF.Note") || contClass.isEmpty()) { mimeTypes.append(KMime::Message::mimeType()); } break; default: break; } collection.setContentMimeTypes(mimeTypes); Collection::Rights colRights; EwsEffectiveRights ewsRights = folder[EwsFolderFieldEffectiveRights].value(); // FIXME: For now full read/write support is only implemented for e-mail. In order to avoid // potential problems block write access to all other folder types. if (folder.type() == EwsFolderTypeMail) { if (ewsRights.canDelete()) { colRights |= Collection::CanDeleteCollection | Collection::CanDeleteItem; } if (ewsRights.canModify()) { colRights |= Collection::CanChangeCollection | Collection::CanChangeItem; } if (ewsRights.canCreateContents()) { colRights |= Collection::CanCreateItem; } if (ewsRights.canCreateHierarchy()) { colRights |= Collection::CanCreateCollection; } } collection.setRights(colRights); EwsId id = folder[EwsFolderFieldFolderId].value(); collection.setRemoteId(id.id()); collection.setRemoteRevision(id.changeKey()); return collection; } -EwsFetchFoldersJob::EwsFetchFoldersJob(EwsClient &client, const Akonadi::Collection &rootCollection, - QObject *parent) - : EwsJob(parent), - d_ptr(new EwsFetchFoldersJobPrivate(this, client, rootCollection)) +EwsFetchFoldersJob::EwsFetchFoldersJob(EwsClient &client, const Akonadi::Collection &rootCollection, QObject *parent) + : EwsJob(parent) + , d_ptr(new EwsFetchFoldersJobPrivate(this, client, rootCollection)) { qRegisterMetaType(); } EwsFetchFoldersJob::~EwsFetchFoldersJob() { } void EwsFetchFoldersJob::start() { Q_D(const EwsFetchFoldersJob); EwsSyncFolderHierarchyRequest *syncFoldersReq = new EwsSyncFolderHierarchyRequest(d->mClient, this); syncFoldersReq->setFolderId(EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape; shape << propPidTagContainerClass; shape << EwsPropertyField(QStringLiteral("folder:EffectiveRights")); shape << EwsPropertyField(QStringLiteral("folder:ParentFolderId")); syncFoldersReq->setFolderShape(shape); if (!mSyncState.isNull()) { syncFoldersReq->setSyncState(mSyncState); } connect(syncFoldersReq, &EwsSyncFolderHierarchyRequest::result, d, &EwsFetchFoldersJobPrivate::remoteFolderFullFetchDone); // Don't add this as a subjob as the error is handled in its own way rather than throwing an // error code to the parent. syncFoldersReq->start(); } - diff --git a/resources/ews/ewsfetchfoldersjob.h b/resources/ews/ewsfetchfoldersjob.h index 038133a40..71f605603 100644 --- a/resources/ews/ewsfetchfoldersjob.h +++ b/resources/ews/ewsfetchfoldersjob.h @@ -1,62 +1,63 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFETCHFOLDERSJOB_H #define EWSFETCHFOLDERSJOB_H #include #include #include "ewsjob.h" #include "ewsfolder.h" class EwsClient; class EwsFetchFoldersJobPrivate; class EwsFetchFoldersJob : public EwsJob { Q_OBJECT public: EwsFetchFoldersJob(EwsClient &client, const Akonadi::Collection &rootCollection, QObject *parent); ~EwsFetchFoldersJob() override; Akonadi::Collection::List folders() const { return mFolders; } + const QString &syncState() const { return mSyncState; } void start() override; Q_SIGNALS: void status(int status, const QString &message = QString()); void percent(int progress); private: Akonadi::Collection::List mFolders; QString mSyncState; QScopedPointer d_ptr; Q_DECLARE_PRIVATE(EwsFetchFoldersJob) }; #endif diff --git a/resources/ews/ewsfetchitemdetailjob.cpp b/resources/ews/ewsfetchitemdetailjob.cpp index f9347da87..43984c214 100644 --- a/resources/ews/ewsfetchitemdetailjob.cpp +++ b/resources/ews/ewsfetchitemdetailjob.cpp @@ -1,65 +1,67 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchitemdetailjob.h" #include "ewsgetitemrequest.h" EwsFetchItemDetailJob::EwsFetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) - : KCompositeJob(parent), mDeletedItems(nullptr), mClient(client), mCollection(collection) + : KCompositeJob(parent) + , mDeletedItems(nullptr) + , mClient(client) + , mCollection(collection) { mRequest = new EwsGetItemRequest(client, this); connect(mRequest.data(), &KJob::result, this, &EwsFetchItemDetailJob::itemDetailFetched); addSubjob(mRequest); } EwsFetchItemDetailJob::~EwsFetchItemDetailJob() { } -void EwsFetchItemDetailJob::setItemLists(const Akonadi::Item::List &changedItems, - Akonadi::Item::List *deletedItems) +void EwsFetchItemDetailJob::setItemLists(const Akonadi::Item::List &changedItems, Akonadi::Item::List *deletedItems) { mChangedItems = changedItems; mDeletedItems = deletedItems; EwsId::List ids; ids.reserve(changedItems.count()); Q_FOREACH (const Akonadi::Item &item, changedItems) { EwsId id(item.remoteId(), item.remoteRevision()); ids.append(id); } mRequest->setItemIds(ids); } void EwsFetchItemDetailJob::itemDetailFetched(KJob *job) { if (!job->error() && job == mRequest) { Q_ASSERT(mChangedItems.size() == mRequest->responses().size()); processItems(mRequest->responses()); } } void EwsFetchItemDetailJob::start() { mRequest->start(); } diff --git a/resources/ews/ewsfetchitemdetailjob.h b/resources/ews/ewsfetchitemdetailjob.h index efc8fabea..d02e85bc2 100644 --- a/resources/ews/ewsfetchitemdetailjob.h +++ b/resources/ews/ewsfetchitemdetailjob.h @@ -1,62 +1,61 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFETCHITEMDETAILJOB_H #define EWSFETCHITEMDETAILJOB_H #include - #include #include #include "ewsclient.h" #include "ewsgetitemrequest.h" #include "ewsid.h" #include "ewsitem.h" #include "ewstypes.h" class EwsFetchItemDetailJob : public KCompositeJob { Q_OBJECT public: EwsFetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection); ~EwsFetchItemDetailJob() override; void setItemLists(const Akonadi::Item::List &changedItems, Akonadi::Item::List *deletedItems); Akonadi::Item::List changedItems() const { return mChangedItems; } void start() override; protected: virtual void processItems(const QList &responses) = 0; QPointer mRequest; Akonadi::Item::List mChangedItems; Akonadi::Item::List *mDeletedItems = nullptr; EwsClient &mClient; const Akonadi::Collection mCollection; private Q_SLOTS: void itemDetailFetched(KJob *job); }; #endif diff --git a/resources/ews/ewsfetchitemsjob.cpp b/resources/ews/ewsfetchitemsjob.cpp index f9998bc9b..c742ff005 100644 --- a/resources/ews/ewsfetchitemsjob.cpp +++ b/resources/ews/ewsfetchitemsjob.cpp @@ -1,452 +1,457 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchitemsjob.h" #include #include #include "ewsfinditemrequest.h" #include "ewssyncfolderitemsrequest.h" #include "ewsclient.h" #include "ewsmailbox.h" #include "ewsitemhandler.h" #include "ewsfetchitemdetailjob.h" #include "tags/ewstagstore.h" #include "tags/ewsakonaditagssyncjob.h" #include "ewsresource.h" #include "ewsresource_debug.h" using namespace Akonadi; static Q_CONSTEXPR int listBatchSize = 100; static Q_CONSTEXPR int fetchBatchSize = 50; /** * The fetch items job is processed in two stages. * * The first stage is to query the list of messages on the remote and local sides. For this purpose * an EwsSyncFolderItemsRequest is started to retrieve remote items (list of ids only) and an ItemFetchJob * is started to fetch local items (from cache only). Both of these jobs are started simultaneously. * * The second stage begins when both item list query jobs have finished. The goal of this stage is * to determine a list of items to fetch more details for. Since the EwsSyncFolderItemsRequest can * retrieve incremental information further processing depends on whether the sync request was a * full sync (no sync state) or an incremental sync. * * In case of a full sync both item lists are compared to determine lists of new/changed items and * deleted items. For an incremental sync there is no need to compare as the lists of * added/changed/deleted items are already given. 'IsRead' flag changes changes are treated * specially - the modification is trivial and is performed straight away without fetching item * details. * * The list of new/changed items is then used to perform a second remote request in order to * retrieve the details of these items. For e-mail items the second fetch only retrieves the * item headers. For other items the full MIME content is fetched. * * In case of an incremental sync the compare code checks if items marked as 'changed' or 'deleted' * exist in Akonadi database. If not an error is raised. This serves as an information to the * resource class that an incremental sync has failed due to an out-of-sync state and a full sync * is required to bring thing back to order. * * In addition to a regular sync it is sometimes necessary to check for existence of some specific * items. This happens when some operation failed and the resource tries to work its way around to * get Akonadi into a synchronous state after the failure. For this purpose the caller can provide * a list of item identifiers to look for besides the regular sync. If this list contains elements * another request (EwsGetItemRequest) is issued in parallel for the selected items. Once this * request returns the list is cross-checked with the server responses. If an item is found on the * server side it is added to the "new items" list. Otherwise it is added to the "deleted items" * list. In the latter case the code for determining if the deleted item is in the Akonadi database * will not raise an error for such deleted item. This helps to avoid unnecessary full syncs. * * The fetch item job also allows providing a list of expected item changes. This is necessary * because the incremental sync will return events for operations made by the resource itself. This * can lead to false failures when for example an item was deleted (i.e. does not exist in Akonadi * any more) and the incremental sync returns a delete event for this item. Typically this would * result in an error and force a full sync. Providing this list allows for this particular error * to be safely ignored. */ -EwsFetchItemsJob::EwsFetchItemsJob(const Collection &collection, EwsClient &client, - const QString &syncState, const EwsId::List &itemsToCheck, - EwsTagStore *tagStore, EwsResource *parent) - : EwsJob(parent), mCollection(collection), mClient(client), mItemsToCheck(itemsToCheck), - mPendingJobs(0), mTotalItems(0), mSyncState(syncState), mFullSync(syncState.isNull()), - mTagStore(tagStore), mTagsSynced(false) +EwsFetchItemsJob::EwsFetchItemsJob(const Collection &collection, EwsClient &client, const QString &syncState, const EwsId::List &itemsToCheck, EwsTagStore *tagStore, EwsResource *parent) + : EwsJob(parent) + , mCollection(collection) + , mClient(client) + , mItemsToCheck(itemsToCheck) + , mPendingJobs(0) + , mTotalItems(0) + , mSyncState(syncState) + , mFullSync(syncState.isNull()) + , mTagStore(tagStore) + , mTagsSynced(false) { qRegisterMetaType(); } EwsFetchItemsJob::~EwsFetchItemsJob() { } void EwsFetchItemsJob::start() { /* Begin stage 1 - query item list from local and remote side. */ EwsSyncFolderItemsRequest *syncItemsReq = new EwsSyncFolderItemsRequest(mClient, this); syncItemsReq->setFolderId(EwsId(mCollection.remoteId(), mCollection.remoteRevision())); EwsItemShape shape(EwsShapeIdOnly); shape << EwsResource::tagsProperty; syncItemsReq->setItemShape(shape); if (!mSyncState.isNull()) { syncItemsReq->setSyncState(mSyncState); } syncItemsReq->setMaxChanges(listBatchSize); connect(syncItemsReq, &EwsSyncFolderItemsRequest::result, this, &EwsFetchItemsJob::remoteItemFetchDone); addSubjob(syncItemsReq); ItemFetchJob *itemJob = new ItemFetchJob(mCollection); ItemFetchScope itemScope; itemScope.setCacheOnly(true); itemScope.fetchFullPayload(false); itemJob->setFetchScope(itemScope); connect(itemJob, &ItemFetchJob::result, this, &EwsFetchItemsJob::localItemFetchDone); addSubjob(itemJob); mPendingJobs = 2; syncItemsReq->start(); itemJob->start(); if (!mItemsToCheck.isEmpty()) { EwsGetItemRequest *getItemReq = new EwsGetItemRequest(mClient, this); getItemReq->setItemIds(mItemsToCheck); getItemReq->setItemShape(EwsItemShape(EwsShapeIdOnly)); connect(getItemReq, &EwsGetItemRequest::result, this, &EwsFetchItemsJob::checkedItemsFetchFinished); ++mPendingJobs; getItemReq->start(); } } void EwsFetchItemsJob::localItemFetchDone(KJob *job) { - ItemFetchJob *fetchJob = qobject_cast(job); + ItemFetchJob *fetchJob = qobject_cast(job); qCDebug(EWSRES_LOG) << "EwsFetchItemsJob::localItemFetchDone"; if (!fetchJob) { setErrorMsg(QStringLiteral("Invalid item fetch job pointer.")); doKill(); emitResult(); return; } if (!fetchJob->error()) { removeSubjob(job); mLocalItems = fetchJob->items(); --mPendingJobs; if (mPendingJobs == 0) { compareItemLists(); } } } void EwsFetchItemsJob::remoteItemFetchDone(KJob *job) { - EwsSyncFolderItemsRequest *itemReq = qobject_cast(job); + EwsSyncFolderItemsRequest *itemReq = qobject_cast(job); qCDebug(EWSRES_LOG) << "EwsFetchItemsJob::remoteItemFetchDone"; if (!itemReq) { setErrorMsg(QStringLiteral("Invalid find item request pointer.")); doKill(); emitResult(); return; } if (!itemReq->error()) { removeSubjob(job); Q_FOREACH (const EwsSyncFolderItemsRequest::Change &change, itemReq->changes()) { switch (change.type()) { case EwsSyncFolderItemsRequest::Create: mRemoteAddedItems.append(change.item()); break; case EwsSyncFolderItemsRequest::Update: mRemoteChangedItems.append(change.item()); break; case EwsSyncFolderItemsRequest::Delete: mRemoteDeletedIds.append(change.itemId()); break; case EwsSyncFolderItemsRequest::ReadFlagChange: mRemoteFlagChangedIds.insert(change.itemId(), change.isRead()); break; default: break; } } if (!itemReq->includesLastItem()) { EwsSyncFolderItemsRequest *syncItemsReq = new EwsSyncFolderItemsRequest(mClient, this); syncItemsReq->setFolderId(EwsId(mCollection.remoteId(), mCollection.remoteRevision())); EwsItemShape shape(EwsShapeIdOnly); shape << EwsResource::tagsProperty; syncItemsReq->setItemShape(shape); syncItemsReq->setSyncState(itemReq->syncState()); syncItemsReq->setMaxChanges(listBatchSize); connect(syncItemsReq, &KJob::result, this, &EwsFetchItemsJob::remoteItemFetchDone); addSubjob(syncItemsReq); syncItemsReq->start(); } else { mSyncState = itemReq->syncState(); --mPendingJobs; if (mPendingJobs == 0) { compareItemLists(); } } } } void EwsFetchItemsJob::checkedItemsFetchFinished(KJob *job) { - EwsGetItemRequest *req = qobject_cast(job); + EwsGetItemRequest *req = qobject_cast(job); if (!req) { setErrorMsg(QStringLiteral("Invalid item fetch job pointer.")); doKill(); emitResult(); return; } if (!req->error()) { removeSubjob(job); Q_ASSERT(mItemsToCheck.size() == req->responses().size()); EwsId::List::const_iterator it = mItemsToCheck.cbegin(); Q_FOREACH (const EwsGetItemRequest::Response &resp, req->responses()) { if (resp.isSuccess()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Checked item %1 found - readding").arg(ewsHash(it->id())); mRemoteAddedItems.append(resp.item()); } else { qCDebugNC(EWSRES_LOG) << QStringLiteral("Checked item %1 not found - removing").arg(ewsHash(it->id())); mRemoteDeletedIds.append(*it); } ++it; } --mPendingJobs; if (mPendingJobs == 0) { compareItemLists(); } } } void EwsFetchItemsJob::compareItemLists() { /* Begin stage 2 - determine list of new/changed items and fetch details about them. */ Item::List toFetchItems[EwsItemTypeUnknown + 1]; Q_EMIT status(1, QStringLiteral("Retrieving items")); Q_EMIT percent(0); QHash itemHash; Q_FOREACH (const Item &item, mLocalItems) { itemHash.insert(item.remoteId(), item); } Q_FOREACH (const EwsItem &ewsItem, mRemoteAddedItems) { /* In case of a full sync all existing items appear as added on the remote side. Therefore * look for the item in the local list before creating a new copy. */ EwsId id(ewsItem[EwsItemFieldItemId].value()); QHash::iterator it = itemHash.find(id.id()); EwsItemType type = ewsItem.internalType(); if (type == EwsItemTypeUnknown) { /* Ignore unknown items. */ continue; } QString mimeType = EwsItemHandler::itemHandler(type)->mimeType(); if (it == itemHash.end()) { Item item(mimeType); item.setParentCollection(mCollection); EwsId id = ewsItem[EwsItemFieldItemId].value(); item.setRemoteId(id.id()); item.setRemoteRevision(id.changeKey()); if (!mTagStore->readEwsProperties(item, ewsItem, mTagsSynced)) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Missing tags encountered - forcing sync"); syncTags(); return; } toFetchItems[type].append(item); } else { Item &item = *it; item.clearPayload(); item.setRemoteRevision(id.changeKey()); if (!mTagStore->readEwsProperties(item, ewsItem, mTagsSynced)) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Missing tags encountered - forcing sync"); syncTags(); return; } toFetchItems[type].append(item); itemHash.erase(it); } } if (mFullSync) { /* In case of a full sync all items that are still on the local item list do not exist * remotely and need to be deleted locally. */ const QHash::iterator end(itemHash.end()); for (QHash::iterator it = itemHash.begin(); it != end; ++it) { mDeletedItems.append(it.value()); } } else { Q_FOREACH (const EwsItem &ewsItem, mRemoteChangedItems) { EwsId id(ewsItem[EwsItemFieldItemId].value()); QHash::iterator it = itemHash.find(id.id()); if (it == itemHash.end()) { setErrorMsg(QStringLiteral("Got update for item %1, but item not found in local store.") .arg(ewsHash(id.id()))); emitResult(); return; } Item &item = *it; item.clearPayload(); item.setRemoteRevision(id.changeKey()); if (!mTagStore->readEwsProperties(item, ewsItem, mTagsSynced)) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Missing tags encountered - forcing sync"); syncTags(); return; } EwsItemType type = ewsItem.internalType(); toFetchItems[type].append(item); itemHash.erase(it); } // In case of an incremental sync deleted items will be given explicitly. */ Q_FOREACH (const EwsId &id, mRemoteDeletedIds) { QHash::iterator it = itemHash.find(id.id()); /* If one or more items marked as deleted are not found it means that the folder is out * of sync. The only way to fix this is to issue a full sync. * The only exception is when an item is checked explicitly. In such case the absence * of this item can be ignored. */ if (it == itemHash.end()) { QHash::iterator qit = mQueuedUpdates[EwsDeletedEvent].find(id.id()); if (EWSRES_LOG().isDebugEnabled() && qit != mQueuedUpdates[EwsDeletedEvent].end()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Match for queued deletion of item %1").arg(ewsHash(id.id())); } if (!mItemsToCheck.contains(id) && qit == mQueuedUpdates[EwsDeletedEvent].end()) { setErrorMsg(QStringLiteral("Got delete for item %1, but item not found in local store.") .arg(ewsHash(id.id()))); emitResult(); return; } } else { mDeletedItems.append(*it); } } QHash::const_iterator it; EwsItemHandler *handler = EwsItemHandler::itemHandler(EwsItemTypeMessage); for (it = mRemoteFlagChangedIds.cbegin(); it != mRemoteFlagChangedIds.cend(); ++it) { QHash::iterator iit = itemHash.find(it.key().id()); if (iit == itemHash.end()) { setErrorMsg(QStringLiteral("Got read flag change for item %1, but item not found in local store.") .arg(it.key().id())); emitResult(); return; } Item &item = *iit; handler->setSeenFlag(item, it.value()); mChangedItems.append(item); itemHash.erase(iit); } } qCDebugNC(EWSRES_LOG) << QStringLiteral("Changed %2, deleted %3, new %4") - .arg(mRemoteChangedItems.size()) - .arg(mDeletedItems.size()).arg(mRemoteAddedItems.size()); + .arg(mRemoteChangedItems.size()) + .arg(mDeletedItems.size()).arg(mRemoteAddedItems.size()); bool fetch = false; for (unsigned iType = 0; iType < sizeof(toFetchItems) / sizeof(toFetchItems[0]); ++iType) { if (!toFetchItems[iType].isEmpty()) { for (int i = 0; i < toFetchItems[iType].size(); i += fetchBatchSize) { EwsItemHandler *handler = EwsItemHandler::itemHandler(static_cast(iType)); if (!handler) { // TODO: Temporarily ignore unsupported item types. qCWarning(EWSRES_LOG) << QStringLiteral("Unable to initialize fetch for item type %1") - .arg(iType); + .arg(iType); /*setErrorMsg(QStringLiteral("Unable to initialize fetch for item type %1").arg(iType)); emitResult(); return;*/ } else { EwsFetchItemDetailJob *job = handler->fetchItemDetailJob(mClient, this, mCollection); Item::List itemList = toFetchItems[iType].mid(i, fetchBatchSize); job->setItemLists(itemList, &mDeletedItems); connect(job, &KJob::result, this, &EwsFetchItemsJob::itemDetailFetchDone); addSubjob(job); fetch = true; } } } } if (!fetch) { // Nothing to fetch - we're done here. emitResult(); } else { subjobs().first()->start(); } } void EwsFetchItemsJob::itemDetailFetchDone(KJob *job) { removeSubjob(job); if (!job->error()) { - EwsFetchItemDetailJob *detailJob = qobject_cast(job); + EwsFetchItemDetailJob *detailJob = qobject_cast(job); if (detailJob) { mChangedItems += detailJob->changedItems(); } if (subjobs().isEmpty()) { emitResult(); } else { subjobs().first()->start(); } } } void EwsFetchItemsJob::setQueuedUpdates(const QueuedUpdateList &updates) { mQueuedUpdates.clear(); Q_FOREACH (const QueuedUpdate &upd, updates) { mQueuedUpdates[upd.type].insert(upd.id, upd.changeKey); qCDebugNC(EWSRES_LOG) << QStringLiteral("Queued update %1 for item %2").arg(upd.type).arg(ewsHash(upd.id)); } } void EwsFetchItemsJob::syncTags() { if (mTagsSynced) { setErrorMsg(QStringLiteral("Missing tags encountered despite previous sync.")); emitResult(); } else { EwsAkonadiTagsSyncJob *job = new EwsAkonadiTagsSyncJob(mTagStore, mClient, - qobject_cast(parent())->rootCollection(), this); + qobject_cast(parent())->rootCollection(), this); connect(job, &EwsAkonadiTagsSyncJob::result, this, &EwsFetchItemsJob::tagSyncFinished); job->start(); mTagsSynced = true; } } void EwsFetchItemsJob::tagSyncFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorText()); emitResult(); } else { compareItemLists(); } } diff --git a/resources/ews/ewsfetchitemsjob.h b/resources/ews/ewsfetchitemsjob.h index 53dabc285..aec9ec2ff 100644 --- a/resources/ews/ewsfetchitemsjob.h +++ b/resources/ews/ewsfetchitemsjob.h @@ -1,112 +1,112 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSFETCHITEMSJOB_H #define EWSFETCHITEMSJOB_H #include #include "ewsjob.h" #include "ewsfinditemrequest.h" -namespace Akonadi -{ +namespace Akonadi { class Collection; } class EwsClient; class EwsTagStore; class EwsResource; class EwsFetchItemsJob : public EwsJob { Q_OBJECT public: struct QueuedUpdate { QString id; QString changeKey; EwsEventType type; }; typedef QList QueuedUpdateList; - EwsFetchItemsJob(const Akonadi::Collection &collection, EwsClient &client, - const QString &syncState, const EwsId::List &itemsToCheck, - EwsTagStore *tagStore, EwsResource *parent); + EwsFetchItemsJob(const Akonadi::Collection &collection, EwsClient &client, const QString &syncState, const EwsId::List &itemsToCheck, EwsTagStore *tagStore, EwsResource *parent); ~EwsFetchItemsJob() override; Akonadi::Item::List changedItems() const { return mChangedItems; } + Akonadi::Item::List deletedItems() const { return mDeletedItems; } + const QString &syncState() const { return mSyncState; } + const Akonadi::Collection &collection() const { return mCollection; } void setQueuedUpdates(const QueuedUpdateList &updates); void start() override; private Q_SLOTS: void localItemFetchDone(KJob *job); void remoteItemFetchDone(KJob *job); void itemDetailFetchDone(KJob *job); void checkedItemsFetchFinished(KJob *job); void tagSyncFinished(KJob *job); Q_SIGNALS: void status(int status, const QString &message = QString()); void percent(int progress); private: void compareItemLists(); void syncTags(); /*struct QueuedUpdateInt { QString changeKey; EwsEventType type; };*/ typedef QHash > QueuedUpdateHash; const Akonadi::Collection mCollection; EwsClient &mClient; EwsId::List mItemsToCheck; Akonadi::Item::List mLocalItems; EwsItem::List mRemoteAddedItems; EwsItem::List mRemoteChangedItems; EwsId::List mRemoteDeletedIds; QHash mRemoteFlagChangedIds; int mPendingJobs; unsigned mTotalItems; QString mSyncState; bool mFullSync; QueuedUpdateHash mQueuedUpdates; EwsTagStore *mTagStore = nullptr; bool mTagsSynced; Akonadi::Item::List mChangedItems; Akonadi::Item::List mDeletedItems; }; #endif diff --git a/resources/ews/ewsitemhandler.cpp b/resources/ews/ewsitemhandler.cpp index 96e798a1e..920cad8c0 100644 --- a/resources/ews/ewsitemhandler.cpp +++ b/resources/ews/ewsitemhandler.cpp @@ -1,122 +1,122 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsitemhandler.h" #include "ewsresource.h" #include "ewsresource_debug.h" struct HandlerFactory { EwsItemType type; EwsItemHandler::ItemHandlerFactory factory; }; typedef QList HandlerList; -typedef QHash> HandlerHash; +typedef QHash > HandlerHash; Q_GLOBAL_STATIC(HandlerList, handlerFactories) Q_GLOBAL_STATIC(HandlerHash, handlers) EwsItemHandler::~EwsItemHandler() { } void EwsItemHandler::registerItemHandler(EwsItemType type, const ItemHandlerFactory &factory) { handlerFactories->append({type, factory}); } + EwsItemHandler *EwsItemHandler::itemHandler(EwsItemType type) { HandlerHash::iterator it = handlers->find(type); if (it != handlers->end()) { return it->data(); } else { const HandlerList::const_iterator end(handlerFactories->cend()); for (HandlerList::const_iterator it = handlerFactories->cbegin(); it != end; ++it) { if (it->type == type) { EwsItemHandler *handler = it->factory(); (*handlers)[type].reset(handler); return handler; } } qCWarning(EWSRES_LOG) << QStringLiteral("Could not find handler for item type %1").arg(type); return nullptr; } } EwsItemType EwsItemHandler::mimeToItemType(const QString &mimeType) { if (mimeType == itemHandler(EwsItemTypeMessage)->mimeType()) { return EwsItemTypeMessage; } else if (mimeType == itemHandler(EwsItemTypeCalendarItem)->mimeType()) { return EwsItemTypeCalendarItem; } else if (mimeType == itemHandler(EwsItemTypeTask)->mimeType()) { return EwsItemTypeTask; } else if (mimeType == itemHandler(EwsItemTypeContact)->mimeType()) { return EwsItemTypeContact; } else { return EwsItemTypeItem; } } QHash EwsItemHandler::writeFlags(const QSet &flags) { QHash propertyHash; if (flags.isEmpty()) { propertyHash.insert(EwsResource::flagsProperty, QVariant()); } else { QStringList flagList; flagList.reserve(flags.count()); for (const QByteArray &flag : flags) { flagList.append(QString::fromLatin1(flag)); } propertyHash.insert(EwsResource::flagsProperty, flagList); } return propertyHash; } QSet EwsItemHandler::readFlags(const EwsItem &item) { QSet flags; QVariant flagProp = item[EwsResource::flagsProperty]; if (!flagProp.isNull() && (flagProp.canConvert())) { const QStringList flagList = flagProp.toStringList(); flags.reserve(flagList.count()); for (const QString &flag : flagList) { flags.insert(flag.toLatin1()); } } return flags; } QList EwsItemHandler::flagsProperties() { return {EwsResource::flagsProperty}; } QList EwsItemHandler::tagsProperties() { return {EwsResource::tagsProperty}; } - diff --git a/resources/ews/ewsitemhandler.h b/resources/ews/ewsitemhandler.h index 5d3b3864a..455d4ed3f 100644 --- a/resources/ews/ewsitemhandler.h +++ b/resources/ews/ewsitemhandler.h @@ -1,84 +1,79 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSITEMHANDLER_H #define EWSITEMHANDLER_H #include #include #include "ewspropertyfield.h" #include "ewstypes.h" -namespace Akonadi -{ +namespace Akonadi { class Collection; class Item; } class EwsClient; class EwsFetchItemDetailJob; class EwsModifyItemJob; class EwsCreateItemJob; class EwsItem; class EwsTagStore; class EwsResource; class EwsItemHandler { public: virtual ~EwsItemHandler(); - virtual EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) = 0; + virtual EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) = 0; virtual void setSeenFlag(Akonadi::Item &item, bool value) = 0; virtual QString mimeType() = 0; virtual bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) = 0; - virtual EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) = 0; - virtual EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) = 0; + virtual EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) = 0; + virtual EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) = 0; - typedef std::function ItemHandlerFactory; + typedef std::function ItemHandlerFactory; static void registerItemHandler(EwsItemType type, const ItemHandlerFactory &factory); static EwsItemHandler *itemHandler(EwsItemType type); static EwsItemType mimeToItemType(const QString &mimeType); static QHash writeFlags(const QSet &flags); static QSet readFlags(const EwsItem &item); static QList flagsProperties(); static QList tagsProperties(); private: struct HandlerFactory { EwsItemType type; ItemHandlerFactory factory; }; }; #define EWS_DECLARE_ITEM_HANDLER(clsname, type) \ class type ## _itemhandler_registrar { \ public: \ type ## _itemhandler_registrar() \ { \ EwsItemHandler::registerItemHandler(type, &clsname::factory); \ } \ }; \ const type ## _itemhandler_registrar type ## _itemhandler_registrar_object; #endif diff --git a/resources/ews/ewsmodifyitemflagsjob.cpp b/resources/ews/ewsmodifyitemflagsjob.cpp index d77071674..d5261d8a5 100644 --- a/resources/ews/ewsmodifyitemflagsjob.cpp +++ b/resources/ews/ewsmodifyitemflagsjob.cpp @@ -1,93 +1,96 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifyitemflagsjob.h" #include "ewsmodifyitemjob.h" #include "ewsitemhandler.h" using namespace Akonadi; -EwsModifyItemFlagsJob::EwsModifyItemFlagsJob(EwsClient &client, QObject *parent, const Item::List &items, - const QSet &addedFlags, const QSet &removedFlags) - : EwsJob(parent), mItems(items), mClient(client), mAddedFlags(addedFlags), mRemovedFlags(removedFlags) +EwsModifyItemFlagsJob::EwsModifyItemFlagsJob(EwsClient &client, QObject *parent, const Item::List &items, const QSet &addedFlags, const QSet &removedFlags) + : EwsJob(parent) + , mItems(items) + , mClient(client) + , mAddedFlags(addedFlags) + , mRemovedFlags(removedFlags) { } EwsModifyItemFlagsJob::~EwsModifyItemFlagsJob() { } void EwsModifyItemFlagsJob::itemModifyFinished(KJob *job) { if (job->error()) { setErrorText(job->errorString()); emitResult(); return; } - EwsModifyItemJob *req = qobject_cast(job); + EwsModifyItemJob *req = qobject_cast(job); if (!req) { setErrorText(QStringLiteral("Invalid EwsModifyItemJob job object")); emitResult(); return; } mResultItems += req->items(); removeSubjob(job); if (subjobs().isEmpty()) { Q_ASSERT(mResultItems.size() == mItems.size()); emitResult(); } } void EwsModifyItemFlagsJob::start() { Item::List items[EwsItemTypeUnknown]; Q_FOREACH (const Item &item, mItems) { EwsItemType type = EwsItemHandler::mimeToItemType(item.mimeType()); if (type == EwsItemTypeUnknown) { setErrorText(QStringLiteral("Unknown item type %1 for item %2").arg(item.mimeType()).arg(item.remoteId())); emitResult(); return; } else { items[type].append(item); } } bool started = false; for (int type = 0; type < EwsItemTypeUnknown; type++) { if (!items[static_cast(type)].isEmpty()) { EwsItemHandler *handler = EwsItemHandler::itemHandler(static_cast(type)); EwsModifyItemJob *job = handler->modifyItemJob(mClient, items[type], - QSet() << "FLAGS", this); + QSet() << "FLAGS", this); connect(job, &EwsModifyItemJob::result, this, &EwsModifyItemFlagsJob::itemModifyFinished); addSubjob(job); job->start(); started = true; } } if (!started) { setErrorText(QStringLiteral("No items to process")); emitResult(); } } diff --git a/resources/ews/ewsmodifyitemflagsjob.h b/resources/ews/ewsmodifyitemflagsjob.h index dbeb5bab1..0b5a6271f 100644 --- a/resources/ews/ewsmodifyitemflagsjob.h +++ b/resources/ews/ewsmodifyitemflagsjob.h @@ -1,54 +1,53 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYITEMFLAGSJOB_H #define EWSMODIFYITEMFLAGSJOB_H #include #include #include "ewstypes.h" #include "ewsclient.h" #include "ewsjob.h" class EwsModifyItemFlagsJob : public EwsJob { Q_OBJECT public: - EwsModifyItemFlagsJob(EwsClient &client, QObject *parent, const Akonadi::Item::List &, - const QSet &addedFlags, const QSet &removedFlags); + EwsModifyItemFlagsJob(EwsClient &client, QObject *parent, const Akonadi::Item::List &, const QSet &addedFlags, const QSet &removedFlags); ~EwsModifyItemFlagsJob() override; Akonadi::Item::List items() const { return mResultItems; } void start() override; protected: Akonadi::Item::List mItems; Akonadi::Item::List mResultItems; EwsClient &mClient; QSet mAddedFlags; QSet mRemovedFlags; private Q_SLOTS: void itemModifyFinished(KJob *job); }; #endif diff --git a/resources/ews/ewsmodifyitemjob.cpp b/resources/ews/ewsmodifyitemjob.cpp index 3130d2121..9480b217b 100644 --- a/resources/ews/ewsmodifyitemjob.cpp +++ b/resources/ews/ewsmodifyitemjob.cpp @@ -1,42 +1,43 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifyitemjob.h" -EwsModifyItemJob::EwsModifyItemJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) - : EwsJob(parent), mItems(items), mParts(parts), mClient(client) +EwsModifyItemJob::EwsModifyItemJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) + : EwsJob(parent) + , mItems(items) + , mParts(parts) + , mClient(client) { } EwsModifyItemJob::~EwsModifyItemJob() { } -void EwsModifyItemJob::setModifiedFlags(const QSet &addedFlags, - const QSet &removedFlags) +void EwsModifyItemJob::setModifiedFlags(const QSet &addedFlags, const QSet &removedFlags) { mAddedFlags = addedFlags; mRemovedFlags = removedFlags; } const Akonadi::Item::List &EwsModifyItemJob::items() const { return mItems; } diff --git a/resources/ews/ewsmodifyitemjob.h b/resources/ews/ewsmodifyitemjob.h index 4c332df09..fd79bcc90 100644 --- a/resources/ews/ewsmodifyitemjob.h +++ b/resources/ews/ewsmodifyitemjob.h @@ -1,48 +1,47 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYITEMJOB_H #define EWSMODIFYITEMJOB_H #include #include "ewsjob.h" class EwsClient; class EwsModifyItemJob : public EwsJob { Q_OBJECT public: - EwsModifyItemJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyItemJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyItemJob() override; void setModifiedFlags(const QSet &addedFlags, const QSet &removedFlags); const Akonadi::Item::List &items() const; protected: Akonadi::Item::List mItems; const QSet mParts; EwsClient &mClient; QSet mAddedFlags; QSet mRemovedFlags; }; #endif diff --git a/resources/ews/ewsmtaconfigdialog.cpp b/resources/ews/ewsmtaconfigdialog.cpp index 4ac6ae0d4..7dee834c0 100644 --- a/resources/ews/ewsmtaconfigdialog.cpp +++ b/resources/ews/ewsmtaconfigdialog.cpp @@ -1,92 +1,92 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmtaconfigdialog.h" #include #include #include #include #include #include #include #include #include "ewsmtaresource.h" #include "ewsres_mta_debug.h" #include "ewsmtasettings.h" #include "ui_ewsmtaconfigdialog.h" EwsMtaConfigDialog::EwsMtaConfigDialog(EwsMtaResource *parentResource, WId wId) - : QDialog(), mParentResource(parentResource) + : QDialog() + , mParentResource(parentResource) { if (wId) { KWindowSystem::setMainWindow(this, wId); } QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); mainLayout->addWidget(mainWidget); QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(mButtonBox, &QDialogButtonBox::accepted, this, &EwsMtaConfigDialog::accept); connect(mButtonBox, &QDialogButtonBox::rejected, this, &EwsMtaConfigDialog::reject); mainLayout->addWidget(mButtonBox); setWindowTitle(i18n("Microsoft Exchange Mail Transport Configuration")); mUi = new Ui::SetupServerView; mUi->setupUi(mainWidget); mUi->accountName->setText(parentResource->name()); Akonadi::AgentFilterProxyModel *model = mUi->resourceWidget->agentFilterProxyModel(); model->addCapabilityFilter(QStringLiteral("X-EwsMailTransport")); mUi->resourceWidget->view()->setSelectionMode(QAbstractItemView::SingleSelection); for (int i = 0, total = model->rowCount(); i < total; ++i) { QModelIndex index = model->index(i, 0); QVariant v = model->data(index, Akonadi::AgentInstanceModel::InstanceIdentifierRole); if (v.toString() == EwsMtaSettings::ewsResource()) { mUi->resourceWidget->view()->setCurrentIndex(index); } } connect(okButton, &QPushButton::clicked, this, &EwsMtaConfigDialog::save); } EwsMtaConfigDialog::~EwsMtaConfigDialog() { delete mUi; } void EwsMtaConfigDialog::save() { if (!mUi->resourceWidget->selectedAgentInstances().isEmpty()) { EwsMtaSettings::setEwsResource(mUi->resourceWidget->selectedAgentInstances().first().identifier()); mParentResource->setName(mUi->accountName->text()); EwsMtaSettings::self()->save(); } else { qCWarning(EWSRES_MTA_LOG) << "Any agent instance selected"; } } - diff --git a/resources/ews/ewsmtaconfigdialog.h b/resources/ews/ewsmtaconfigdialog.h index 72ee0002a..b876eb42a 100644 --- a/resources/ews/ewsmtaconfigdialog.h +++ b/resources/ews/ewsmtaconfigdialog.h @@ -1,49 +1,47 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSMTACONFIGDIALOG_H #define EWSMTACONFIGDIALOG_H #include class QDialogButtonBox; -namespace Akonadi -{ +namespace Akonadi { } -namespace Ui -{ +namespace Ui { class SetupServerView; } class EwsMtaResource; class EwsMtaConfigDialog : public QDialog { Q_OBJECT public: explicit EwsMtaConfigDialog(EwsMtaResource *parentResource, WId windowId); ~EwsMtaConfigDialog() override; private Q_SLOTS: void save(); private: QDialogButtonBox *mButtonBox = nullptr; EwsMtaResource *mParentResource = nullptr; Ui::SetupServerView *mUi = nullptr; }; #endif diff --git a/resources/ews/ewsmtaresource.cpp b/resources/ews/ewsmtaresource.cpp index 29cfcdd65..e0238a9bd 100644 --- a/resources/ews/ewsmtaresource.cpp +++ b/resources/ews/ewsmtaresource.cpp @@ -1,132 +1,133 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsmtaresource.h" #include #include #include #include "ewsresource_debug.h" #include "ewsmtaconfigdialog.h" #include "ewsmtasettings.h" #include "ewsresourceinterface.h" using namespace Akonadi; EwsMtaResource::EwsMtaResource(const QString &id) - : Akonadi::ResourceBase(id), mEwsResource(nullptr) + : Akonadi::ResourceBase(id) + , mEwsResource(nullptr) { } EwsMtaResource::~EwsMtaResource() { } bool EwsMtaResource::connectEws() { if (mEwsResource) { return true; } mEwsResource = new OrgKdeAkonadiEwsResourceInterface( QStringLiteral("org.freedesktop.Akonadi.Resource.") + EwsMtaSettings::ewsResource(), QStringLiteral("/"), QDBusConnection::sessionBus(), this); if (!mEwsResource->isValid()) { delete mEwsResource; mEwsResource = nullptr; } else { connect(mEwsResource, &OrgKdeAkonadiEwsResourceInterface::messageSent, this, &EwsMtaResource::messageSent); } return mEwsResource != nullptr; } void EwsMtaResource::configure(WId windowId) { QPointer dlg = new EwsMtaConfigDialog(this, windowId); if (dlg->exec()) { EwsMtaSettings::self()->save(); } delete dlg; } void EwsMtaResource::sendItem(const Akonadi::Item &item) { if (!connectEws()) { itemSent(item, TransportFailed, i18n("Unable to connect to master EWS resource")); return; } mItemHash.insert(item.remoteId(), item); KMime::Message::Ptr msg = item.payload(); /* Exchange doesn't just store whatever MIME content that was sent to it - it will parse it and send * further the version assembled back from the parsed parts. It seems that this parsing doesn't work well * with the quoted-printable encoding, which KMail prefers. This results in malformed encoding, which the * sender doesn't even see. * As a workaround force encoding of the body (or in case of multipart - all parts) to Base64. */ if (msg->contents().isEmpty()) { msg->changeEncoding(KMime::Headers::CEbase64); msg->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); } else { Q_FOREACH (KMime::Content *content, msg->contents()) { content->changeEncoding(KMime::Headers::CEbase64); content->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); } } msg->assemble(); QByteArray mimeContent = msg->encodedContent(true); mEwsResource->sendMessage(item.remoteId(), mimeContent); } void EwsMtaResource::messageSent(const QString &id, const QString &error) { QHash::iterator it = mItemHash.find(id); if (it != mItemHash.end()) { if (error.isEmpty()) { itemSent(*it, TransportSucceeded, QString()); } else { itemSent(*it, TransportFailed, error); } mItemHash.erase(it); } } void EwsMtaResource::retrieveCollections() { collectionsRetrieved(Collection::List()); } void EwsMtaResource::retrieveItems(const Akonadi::Collection &collection) { Q_UNUSED(collection) itemsRetrieved(Item::List()); } bool EwsMtaResource::retrieveItems(const Akonadi::Item::List &items, const QSet &parts) { Q_UNUSED(parts) itemsRetrieved(items); return true; } AKONADI_RESOURCE_MAIN(EwsMtaResource) diff --git a/resources/ews/ewsprogressdialog.cpp b/resources/ews/ewsprogressdialog.cpp index 8b2de25cc..fbcaff232 100644 --- a/resources/ews/ewsprogressdialog.cpp +++ b/resources/ews/ewsprogressdialog.cpp @@ -1,76 +1,75 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewsprogressdialog.h" #include #include #include #include #include #include - EwsProgressDialog::EwsProgressDialog(QWidget *parent, Type type) : QDialog(parent) { setModal(true); QLabel *statusLabel = new QLabel(this); QProgressBar *progress = new QProgressBar(this); progress->setMaximum(0); QVBoxLayout *vLayout = new QVBoxLayout(this); vLayout->setMargin(0); vLayout->addWidget(statusLabel); vLayout->addWidget(progress); QPushButton *cancelButton = new QPushButton(this); cancelButton->setText(i18n("Cancel")); QWidget *progressContainer = new QWidget(this); progressContainer->setLayout(vLayout); QHBoxLayout *hLayout = new QHBoxLayout(this); hLayout->addWidget(progressContainer); hLayout->addWidget(cancelButton); hLayout->setSizeConstraint(QLayout::SetFixedSize); switch (type) { case AutoDiscovery: setWindowTitle(i18n("Exchange server autodiscovery")); statusLabel->setText(i18n("Performing Microsoft Exchange server autodiscovery...")); break; case TryConnect: setWindowTitle(i18n("Connecting to Exchange")); statusLabel->setText(i18n("Connecting to Microsoft Exchange server...")); break; } connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject); } EwsProgressDialog::~EwsProgressDialog() { } diff --git a/resources/ews/ewsprogressdialog.h b/resources/ews/ewsprogressdialog.h index 483cc48a9..6cb7ada1d 100644 --- a/resources/ews/ewsprogressdialog.h +++ b/resources/ews/ewsprogressdialog.h @@ -1,39 +1,38 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 EWSPROGRESSDIALOG_H #define EWSPROGRESSDIALOG_H #include - class EwsProgressDialog : public QDialog { Q_OBJECT public: enum Type { AutoDiscovery, TryConnect }; explicit EwsProgressDialog(QWidget *parent, Type type); ~EwsProgressDialog() override; }; #endif diff --git a/resources/ews/ewsresource.cpp b/resources/ews/ewsresource.cpp index 890b544ed..ffc5900d9 100644 --- a/resources/ews/ewsresource.cpp +++ b/resources/ews/ewsresource.cpp @@ -1,1412 +1,1403 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 "ewsresource.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ewsfetchitemsjob.h" #include "ewsfetchfoldersjob.h" #include "ewsfetchfoldersincrjob.h" #include "ewsgetitemrequest.h" #include "ewsupdateitemrequest.h" #include "ewsmodifyitemflagsjob.h" #include "ewsmoveitemrequest.h" #include "ewsdeleteitemrequest.h" #include "ewscreatefolderrequest.h" #include "ewsmovefolderrequest.h" #include "ewsupdatefolderrequest.h" #include "ewsdeletefolderrequest.h" #include "ewssubscriptionmanager.h" #include "ewsgetfolderrequest.h" #include "ewsitemhandler.h" #include "ewsmodifyitemjob.h" #include "ewscreateitemjob.h" #include "ewsconfigdialog.h" #include "ewssettings.h" #include "auth/ewsabstractauth.h" #ifdef HAVE_SEPARATE_MTA_RESOURCE #include "ewscreateitemrequest.h" #endif #include "tags/ewstagstore.h" #include "tags/ewsupdateitemstagsjob.h" #include "tags/ewsglobaltagswritejob.h" #include "tags/ewsglobaltagsreadjob.h" #include "ewsresource_debug.h" #include "ewsresourceadaptor.h" #include "ewssettingsadaptor.h" #include "ewswalletadaptor.h" using namespace Akonadi; struct SpecialFolders { EwsDistinguishedId did; SpecialMailCollections::Type type; QString iconName; }; static const QVector specialFolderList = { {EwsDIdInbox, SpecialMailCollections::Inbox, QStringLiteral("mail-folder-inbox")}, {EwsDIdOutbox, SpecialMailCollections::Outbox, QStringLiteral("mail-folder-outbox")}, {EwsDIdSentItems, SpecialMailCollections::SentMail, QStringLiteral("mail-folder-sent")}, {EwsDIdDeletedItems, SpecialMailCollections::Trash, QStringLiteral("user-trash")}, {EwsDIdDrafts, SpecialMailCollections::Drafts, QStringLiteral("document-properties")} }; const QString EwsResource::akonadiEwsPropsetUuid = QStringLiteral("9bf757ae-69b5-4d8a-bf1d-2dd0c0871a28"); const EwsPropertyField EwsResource::globalTagsProperty(EwsResource::akonadiEwsPropsetUuid, - QStringLiteral("GlobalTags"), EwsPropTypeStringArray); + QStringLiteral("GlobalTags"), EwsPropTypeStringArray); const EwsPropertyField EwsResource::globalTagsVersionProperty(EwsResource::akonadiEwsPropsetUuid, - QStringLiteral("GlobalTagsVersion"), EwsPropTypeInteger); + QStringLiteral("GlobalTagsVersion"), EwsPropTypeInteger); const EwsPropertyField EwsResource::tagsProperty(EwsResource::akonadiEwsPropsetUuid, - QStringLiteral("Tags"), EwsPropTypeStringArray); + QStringLiteral("Tags"), EwsPropTypeStringArray); const EwsPropertyField EwsResource::flagsProperty(EwsResource::akonadiEwsPropsetUuid, - QStringLiteral("Flags"), EwsPropTypeStringArray); + QStringLiteral("Flags"), EwsPropTypeStringArray); static Q_CONSTEXPR int InitialReconnectTimeout = 60; static Q_CONSTEXPR int ReconnectTimeout = 300; EwsResource::EwsResource(const QString &id) - : Akonadi::ResourceBase(id), mAuthStage(0), mTagsRetrieved(false), mReconnectTimeout(InitialReconnectTimeout), - mSettings(new EwsSettings(winIdForDialogs())) + : Akonadi::ResourceBase(id) + , mAuthStage(0) + , mTagsRetrieved(false) + , mReconnectTimeout(InitialReconnectTimeout) + , mSettings(new EwsSettings(winIdForDialogs())) { mEwsClient.setUserAgent(mSettings->userAgent()); mEwsClient.setEnableNTLMv2(mSettings->enableNTLMv2()); changeRecorder()->fetchCollection(true); changeRecorder()->collectionFetchScope().setAncestorRetrieval(CollectionFetchScope::Parent); changeRecorder()->itemFetchScope().fetchFullPayload(true); changeRecorder()->itemFetchScope().setAncestorRetrieval(ItemFetchScope::Parent); changeRecorder()->itemFetchScope().setFetchModificationTime(false); changeRecorder()->itemFetchScope().setFetchTags(true); mRootCollection.setParentCollection(Collection::root()); mRootCollection.setName(name()); mRootCollection.setContentMimeTypes(QStringList() << Collection::mimeType() << KMime::Message::mimeType()); mRootCollection.setRights(Collection::ReadOnly); setScheduleAttributeSyncBeforeItemSync(true); // Load the sync state QByteArray data = QByteArray::fromBase64(mSettings->syncState().toLatin1()); if (!data.isEmpty()) { data = qUncompress(data); if (!data.isEmpty()) { QDataStream stream(data); stream >> mSyncState; } } data = QByteArray::fromBase64(mSettings->folderSyncState().toLatin1()); if (!data.isEmpty()) { data = qUncompress(data); if (!data.isEmpty()) { mFolderSyncState = QString::fromLatin1(data); } } setHierarchicalRemoteIdentifiersEnabled(true); mTagStore = new EwsTagStore(this); QMetaObject::invokeMethod(this, &EwsResource::delayedInit, Qt::QueuedConnection); connect(this, &AgentBase::reloadConfiguration, this, &EwsResource::reloadConfig); } EwsResource::~EwsResource() { } void EwsResource::delayedInit() { new EwsResourceAdaptor(this); new EwsSettingsAdaptor(mSettings.data()); new EwsWalletAdaptor(mSettings.data()); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), - mSettings.data(), QDBusConnection::ExportAdaptors); + mSettings.data(), QDBusConnection::ExportAdaptors); } void EwsResource::resetUrl() { Q_EMIT status(Running, i18nc("@info:status", "Connecting to Exchange server")); EwsGetFolderRequest *req = new EwsGetFolderRequest(mEwsClient, this); const EwsId::List folders {EwsId(EwsDIdMsgFolderRoot), EwsId(EwsDIdInbox)}; req->setFolderIds(folders); EwsFolderShape shape(EwsShapeIdOnly); shape << EwsPropertyField(QStringLiteral("folder:DisplayName")); // Use the opportunity of reading the root folder to read the tag data. shape << globalTagsProperty << globalTagsVersionProperty; req->setFolderShape(shape); connect(req, &EwsRequest::result, this, &EwsResource::rootFolderFetchFinished); req->start(); } void EwsResource::rootFolderFetchFinished(KJob *job) { - EwsGetFolderRequest *req = qobject_cast(job); + EwsGetFolderRequest *req = qobject_cast(job); if (!req) { Q_EMIT status(Idle, i18nc("@info:status", "Unable to connect to Exchange server")); setTemporaryOffline(reconnectTimeout()); qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsGetFolderRequest job object"); return; } if (req->error()) { Q_EMIT status(Idle, i18nc("@info:status", "Unable to connect to Exchange server")); setTemporaryOffline(reconnectTimeout()); qWarning() << "ERROR" << req->errorString(); return; } if (req->responses().size() != 2) { Q_EMIT status(Idle, i18nc("@info:status", "Unable to connect to Exchange server")); setTemporaryOffline(reconnectTimeout()); qCWarning(EWSRES_LOG) << QStringLiteral("Invalid number of responses received"); return; } EwsFolder folder = req->responses()[1].folder(); EwsId id = folder[EwsFolderFieldFolderId].value(); if (id.type() == EwsId::Real) { /* Since KDE PIM is heavily based on IMAP philosophy it would only consider for filtering * folders with the remote identifier set to "INBOX". While this is true for IMAP/POP3, Exchange * uses Base64-encoded strings with data private to the server. In order for mail filtering to work * the EWS resource has pretended that the inbox folder's remote name is "INBOX". Since KDE Applications * 17.12 this workaround is no longer needed, however in order to clean-up after old Akonadi EWS * installations the code below sets the correct Exchange id for the Inbox folder. * * At some day in the future this part of code can be removed too. */ Collection c; c.setRemoteId(QStringLiteral("INBOX")); CollectionFetchJob *job = new CollectionFetchJob(c, CollectionFetchJob::Base, this); job->setFetchScope(changeRecorder()->collectionFetchScope()); job->fetchScope().setResource(identifier()); job->fetchScope().setListFilter(CollectionFetchScope::Sync); job->setProperty("inboxId", id.id()); connect(job, &CollectionFetchJob::result, this, &EwsResource::adjustInboxRemoteIdFetchFinished); int inboxIdx = mSettings->serverSubscriptionList().indexOf(QStringLiteral("INBOX")); if (inboxIdx >= 0) { QStringList subList = mSettings->serverSubscriptionList(); subList[inboxIdx] = id.id(); mSettings->setServerSubscriptionList(subList); } } folder = req->responses().first().folder(); id = folder[EwsFolderFieldFolderId].value(); if (id.type() == EwsId::Real) { mRootCollection.setRemoteId(id.id()); mRootCollection.setRemoteRevision(id.changeKey()); qCDebug(EWSRES_LOG) << "Root folder is " << id; Q_EMIT status(Idle, i18nc("@info:status Resource is ready", "Ready")); if (mSettings->serverSubscription()) { mSubManager.reset(new EwsSubscriptionManager(mEwsClient, id, mSettings.data(), this)); connect(mSubManager.data(), &EwsSubscriptionManager::foldersModified, this, &EwsResource::foldersModifiedEvent); connect(mSubManager.data(), &EwsSubscriptionManager::folderTreeModified, this, &EwsResource::folderTreeModifiedEvent); connect(mSubManager.data(), &EwsSubscriptionManager::fullSyncRequested, this, &EwsResource::fullSyncRequestedEvent); /* Use a queued connection here as the connectionError() method will actually destroy the subscription manager. If this * was done with a direct connection this would have ended up with destroying the caller object followed by a crash. */ connect(mSubManager.data(), &EwsSubscriptionManager::connectionError, this, &EwsResource::connectionError, Qt::QueuedConnection); mSubManager->start(); } synchronizeCollectionTree(); mTagStore->readTags(folder[globalTagsProperty].toStringList(), folder[globalTagsVersionProperty].toInt()); } } void EwsResource::adjustInboxRemoteIdFetchFinished(KJob *job) { if (!job->error()) { - CollectionFetchJob *fetchJob = qobject_cast(job); + CollectionFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); if (!fetchJob->collections().isEmpty()) { Collection c = fetchJob->collections()[0]; c.setRemoteId(fetchJob->property("inboxId").toString()); CollectionModifyJob *modifyJob = new CollectionModifyJob(c, this); modifyJob->start(); } } } void EwsResource::retrieveCollections() { if (mRootCollection.remoteId().isNull()) { cancelTask(i18nc("@info:status", "Root folder id not known.")); return; } Q_EMIT status(Running, i18nc("@info:status", "Retrieving collection tree")); if (!mFolderSyncState.isEmpty() && !mRootCollection.isValid()) { /* When doing an incremental sync the real Akonadi identifier of the root collection must * be known, because the retrieved list of changes needs to include all parent folders up * to the root. None of the child collections are required to be valid, but the root must * be, as it needs to be the anchor point. */ CollectionFetchJob *fetchJob = new CollectionFetchJob(mRootCollection, - CollectionFetchJob::Base); + CollectionFetchJob::Base); connect(fetchJob, &CollectionFetchJob::result, this, &EwsResource::rootCollectionFetched); fetchJob->start(); } else { doRetrieveCollections(); } synchronizeTags(); } void EwsResource::rootCollectionFetched(KJob *job) { if (job->error()) { qCWarning(EWSRES_LOG) << "ERROR" << job->errorString(); } else { - CollectionFetchJob *fetchJob = qobject_cast(job); + CollectionFetchJob *fetchJob = qobject_cast(job); if (fetchJob && !fetchJob->collections().isEmpty()) { mRootCollection = fetchJob->collections().first(); qCDebugNC(EWSRES_LOG) << QStringLiteral("Root collection fetched: ") << mRootCollection; } } /* If the fetch failed for whatever reason force a full sync, which doesn't require the root * collection to be valid. */ if (!mRootCollection.isValid()) { mFolderSyncState.clear(); } doRetrieveCollections(); } void EwsResource::doRetrieveCollections() { if (mFolderSyncState.isEmpty()) { EwsFetchFoldersJob *job = new EwsFetchFoldersJob(mEwsClient, mRootCollection, this); connect(job, &EwsFetchFoldersJob::result, this, &EwsResource::fetchFoldersJobFinished); job->start(); } else { EwsFetchFoldersIncrJob *job = new EwsFetchFoldersIncrJob(mEwsClient, mFolderSyncState, - mRootCollection, this); + mRootCollection, this); connect(job, &EwsFetchFoldersIncrJob::result, this, &EwsResource::fetchFoldersIncrJobFinished); job->start(); } } void EwsResource::connectionError() { Q_EMIT status(Broken, i18nc("@info:status", "Unable to connect to Exchange server")); setTemporaryOffline(reconnectTimeout()); } void EwsResource::retrieveItems(const Collection &collection) { Q_EMIT status(1, QStringLiteral("Retrieving item list")); Q_EMIT status(Running, i18nc("@info:status", "Retrieving %1 items", collection.name())); QString rid = collection.remoteId(); EwsFetchItemsJob *job = new EwsFetchItemsJob(collection, mEwsClient, - mSyncState.value(rid), mItemsToCheck.value(rid), mTagStore, this); + mSyncState.value(rid), mItemsToCheck.value(rid), mTagStore, this); job->setQueuedUpdates(mQueuedUpdates.value(collection.remoteId())); mQueuedUpdates.remove(collection.remoteId()); connect(job, &EwsFetchItemsJob::result, this, &EwsResource::itemFetchJobFinished); connect(job, &EwsFetchItemsJob::status, this, [this](int s, const QString &message) { Q_EMIT status(s, message); }); connect(job, &EwsFetchItemsJob::percent, this, [this](int p) { Q_EMIT percent(p); }); job->start(); } bool EwsResource::retrieveItems(const Item::List &items, const QSet &parts) { qCDebugNC(EWSRES_AGENTIF_LOG) << "retrieveItems: start " << items << parts; EwsGetItemRequest *req = new EwsGetItemRequest(mEwsClient, this); EwsId::List ids; ids.reserve(items.count()); for (const Item &item : items) { ids << EwsId(item.remoteId(), item.remoteRevision()); } req->setItemIds(ids); EwsItemShape shape(EwsShapeIdOnly); shape << EwsPropertyField(QStringLiteral("item:MimeContent")); req->setItemShape(shape); req->setProperty("items", QVariant::fromValue(items)); connect(req, &EwsGetItemRequest::result, this, &EwsResource::getItemsRequestFinished); req->start(); return true; } void EwsResource::getItemsRequestFinished(KJob *job) { if (job->error()) { qWarning() << "ERROR" << job->errorString(); cancelTask(i18nc("@info:status", "Failed to process items retrieval request")); return; } - EwsGetItemRequest *req = qobject_cast(job); + EwsGetItemRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsGetItemRequest job object"); cancelTask(i18nc("@info:status", "Failed to retrieve items - internal error")); return; } const Item::List items = req->property("items").value(); QHash itemHash; itemHash.reserve(items.count()); for (const Item &item : items) { itemHash.insert(item.remoteId(), item); } const EwsGetItemRequest::Response &resp = req->responses()[0]; if (!resp.isSuccess()) { qCWarningNC(EWSRES_AGENTIF_LOG) << QStringLiteral("retrieveItems: Item fetch failed."); cancelTask(i18nc("@info:status", "Failed to retrieve items")); return; } if (items.size() != req->responses().size()) { qCWarningNC(EWSRES_AGENTIF_LOG) << QStringLiteral("retrieveItems: incorrect number of responses."); cancelTask(i18nc("@info:status", "Failed to retrieve items - incorrect number of responses")); return; - } Q_FOREACH (const EwsGetItemRequest::Response &resp, req->responses()) { const EwsItem &ewsItem = resp.item(); EwsId id = ewsItem[EwsItemFieldItemId].value(); auto it = itemHash.find(id.id()); if (it == itemHash.end()) { qCWarningNC(EWSRES_AGENTIF_LOG) << QStringLiteral("retrieveItems: Akonadi item not found for item %1.").arg(id.id()); cancelTask(i18nc("@info:status", "Failed to retrieve items - Akonadi item not found for item %1", id.id())); return; } EwsItemType type = ewsItem.internalType(); if (type == EwsItemTypeUnknown) { qCWarningNC(EWSRES_AGENTIF_LOG) << QStringLiteral("retrieveItems: Unknown item type for item %1!").arg(id.id()); cancelTask(i18nc("@info:status", "Failed to retrieve items - Unknown item type for item %1", id.id())); return; } if (!EwsItemHandler::itemHandler(type)->setItemPayload(*it, ewsItem)) { qCWarningNC(EWSRES_AGENTIF_LOG) << "retrieveItems: Failed to fetch item payload"; cancelTask(i18nc("@info:status", "Failed to fetch item payload")); return; } } qCDebugNC(EWSRES_AGENTIF_LOG) << "retrieveItems: done"; itemsRetrieved(itemHash.values().toVector()); } void EwsResource::reloadConfig() { mSubManager.reset(nullptr); mEwsClient.setUrl(mSettings->baseUrl()); setUpAuth(); mEwsClient.setAuth(mAuth.data()); } void EwsResource::configure(WId windowId) { QPointer dlg = new EwsConfigDialog(this, mEwsClient, windowId, mSettings.data()); if (dlg->exec()) { reloadConfig(); Q_EMIT configurationDialogAccepted(); } else { Q_EMIT configurationDialogRejected(); } delete dlg; } void EwsResource::fetchFoldersJobFinished(KJob *job) { Q_EMIT status(Idle, i18nc("@info:status The resource is ready", "Ready")); - EwsFetchFoldersJob *req = qobject_cast(job); + EwsFetchFoldersJob *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsFetchFoldersJob job object"); cancelTask(i18nc("@info:status", "Failed to retrieve folders - internal error")); return; } if (req->error()) { qWarning() << "ERROR" << req->errorString(); cancelTask(i18nc("@info:status", "Failed to process folders retrieval request")); return; } mFolderSyncState = req->syncState(); saveState(); collectionsRetrieved(req->folders()); fetchSpecialFolders(); } void EwsResource::fetchFoldersIncrJobFinished(KJob *job) { Q_EMIT status(Idle, i18nc("@info:status The resource is ready", "Ready")); - EwsFetchFoldersIncrJob *req = qobject_cast(job); + EwsFetchFoldersIncrJob *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsFetchFoldersIncrJob job object"); cancelTask(i18nc("@info:status", "Invalid incremental folders retrieval request job object")); return; } if (req->error()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("ERROR") << req->errorString(); /* Retry with a full sync. */ qCWarningNC(EWSRES_LOG) << QStringLiteral("Retrying with a full sync."); mFolderSyncState.clear(); doRetrieveCollections(); return; } mFolderSyncState = req->syncState(); saveState(); collectionsRetrievedIncremental(req->changedFolders(), req->deletedFolders()); fetchSpecialFolders(); } void EwsResource::itemFetchJobFinished(KJob *job) { - EwsFetchItemsJob *fetchJob = qobject_cast(job); + EwsFetchItemsJob *fetchJob = qobject_cast(job); if (!fetchJob) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsFetchItemsJobjob object"); cancelTask(i18nc("@info:status", "Failed to retrieve items - internal error")); return; } if (job->error()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Item fetch error:") << job->errorString(); if (mSyncState.contains(fetchJob->collection().remoteId())) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Retrying with empty state."); // Retry with a clear sync state. mSyncState.remove(fetchJob->collection().remoteId()); retrieveItems(fetchJob->collection()); } else { qCDebugNC(EWSRES_LOG) << QStringLiteral("Clean sync failed."); // No more hope cancelTask(i18nc("@info:status", "Failed to retrieve items")); return; } } else { mSyncState[fetchJob->collection().remoteId()] = fetchJob->syncState(); itemsRetrievedIncremental(fetchJob->changedItems(), fetchJob->deletedItems()); } saveState(); mItemsToCheck.remove(fetchJob->collection().remoteId()); Q_EMIT status(Idle, i18nc("@info:status The resource is ready", "Ready")); } void EwsResource::itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers) { qCDebugNC(EWSRES_AGENTIF_LOG) << "itemChanged: start " << item << partIdentifiers; EwsItemType type = EwsItemHandler::mimeToItemType(item.mimeType()); if (isEwsMessageItemType(type)) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemChanged: Item type not supported for changing"; cancelTask(i18nc("@info:status", "Item type not supported for changing")); } else { EwsModifyItemJob *job = EwsItemHandler::itemHandler(type)->modifyItemJob(mEwsClient, - Item::List() << item, partIdentifiers, this); + Item::List() << item, partIdentifiers, this); connect(job, &KJob::result, this, &EwsResource::itemChangeRequestFinished); job->start(); } } -void EwsResource::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, - const QSet &removedFlags) +void EwsResource::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) { qCDebug(EWSRES_AGENTIF_LOG) << "itemsFlagsChanged: start" << items << addedFlags << removedFlags; EwsModifyItemFlagsJob *job = new EwsModifyItemFlagsJob(mEwsClient, this, items, addedFlags, removedFlags); connect(job, &EwsModifyItemFlagsJob::result, this, &EwsResource::itemModifyFlagsRequestFinished); job->start(); } void EwsResource::itemModifyFlagsRequestFinished(KJob *job) { if (job->error()) { qCWarning(EWSRES_AGENTIF_LOG) << "itemsFlagsChanged:" << job->errorString(); cancelTask(i18nc("@info:status", "Failed to process item flags update request")); return; } - EwsModifyItemFlagsJob *req = qobject_cast(job); + EwsModifyItemFlagsJob *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_AGENTIF_LOG) << "itemsFlagsChanged: Invalid EwsModifyItemFlagsJob job object"; cancelTask(i18nc("@info:status", "Failed to update item flags - internal error")); return; } qCDebug(EWSRES_AGENTIF_LOG) << "itemsFlagsChanged: done"; changesCommitted(req->items()); } void EwsResource::itemChangeRequestFinished(KJob *job) { if (job->error()) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemChanged: " << job->errorString(); cancelTask(i18nc("@info:status", "Failed to process item update request")); return; } - EwsModifyItemJob *req = qobject_cast(job); + EwsModifyItemJob *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemChanged: Invalid EwsModifyItemJob job object"; cancelTask(i18nc("@info:status", "Failed to update item - internal error")); return; } qCDebugNC(EWSRES_AGENTIF_LOG) << "itemChanged: done"; changesCommitted(req->items()); } -void EwsResource::itemsMoved(const Item::List &items, const Collection &sourceCollection, - const Collection &destinationCollection) +void EwsResource::itemsMoved(const Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection) { qCDebug(EWSRES_AGENTIF_LOG) << "itemsMoved: start" << items << sourceCollection << destinationCollection; EwsId::List ids; ids.reserve(items.count()); for (const Item &item : items) { EwsId id(item.remoteId(), item.remoteRevision()); ids.append(id); } EwsMoveItemRequest *req = new EwsMoveItemRequest(mEwsClient, this); req->setItemIds(ids); EwsId destId(destinationCollection.remoteId(), QString()); req->setDestinationFolderId(destId); req->setProperty("items", QVariant::fromValue(items)); req->setProperty("sourceCollection", QVariant::fromValue(sourceCollection)); req->setProperty("destinationCollection", QVariant::fromValue(destinationCollection)); connect(req, &KJob::result, this, &EwsResource::itemMoveRequestFinished); req->start(); } void EwsResource::itemMoveRequestFinished(KJob *job) { if (job->error()) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsMoved: " << job->errorString(); cancelTask(i18nc("@info:status", "Failed to process item move request")); return; } - EwsMoveItemRequest *req = qobject_cast(job); + EwsMoveItemRequest *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsMoved: Invalid EwsMoveItemRequest job object"; cancelTask(i18nc("@info:status", "Failed to move item - internal error")); return; } Item::List items = job->property("items").value(); if (items.count() != req->responses().count()) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsMoved: Invalid number of responses received from server"; cancelTask(i18nc("@info:status", "Failed to move item - invalid number of responses received from server")); return; } /* When moving a batch of items it is possible that the operation will fail for some of them. * Unfortunately Akonadi doesn't provide a way to report such partial success/failure. In order * to work around this in case of partial failure the source and destination folders will be * resynchronised. In order to avoid doing a full sync a hint will be provided in order to * indicate the item(s) to check. */ Item::List movedItems; EwsId::List failedIds; Collection srcCol = req->property("sourceCollection").value(); Collection dstCol = req->property("destinationCollection").value(); Item::List::iterator it = items.begin(); Q_FOREACH (const EwsMoveItemRequest::Response &resp, req->responses()) { Item &item = *it; if (resp.isSuccess()) { qCDebugNC(EWSRES_AGENTIF_LOG) << QStringLiteral("itemsMoved: succeeded for item %1 (new id: %2)") - .arg(ewsHash(item.remoteId())).arg(ewsHash(resp.itemId().id())); + .arg(ewsHash(item.remoteId())).arg(ewsHash(resp.itemId().id())); if (item.isValid()) { /* Log item deletion in the source folder so that the next sync doesn't trip over * non-existent items. Use old remote ids for that. */ if (mSubManager) { mSubManager->queueUpdate(EwsDeletedEvent, item.remoteId(), QString()); } mQueuedUpdates[srcCol.remoteId()].append({item.remoteId(), QString(), EwsDeletedEvent}); item.setRemoteId(resp.itemId().id()); item.setRemoteRevision(resp.itemId().changeKey()); movedItems.append(item); } } else { Q_EMIT warning(QStringLiteral("Move failed for item %1").arg(item.remoteId())); qCDebugNC(EWSRES_AGENTIF_LOG) << QStringLiteral("itemsMoved: failed for item %1").arg(ewsHash(item.remoteId())); failedIds.append(EwsId(item.remoteId(), QString())); } ++it; } if (!failedIds.isEmpty()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to move %1 items. Forcing src & dst folder sync.") - .arg(failedIds.size()); + .arg(failedIds.size()); mItemsToCheck[srcCol.remoteId()] += failedIds; foldersModifiedEvent(EwsId::List({EwsId(srcCol.remoteId(), QString())})); mItemsToCheck[dstCol.remoteId()] += failedIds; foldersModifiedEvent(EwsId::List({EwsId(dstCol.remoteId(), QString())})); } qCDebugNC(EWSRES_AGENTIF_LOG) << "itemsMoved: done"; changesCommitted(movedItems); } void EwsResource::itemsRemoved(const Item::List &items) { qCDebugNC(EWSRES_AGENTIF_LOG) << "itemsRemoved: start" << items; EwsId::List ids; ids.reserve(items.count()); for (const Item &item : items) { EwsId id(item.remoteId(), item.remoteRevision()); ids.append(id); } EwsDeleteItemRequest *req = new EwsDeleteItemRequest(mEwsClient, this); req->setItemIds(ids); req->setProperty("items", QVariant::fromValue(items)); connect(req, &EwsDeleteItemRequest::result, this, &EwsResource::itemDeleteRequestFinished); req->start(); - } void EwsResource::itemDeleteRequestFinished(KJob *job) { if (job->error()) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsRemoved: " << job->errorString(); cancelTask(i18nc("@info:status", "Failed to process item delete request")); return; } - EwsDeleteItemRequest *req = qobject_cast(job); + EwsDeleteItemRequest *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsRemoved: Invalid EwsDeleteItemRequest job object"; cancelTask(i18nc("@info:status", "Failed to delete item - internal error")); return; } Item::List items = job->property("items").value(); if (items.count() != req->responses().count()) { qCWarningNC(EWSRES_AGENTIF_LOG) << "itemsRemoved: Invalid number of responses received from server"; cancelTask(i18nc("@info:status", "Failed to delete item - invalid number of responses received from server")); return; } /* When removing a batch of items it is possible that the operation will fail for some of them. * Unfortunately Akonadi doesn't provide a way to report such partial success/failure. In order * to work around this in case of partial failure the original folder(s) will be resynchronised. * In order to avoid doing a full sync a hint will be provided in order to indicate the item(s) * to check. */ EwsId::List foldersToSync; Item::List::iterator it = items.begin(); Q_FOREACH (const EwsDeleteItemRequest::Response &resp, req->responses()) { Item &item = *it; if (resp.isSuccess()) { qCDebugNC(EWSRES_AGENTIF_LOG) << QStringLiteral("itemsRemoved: succeeded for item %1").arg(ewsHash(item.remoteId())); if (mSubManager) { mSubManager->queueUpdate(EwsDeletedEvent, item.remoteId(), QString()); } mQueuedUpdates[item.parentCollection().remoteId()].append({item.remoteId(), QString(), EwsDeletedEvent}); } else { Q_EMIT warning(QStringLiteral("Delete failed for item %1").arg(item.remoteId())); qCWarningNC(EWSRES_AGENTIF_LOG) << QStringLiteral("itemsRemoved: failed for item %1").arg(ewsHash(item.remoteId())); EwsId colId = EwsId(item.parentCollection().remoteId(), QString()); mItemsToCheck[colId.id()].append(EwsId(item.remoteId(), QString())); if (!foldersToSync.contains(colId)) { foldersToSync.append(colId); } } ++it; } if (!foldersToSync.isEmpty()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Need to force sync for %1 folders.") - .arg(foldersToSync.size()); + .arg(foldersToSync.size()); foldersModifiedEvent(foldersToSync); } qCDebug(EWSRES_AGENTIF_LOG) << "itemsRemoved: done"; changeProcessed(); } void EwsResource::itemAdded(const Item &item, const Collection &collection) { EwsItemType type = EwsItemHandler::mimeToItemType(item.mimeType()); if (isEwsMessageItemType(type)) { cancelTask(i18nc("@info:status", "Item type not supported for creation")); } else { EwsCreateItemJob *job = EwsItemHandler::itemHandler(type)->createItemJob(mEwsClient, item, - collection, mTagStore, this); + collection, mTagStore, this); connect(job, &EwsCreateItemJob::result, this, &EwsResource::itemCreateRequestFinished); job->start(); } } void EwsResource::itemCreateRequestFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process item create request")); return; } - EwsCreateItemJob *req = qobject_cast(job); + EwsCreateItemJob *req = qobject_cast(job); if (!req) { cancelTask(i18nc("@info:status", "Failed to create item - internal error")); return; } changeCommitted(req->item()); } void EwsResource::collectionAdded(const Collection &collection, const Collection &parent) { EwsFolderType type; QStringList mimeTypes = collection.contentMimeTypes(); if (mimeTypes.contains(EwsItemHandler::itemHandler(EwsItemTypeCalendarItem)->mimeType())) { type = EwsFolderTypeCalendar; } else if (mimeTypes.contains(EwsItemHandler::itemHandler(EwsItemTypeContact)->mimeType())) { type = EwsFolderTypeContacts; } else if (mimeTypes.contains(EwsItemHandler::itemHandler(EwsItemTypeTask)->mimeType())) { type = EwsFolderTypeTasks; } else if (mimeTypes.contains(EwsItemHandler::itemHandler(EwsItemTypeMessage)->mimeType())) { type = EwsFolderTypeMail; } else { qCWarningNC(EWSRES_LOG) << QStringLiteral("Cannot determine EWS folder type."); cancelTask(i18nc("@info:status", "Failed to add collection - cannot determine EWS folder type")); return; } EwsFolder folder; folder.setType(type); folder.setField(EwsFolderFieldDisplayName, collection.name()); EwsCreateFolderRequest *req = new EwsCreateFolderRequest(mEwsClient, this); req->setParentFolderId(EwsId(parent.remoteId())); req->setFolders(EwsFolder::List() << folder); req->setProperty("collection", QVariant::fromValue(collection)); connect(req, &EwsCreateFolderRequest::result, this, &EwsResource::folderCreateRequestFinished); req->start(); } void EwsResource::folderCreateRequestFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process folder create request")); return; } - EwsCreateFolderRequest *req = qobject_cast(job); + EwsCreateFolderRequest *req = qobject_cast(job); if (!req) { cancelTask(i18nc("@info:status", "Failed to create folder - internal error")); return; } Collection col = job->property("collection").value(); EwsCreateFolderRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { const EwsId &id = resp.folderId(); col.setRemoteId(id.id()); col.setRemoteRevision(id.changeKey()); changeCommitted(col); } else { cancelTask(i18nc("@info:status", "Failed to create folder")); } } -void EwsResource::collectionMoved(const Collection &collection, const Collection &collectionSource, - const Collection &collectionDestination) +void EwsResource::collectionMoved(const Collection &collection, const Collection &collectionSource, const Collection &collectionDestination) { Q_UNUSED(collectionSource) EwsId::List ids; ids.append(EwsId(collection.remoteId(), collection.remoteRevision())); EwsMoveFolderRequest *req = new EwsMoveFolderRequest(mEwsClient, this); req->setFolderIds(ids); EwsId destId(collectionDestination.remoteId()); req->setDestinationFolderId(destId); req->setProperty("collection", QVariant::fromValue(collection)); connect(req, &EwsMoveFolderRequest::result, this, &EwsResource::folderMoveRequestFinished); req->start(); } void EwsResource::folderMoveRequestFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process folder move request")); return; } - EwsMoveFolderRequest *req = qobject_cast(job); + EwsMoveFolderRequest *req = qobject_cast(job); if (!req) { cancelTask(i18nc("@info:status", "Failed to move folder - internal error")); return; } Collection col = job->property("collection").value(); if (req->responses().count() != 1) { cancelTask(i18nc("@info:status", "Failed to move folder - invalid number of responses received from server")); return; } EwsMoveFolderRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { const EwsId &id = resp.folderId(); col.setRemoteId(id.id()); col.setRemoteRevision(id.changeKey()); changeCommitted(col); } else { cancelTask(i18nc("@info:status", "Failed to move folder")); } } -void EwsResource::collectionChanged(const Collection &collection, - const QSet &changedAttributes) +void EwsResource::collectionChanged(const Collection &collection, const QSet &changedAttributes) { if (changedAttributes.contains("NAME")) { EwsUpdateFolderRequest *req = new EwsUpdateFolderRequest(mEwsClient, this); EwsUpdateFolderRequest::FolderChange fc(EwsId(collection.remoteId(), collection.remoteRevision()), EwsFolderTypeMail); EwsUpdateFolderRequest::Update *upd = new EwsUpdateFolderRequest::SetUpdate(EwsPropertyField(QStringLiteral("folder:DisplayName")), - collection.name()); + collection.name()); fc.addUpdate(upd); req->addFolderChange(fc); req->setProperty("collection", QVariant::fromValue(collection)); connect(req, &EwsUpdateFolderRequest::finished, this, &EwsResource::folderUpdateRequestFinished); req->start(); } else { changeCommitted(collection); } } void EwsResource::collectionChanged(const Akonadi::Collection &collection) { Q_UNUSED(collection) } void EwsResource::folderUpdateRequestFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process folder update request")); return; } - EwsUpdateFolderRequest *req = qobject_cast(job); + EwsUpdateFolderRequest *req = qobject_cast(job); if (!req) { cancelTask(i18nc("@info:status", "Failed to update folder - internal error")); return; } Collection col = job->property("collection").value(); if (req->responses().count() != 1) { cancelTask(i18nc("@info:status", "Failed to update folder - invalid number of responses received from server")); return; } EwsUpdateFolderRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { const EwsId &id = resp.folderId(); col.setRemoteId(id.id()); col.setRemoteRevision(id.changeKey()); changeCommitted(col); } else { cancelTask(i18nc("@info:status", "Failed to update folder")); } } void EwsResource::collectionRemoved(const Collection &collection) { EwsDeleteFolderRequest *req = new EwsDeleteFolderRequest(mEwsClient, this); EwsId::List ids; ids.append(EwsId(collection.remoteId(), collection.remoteRevision())); req->setFolderIds(ids); connect(req, &EwsDeleteFolderRequest::result, this, &EwsResource::folderDeleteRequestFinished); req->start(); - } void EwsResource::folderDeleteRequestFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process folder delete request")); return; } - EwsDeleteFolderRequest *req = qobject_cast(job); + EwsDeleteFolderRequest *req = qobject_cast(job); if (!req) { cancelTask(i18nc("@info:status", "Failed to delete folder - internal error")); return; } EwsDeleteFolderRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { changeProcessed(); } else { cancelTask(i18nc("@info:status", "Failed to delete folder")); mFolderSyncState.clear(); synchronizeCollectionTree(); } } void EwsResource::sendItem(const Akonadi::Item &item) { EwsItemType type = EwsItemHandler::mimeToItemType(item.mimeType()); if (isEwsMessageItemType(type)) { itemSent(item, TransportFailed, i18nc("@info:status", "Item type not supported for creation")); } else { EwsCreateItemJob *job = EwsItemHandler::itemHandler(type)->createItemJob(mEwsClient, item, - Collection(), mTagStore, this); + Collection(), mTagStore, this); job->setSend(true); job->setProperty("item", QVariant::fromValue(item)); connect(job, &EwsCreateItemJob::result, this, &EwsResource::itemSendRequestFinished); job->start(); } } void EwsResource::itemSendRequestFinished(KJob *job) { Item item = job->property("item").value(); if (job->error()) { itemSent(item, TransportFailed, i18nc("@info:status", "Failed to process item send request")); return; } - EwsCreateItemJob *req = qobject_cast(job); + EwsCreateItemJob *req = qobject_cast(job); if (!req) { itemSent(item, TransportFailed, i18nc("@info:status", "Failed to send item - internal error")); return; } itemSent(item, TransportSucceeded); } void EwsResource::sendMessage(const QString &id, const QByteArray &content) { #ifdef HAVE_SEPARATE_MTA_RESOURCE EwsCreateItemRequest *req = new EwsCreateItemRequest(mEwsClient, this); EwsItem item; item.setType(EwsItemTypeMessage); item.setField(EwsItemFieldMimeContent, content); req->setItems(EwsItem::List() << item); req->setMessageDisposition(EwsDispSendOnly); req->setProperty("requestId", id); connect(req, &EwsCreateItemRequest::finished, this, &EwsResource::messageSendRequestFinished); req->start(); #endif } #ifdef HAVE_SEPARATE_MTA_RESOURCE void EwsResource::messageSendRequestFinished(KJob *job) { QString id = job->property("requestId").toString(); if (job->error()) { Q_EMIT messageSent(id, i18nc("@info:status", "Failed to process item send request")); return; } - EwsCreateItemRequest *req = qobject_cast(job); + EwsCreateItemRequest *req = qobject_cast(job); if (!req) { Q_EMIT messageSent(id, i18nc("@info:status", "Failed to send item - internal error")); return; } if (req->responses().count() != 1) { Q_EMIT messageSent(id, i18nc("@info:status", "Invalid number of responses received from server")); return; } EwsCreateItemRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { Q_EMIT messageSent(id, QString()); } else { Q_EMIT messageSent(id, resp.responseMessage()); } } + #endif void EwsResource::foldersModifiedEvent(const EwsId::List &folders) { Q_FOREACH (const EwsId &id, folders) { Collection c; c.setRemoteId(id.id()); CollectionFetchJob *job = new CollectionFetchJob(c, CollectionFetchJob::Base); job->setFetchScope(changeRecorder()->collectionFetchScope()); job->fetchScope().setResource(identifier()); job->fetchScope().setListFilter(CollectionFetchScope::Sync); connect(job, &KJob::result, this, &EwsResource::foldersModifiedCollectionSyncFinished); } - } void EwsResource::foldersModifiedCollectionSyncFinished(KJob *job) { if (job->error()) { qCDebug(EWSRES_LOG) << QStringLiteral("Failed to fetch collection tree for sync."); return; } - CollectionFetchJob *fetchJob = qobject_cast(job); + CollectionFetchJob *fetchJob = qobject_cast(job); synchronizeCollection(fetchJob->collections()[0].id()); } void EwsResource::folderTreeModifiedEvent() { synchronizeCollectionTree(); } void EwsResource::fullSyncRequestedEvent() { synchronize(); } void EwsResource::clearFolderSyncState() { mSyncState.clear(); saveState(); } void EwsResource::clearFolderSyncState(const QString &folderId) { mSyncState.remove(folderId); saveState(); } void EwsResource::clearFolderTreeSyncState() { mFolderSyncState.clear(); saveState(); } void EwsResource::fetchSpecialFolders() { CollectionFetchJob *job = new CollectionFetchJob(mRootCollection, CollectionFetchJob::Recursive, this); connect(job, &CollectionFetchJob::collectionsReceived, this, &EwsResource::specialFoldersCollectionsRetrieved); - connect(job, &CollectionFetchJob::result, this, [this](KJob * job) { + connect(job, &CollectionFetchJob::result, this, [this](KJob *job) { if (job->error()) { qCWarningNC(EWSRES_LOG) << "Special folders fetch failed:" << job->errorString(); } }); job->start(); } void EwsResource::specialFoldersCollectionsRetrieved(const Collection::List &folders) { EwsId::List queryItems; queryItems.reserve(specialFolderList.count()); Q_FOREACH (const SpecialFolders &sf, specialFolderList) { queryItems.append(EwsId(sf.did)); } if (!queryItems.isEmpty()) { EwsGetFolderRequest *req = new EwsGetFolderRequest(mEwsClient, this); req->setFolderShape(EwsFolderShape(EwsShapeIdOnly)); req->setFolderIds(queryItems); req->setProperty("collections", QVariant::fromValue(folders)); connect(req, &EwsGetFolderRequest::finished, this, &EwsResource::specialFoldersFetchFinished); req->start(); } } void EwsResource::specialFoldersFetchFinished(KJob *job) { if (job->error()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Special collection fetch failed:") << job->errorString(); return; } - EwsGetFolderRequest *req = qobject_cast(job); + EwsGetFolderRequest *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Special collection fetch failed:") << QStringLiteral("Invalid EwsGetFolderRequest job object"); return; } const Collection::List collections = req->property("collections").value(); if (req->responses().size() != specialFolderList.size()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Special collection fetch failed:") << QStringLiteral("Invalid number of responses received"); return; } QMap map; for (const Collection &col : collections) { map.insert(col.remoteId(), col); } auto it = specialFolderList.cbegin(); Q_FOREACH (const EwsGetFolderRequest::Response &resp, req->responses()) { if (resp.isSuccess()) { EwsId fid = resp.folder()[EwsFolderFieldFolderId].value(); QMap::iterator mapIt = map.find(fid.id()); if (mapIt != map.end()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Registering folder %1(%2) as special collection %3") - .arg(ewsHash(mapIt->remoteId())).arg(mapIt->id()).arg(it->type); + .arg(ewsHash(mapIt->remoteId())).arg(mapIt->id()).arg(it->type); SpecialMailCollections::self()->registerCollection(it->type, *mapIt); if (!mapIt->hasAttribute()) { EntityDisplayAttribute *attr = mapIt->attribute(Collection::AddIfMissing); attr->setIconName(it->iconName); CollectionModifyJob *modJob = new CollectionModifyJob(*mapIt, this); modJob->start(); } } } it++; } } void EwsResource::saveState() { QByteArray str; QDataStream dataStream(&str, QIODevice::WriteOnly); dataStream << mSyncState; mSettings->setSyncState(QString::fromLatin1(qCompress(str, 9).toBase64())); mSettings->setFolderSyncState(QString::fromLatin1(qCompress(mFolderSyncState.toLatin1(), 9).toBase64())); mSettings->save(); } void EwsResource::doSetOnline(bool online) { if (online) { reloadConfig(); } else { mSubManager.reset(nullptr); } } int EwsResource::reconnectTimeout() { // Return InitialReconnectTimeout for the first time, then ReconnectTimeout. int timeout = mReconnectTimeout; mReconnectTimeout = ReconnectTimeout; return timeout; } -void EwsResource::itemsTagsChanged(const Item::List &items, const QSet &addedTags, - const QSet &removedTags) +void EwsResource::itemsTagsChanged(const Item::List &items, const QSet &addedTags, const QSet &removedTags) { Q_UNUSED(addedTags) Q_UNUSED(removedTags) EwsUpdateItemsTagsJob *job = new EwsUpdateItemsTagsJob(items, mTagStore, mEwsClient, this); connect(job, &EwsUpdateItemsTagsJob::result, this, &EwsResource::itemsTagChangeFinished); job->start(); } void EwsResource::itemsTagChangeFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process item tags update request")); return; } - EwsUpdateItemsTagsJob *updJob = qobject_cast(job); + EwsUpdateItemsTagsJob *updJob = qobject_cast(job); if (!updJob) { cancelTask(i18nc("@info:status", "Failed to update item tags - internal error")); return; } changesCommitted(updJob->items()); } void EwsResource::tagAdded(const Tag &tag) { mTagStore->addTag(tag); EwsGlobalTagsWriteJob *job = new EwsGlobalTagsWriteJob(mTagStore, mEwsClient, mRootCollection, this); connect(job, &EwsGlobalTagsWriteJob::result, this, &EwsResource::globalTagChangeFinished); job->start(); } void EwsResource::tagChanged(const Tag &tag) { mTagStore->addTag(tag); EwsGlobalTagsWriteJob *job = new EwsGlobalTagsWriteJob(mTagStore, mEwsClient, mRootCollection, this); connect(job, &EwsGlobalTagsWriteJob::result, this, &EwsResource::globalTagChangeFinished); job->start(); } - void EwsResource::tagRemoved(const Tag &tag) { mTagStore->removeTag(tag); EwsGlobalTagsWriteJob *job = new EwsGlobalTagsWriteJob(mTagStore, mEwsClient, mRootCollection, this); connect(job, &EwsGlobalTagsWriteJob::result, this, &EwsResource::globalTagChangeFinished); job->start(); } void EwsResource::globalTagChangeFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process global tag update request")); } else { changeProcessed(); } } - void EwsResource::retrieveTags() { EwsGlobalTagsReadJob *job = new EwsGlobalTagsReadJob(mTagStore, mEwsClient, mRootCollection, this); connect(job, &EwsGlobalTagsReadJob::result, this, &EwsResource::globalTagsRetrievalFinished); job->start(); } void EwsResource::globalTagsRetrievalFinished(KJob *job) { if (job->error()) { cancelTask(i18nc("@info:status", "Failed to process global tags retrieval request")); } else { - EwsGlobalTagsReadJob *readJob = qobject_cast(job); + EwsGlobalTagsReadJob *readJob = qobject_cast(job); Q_ASSERT(readJob); tagsRetrieved(readJob->tags(), QHash()); } } void EwsResource::setUpAuth() { EwsAbstractAuth *auth = mSettings->loadAuth(this); /* Use queued connections here to avoid stack overflow when the reauthentication proceeds through all stages. */ connect(auth, &EwsAbstractAuth::authSucceeded, this, &EwsResource::authSucceeded, Qt::QueuedConnection); connect(auth, &EwsAbstractAuth::authFailed, this, &EwsResource::authFailed, Qt::QueuedConnection); connect(auth, &EwsAbstractAuth::requestAuthFailed, this, &EwsResource::requestAuthFailed, Qt::QueuedConnection); qCDebugNC(EWSRES_LOG) << QStringLiteral("Initializing authentication"); mAuth.reset(auth); auth->init(); } void EwsResource::authSucceeded() { mAuthStage = 0; resetUrl(); } void EwsResource::reauthNotificationDismissed(bool accepted) { if (mReauthNotification) { mReauthNotification.clear(); if (accepted) { mAuth->authenticate(true); } else { Q_EMIT authFailed(QStringLiteral("Interactive authentication request denied")); } } } void EwsResource::authFailed(const QString &error) { qCWarningNC(EWSRES_LOG) << "Authentication failed: " << error; reauthenticate(); } void EwsResource::reauthenticate() { switch (mAuthStage) { case 0: - { if (mAuth->authenticate(false)) { break; } else { ++mAuthStage; } - } /* fall through */ case 1: { const auto reauthPrompt = mAuth->reauthPrompt(); if (!reauthPrompt.isNull()) { mReauthNotification = new KNotification(QStringLiteral("auth-expired"), KNotification::Persistent, this); mReauthNotification->setText(reauthPrompt.arg(name())); mReauthNotification->setActions(QStringList(i18nc("@action:button", "Authenticate"))); mReauthNotification->setComponentName(QStringLiteral("akonadi_ews_resource")); auto acceptedFn = std::bind(&EwsResource::reauthNotificationDismissed, this, true); auto rejectedFn = std::bind(&EwsResource::reauthNotificationDismissed, this, false); connect(mReauthNotification.data(), &KNotification::action1Activated, this, acceptedFn); connect(mReauthNotification.data(), &KNotification::closed, this, rejectedFn); connect(mReauthNotification.data(), &KNotification::ignored, this, rejectedFn); mReauthNotification->sendEvent(); break; } } /* fall through */ case 2: Q_EMIT status(Broken, i18nc("@info:status", "Authentication failed")); break; } ++mAuthStage; } void EwsResource::requestAuthFailed() { qCWarningNC(EWSRES_LOG) << "requestAuthFailed - going offline"; setTemporaryOffline(reconnectTimeout()); Q_EMIT status(Broken, i18nc("@info:status", "Authentication failed")); reauthenticate(); } AKONADI_RESOURCE_MAIN(EwsResource) diff --git a/resources/ews/ewsresource.h b/resources/ews/ewsresource.h index 45edadec8..02e7e1901 100644 --- a/resources/ews/ewsresource.h +++ b/resources/ews/ewsresource.h @@ -1,174 +1,170 @@ /* Copyright (C) 2015-2018 Krzysztof Nowicki 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 EWSRESOURCE_H #define EWSRESOURCE_H #include #include #include #include #include "ewsclient.h" #include "ewsfetchitemsjob.h" #include "ewsid.h" #include class FetchItemState; class EwsAbstractAuth; class EwsSubscriptionManager; class EwsTagStore; class EwsSettings; class KNotification; -class EwsResource : public Akonadi::ResourceBase, public Akonadi::AgentBase::ObserverV4, - public Akonadi::TransportResourceBase +class EwsResource : public Akonadi::ResourceBase, public Akonadi::AgentBase::ObserverV4, public Akonadi::TransportResourceBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.Ews.Resource") public: static const QString akonadiEwsPropsetUuid; static const EwsPropertyField globalTagsProperty; static const EwsPropertyField globalTagsVersionProperty; static const EwsPropertyField tagsProperty; static const EwsPropertyField flagsProperty; explicit EwsResource(const QString &id); ~EwsResource() override; - void itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, - const QSet &removedTags) override; + void itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags) override; void tagAdded(const Akonadi::Tag &tag) override; void tagChanged(const Akonadi::Tag &tag) override; void tagRemoved(const Akonadi::Tag &tag) override; void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; - void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, - const Akonadi::Collection &collectionDestination) override; + void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; void collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes) override; void collectionChanged(const Akonadi::Collection &collection) override; void collectionRemoved(const Akonadi::Collection &collection) override; void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; void itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers) override; - void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, - const QSet &removedFlags) override; - void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, - const Akonadi::Collection &destinationCollection) override; + void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) override; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destinationCollection) override; void itemsRemoved(const Akonadi::Item::List &items) override; void sendItem(const Akonadi::Item &item) override; const Akonadi::Collection &rootCollection() const { return mRootCollection; } EwsSettings *settings() { return mSettings.data(); } + protected: void doSetOnline(bool online) override; public Q_SLOTS: void configure(WId windowId) override; Q_SCRIPTABLE void clearFolderSyncState(const QString &folderId); Q_SCRIPTABLE void clearFolderSyncState(); Q_SCRIPTABLE void clearFolderTreeSyncState(); protected Q_SLOTS: void retrieveCollections() override; void retrieveItems(const Akonadi::Collection &collection) override; bool retrieveItems(const Akonadi::Item::List &items, const QSet &parts) override; void retrieveTags() override; private Q_SLOTS: void fetchFoldersJobFinished(KJob *job); void fetchFoldersIncrJobFinished(KJob *job); void itemFetchJobFinished(KJob *job); void getItemsRequestFinished(KJob *job); void itemChangeRequestFinished(KJob *job); void itemModifyFlagsRequestFinished(KJob *job); void itemMoveRequestFinished(KJob *job); void itemDeleteRequestFinished(KJob *job); void itemCreateRequestFinished(KJob *job); void itemSendRequestFinished(KJob *job); void folderCreateRequestFinished(KJob *job); void folderMoveRequestFinished(KJob *job); void folderUpdateRequestFinished(KJob *job); void folderDeleteRequestFinished(KJob *job); void delayedInit(); void foldersModifiedEvent(const EwsId::List &folders); void foldersModifiedCollectionSyncFinished(KJob *job); void folderTreeModifiedEvent(); void fullSyncRequestedEvent(); void rootFolderFetchFinished(KJob *job); void specialFoldersFetchFinished(KJob *job); void itemsTagChangeFinished(KJob *job); void globalTagChangeFinished(KJob *job); void globalTagsRetrievalFinished(KJob *job); void adjustInboxRemoteIdFetchFinished(KJob *job); void rootCollectionFetched(KJob *job); void connectionError(); void reloadConfig(); void authSucceeded(); void authFailed(const QString &error); void requestAuthFailed(); public Q_SLOTS: Q_SCRIPTABLE void sendMessage(const QString &id, const QByteArray &content); Q_SIGNALS: Q_SCRIPTABLE void messageSent(const QString &id, const QString &error); #ifdef HAVE_SEPARATE_MTA_RESOURCE private Q_SLOTS: void messageSendRequestFinished(KJob *job); #endif private: void finishItemsFetch(FetchItemState *state); void fetchSpecialFolders(); void specialFoldersCollectionsRetrieved(const Akonadi::Collection::List &folders); void saveState(); void resetUrl(); void doRetrieveCollections(); int reconnectTimeout(); void setUpAuth(); void reauthNotificationDismissed(bool accepted); void reauthenticate(); EwsClient mEwsClient; Akonadi::Collection mRootCollection; QScopedPointer mSubManager; QHash mSyncState; QString mFolderSyncState; QHash mItemsToCheck; QHash mQueuedUpdates; QString mPassword; QScopedPointer mAuth; int mAuthStage; QPointer mReauthNotification; bool mTagsRetrieved; int mReconnectTimeout; EwsTagStore *mTagStore = nullptr; QScopedPointer mSettings; }; #endif diff --git a/resources/ews/ewsresource_debug.h b/resources/ews/ewsresource_debug.h index b7b3eb2a5..28ae8aabf 100644 --- a/resources/ews/ewsresource_debug.h +++ b/resources/ews/ewsresource_debug.h @@ -1,82 +1,81 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 EWSRESOURCE_DEBUG_H #define EWSRESOURCE_DEBUG_H - #include #include #include "ewsclient.h" #include "ewsclient_debug.h" #include "ewsres_agentif_debug.h" #include "ewsres_debug.h" inline QDebug operator<<(QDebug debug, const Akonadi::Item::List &items) { QDebugStateSaver saver(debug); QStringList itemStrs; itemStrs.reserve(items.count()); for (const Akonadi::Item &item : items) { itemStrs.append(ewsHash(item.remoteId())); } debug.nospace().noquote() << "Akonadi::Item::List(" << itemStrs.join(QChar::fromLatin1(',')) << ")"; return debug.maybeSpace(); } inline QDebug operator<<(QDebug debug, const Akonadi::Item &item) { QDebugStateSaver saver(debug); debug.nospace().noquote() << "Akonadi::Item(" << ewsHash(item.remoteId()) << ")"; return debug.maybeSpace(); } inline QDebug operator<<(QDebug debug, const Akonadi::Collection::List &cols) { QDebugStateSaver saver(debug); QStringList itemStrs; itemStrs.reserve(cols.count()); for (const Akonadi::Collection &col : cols) { itemStrs.append(EwsClient::folderHash.value(col.remoteId(), ewsHash(col.remoteId()))); } debug.nospace().noquote() << "Akonadi::Collection::List(" << itemStrs.join(QLatin1Char(',')) << ")"; return debug.maybeSpace(); } inline QDebug operator<<(QDebug debug, const Akonadi::Collection &col) { QDebugStateSaver saver(debug); debug.nospace().noquote() << "Akonadi::Collection(" << EwsClient::folderHash.value(col.remoteId(), ewsHash(col.remoteId())) << ")"; return debug.maybeSpace(); } inline QDebug operator<<(QDebug debug, const QSet &items) { QDebugStateSaver saver(debug); QStringList itemStrs; itemStrs.reserve(items.count()); for (const QByteArray &item : items) { itemStrs.append(QString::fromLatin1(item)); } debug.nospace().noquote() << "QSet(" << itemStrs.join(QLatin1Char(',')) << ")"; return debug.maybeSpace(); } #endif diff --git a/resources/ews/ewssettings.cpp b/resources/ews/ewssettings.cpp index 314b643ed..8d9007d3a 100644 --- a/resources/ews/ewssettings.cpp +++ b/resources/ews/ewssettings.cpp @@ -1,349 +1,355 @@ /* Copyright (C) 2017-2018 Krzysztof Nowicki 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 "ewssettings.h" #include #include #include #include "auth/ewspasswordauth.h" #include "auth/ewsoauth.h" #include "ewsresource_debug.h" static const QString ewsWalletFolder = QStringLiteral("akonadi-ews"); // Allow unittest to override this to shorten test execution #ifdef EWSSETTINGS_UNITTEST constexpr int walletTimeout = 2000; #else constexpr int walletTimeout = 30000; #endif using namespace KWallet; EwsSettings::EwsSettings(WId windowId) - : EwsSettingsBase(), mWindowId(windowId), mPasswordReadPending(false), mPasswordWritePending(false), - mMapReadPending(false), mMapWritePending(false), mWalletTimer(this) + : EwsSettingsBase() + , mWindowId(windowId) + , mPasswordReadPending(false) + , mPasswordWritePending(false) + , mMapReadPending(false) + , mMapWritePending(false) + , mWalletTimer(this) { mWalletTimer.setInterval(walletTimeout); mWalletTimer.setSingleShot(true); connect(&mWalletTimer, &QTimer::timeout, this, [this]() { qCWarning(EWSRES_LOG) << "Timeout waiting for wallet open"; onWalletOpened(false); }); } EwsSettings::~EwsSettings() { } bool EwsSettings::requestWalletOpen() { if (!mWallet) { mWallet = Wallet::openWallet(Wallet::NetworkWallet(), mWindowId, Wallet::Asynchronous); if (mWallet) { connect(mWallet.data(), &Wallet::walletOpened, this, &EwsSettings::onWalletOpened); mWalletTimer.start(); return true; } else { qCWarning(EWSRES_LOG) << "Failed to open wallet"; } } return false; } void EwsSettings::requestPassword(bool ask) { bool status = false; qCDebug(EWSRES_LOG) << "requestPassword: start"; if (!mPassword.isNull()) { qCDebug(EWSRES_LOG) << "requestPassword: password already set"; Q_EMIT passwordRequestFinished(mPassword); return; } if (requestWalletOpen()) { mPasswordReadPending = true; return; } if (mWallet && mWallet->isOpen()) { mPassword = readPassword(); mWallet.clear(); status = true; } if (!status) { if (!ask) { qCDebug(EWSRES_LOG) << "requestPassword: Not allowed to ask"; } else { qCDebug(EWSRES_LOG) << "requestPassword: Requesting interactively"; mPasswordDlg = new KPasswordDialog(nullptr); mPasswordDlg->setModal(true); mPasswordDlg->setPrompt(i18n("Please enter password for user '%1' and Exchange account '%2'.", - username(), email())); + username(), email())); if (mPasswordDlg->exec() == QDialog::Accepted) { mPassword = mPasswordDlg->password(); setPassword(mPassword); } delete mPasswordDlg; } } Q_EMIT passwordRequestFinished(mPassword); } void EwsSettings::requestMap() { qCDebug(EWSRES_LOG) << "requestMap: start"; if (!mMap.isEmpty()) { qCDebug(EWSRES_LOG) << "requestMap: already set"; Q_EMIT mapRequestFinished(mMap); return; } if (requestWalletOpen()) { mMapReadPending = true; return; } if (mWallet && mWallet->isOpen()) { mMap = readMap(); mWallet.clear(); } Q_EMIT mapRequestFinished(mMap); } QString EwsSettings::readPassword() const { QString password; if (mWallet->hasFolder(ewsWalletFolder)) { mWallet->setFolder(ewsWalletFolder); mWallet->readPassword(config()->name(), password); } else { mWallet->createFolder(ewsWalletFolder); } return password; } QMap EwsSettings::readMap() const { QMap map; if (mWallet->hasFolder(ewsWalletFolder)) { mWallet->setFolder(ewsWalletFolder); mWallet->readMap(config()->name(), map); } else { mWallet->createFolder(ewsWalletFolder); } return map; } void EwsSettings::satisfyPasswordReadRequest(bool success) { if (success) { if (mPassword.isNull()) { mPassword = readPassword(); } qCDebug(EWSRES_LOG) << "satisfyPasswordReadRequest: got password"; Q_EMIT passwordRequestFinished(mPassword); } else { qCDebug(EWSRES_LOG) << "satisfyPasswordReadRequest: failed to retrieve password"; Q_EMIT passwordRequestFinished(QString()); } mPasswordReadPending = false; } void EwsSettings::satisfyPasswordWriteRequest(bool success) { if (success) { if (!mWallet->hasFolder(ewsWalletFolder)) { mWallet->createFolder(ewsWalletFolder); } mWallet->setFolder(ewsWalletFolder); mWallet->writePassword(config()->name(), mPassword); } mPasswordWritePending = false; } void EwsSettings::satisfyMapReadRequest(bool success) { if (success) { if (mMap.isEmpty()) { mMap = readMap(); } qCDebug(EWSRES_LOG) << "satisfyMapReadRequest: got map"; Q_EMIT mapRequestFinished(mMap); } else { qCDebug(EWSRES_LOG) << "satisfyTokensReadRequest: failed to retrieve map"; Q_EMIT mapRequestFinished(QMap()); } mMapReadPending = false; } void EwsSettings::satisfyMapWriteRequest(bool success) { if (success) { if (!mWallet->hasFolder(ewsWalletFolder)) { mWallet->createFolder(ewsWalletFolder); } mWallet->setFolder(ewsWalletFolder); mWallet->writeMap(config()->name(), mMap); } mMapWritePending = false; } void EwsSettings::onWalletOpened(bool success) { mWalletTimer.stop(); if (mWallet) { if (mPasswordReadPending) { satisfyPasswordReadRequest(success); } if (mPasswordWritePending) { satisfyPasswordWriteRequest(success); } if (mMapReadPending) { satisfyMapReadRequest(success); } if (mMapWritePending) { satisfyMapWriteRequest(success); } mWallet.clear(); } } void EwsSettings::setPassword(const QString &password) { if (password.isNull()) { qCWarning(EWSRES_LOG) << "Trying to set a null password"; return; } mPassword = password; /* If a pending password request is running, satisfy it. */ if (mWallet && mPasswordReadPending) { satisfyPasswordReadRequest(true); } if (mPasswordDlg) { mPasswordDlg->reject(); } if (requestWalletOpen()) { mPasswordWritePending = true; } } void EwsSettings::setMap(const QMap &map) { if (map.isEmpty()) { qCWarning(EWSRES_LOG) << "Trying to set null map"; return; } for (auto it = map.begin(); it != map.end(); ++it) { qDebug() << "setMap:" << it.key(); if (!it.value().isNull()) { mMap[it.key()] = it.value(); } else { mMap.remove(it.key()); } } /* Remote items with null value - this is a way to remove unwanted items from the map. */ for (auto it = mMap.begin(); it != mMap.end(); ++it) { while (it->isNull()) { it = mMap.erase(it); } } /* If a pending password request is running, satisfy it. */ if (mWallet && mMapReadPending) { satisfyMapReadRequest(true); } if (requestWalletOpen()) { mMapWritePending = true; } } void EwsSettings::setTestPassword(const QString &password) { if (password.isNull()) { qCWarning(EWSRES_LOG) << "Trying to set a null password"; return; } mPassword = password; if (mWallet) { satisfyPasswordReadRequest(true); } if (mPasswordDlg) { mPasswordDlg->reject(); } } /* Not needed for unittests - exclude to avoid excess link-time dependencies. */ #ifndef EWSSETTINGS_UNITTEST EwsAbstractAuth *EwsSettings::loadAuth(QObject *parent) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Setting up authentication"); EwsAbstractAuth *auth = nullptr; const auto mode = authMode(); if (mode == QStringLiteral("oauth2")) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Using OAuth2 authentication"); auth = new EwsOAuth(parent, email(), oAuth2AppId(), oAuth2ReturnUri()); } if (mode == QStringLiteral("username-password")) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Using password-based authentication"); QString user; if (!hasDomain()) { user = username(); } else { user = domain() + QLatin1Char('\\') + username(); } auth = new EwsPasswordAuth(user, parent); } #ifdef HAVE_QCA if (!pKeyCert().isNull() && !pKeyKey().isNull()) { auth->setPKeyAuthCertificateFiles(pKeyCert(), pKeyKey()); } #endif connect(auth, &EwsAbstractAuth::requestWalletPassword, this, &EwsSettings::requestPassword); connect(auth, &EwsAbstractAuth::requestWalletMap, this, &EwsSettings::requestMap); connect(this, &EwsSettings::passwordRequestFinished, auth, &EwsAbstractAuth::walletPasswordRequestFinished); connect(this, &EwsSettings::mapRequestFinished, auth, &EwsAbstractAuth::walletMapRequestFinished); connect(auth, &EwsAbstractAuth::setWalletPassword, this, &EwsSettings::setPassword); connect(auth, &EwsAbstractAuth::setWalletMap, this, &EwsSettings::setMap); return auth; } + #endif /* EWSSETTINGS_UNITTEST */ diff --git a/resources/ews/ewssubscribedfoldersjob.cpp b/resources/ews/ewssubscribedfoldersjob.cpp index 331093a0f..9243702bd 100644 --- a/resources/ews/ewssubscribedfoldersjob.cpp +++ b/resources/ews/ewssubscribedfoldersjob.cpp @@ -1,102 +1,102 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssubscribedfoldersjob.h" #include "ewsclient.h" #include "ewssettings.h" #include "ewsgetfolderrequest.h" #include "ewsresource_debug.h" EwsSubscribedFoldersJob::EwsSubscribedFoldersJob(EwsClient &client, EwsSettings *settings, QObject *parent) - : EwsJob(parent), mClient(client), mSettings(settings) + : EwsJob(parent) + , mClient(client) + , mSettings(settings) { - } EwsSubscribedFoldersJob::~EwsSubscribedFoldersJob() { } void EwsSubscribedFoldersJob::start() { EwsId::List ids; // Before subscribing make sure the subscription list doesn't contain invalid folders. // Do this also for the default list in order to transform the distinguished IDs into real ones. if (mSettings->serverSubscriptionList() == QStringList() << QStringLiteral("default")) { ids = defaultSubscriptionFolders(); } else { ids.reserve(mSettings->serverSubscriptionList().count()); Q_FOREACH (const QString &id, mSettings->serverSubscriptionList()) { ids << EwsId(id); } } EwsGetFolderRequest *req = new EwsGetFolderRequest(mClient, this); req->setFolderShape(EwsFolderShape(EwsShapeIdOnly)); req->setFolderIds(ids); req->setProperty("ids", QVariant::fromValue(ids)); connect(req, &EwsRequest::result, this, &EwsSubscribedFoldersJob::verifySubFoldersRequestFinished); req->start(); } void EwsSubscribedFoldersJob::verifySubFoldersRequestFinished(KJob *job) { if (!job->error()) { - EwsGetFolderRequest *req = qobject_cast(job); + EwsGetFolderRequest *req = qobject_cast(job); Q_ASSERT(req); mFolders.clear(); EwsId::List sourceIds = req->property("ids").value(); QStringList idList; Q_ASSERT(req->responses().size() == sourceIds.size()); auto it = sourceIds.cbegin(); Q_FOREACH (const EwsGetFolderRequest::Response &resp, req->responses()) { if (resp.isSuccess()) { // Take just the id without the change key as the actual folder version is irrelevant // here QString id = resp.folder()[EwsFolderFieldFolderId].value().id(); mFolders << EwsId(id); idList << id; } else { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid folder %1 - skipping").arg(it->id()); } it++; } // Once verified write the final list back to the configuration. mSettings->setServerSubscriptionList(idList); } else { setErrorMsg(job->errorString(), job->error()); } emitResult(); } const EwsId::List &EwsSubscribedFoldersJob::defaultSubscriptionFolders() { static const EwsId::List list = {EwsId(EwsDIdInbox), EwsId(EwsDIdCalendar), EwsId(EwsDIdTasks), EwsId(EwsDIdContacts)}; return list; } - diff --git a/resources/ews/ewssubscriptionmanager.cpp b/resources/ews/ewssubscriptionmanager.cpp index 0ca1cec53..8508421ed 100644 --- a/resources/ews/ewssubscriptionmanager.cpp +++ b/resources/ews/ewssubscriptionmanager.cpp @@ -1,323 +1,326 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssubscriptionmanager.h" #include "ewsresource_debug.h" #include "ewsgeteventsrequest.h" #include "ewsgetfolderrequest.h" #include "ewsgetstreamingeventsrequest.h" #include "ewssubscribedfoldersjob.h" #include "ewssubscriberequest.h" #include "ewsunsubscriberequest.h" #include "ewssettings.h" #include // TODO: Allow customization static Q_CONSTEXPR uint pollInterval = 10; /* seconds */ static Q_CONSTEXPR uint streamingTimeout = 30; /* minutes */ static Q_CONSTEXPR uint streamingConnTimeout = 60; /* seconds */ -EwsSubscriptionManager::EwsSubscriptionManager(EwsClient &client, const EwsId &rootId, - EwsSettings *settings, QObject *parent) - : QObject(parent), mEwsClient(client), mPollTimer(this), mMsgRootId(rootId), mFolderTreeChanged(false), - mEventReq(nullptr), mSettings(settings) +EwsSubscriptionManager::EwsSubscriptionManager(EwsClient &client, const EwsId &rootId, EwsSettings *settings, QObject *parent) + : QObject(parent) + , mEwsClient(client) + , mPollTimer(this) + , mMsgRootId(rootId) + , mFolderTreeChanged(false) + , mEventReq(nullptr) + , mSettings(settings) { mStreamingEvents = mEwsClient.serverVersion().supports(EwsServerVersion::StreamingSubscription); mStreamingTimer.setInterval(streamingConnTimeout * 1000); mStreamingTimer.setSingleShot(true); connect(&mStreamingTimer, &QTimer::timeout, this, &EwsSubscriptionManager::streamingConnectionTimeout); } EwsSubscriptionManager::~EwsSubscriptionManager() { cancelSubscription(); } void EwsSubscriptionManager::start() { // Set-up change notification subscription (if needed) if (mSettings->eventSubscriptionId().isEmpty()) { setupSubscription(); } else { reset(); } if (!mStreamingEvents) { mPollTimer.setInterval(pollInterval * 1000); mPollTimer.setSingleShot(false); connect(&mPollTimer, &QTimer::timeout, this, &EwsSubscriptionManager::getEvents); } } void EwsSubscriptionManager::cancelSubscription() { if (!mSettings->eventSubscriptionId().isEmpty()) { QPointer req = new EwsUnsubscribeRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->exec(); mSettings->setEventSubscriptionId(QString()); mSettings->setEventSubscriptionWatermark(QString()); mSettings->save(); } } void EwsSubscriptionManager::setupSubscription() { EwsSubscribedFoldersJob *job = new EwsSubscribedFoldersJob(mEwsClient, mSettings, this); connect(job, &EwsRequest::result, this, &EwsSubscriptionManager::verifySubFoldersRequestFinished); job->start(); } void EwsSubscriptionManager::verifySubFoldersRequestFinished(KJob *job) { if (!job->error()) { - EwsSubscribedFoldersJob *folderJob = qobject_cast(job); + EwsSubscribedFoldersJob *folderJob = qobject_cast(job); Q_ASSERT(folderJob); setupSubscriptionReq(folderJob->folders()); } else { Q_EMIT connectionError(); } } void EwsSubscriptionManager::setupSubscriptionReq(const EwsId::List &ids) { EwsSubscribeRequest *req = new EwsSubscribeRequest(mEwsClient, this); //req->setAllFolders(true); QList events; events << EwsNewMailEvent; events << EwsMovedEvent; events << EwsCopiedEvent; events << EwsModifiedEvent; events << EwsDeletedEvent; events << EwsCreatedEvent; req->setEventTypes(events); if (mStreamingEvents) { req->setType(EwsSubscribeRequest::StreamingSubscription); } else { req->setType(EwsSubscribeRequest::PullSubscription); } req->setFolderIds(ids); req->setAllFolders(false); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::subscribeRequestFinished); req->start(); } void EwsSubscriptionManager::reset() { mPollTimer.stop(); getEvents(); if (!mStreamingEvents) { mPollTimer.start(); } } void EwsSubscriptionManager::resetSubscription() { mPollTimer.stop(); cancelSubscription(); setupSubscription(); } void EwsSubscriptionManager::subscribeRequestFinished(KJob *job) { if (!job->error()) { - EwsSubscribeRequest *req = qobject_cast(job); + EwsSubscribeRequest *req = qobject_cast(job); if (req) { mSettings->setEventSubscriptionId(req->response().subscriptionId()); if (mStreamingEvents) { getEvents(); } else { mSettings->setEventSubscriptionWatermark(req->response().watermark()); getEvents(); mPollTimer.start(); } mSettings->save(); } } else { Q_EMIT connectionError(); } } void EwsSubscriptionManager::getEvents() { if (mStreamingEvents) { EwsGetStreamingEventsRequest *req = new EwsGetStreamingEventsRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->setTimeout(streamingTimeout); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); connect(req, &EwsGetStreamingEventsRequest::eventsReceived, this, &EwsSubscriptionManager::streamingEventsReceived); req->start(); mEventReq = req; mStreamingTimer.start(); } else { EwsGetEventsRequest *req = new EwsGetEventsRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->setWatermark(mSettings->eventSubscriptionWatermark()); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); req->start(); mEventReq = req; } } void EwsSubscriptionManager::getEventsRequestFinished(KJob *job) { mStreamingTimer.stop(); mEventReq->deleteLater(); mEventReq = nullptr; - EwsEventRequestBase *req = qobject_cast(job); + EwsEventRequestBase *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); reset(); return; } - if ((!req->responses().isEmpty()) && - ((req->responses()[0].responseCode() == QStringLiteral("ErrorInvalidSubscription")) || - (req->responses()[0].responseCode() == QStringLiteral("ErrorSubscriptionNotFound")))) { + if ((!req->responses().isEmpty()) + && ((req->responses()[0].responseCode() == QStringLiteral("ErrorInvalidSubscription")) + || (req->responses()[0].responseCode() == QStringLiteral("ErrorSubscriptionNotFound")))) { mSettings->setEventSubscriptionId(QString()); mSettings->setEventSubscriptionWatermark(QString()); mSettings->save(); resetSubscription(); return; } if (!job->error()) { processEvents(req, true); if (mStreamingEvents) { getEvents(); } } else { reset(); } } void EwsSubscriptionManager::streamingEventsReceived(KJob *job) { mStreamingTimer.stop(); - EwsEventRequestBase *req = qobject_cast(job); + EwsEventRequestBase *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); reset(); return; } if (!job->error()) { processEvents(req, false); mStreamingTimer.start(); } } void EwsSubscriptionManager::streamingConnectionTimeout() { if (mEventReq) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Streaming request timeout - restarting"); mEventReq->deleteLater(); mEventReq = nullptr; getEvents(); } } void EwsSubscriptionManager::processEvents(EwsEventRequestBase *req, bool finished) { bool moreEvents = false; Q_FOREACH (const EwsGetEventsRequest::Response &resp, req->responses()) { Q_FOREACH (const EwsGetEventsRequest::Notification &nfy, resp.notifications()) { Q_FOREACH (const EwsGetEventsRequest::Event &event, nfy.events()) { - bool skip = false; EwsId id = event.itemId(); for (auto it = mQueuedUpdates.find(id.id()); it != mQueuedUpdates.end(); ++it) { if (it->type == event.type() && (it->type == EwsDeletedEvent || it->changeKey == id.changeKey())) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Skipped queued update type %1 for item %2"); skip = true; mQueuedUpdates.erase(it); break; } } mSettings->setEventSubscriptionWatermark(event.watermark()); if (!skip) { switch (event.type()) { case EwsCopiedEvent: case EwsMovedEvent: if (!event.itemIsFolder()) { mUpdatedFolderIds.insert(event.oldParentFolderId()); } /* fall through */ case EwsCreatedEvent: case EwsDeletedEvent: case EwsModifiedEvent: case EwsNewMailEvent: if (event.itemIsFolder()) { mFolderTreeChanged = true; } else { mUpdatedFolderIds.insert(event.parentFolderId()); } break; case EwsStatusEvent: // Do nothing break; default: break; } } } if (nfy.hasMoreEvents()) { moreEvents = true; } } if (mStreamingEvents) { - EwsGetStreamingEventsRequest *req2 = qobject_cast(req); + EwsGetStreamingEventsRequest *req2 = qobject_cast(req); if (req2) { req2->eventsProcessed(resp); } } } if (moreEvents && finished) { getEvents(); } else { if (mFolderTreeChanged) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Found modified folder tree"); Q_EMIT folderTreeModified(); mFolderTreeChanged = false; } if (!mUpdatedFolderIds.isEmpty()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Found %1 modified folders") - .arg(mUpdatedFolderIds.size()); + .arg(mUpdatedFolderIds.size()); Q_EMIT foldersModified(mUpdatedFolderIds.toList()); mUpdatedFolderIds.clear(); } } } void EwsSubscriptionManager::queueUpdate(EwsEventType type, const QString &id, const QString &changeKey) { mQueuedUpdates.insert(id, {type, changeKey}); } diff --git a/resources/ews/ewssubscriptionwidget.cpp b/resources/ews/ewssubscriptionwidget.cpp index 738195a3e..778588b47 100644 --- a/resources/ews/ewssubscriptionwidget.cpp +++ b/resources/ews/ewssubscriptionwidget.cpp @@ -1,389 +1,391 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewssubscriptionwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include "ewsclient.h" #include "ewsfindfolderrequest.h" #include "ewssubscribedfoldersjob.h" #include "ewssettings.h" class EwsSubscriptionFilterModel : public KRecursiveFilterProxyModel { Q_OBJECT public: explicit EwsSubscriptionFilterModel(QObject *parent = nullptr); ~EwsSubscriptionFilterModel() override; public Q_SLOTS: void setFilterSelected(bool enabled); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private: bool hasCheckedChildren(const QModelIndex &index) const; bool mFilterSelected; }; class EwsSubscriptionWidgetPrivate : public QObject { Q_OBJECT public: EwsSubscriptionWidgetPrivate(EwsClient &client, EwsSettings *settings, QObject *parent); ~EwsSubscriptionWidgetPrivate(); enum TreeModelRoles { ItemIdRole = Qt::UserRole + 1 }; void populateFolderTree(); public Q_SLOTS: void enableCheckBoxToggled(bool checked); void reloadFolderList(bool); void resetSelection(bool); void readFolderListFinished(KJob *job); void subscribedFoldersJobFinished(KJob *job); void treeItemChanged(QStandardItem *item); void filterTextChanged(const QString &text); public: bool mEnabled = true; QCheckBox *mEnableCheckBox = nullptr; QTreeView *mFolderTreeView = nullptr; QWidget *mSubContainer = nullptr; QPushButton *mRefreshButton = nullptr; EwsClient &mClient; KMessageWidget *mMsgWidget = nullptr; QStandardItemModel *mFolderTreeModel = nullptr; - QHash mFolderItemHash; + QHash mFolderItemHash; int mFolderListPendingRequests = 0; EwsFolder::List mFolders; EwsId::List mSubscribedIds; EwsId::List mOrigSubscribedIds; bool mSubscribedIdsRetrieved = true; EwsSubscriptionFilterModel *mFilterModel = nullptr; EwsSettings *mSettings = nullptr; EwsSubscriptionWidget *q_ptr = nullptr; Q_DECLARE_PUBLIC(EwsSubscriptionWidget) }; EwsSubscriptionFilterModel::EwsSubscriptionFilterModel(QObject *parent) - : KRecursiveFilterProxyModel(parent), mFilterSelected(false) + : KRecursiveFilterProxyModel(parent) + , mFilterSelected(false) { } EwsSubscriptionFilterModel::~EwsSubscriptionFilterModel() { } bool EwsSubscriptionFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool show = true; if (mFilterSelected) { QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); show = sourceIndex.data(Qt::CheckStateRole).toInt() == Qt::Checked; show |= hasCheckedChildren(sourceIndex); } if (!show) { return false; } else { return KRecursiveFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } } bool EwsSubscriptionFilterModel::hasCheckedChildren(const QModelIndex &index) const { QModelIndex child; int row = 0; child = index.child(row, 0); while (child.isValid()) { if (child.data(Qt::CheckStateRole).toInt() == Qt::Checked) { return true; } else if (hasCheckedChildren(child)) { return true; } child = index.child(++row, 0); } return false; } void EwsSubscriptionFilterModel::setFilterSelected(bool enabled) { mFilterSelected = enabled; invalidateFilter(); } -EwsSubscriptionWidgetPrivate::EwsSubscriptionWidgetPrivate(EwsClient &client, EwsSettings *settings, - QObject *parent) - : QObject(parent), mClient(client), mSubscribedIdsRetrieved(false), mSettings(settings) +EwsSubscriptionWidgetPrivate::EwsSubscriptionWidgetPrivate(EwsClient &client, EwsSettings *settings, QObject *parent) + : QObject(parent) + , mClient(client) + , mSubscribedIdsRetrieved(false) + , mSettings(settings) { } EwsSubscriptionWidgetPrivate::~EwsSubscriptionWidgetPrivate() { } void EwsSubscriptionWidgetPrivate::enableCheckBoxToggled(bool checked) { mSubContainer->setEnabled(checked); } void EwsSubscriptionWidgetPrivate::reloadFolderList(bool) { if (mClient.isConfigured()) { EwsFindFolderRequest *req = new EwsFindFolderRequest(mClient, this); EwsFolderShape shape(EwsShapeIdOnly); shape << EwsPropertyField(QStringLiteral("folder:DisplayName")); shape << EwsPropertyField(QStringLiteral("folder:ParentFolderId")); req->setFolderShape(shape); req->setParentFolderId(EwsId(EwsDIdMsgFolderRoot)); connect(req, &EwsRequest::result, this, &EwsSubscriptionWidgetPrivate::readFolderListFinished); req->start(); mFolderListPendingRequests = 1; if (!mSubscribedIdsRetrieved) { EwsSubscribedFoldersJob *job = new EwsSubscribedFoldersJob(mClient, mSettings, this); connect(job, &EwsRequest::result, this, &EwsSubscriptionWidgetPrivate::subscribedFoldersJobFinished); job->start(); mFolderListPendingRequests++; } mRefreshButton->setEnabled(false); - } else { mMsgWidget->setText(i18nc("@info", "Exchange server not configured.")); mMsgWidget->setMessageType(KMessageWidget::Error); mMsgWidget->animatedShow(); } } void EwsSubscriptionWidgetPrivate::readFolderListFinished(KJob *job) { if (job->error()) { mMsgWidget->setText(i18nc("@info", "Failed to retrieve folder list.")); mMsgWidget->setMessageType(KMessageWidget::Error); mMsgWidget->animatedShow(); mRefreshButton->setEnabled(true); } else { - EwsFindFolderRequest *req = qobject_cast(job); + EwsFindFolderRequest *req = qobject_cast(job); Q_ASSERT(req); mFolders = req->folders(); mFolderListPendingRequests--; if (mFolderListPendingRequests == 0) { mRefreshButton->setEnabled(true); populateFolderTree(); } } } void EwsSubscriptionWidgetPrivate::subscribedFoldersJobFinished(KJob *job) { if (job->error()) { mMsgWidget->setText(i18nc("@info", "Failed to retrieve folder list.")); mMsgWidget->setMessageType(KMessageWidget::Error); mMsgWidget->animatedShow(); mRefreshButton->setEnabled(true); } else { - EwsSubscribedFoldersJob *req = qobject_cast(job); + EwsSubscribedFoldersJob *req = qobject_cast(job); Q_ASSERT(req); mSubscribedIds = req->folders(); mFolderListPendingRequests--; mOrigSubscribedIds = mSubscribedIds; mSubscribedIdsRetrieved = true; if (mFolderListPendingRequests == 0) { mRefreshButton->setEnabled(true); populateFolderTree(); } } - } void EwsSubscriptionWidgetPrivate::populateFolderTree() { mFolderTreeModel->clear(); mFolderItemHash.clear(); Q_FOREACH (const EwsFolder &folder, mFolders) { QStandardItem *item = new QStandardItem(folder[EwsFolderFieldDisplayName].toString()); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); item->setCheckable(true); EwsId id = folder[EwsFolderFieldFolderId].value(); item->setData(id.id(), ItemIdRole); if (mSubscribedIds.contains(EwsId(id.id()))) { item->setCheckState(Qt::Checked); } EwsId parentId = folder[EwsFolderFieldParentFolderId].value(); if (parentId.type() != EwsId::Unspecified) { QStandardItem *parentItem = mFolderItemHash.value(parentId.id()); if (parentItem) { parentItem->appendRow(item); } } mFolderItemHash.insert(id.id(), item); } - Q_FOREACH (QStandardItem* item, mFolderItemHash) { + Q_FOREACH (QStandardItem *item, mFolderItemHash) { if (!item->parent()) { mFolderTreeModel->appendRow(item); } } } void EwsSubscriptionWidgetPrivate::treeItemChanged(QStandardItem *item) { EwsId id = EwsId(item->data(ItemIdRole).toString()); if (item->checkState() == Qt::Checked) { mSubscribedIds += id; } else { mSubscribedIds.removeOne(id); } } void EwsSubscriptionWidgetPrivate::filterTextChanged(const QString &text) { mFilterModel->setFilterFixedString(text); } void EwsSubscriptionWidgetPrivate::resetSelection(bool) { mSubscribedIds = mOrigSubscribedIds; populateFolderTree(); } EwsSubscriptionWidget::EwsSubscriptionWidget(EwsClient &client, EwsSettings *settings, QWidget *parent) - : QWidget(parent), d_ptr(new EwsSubscriptionWidgetPrivate(client, settings, this)) + : QWidget(parent) + , d_ptr(new EwsSubscriptionWidgetPrivate(client, settings, this)) { Q_D(EwsSubscriptionWidget); d->mEnabled = d->mSettings->serverSubscription(); QVBoxLayout *topLayout = new QVBoxLayout(this); d->mMsgWidget = new KMessageWidget(this); d->mMsgWidget->setVisible(false); d->mEnableCheckBox = new QCheckBox(i18nc("@option:check", "Enable server-side subscriptions"), this); d->mEnableCheckBox->setChecked(d->mEnabled); d->mSubContainer = new QWidget(this); QVBoxLayout *subContainerLayout = new QVBoxLayout(d->mSubContainer); subContainerLayout->setMargin(0); QLineEdit *filterLineEdit = new QLineEdit(this); filterLineEdit->setPlaceholderText(i18nc("@label:textbox", "Filter folders")); QWidget *treeContainer = new QWidget(this); QHBoxLayout *treeContainerLayout = new QHBoxLayout(treeContainer); treeContainerLayout->setMargin(0); d->mFolderTreeView = new QTreeView(this); d->mFolderTreeModel = new QStandardItemModel(this); d->mFilterModel = new EwsSubscriptionFilterModel(this); d->mFilterModel->setSourceModel(d->mFolderTreeModel); d->mFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); d->mFilterModel->sort(0, Qt::AscendingOrder); d->mFolderTreeView->setModel(d->mFilterModel); d->mFolderTreeView->header()->hide(); QWidget *buttonContainer = new QWidget(this); QVBoxLayout *buttonContainerLayout = new QVBoxLayout(buttonContainer); buttonContainerLayout->setMargin(0); d->mRefreshButton = new QPushButton(this); d->mRefreshButton->setText(i18nc("@action:button", "Reload &List")); QPushButton *resetButton = new QPushButton(this); resetButton->setText(i18nc("@action:button", "&Reset")); buttonContainerLayout->addWidget(d->mRefreshButton); buttonContainerLayout->addWidget(resetButton); buttonContainerLayout->addStretch(); treeContainerLayout->addWidget(d->mFolderTreeView); treeContainerLayout->addWidget(buttonContainer); QCheckBox *subOnlyCheckBox = new QCheckBox(i18nc("@option:check", "Subscribed only"), this); subContainerLayout->addWidget(filterLineEdit); subContainerLayout->addWidget(treeContainer); subContainerLayout->addWidget(subOnlyCheckBox); topLayout->addWidget(d->mMsgWidget); topLayout->addWidget(d->mEnableCheckBox); topLayout->addWidget(d->mSubContainer); connect(d->mEnableCheckBox, &QCheckBox::toggled, d, &EwsSubscriptionWidgetPrivate::enableCheckBoxToggled); connect(d->mRefreshButton, &QPushButton::clicked, d, &EwsSubscriptionWidgetPrivate::reloadFolderList); connect(resetButton, &QPushButton::clicked, d, &EwsSubscriptionWidgetPrivate::resetSelection); connect(d->mFolderTreeModel, &QStandardItemModel::itemChanged, d, &EwsSubscriptionWidgetPrivate::treeItemChanged); connect(filterLineEdit, &QLineEdit::textChanged, d, &EwsSubscriptionWidgetPrivate::filterTextChanged); connect(subOnlyCheckBox, &QCheckBox::toggled, d->mFilterModel, &EwsSubscriptionFilterModel::setFilterSelected); d->enableCheckBoxToggled(d->mEnabled); d->reloadFolderList(false); } EwsSubscriptionWidget::~EwsSubscriptionWidget() { } QStringList EwsSubscriptionWidget::subscribedList() const { Q_D(const EwsSubscriptionWidget); QStringList list; list.reserve(d->mSubscribedIds.count()); for (const EwsId &id : qAsConst(d->mSubscribedIds)) { list.append(id.id()); } return list; } bool EwsSubscriptionWidget::subscriptionEnabled() const { Q_D(const EwsSubscriptionWidget); return d->mEnableCheckBox->isChecked(); } bool EwsSubscriptionWidget::subscribedListValid() const { Q_D(const EwsSubscriptionWidget); return d->mSubscribedIdsRetrieved; } #include "ewssubscriptionwidget.moc" diff --git a/resources/ews/mail/ewscreatemailjob.cpp b/resources/ews/mail/ewscreatemailjob.cpp index 2ef3ae535..1779fdd43 100644 --- a/resources/ews/mail/ewscreatemailjob.cpp +++ b/resources/ews/mail/ewscreatemailjob.cpp @@ -1,238 +1,237 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewscreatemailjob.h" #include #include #include #include #include #include #include "ewscreateitemrequest.h" #include "ewsmoveitemrequest.h" #include "ewspropertyfield.h" #include "ewsmailhandler.h" #include "ewsresource_debug.h" using namespace Akonadi; static const EwsPropertyField propPidMessageFlags(0x0e07, EwsPropTypeInteger); -EwsCreateMailJob::EwsCreateMailJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, - EwsResource *parent) - : EwsCreateItemJob(client, item, collection, tagStore, parent), mSend(false) +EwsCreateMailJob::EwsCreateMailJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) + : EwsCreateItemJob(client, item, collection, tagStore, parent) + , mSend(false) { } + EwsCreateMailJob::~EwsCreateMailJob() { } void EwsCreateMailJob::doStart() { if (!mItem.hasPayload()) { setErrorMsg(QStringLiteral("Expected MIME message payload")); emitResult(); } EwsCreateItemRequest *req = new EwsCreateItemRequest(mClient, this); KMime::Message::Ptr msg = mItem.payload(); /* Exchange doesn't just store whatever MIME content that was sent to it - it will parse it and send * further the version assembled back from the parsed parts. It seems that this parsing doesn't work well * with the quoted-printable encoding, which KMail prefers. This results in malformed encoding, which the * sender doesn't even see. * As a workaround force encoding of the body (or in case of multipart - all parts) to Base64. */ if (msg->contents().isEmpty()) { msg->changeEncoding(KMime::Headers::CEbase64); msg->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); } else { Q_FOREACH (KMime::Content *content, msg->contents()) { content->changeEncoding(KMime::Headers::CEbase64); content->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); } } msg->assemble(); QByteArray mimeContent = msg->encodedContent(true); bool sentItemsCreateWorkaround = false; EwsItem item; item.setType(EwsItemTypeMessage); item.setField(EwsItemFieldMimeContent, mimeContent); if (!mSend) { /* When creating items using the CreateItem request Exchange will by default mark the message * as draft. Setting the extended property below causes the message to appear normally. */ item.setProperty(propPidMessageFlags, QStringLiteral("1")); const Akonadi::AgentInstance &inst = Akonadi::AgentManager::self()->instance(mCollection.resource()); /* WORKAROUND: The "Sent Items" folder is a little "special" when it comes to creating items. * Unlike other folders when creating items there the creation date/time is always set to the * current date/time instead of the value from the MIME Date header. This causes mail that * was copied from other folders to appear with the current date/time instead of the original one. * To work around this create the item in the "Drafts" folder first and then move it to "Sent Items". */ if (mCollection == SpecialMailCollections::self()->collection(SpecialMailCollections::SentMail, inst)) { qCInfoNC(EWSRES_LOG) << "Move to \"Sent Items\" detected - activating workaround."; const Collection &draftColl = SpecialMailCollections::self()->collection(SpecialMailCollections::Drafts, inst); req->setSavedFolderId(EwsId(draftColl.remoteId(), draftColl.remoteRevision())); sentItemsCreateWorkaround = true; } else { req->setSavedFolderId(EwsId(mCollection.remoteId(), mCollection.remoteRevision())); } } // Set flags QHash propertyHash = EwsMailHandler::writeFlags(mItem.flags()); for (auto it = propertyHash.cbegin(), end = propertyHash.cend(); it != end; ++it) { if (!it.value().isNull()) { if (it.key().type() == EwsPropertyField::ExtendedField) { item.setProperty(it.key(), it.value()); } else if (it.key().type() == EwsPropertyField::Field) { /* TODO: Currently EwsItem distinguishes between regular fields and extended fields * and keeps them in separate lists. Someday it will make more sense to unify them. * Until that the code below needs to manually translate the field names into * EwsItemField enum items. */ if (it.key().uri() == QStringLiteral("message:IsRead")) { item.setField(EwsItemFieldIsRead, it.value()); } } } } populateCommonProperties(item); req->setItems(EwsItem::List() << item); req->setMessageDisposition(mSend ? EwsDispSendOnly : EwsDispSaveOnly); connect(req, &EwsCreateItemRequest::finished, this, sentItemsCreateWorkaround ? &EwsCreateMailJob::mailCreateWorkaroundFinished : &EwsCreateMailJob::mailCreateFinished); addSubjob(req); req->start(); } void EwsCreateMailJob::mailCreateFinished(KJob *job) { - EwsCreateItemRequest *req = qobject_cast(job); + EwsCreateItemRequest *req = qobject_cast(job); if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } if (!req) { setErrorMsg(QStringLiteral("Invalid EwsCreateItemRequest job object")); emitResult(); return; } if (req->responses().count() != 1) { setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); emitResult(); return; } EwsCreateItemRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { EwsId id = resp.itemId(); mItem.setRemoteId(id.id()); mItem.setRemoteRevision(id.changeKey()); mItem.setParentCollection(mCollection); } else { setErrorMsg(i18n("Failed to create mail item")); } emitResult(); } void EwsCreateMailJob::mailCreateWorkaroundFinished(KJob *job) { - EwsCreateItemRequest *req = qobject_cast(job); + EwsCreateItemRequest *req = qobject_cast(job); if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } if (!req) { setErrorMsg(QStringLiteral("Invalid EwsCreateItemRequest job object")); emitResult(); return; } if (req->responses().count() != 1) { setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); emitResult(); return; } EwsCreateItemRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { EwsId id = resp.itemId(); EwsMoveItemRequest *req = new EwsMoveItemRequest(mClient, this); req->setItemIds(EwsId::List() << id); req->setDestinationFolderId(EwsId(mCollection.remoteId())); connect(req, &EwsCreateItemRequest::finished, this, &EwsCreateMailJob::mailMoveWorkaroundFinished); addSubjob(req); req->start(); } else { setErrorMsg(i18n("Failed to create mail item")); emitResult(); } } void EwsCreateMailJob::mailMoveWorkaroundFinished(KJob *job) { - EwsMoveItemRequest *req = qobject_cast(job); + EwsMoveItemRequest *req = qobject_cast(job); if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } if (!req) { setErrorMsg(QStringLiteral("Invalid EwsMoveItemRequest job object")); emitResult(); return; } if (req->responses().count() != 1) { setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); emitResult(); return; } EwsMoveItemRequest::Response resp = req->responses().first(); if (resp.isSuccess()) { EwsId id = resp.itemId(); mItem.setRemoteId(id.id()); mItem.setRemoteRevision(id.changeKey()); mItem.setParentCollection(mCollection); } else { setErrorMsg(i18n("Failed to create mail item")); } emitResult(); } - bool EwsCreateMailJob::setSend(bool send) { mSend = send; return true; } diff --git a/resources/ews/mail/ewscreatemailjob.h b/resources/ews/mail/ewscreatemailjob.h index 5758ad874..5e316feb2 100644 --- a/resources/ews/mail/ewscreatemailjob.h +++ b/resources/ews/mail/ewscreatemailjob.h @@ -1,43 +1,42 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATEMAILJOB_H #define EWSCREATEMAILJOB_H #include "ewscreateitemjob.h" class EwsCreateMailJob : public EwsCreateItemJob { Q_OBJECT public: - EwsCreateMailJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateMailJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateMailJob() override; bool setSend(bool send = true) override; protected: void doStart() override; private Q_SLOTS: void mailCreateFinished(KJob *job); void mailCreateWorkaroundFinished(KJob *job); void mailMoveWorkaroundFinished(KJob *job); private: bool mSend; }; #endif diff --git a/resources/ews/mail/ewsfetchmaildetailjob.cpp b/resources/ews/mail/ewsfetchmaildetailjob.cpp index 38115ff7f..2bb62d5d3 100644 --- a/resources/ews/mail/ewsfetchmaildetailjob.cpp +++ b/resources/ews/mail/ewsfetchmaildetailjob.cpp @@ -1,167 +1,166 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchmaildetailjob.h" #include #include #include #include "ewsitemshape.h" #include "ewsgetitemrequest.h" #include "ewsmailbox.h" #include "ewsmailhandler.h" #include "ewsresource_debug.h" using namespace Akonadi; EwsFetchMailDetailJob::EwsFetchMailDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) : EwsFetchItemDetailJob(client, parent, collection) { EwsItemShape shape(EwsShapeIdOnly); shape << EwsPropertyField(QStringLiteral("item:Subject")); shape << EwsPropertyField(QStringLiteral("item:Importance")); shape << EwsPropertyField(QStringLiteral("message:From")); shape << EwsPropertyField(QStringLiteral("message:ToRecipients")); shape << EwsPropertyField(QStringLiteral("message:CcRecipients")); shape << EwsPropertyField(QStringLiteral("message:BccRecipients")); shape << EwsPropertyField(QStringLiteral("item:Categories")); shape << EwsPropertyField(QStringLiteral("item:DateTimeReceived")); shape << EwsPropertyField(QStringLiteral("item:InReplyTo")); shape << EwsPropertyField(QStringLiteral("message:References")); shape << EwsPropertyField(QStringLiteral("message:ReplyTo")); shape << EwsPropertyField(QStringLiteral("message:InternetMessageId")); shape << EwsPropertyField(QStringLiteral("item:Size")); Q_FOREACH (const EwsPropertyField &field, EwsMailHandler::flagsProperties()) { shape << field; } Q_FOREACH (const EwsPropertyField &field, EwsItemHandler::tagsProperties()) { shape << field; } mRequest->setItemShape(shape); } - EwsFetchMailDetailJob::~EwsFetchMailDetailJob() { } void EwsFetchMailDetailJob::processItems(const QList &responses) { Item::List::iterator it = mChangedItems.begin(); for (const EwsGetItemRequest::Response &resp : responses) { Item &item = *it; if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item %1").arg(item.remoteId()); continue; } const EwsItem &ewsItem = resp.item(); KMime::Message::Ptr msg(new KMime::Message); // Rebuild the message headers QVariant v = ewsItem[EwsItemFieldSubject]; if (Q_LIKELY(v.isValid())) { msg->subject()->fromUnicodeString(v.toString(), "utf-8"); } v = ewsItem[EwsItemFieldFrom]; if (Q_LIKELY(v.isValid())) { const EwsMailbox mbox = v.value(); msg->from()->addAddress(mbox); } v = ewsItem[EwsItemFieldToRecipients]; if (Q_LIKELY(v.isValid())) { const EwsMailbox::List mboxList = v.value(); for (const EwsMailbox &mbox : mboxList) { msg->to()->addAddress(mbox); } } v = ewsItem[EwsItemFieldCcRecipients]; if (Q_LIKELY(v.isValid())) { const EwsMailbox::List mboxList = v.value(); for (const EwsMailbox &mbox : mboxList) { msg->cc()->addAddress(mbox); } } v = ewsItem[EwsItemFieldBccRecipients]; if (v.isValid()) { const EwsMailbox::List mboxList = v.value(); for (const EwsMailbox &mbox : mboxList) { msg->bcc()->addAddress(mbox); } } v = ewsItem[EwsItemFieldInternetMessageId]; if (v.isValid()) { msg->messageID()->from7BitString(v.toString().toLatin1()); } v = ewsItem[EwsItemFieldInReplyTo]; if (v.isValid()) { msg->inReplyTo()->from7BitString(v.toString().toLatin1()); } v = ewsItem[EwsItemFieldDateTimeReceived]; if (v.isValid()) { msg->date()->setDateTime(v.toDateTime()); } v = ewsItem[EwsItemFieldReferences]; if (v.isValid()) { msg->references()->from7BitString(v.toString().toLatin1()); } v = ewsItem[EwsItemFieldReplyTo]; if (v.isValid()) { const EwsMailbox mbox = v.value(); msg->replyTo()->addAddress(mbox); } msg->assemble(); item.setPayload(KMime::Message::Ptr(msg)); v = ewsItem[EwsItemFieldSize]; if (v.isValid()) { item.setSize(v.toUInt()); } // Workaround for Akonadi bug // When setting flags, adding each of them separately vs. setting a list in one go makes // a difference. In the former case the item treats this as an incremental change and // records flags added and removed. In the latter it sets a flag indicating that flags were // reset. // For some strange reason Akonadi is not seeing the flags in the latter case. Q_FOREACH (const QByteArray &flag, EwsMailHandler::readFlags(ewsItem)) { item.setFlag(flag); } qCDebugNC(EWSRES_LOG) << "EwsFetchMailDetailJob::processItems:" << ewsHash(item.remoteId()) << item.flags(); ++it; } qCDebugNC(EWSRES_LOG) << "EwsFetchMailDetailJob::processItems: done"; emitResult(); } diff --git a/resources/ews/mail/ewsmailhandler.cpp b/resources/ews/mail/ewsmailhandler.cpp index e00845b81..4dd1c12cc 100644 --- a/resources/ews/mail/ewsmailhandler.cpp +++ b/resources/ews/mail/ewsmailhandler.cpp @@ -1,179 +1,175 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmailhandler.h" #include #include #include #include "ewsfetchmaildetailjob.h" #include "ewsmodifymailjob.h" #include "ewscreatemailjob.h" #include "ewsresource_debug.h" using namespace Akonadi; static const EwsPropertyField propPidFlagStatus(0x1090, EwsPropTypeInteger); EwsMailHandler::EwsMailHandler() { } EwsMailHandler::~EwsMailHandler() { } EwsItemHandler *EwsMailHandler::factory() { return new EwsMailHandler(); } -EwsFetchItemDetailJob *EwsMailHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) +EwsFetchItemDetailJob *EwsMailHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) { return new EwsFetchMailDetailJob(client, parent, collection); } void EwsMailHandler::setSeenFlag(Item &item, bool value) { if (value) { item.setFlag(MessageFlags::Seen); } else { item.clearFlag(MessageFlags::Seen); } } QString EwsMailHandler::mimeType() { return KMime::Message::mimeType(); } bool EwsMailHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) { QByteArray mimeContent = ewsItem[EwsItemFieldMimeContent].toByteArray(); if (mimeContent.isEmpty()) { qCWarning(EWSRES_LOG) << QStringLiteral("MIME content is empty!"); return false; } mimeContent.replace("\r\n", "\n"); KMime::Message::Ptr msg(new KMime::Message); msg->setContent(mimeContent); msg->parse(); // Some messages might just be empty (just headers). This results in the body being empty. // The problem is that when Akonadi sees an empty body it will interpret this as "body not // yet loaded" and will retry which will cause an endless loop. To work around this put a // single newline so that it is not empty. if (msg->body().isEmpty()) { msg->setBody("\n"); } item.setPayload(msg); return true; } -EwsModifyItemJob *EwsMailHandler::modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) +EwsModifyItemJob *EwsMailHandler::modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) { return new EwsModifyMailJob(client, items, parts, parent); } -EwsCreateItemJob *EwsMailHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) +EwsCreateItemJob *EwsMailHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) { return new EwsCreateMailJob(client, item, collection, tagStore, parent); } QHash EwsMailHandler::writeFlags(const QSet &flags) { // Strip all the message flags that can be stored in dedicated Exchange fields and leave // any remaining ones to be stored in a private property. QSet unknownFlags; QHash propertyHash; bool isRead = false; bool isFlagged = false; for (const QByteArray &flag : flags) { if (flag == MessageFlags::Seen) { isRead = true; } else if (flag == MessageFlags::Flagged) { isFlagged = true; - } else if (flag == MessageFlags::HasAttachment || flag == MessageFlags::HasInvitation || - flag == MessageFlags::Signed || flag == MessageFlags::Encrypted) { + } else if (flag == MessageFlags::HasAttachment || flag == MessageFlags::HasInvitation + || flag == MessageFlags::Signed || flag == MessageFlags::Encrypted) { // These flags are read-only. Remove them from the unknown list but don't do anything with them. } else { unknownFlags.insert(flag); } } propertyHash.insert(EwsPropertyField(QStringLiteral("message:IsRead")), isRead ? QStringLiteral("true") : QStringLiteral("false")); if (isFlagged) { propertyHash.insert(propPidFlagStatus, QStringLiteral("2")); } else { propertyHash.insert(propPidFlagStatus, QVariant()); } propertyHash.unite(EwsItemHandler::writeFlags(unknownFlags)); return propertyHash; } QSet EwsMailHandler::readFlags(const EwsItem &item) { QSet flags = EwsItemHandler::readFlags(item); QVariant v = item[EwsItemFieldIsRead]; if (v.isValid() && v.toBool()) { flags.insert(MessageFlags::Seen); } v = item[EwsItemFieldHasAttachments]; if (v.isValid() && v.toBool()) { flags.insert(MessageFlags::HasAttachment); } QVariant flagProp = item[propPidFlagStatus]; if (!flagProp.isNull() && (flagProp.toUInt() == 2)) { flags.insert(MessageFlags::Flagged); } if (item.type() == EwsItemTypeMeetingRequest) { flags.insert(MessageFlags::HasInvitation); } return flags; } QList EwsMailHandler::flagsProperties() { QList props = EwsItemHandler::flagsProperties(); props.append(propPidFlagStatus); props.append(EwsPropertyField(QStringLiteral("message:IsRead"))); props.append(EwsPropertyField(QStringLiteral("item:HasAttachments"))); return props; } EWS_DECLARE_ITEM_HANDLER(EwsMailHandler, EwsItemTypeMessage) EWS_DECLARE_ITEM_HANDLER(EwsMailHandler, EwsItemTypePostItem) diff --git a/resources/ews/mail/ewsmailhandler.h b/resources/ews/mail/ewsmailhandler.h index da339cd04..5979df8e3 100644 --- a/resources/ews/mail/ewsmailhandler.h +++ b/resources/ews/mail/ewsmailhandler.h @@ -1,47 +1,43 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMAILHANDLER_H #define EWSMAILHANDLER_H #include "ewsitemhandler.h" class EwsMailHandler : public EwsItemHandler { public: EwsMailHandler(); ~EwsMailHandler() override; - EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) override; + EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) override; void setSeenFlag(Akonadi::Item &item, bool value) override; QString mimeType() override; bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) override; - EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) override; - EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) override; + EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) override; + EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) override; static EwsItemHandler *factory(); static QHash writeFlags(const QSet &flags); static QSet readFlags(const EwsItem &item); static QList flagsProperties(); }; #endif diff --git a/resources/ews/mail/ewsmodifymailjob.cpp b/resources/ews/mail/ewsmodifymailjob.cpp index b82b86fda..547bc95a6 100644 --- a/resources/ews/mail/ewsmodifymailjob.cpp +++ b/resources/ews/mail/ewsmodifymailjob.cpp @@ -1,106 +1,106 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifymailjob.h" #include #include "ewsupdateitemrequest.h" #include "ewsmailhandler.h" #include "ewsresource_debug.h" using namespace Akonadi; -EwsModifyMailJob::EwsModifyMailJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) +EwsModifyMailJob::EwsModifyMailJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) : EwsModifyItemJob(client, items, parts, parent) { } + EwsModifyMailJob::~EwsModifyMailJob() { } void EwsModifyMailJob::start() { bool doSubmit = false; EwsUpdateItemRequest *req = new EwsUpdateItemRequest(mClient, this); EwsId itemId; Q_FOREACH (const Item &item, mItems) { itemId = EwsId(item.remoteId(), item.remoteRevision()); if (mParts.contains("FLAGS")) { EwsUpdateItemRequest::ItemChange ic(itemId, EwsItemTypeMessage); QHash propertyHash = EwsMailHandler::writeFlags(item.flags()); for (auto it = propertyHash.cbegin(), end = propertyHash.cend(); it != end; ++it) { EwsUpdateItemRequest::Update *upd; if (it.value().isNull()) { upd = new EwsUpdateItemRequest::DeleteUpdate(it.key()); } else { upd = new EwsUpdateItemRequest::SetUpdate(it.key(), it.value()); } ic.addUpdate(upd); } req->addItemChange(ic); doSubmit = true; } } if (doSubmit) { connect(req, &KJob::result, this, &EwsModifyMailJob::updateItemFinished); req->start(); } else { delete req; emitResult(); } } void EwsModifyMailJob::updateItemFinished(KJob *job) { if (job->error()) { setErrorText(job->errorString()); emitResult(); return; } - EwsUpdateItemRequest *req = qobject_cast(job); + EwsUpdateItemRequest *req = qobject_cast(job); if (!req) { setErrorText(QStringLiteral("Invalid EwsUpdateItemRequest job object")); emitResult(); return; } Q_ASSERT(req->responses().size() == mItems.size()); Item::List::iterator it = mItems.begin(); Q_FOREACH (const EwsUpdateItemRequest::Response &resp, req->responses()) { if (!resp.isSuccess()) { setErrorText(QStringLiteral("Item update failed: ") + resp.responseMessage()); emitResult(); return; } it->setRemoteRevision(resp.itemId().changeKey()); } emitResult(); } diff --git a/resources/ews/mail/ewsmodifymailjob.h b/resources/ews/mail/ewsmodifymailjob.h index 10698d0c2..018baf1a7 100644 --- a/resources/ews/mail/ewsmodifymailjob.h +++ b/resources/ews/mail/ewsmodifymailjob.h @@ -1,37 +1,36 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYMAILJOB_H #define EWSMODIFYMAILJOB_H #include "ewsmodifyitemjob.h" class EwsModifyMailJob : public EwsModifyItemJob { Q_OBJECT public: - EwsModifyMailJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyMailJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyMailJob() override; void start() override; private Q_SLOTS: void updateItemFinished(KJob *job); }; #endif diff --git a/resources/ews/tags/ewsakonaditagssyncjob.cpp b/resources/ews/tags/ewsakonaditagssyncjob.cpp index 31062797e..8d9acdc4c 100644 --- a/resources/ews/tags/ewsakonaditagssyncjob.cpp +++ b/resources/ews/tags/ewsakonaditagssyncjob.cpp @@ -1,72 +1,75 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsakonaditagssyncjob.h" #include #include #include "ewstagstore.h" #include "ewsglobaltagswritejob.h" using namespace Akonadi; -EwsAkonadiTagsSyncJob::EwsAkonadiTagsSyncJob(EwsTagStore *tagStore, EwsClient &client, - const Collection &rootCollection, QObject *parent) - : EwsJob(parent), mTagStore(tagStore), mClient(client), mRootCollection(rootCollection) +EwsAkonadiTagsSyncJob::EwsAkonadiTagsSyncJob(EwsTagStore *tagStore, EwsClient &client, const Collection &rootCollection, QObject *parent) + : EwsJob(parent) + , mTagStore(tagStore) + , mClient(client) + , mRootCollection(rootCollection) { } EwsAkonadiTagsSyncJob::~EwsAkonadiTagsSyncJob() { } void EwsAkonadiTagsSyncJob::start() { TagFetchJob *job = new TagFetchJob(this); job->fetchScope().setFetchRemoteId(true); connect(job, &TagFetchJob::result, this, &EwsAkonadiTagsSyncJob::tagFetchFinished); } void EwsAkonadiTagsSyncJob::tagFetchFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } - TagFetchJob *tagJob = qobject_cast(job); + TagFetchJob *tagJob = qobject_cast(job); Q_ASSERT(tagJob); if (mTagStore->syncTags(tagJob->tags())) { EwsGlobalTagsWriteJob *tagJob = new EwsGlobalTagsWriteJob(mTagStore, mClient, mRootCollection, this); connect(tagJob, &EwsGlobalTagsWriteJob::result, this, &EwsAkonadiTagsSyncJob::tagWriteFinished); tagJob->start(); } else { emitResult(); } } + void EwsAkonadiTagsSyncJob::tagWriteFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorString()); } emitResult(); } diff --git a/resources/ews/tags/ewsakonaditagssyncjob.h b/resources/ews/tags/ewsakonaditagssyncjob.h index 60de8415f..ca93f29ac 100644 --- a/resources/ews/tags/ewsakonaditagssyncjob.h +++ b/resources/ews/tags/ewsakonaditagssyncjob.h @@ -1,51 +1,49 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSAKONADITAGSSYNCJOB_H #define EWSAKONADITAGSSYNCJOB_H #include "ewsjob.h" class EwsTagStore; class EwsClient; -namespace Akonadi -{ +namespace Akonadi { class Collection; } class EwsAkonadiTagsSyncJob : public EwsJob { Q_OBJECT public: - EwsAkonadiTagsSyncJob(EwsTagStore *tagStore, EwsClient &client, - const Akonadi::Collection &rootCollection, QObject *parent); + EwsAkonadiTagsSyncJob(EwsTagStore *tagStore, EwsClient &client, const Akonadi::Collection &rootCollection, QObject *parent); ~EwsAkonadiTagsSyncJob() override; void start() override; private Q_SLOTS: void tagFetchFinished(KJob *job); void tagWriteFinished(KJob *job); private: EwsTagStore *mTagStore = nullptr; EwsClient &mClient; const Akonadi::Collection &mRootCollection; }; #endif diff --git a/resources/ews/tags/ewsglobaltagsreadjob.cpp b/resources/ews/tags/ewsglobaltagsreadjob.cpp index eb76d347d..02afbf1bf 100644 --- a/resources/ews/tags/ewsglobaltagsreadjob.cpp +++ b/resources/ews/tags/ewsglobaltagsreadjob.cpp @@ -1,88 +1,90 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsglobaltagsreadjob.h" #include "ewsclient.h" #include "ewsgetfolderrequest.h" #include "ewstagstore.h" #include "ewsresource.h" #include "ewsresource_debug.h" using namespace Akonadi; -EwsGlobalTagsReadJob::EwsGlobalTagsReadJob(EwsTagStore *tagStore, EwsClient &client, - const Collection &rootCollection, QObject *parent) - : EwsJob(parent), mTagStore(tagStore), mClient(client), mRootCollection(rootCollection) +EwsGlobalTagsReadJob::EwsGlobalTagsReadJob(EwsTagStore *tagStore, EwsClient &client, const Collection &rootCollection, QObject *parent) + : EwsJob(parent) + , mTagStore(tagStore) + , mClient(client) + , mRootCollection(rootCollection) { } EwsGlobalTagsReadJob::~EwsGlobalTagsReadJob() { } void EwsGlobalTagsReadJob::start() { EwsGetFolderRequest *req = new EwsGetFolderRequest(mClient, this); req->setFolderIds(EwsId::List() << EwsId(EwsDIdMsgFolderRoot)); EwsFolderShape shape(EwsShapeIdOnly); shape << EwsResource::globalTagsProperty << EwsResource::globalTagsVersionProperty; req->setFolderShape(shape); connect(req, &EwsGetFolderRequest::result, this, &EwsGlobalTagsReadJob::getFolderRequestFinished); req->start(); } void EwsGlobalTagsReadJob::getFolderRequestFinished(KJob *job) { - EwsGetFolderRequest *req = qobject_cast(job); + EwsGetFolderRequest *req = qobject_cast(job); if (!req) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid EwsGetFolderRequest job object"); setErrorMsg(QStringLiteral("Invalid EwsGetFolderRequest job object")); emitResult(); return; } if (req->error()) { setErrorMsg(req->errorString()); emitResult(); return; } if (req->responses().size() != 1) { qCWarning(EWSRES_LOG) << QStringLiteral("Invalid number of responses received"); setErrorMsg(QStringLiteral("Invalid number of responses received")); emitResult(); return; } EwsFolder folder = req->responses().first().folder(); bool status = mTagStore->readTags(folder[EwsResource::globalTagsProperty].toStringList(), folder[EwsResource::globalTagsVersionProperty].toInt()); if (!status) { qCWarning(EWSRES_LOG) << QStringLiteral("Incorrect server tag data"); setErrorMsg(QStringLiteral("Incorrect server tag data")); emitResult(); } mTags = mTagStore->tags(); emitResult(); } diff --git a/resources/ews/tags/ewsglobaltagsreadjob.h b/resources/ews/tags/ewsglobaltagsreadjob.h index 9d0561297..87adb4d70 100644 --- a/resources/ews/tags/ewsglobaltagsreadjob.h +++ b/resources/ews/tags/ewsglobaltagsreadjob.h @@ -1,57 +1,56 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSGLOBALTAGSREADJOB_H #define EWSGLOBALTAGSREADJOB_H #include "ewsjob.h" #include class EwsTagStore; class EwsClient; -namespace Akonadi -{ +namespace Akonadi { class Collection; } class EwsGlobalTagsReadJob : public EwsJob { Q_OBJECT public: - EwsGlobalTagsReadJob(EwsTagStore *tagStore, EwsClient &client, - const Akonadi::Collection &rootCollection, QObject *parent); + EwsGlobalTagsReadJob(EwsTagStore *tagStore, EwsClient &client, const Akonadi::Collection &rootCollection, QObject *parent); ~EwsGlobalTagsReadJob() override; void start() override; const Akonadi::Tag::List &tags() const { return mTags; } + private Q_SLOTS: void getFolderRequestFinished(KJob *job); private: EwsTagStore *mTagStore = nullptr; EwsClient &mClient; const Akonadi::Collection &mRootCollection; Akonadi::Tag::List mTags; }; #endif diff --git a/resources/ews/tags/ewsglobaltagswritejob.cpp b/resources/ews/tags/ewsglobaltagswritejob.cpp index 9aad739c5..7c01a2860 100644 --- a/resources/ews/tags/ewsglobaltagswritejob.cpp +++ b/resources/ews/tags/ewsglobaltagswritejob.cpp @@ -1,67 +1,69 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsglobaltagswritejob.h" #include #include "ewsupdatefolderrequest.h" #include "ewsid.h" #include "ewsitemhandler.h" #include "ewstagstore.h" #include "ewsresource.h" using namespace Akonadi; -EwsGlobalTagsWriteJob::EwsGlobalTagsWriteJob(EwsTagStore *tagStore, EwsClient &client, - const Collection &rootCollection, QObject *parent) - : EwsJob(parent), mTagStore(tagStore), mClient(client), mRootCollection(rootCollection) +EwsGlobalTagsWriteJob::EwsGlobalTagsWriteJob(EwsTagStore *tagStore, EwsClient &client, const Collection &rootCollection, QObject *parent) + : EwsJob(parent) + , mTagStore(tagStore) + , mClient(client) + , mRootCollection(rootCollection) { } EwsGlobalTagsWriteJob::~EwsGlobalTagsWriteJob() { } void EwsGlobalTagsWriteJob::start() { QStringList tagList = mTagStore->serialize(); EwsUpdateFolderRequest *req = new EwsUpdateFolderRequest(mClient, this); EwsUpdateFolderRequest::FolderChange fc(EwsId(mRootCollection.remoteId(), mRootCollection.remoteRevision()), EwsFolderTypeMail); EwsUpdateFolderRequest::Update *upd = new EwsUpdateFolderRequest::SetUpdate(EwsResource::globalTagsProperty, tagList); fc.addUpdate(upd); upd = new EwsUpdateFolderRequest::SetUpdate(EwsResource::globalTagsVersionProperty, - QString::number(mTagStore->version())); + QString::number(mTagStore->version())); fc.addUpdate(upd); req->addFolderChange(fc); connect(req, &EwsUpdateFolderRequest::result, this, &EwsGlobalTagsWriteJob::updateFolderRequestFinished); req->start(); } void EwsGlobalTagsWriteJob::updateFolderRequestFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorText()); } emitResult(); } diff --git a/resources/ews/tags/ewsglobaltagswritejob.h b/resources/ews/tags/ewsglobaltagswritejob.h index 61dc04c78..2d4136739 100644 --- a/resources/ews/tags/ewsglobaltagswritejob.h +++ b/resources/ews/tags/ewsglobaltagswritejob.h @@ -1,49 +1,47 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSGLOBALTAGSWRITEJOB_H #define EWSGLOBALTAGSWRITEJOB_H #include "ewsjob.h" class EwsTagStore; class EwsClient; -namespace Akonadi -{ +namespace Akonadi { class Collection; } class EwsGlobalTagsWriteJob : public EwsJob { Q_OBJECT public: - EwsGlobalTagsWriteJob(EwsTagStore *tagStore, EwsClient &client, - const Akonadi::Collection &rootCollection, QObject *parent); + EwsGlobalTagsWriteJob(EwsTagStore *tagStore, EwsClient &client, const Akonadi::Collection &rootCollection, QObject *parent); ~EwsGlobalTagsWriteJob() override; void start() override; private Q_SLOTS: void updateFolderRequestFinished(KJob *job); private: EwsTagStore *mTagStore = nullptr; EwsClient &mClient; const Akonadi::Collection &mRootCollection; }; #endif diff --git a/resources/ews/tags/ewstagstore.cpp b/resources/ews/tags/ewstagstore.cpp index 8c7fd8b0e..5bb3909bf 100644 --- a/resources/ews/tags/ewstagstore.cpp +++ b/resources/ews/tags/ewstagstore.cpp @@ -1,324 +1,322 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "ewstagstore.h" #include #include #include #include "ewsresource_debug.h" #include "ewsitem.h" #include "ewsresource.h" using namespace Akonadi; static Q_CONSTEXPR quint32 TagDataVer1 = 1; static Q_CONSTEXPR QDataStream::Version TagDataVer1StreamVer = QDataStream::Qt_5_4; EwsTagStore::EwsTagStore(QObject *parent) - : QObject(parent), mVersion(0) + : QObject(parent) + , mVersion(0) { - } EwsTagStore::~EwsTagStore() { - } QByteArray EwsTagStore::serializeTag(const Akonadi::Tag &tag) const { QByteArray tagData; QDataStream stream(&tagData, QIODevice::WriteOnly); stream.setVersion(TagDataVer1StreamVer); stream << TagDataVer1; stream << tag.name() << tag.gid(); Attribute::List attrs = tag.attributes(); stream << static_cast(attrs.size()); Q_FOREACH (const Attribute *attr, attrs) { stream << attr->type(); stream << attr->serialized(); } return tagData; } bool EwsTagStore::unserializeTag(const QByteArray &data, Akonadi::Tag &tag) const { QDataStream stream(data); quint32 ver; stream >> ver; QString name; QByteArray gid; int numAttrs; Attribute::List attributes; switch (ver) { case TagDataVer1: stream.setVersion(TagDataVer1StreamVer); stream >> name >> gid; stream >> numAttrs; if (stream.status() != QDataStream::Ok) { QStringLiteral("Error reading tag version 1"); return false; } for (int i = 0; i < numAttrs; ++i) { QByteArray attrType, attrData; stream >> attrType >> attrData; if (stream.status() != QDataStream::Ok) { QStringLiteral("Error reading tag version 1"); return false; } Attribute *attr = AttributeFactory::createAttribute(attrType); attr->deserialize(data); attributes.append(attr); } break; default: qCWarningNC(EWSRES_LOG) << QStringLiteral("Unknown tag data version (%1)").arg(ver); return false; } tag.setName(name); tag.setGid(gid); Q_FOREACH (Attribute *attr, attributes) { tag.addAttribute(attr); } return true; } bool EwsTagStore::readTags(const QStringList &taglist, int version) { - if (version < mVersion) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Reading tags from older version (have %1, got %2)") - .arg(mVersion).arg(version); + .arg(mVersion).arg(version); return false; } else if (version == mVersion) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Both tag lists in version %1 - not syncing").arg(version); return true; } mTagData.clear(); Q_FOREACH (const QString &tag, taglist) { QByteArray tagdata = qUncompress(QByteArray::fromBase64(tag.toLatin1())); if (tagdata.isNull()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Incorrect tag data"); } else { QDataStream stream(tagdata); stream.setVersion(QDataStream::Qt_5_4); QByteArray key, data; stream >> key >> data; if (stream.status() != QDataStream::Ok) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Incorrect tag entry"); mTagData.clear(); return false; } else { mTagData.insert(key, data); } } } mVersion = version; return true; } QStringList EwsTagStore::serialize() const { QStringList tagList; for (auto it = mTagData.cbegin(), end(mTagData.cend()); it != end; ++it) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_4); stream << it.key(); stream << it.value(); tagList.append(QString::fromLatin1(qCompress(data, 9).toBase64())); } return tagList; } Tag::List EwsTagStore::tags() const { Tag::List tagList; for (auto it = mTagData.cbegin(), end = mTagData.cend(); it != end; ++it) { Tag tag(-1); if (unserializeTag(it.value(), tag)) { tagList.append(tag); } } return tagList; } bool EwsTagStore::containsId(Akonadi::Tag::Id id) const { return mTagIdMap.contains(id); } void EwsTagStore::addTag(const Akonadi::Tag &tag) { addTags(Akonadi::Tag::List() << tag); } void EwsTagStore::addTags(const Akonadi::Tag::List &tags) { syncTags(tags); } bool EwsTagStore::syncTags(const Akonadi::Tag::List &tags) { /* TODO: Remote tag support is partially broken in Akonadi. In particular it is not possible * to associate a remote identifier with a tag as the database schema is broken. Therefore * the EWS resource relies on the fact that the remote identifier is the same as the local uid * of the tag and doesn't set the remote identifier on the tag object. This is possinle as * tags don't exist as Exchange objects and are only stored in private properties that are in * full control. */ bool changed = false; QList tagIds = mTagData.keys(); Q_FOREACH (const Tag &tag, tags) { QByteArray serialized = serializeTag(tag); auto it = mTagData.find(tag.gid()); /* First check if the tag exists or if it has been changed. Only once that is done * check if the store knows the tag name and Akonadi id. The separation is necessary as * the store might have the full list of tags from Exchange, but without Akonadi IDs. When * a sync is done it may only yield those IDs without any of the tags changed. In such case * the function should return false as no actual change has been made. */ if ((it == mTagData.end()) || (*it != serialized)) { mTagData.insert(tag.gid(), serialized); changed = true; } if (it != mTagData.end()) { tagIds.removeOne(tag.gid()); } if (!mTagIdMap.contains(tag.id())) { mTagIdMap.insert(tag.id(), tag.gid()); QString name; if (tag.hasAttribute()) { name = tag.attribute()->displayName(); } else { name = tag.name(); } if (!name.isEmpty()) { mTagNameMap.insert(tag.id(), tag.name()); } } } Q_FOREACH (const QByteArray &tagId, tagIds) { mTagData.remove(tagId); } if (changed) { ++mVersion; } return changed; } void EwsTagStore::removeTag(const Akonadi::Tag &tag) { QByteArray rid = mTagIdMap.value(tag.id()); mTagIdMap.remove(tag.id()); mTagNameMap.remove(tag.id()); mTagData.remove(rid); ++mVersion; } QByteArray EwsTagStore::tagRemoteId(Akonadi::Tag::Id id) const { return mTagIdMap.value(id); } QString EwsTagStore::tagName(Akonadi::Tag::Id id) const { return mTagNameMap.value(id); } Tag::Id EwsTagStore::tagIdForRid(const QByteArray &rid) const { return mTagIdMap.key(rid, -1); } bool EwsTagStore::readEwsProperties(Akonadi::Item &item, const EwsItem &ewsItem, bool ignoreMissing) const { QVariant tagProp = ewsItem[EwsResource::tagsProperty]; if (tagProp.isValid() && tagProp.canConvert()) { QStringList tagRids = tagProp.toStringList(); Q_FOREACH (const QString &tagRid, tagRids) { Tag::Id tagId = tagIdForRid(tagRid.toLatin1()); if (tagId == -1) { /* Tag not found. */ qCDebug(EWSRES_LOG) << QStringLiteral("Found missing tag: %1").arg(tagRid); if (ignoreMissing) { continue; } else { return false; } } Tag tag(tagId); item.setTag(tag); } } return true; } bool EwsTagStore::writeEwsProperties(const Akonadi::Item &item, EwsItem &ewsItem) const { if (!item.tags().isEmpty()) { QStringList tagList; QStringList categoryList; Q_FOREACH (const Tag &tag, item.tags()) { if (!containsId(tag.id())) { return false; } tagList.append(QString::fromLatin1(tagRemoteId(tag.id()))); const QString name = tagName(tag.id()); if (!name.isEmpty()) { categoryList.append(name); } } ewsItem.setProperty(EwsResource::tagsProperty, tagList); ewsItem.setField(EwsItemFieldCategories, categoryList); } return true; } int EwsTagStore::version() const { return mVersion; } diff --git a/resources/ews/tags/ewsupdateitemstagsjob.cpp b/resources/ews/tags/ewsupdateitemstagsjob.cpp index 834ed1905..acf4d0a1f 100644 --- a/resources/ews/tags/ewsupdateitemstagsjob.cpp +++ b/resources/ews/tags/ewsupdateitemstagsjob.cpp @@ -1,175 +1,177 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsupdateitemstagsjob.h" #include #include #include #include #include #include "ewsid.h" #include "ewsupdateitemrequest.h" #include "ewsitemhandler.h" #include "ewsclient.h" #include "ewstagstore.h" #include "ewsresource.h" #include "ewsglobaltagswritejob.h" using namespace Akonadi; -EwsUpdateItemsTagsJob::EwsUpdateItemsTagsJob(const Akonadi::Item::List &items, EwsTagStore *tagStore, - EwsClient &client, EwsResource *parent) - : EwsJob(parent), mItems(items), mTagStore(tagStore), mClient(client) +EwsUpdateItemsTagsJob::EwsUpdateItemsTagsJob(const Akonadi::Item::List &items, EwsTagStore *tagStore, EwsClient &client, EwsResource *parent) + : EwsJob(parent) + , mItems(items) + , mTagStore(tagStore) + , mClient(client) { } EwsUpdateItemsTagsJob::~EwsUpdateItemsTagsJob() { } void EwsUpdateItemsTagsJob::start() { Tag::List unknownTags; /* In the Exchange world it is not possible to add or remove individual tags from an item * - it is necessary to write the full list of tags at once. Unfortunately the tags objects * attached to an item only contain the id. They're missing any persistent identification such * as the uid. If the EWS resource hasn't seen these tags yet it is necessary to fetch them * first before any further processing. */ Q_FOREACH (const Item &item, mItems) { Q_FOREACH (const Tag &tag, item.tags()) { if (!mTagStore->containsId(tag.id())) { unknownTags.append(tag); } } } if (!unknownTags.empty()) { TagFetchJob *job = new TagFetchJob(unknownTags, this); job->fetchScope().setFetchRemoteId(true); connect(job, &TagFetchJob::result, this, &EwsUpdateItemsTagsJob::itemsTagsChangedTagsFetched); } else { doUpdateItemsTags(); } } void EwsUpdateItemsTagsJob::itemsTagsChangedTagsFetched(KJob *job) { if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } - TagFetchJob *tagJob = qobject_cast(job); + TagFetchJob *tagJob = qobject_cast(job); if (!tagJob) { setErrorMsg(QStringLiteral("Invalid TagFetchJob job object")); emitResult(); return; } /* All unknown tags have been fetched and can be written to Exchange. */ mTagStore->addTags(tagJob->tags()); - EwsResource *res = qobject_cast(parent()); + EwsResource *res = qobject_cast(parent()); Q_ASSERT(res); EwsGlobalTagsWriteJob *writeJob = new EwsGlobalTagsWriteJob(mTagStore, mClient, - res->rootCollection(), this); + res->rootCollection(), this); connect(writeJob, &EwsGlobalTagsWriteJob::result, this, &EwsUpdateItemsTagsJob::globalTagsWriteFinished); writeJob->start(); } void EwsUpdateItemsTagsJob::globalTagsWriteFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } doUpdateItemsTags(); } void EwsUpdateItemsTagsJob::doUpdateItemsTags() { EwsUpdateItemRequest *req = new EwsUpdateItemRequest(mClient, this); Q_FOREACH (const Item &item, mItems) { EwsUpdateItemRequest::ItemChange ic(EwsId(item.remoteId(), item.remoteRevision()), EwsItemHandler::mimeToItemType(item.mimeType())); if (!item.tags().isEmpty()) { QStringList tagList; QStringList categoryList; tagList.reserve(item.tags().count()); Q_FOREACH (const Tag &tag, item.tags()) { Q_ASSERT(mTagStore->containsId(tag.id())); tagList.append(QString::fromLatin1(mTagStore->tagRemoteId(tag.id()))); QString name = mTagStore->tagName(tag.id()); if (!name.isEmpty()) { categoryList.append(name); } } EwsUpdateItemRequest::Update *upd = new EwsUpdateItemRequest::SetUpdate(EwsResource::tagsProperty, tagList); ic.addUpdate(upd); upd = new EwsUpdateItemRequest::SetUpdate(EwsPropertyField(QStringLiteral("item:Categories")), categoryList); ic.addUpdate(upd); } else { EwsUpdateItemRequest::Update *upd = new EwsUpdateItemRequest::DeleteUpdate(EwsResource::tagsProperty); ic.addUpdate(upd); upd = new EwsUpdateItemRequest::DeleteUpdate(EwsPropertyField(QStringLiteral("item:Categories"))); ic.addUpdate(upd); } req->addItemChange(ic); } connect(req, &EwsUpdateItemRequest::result, this, &EwsUpdateItemsTagsJob::updateItemsTagsRequestFinished); req->start(); } void EwsUpdateItemsTagsJob::updateItemsTagsRequestFinished(KJob *job) { if (job->error()) { setErrorMsg(job->errorString()); emitResult(); return; } - EwsUpdateItemRequest *req = qobject_cast(job); + EwsUpdateItemRequest *req = qobject_cast(job); if (!req) { setErrorMsg(QStringLiteral("Invalid EwsUpdateItemRequest job object")); return; } Q_ASSERT(mItems.count() == req->responses().count()); auto itemIt = mItems.begin(); Q_FOREACH (const EwsUpdateItemRequest::Response &resp, req->responses()) { if (resp.isSuccess()) { itemIt->setRemoteRevision(resp.itemId().changeKey()); } } emitResult(); } diff --git a/resources/ews/tags/ewsupdateitemstagsjob.h b/resources/ews/tags/ewsupdateitemstagsjob.h index 0dbbfac4e..6ed9a1792 100644 --- a/resources/ews/tags/ewsupdateitemstagsjob.h +++ b/resources/ews/tags/ewsupdateitemstagsjob.h @@ -1,67 +1,67 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSUPDATEITEMSTAGSJOB_H #define EWSUPDATEITEMSTAGSJOB_H #include "ewsjob.h" #include class EwsTagStore; class EwsClient; class EwsResource; /** * @brief Job used to update Exchange items wit tag information from Akonadi * * This job cycles through all items and updates the Exchange database with tag information from * the items. * * The job relies on the tag store to retrieve tag identifiers and names that can be stored in * Exchange. Due to buggy tag implementation in Akonad it can happen that items contain tags not * yet known to the EWS resource. In such case an additional tag fetch job is issues to fetch * information about those tags so that they can be added to the tag store. */ class EwsUpdateItemsTagsJob : public EwsJob { Q_OBJECT public: - EwsUpdateItemsTagsJob(const Akonadi::Item::List &items, EwsTagStore *tagStore, EwsClient &client, - EwsResource *parent); + EwsUpdateItemsTagsJob(const Akonadi::Item::List &items, EwsTagStore *tagStore, EwsClient &client, EwsResource *parent); ~EwsUpdateItemsTagsJob() override; void start() override; Akonadi::Item::List items() { return mItems; } + private Q_SLOTS: void itemsTagsChangedTagsFetched(KJob *job); void updateItemsTagsRequestFinished(KJob *job); void globalTagsWriteFinished(KJob *job); private: void doUpdateItemsTags(); Akonadi::Item::List mItems; EwsTagStore *mTagStore = nullptr; EwsClient &mClient; }; #endif diff --git a/resources/ews/task/ewscreatetaskjob.cpp b/resources/ews/task/ewscreatetaskjob.cpp index 341b1ad87..8535ec483 100644 --- a/resources/ews/task/ewscreatetaskjob.cpp +++ b/resources/ews/task/ewscreatetaskjob.cpp @@ -1,46 +1,45 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewscreatetaskjob.h" #include "ewsresource_debug.h" -EwsCreateTaskJob::EwsCreateTaskJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, - EwsResource *parent) +EwsCreateTaskJob::EwsCreateTaskJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) : EwsCreateItemJob(client, item, collection, tagStore, parent) { } + EwsCreateTaskJob::~EwsCreateTaskJob() { } void EwsCreateTaskJob::doStart() { qCWarning(EWSRES_LOG) << QStringLiteral("Task item creation not implemented!"); emitResult(); } bool EwsCreateTaskJob::setSend(bool send) { Q_UNUSED(send) qCWarning(EWSRES_LOG) << QStringLiteral("Sending task items is not supported!"); return false; } diff --git a/resources/ews/task/ewscreatetaskjob.h b/resources/ews/task/ewscreatetaskjob.h index 4f7d461b8..e06131f4f 100644 --- a/resources/ews/task/ewscreatetaskjob.h +++ b/resources/ews/task/ewscreatetaskjob.h @@ -1,37 +1,36 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSCREATETASKJOB_H #define EWSCREATETASKJOB_H #include "ewscreateitemjob.h" class EwsCreateTaskJob : public EwsCreateItemJob { Q_OBJECT public: - EwsCreateTaskJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); + EwsCreateTaskJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent); ~EwsCreateTaskJob() override; bool setSend(bool send = true) override; protected: void doStart() override; }; #endif diff --git a/resources/ews/task/ewsfetchtaskdetailjob.cpp b/resources/ews/task/ewsfetchtaskdetailjob.cpp index 5cce09cce..8401cc082 100644 --- a/resources/ews/task/ewsfetchtaskdetailjob.cpp +++ b/resources/ews/task/ewsfetchtaskdetailjob.cpp @@ -1,62 +1,60 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsfetchtaskdetailjob.h" #include "ewsitemshape.h" #include "ewsgetitemrequest.h" #include "ewsmailbox.h" #include "ewsresource_debug.h" using namespace Akonadi; EwsFetchTaskDetailJob::EwsFetchTaskDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) : EwsFetchItemDetailJob(client, parent, collection) { EwsItemShape shape(EwsShapeIdOnly); mRequest->setItemShape(shape); } - EwsFetchTaskDetailJob::~EwsFetchTaskDetailJob() { } void EwsFetchTaskDetailJob::processItems(const QList &responses) { Item::List::iterator it = mChangedItems.begin(); for (const EwsGetItemRequest::Response &resp : responses) { Item &item = *it; if (!resp.isSuccess()) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Failed to fetch item %1").arg(item.remoteId()); continue; } //const EwsItem &ewsItem = resp.item(); // TODO: Implement ++it; } emitResult(); } - diff --git a/resources/ews/task/ewsmodifytaskjob.cpp b/resources/ews/task/ewsmodifytaskjob.cpp index af293862f..0b91edf5e 100644 --- a/resources/ews/task/ewsmodifytaskjob.cpp +++ b/resources/ews/task/ewsmodifytaskjob.cpp @@ -1,37 +1,37 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewsmodifytaskjob.h" #include "ewsresource_debug.h" -EwsModifyTaskJob::EwsModifyTaskJob(EwsClient &client, const Akonadi::Item::List &items, - const QSet &parts, QObject *parent) +EwsModifyTaskJob::EwsModifyTaskJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent) : EwsModifyItemJob(client, items, parts, parent) { } + EwsModifyTaskJob::~EwsModifyTaskJob() { } void EwsModifyTaskJob::start() { qCWarning(EWSRES_LOG) << QStringLiteral("Task item modification not implemented!"); emitResult(); } diff --git a/resources/ews/task/ewsmodifytaskjob.h b/resources/ews/task/ewsmodifytaskjob.h index 6afaaf0c4..47813f1bd 100644 --- a/resources/ews/task/ewsmodifytaskjob.h +++ b/resources/ews/task/ewsmodifytaskjob.h @@ -1,35 +1,34 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSMODIFYTASKJOB_H #define EWSMODIFYTASKJOB_H #include "ewsmodifyitemjob.h" class EwsModifyTaskJob : public EwsModifyItemJob { Q_OBJECT public: - EwsModifyTaskJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, - QObject *parent); + EwsModifyTaskJob(EwsClient &client, const Akonadi::Item::List &items, const QSet &parts, QObject *parent); ~EwsModifyTaskJob() override; void start() override; }; #endif diff --git a/resources/ews/task/ewstaskhandler.cpp b/resources/ews/task/ewstaskhandler.cpp index ad6cf9194..7fd5cd3ef 100644 --- a/resources/ews/task/ewstaskhandler.cpp +++ b/resources/ews/task/ewstaskhandler.cpp @@ -1,80 +1,77 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "ewstaskhandler.h" #include #include "ewsfetchtaskdetailjob.h" #include "ewsmodifytaskjob.h" #include "ewscreatetaskjob.h" using namespace Akonadi; EwsTaskHandler::EwsTaskHandler() { } EwsTaskHandler::~EwsTaskHandler() { } EwsItemHandler *EwsTaskHandler::factory() { return new EwsTaskHandler(); } -EwsFetchItemDetailJob *EwsTaskHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) +EwsFetchItemDetailJob *EwsTaskHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) { return new EwsFetchTaskDetailJob(client, parent, collection); } void EwsTaskHandler::setSeenFlag(Item &item, bool value) { Q_UNUSED(item) Q_UNUSED(value) } QString EwsTaskHandler::mimeType() { return KCalCore::Todo::todoMimeType(); } bool EwsTaskHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) { Q_UNUSED(item) Q_UNUSED(ewsItem) return true; } -EwsModifyItemJob *EwsTaskHandler::modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) +EwsModifyItemJob *EwsTaskHandler::modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) { return new EwsModifyTaskJob(client, items, parts, parent); } -EwsCreateItemJob *EwsTaskHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) +EwsCreateItemJob *EwsTaskHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) { return new EwsCreateTaskJob(client, item, collection, tagStore, parent); } EWS_DECLARE_ITEM_HANDLER(EwsTaskHandler, EwsItemTypeTask) diff --git a/resources/ews/task/ewstaskhandler.h b/resources/ews/task/ewstaskhandler.h index 37f10e5f0..412e8fc6c 100644 --- a/resources/ews/task/ewstaskhandler.h +++ b/resources/ews/task/ewstaskhandler.h @@ -1,44 +1,40 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 EWSTASKHANDLER_H #define EWSTASKHANDLER_H #include "ewsitemhandler.h" class EwsTaskHandler : public EwsItemHandler { public: EwsTaskHandler(); ~EwsTaskHandler() override; - EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, - const Akonadi::Collection &collection) override; + EwsFetchItemDetailJob *fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection) override; void setSeenFlag(Akonadi::Item &item, bool value) override; QString mimeType() override; bool setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem) override; - EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, - const QSet &parts, QObject *parent) override; - EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, - const Akonadi::Collection &collection, - EwsTagStore *tagStore, EwsResource *parent) override; + EwsModifyItemJob *modifyItemJob(EwsClient &client, const QVector &items, const QSet &parts, QObject *parent) override; + EwsCreateItemJob *createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent) override; static EwsItemHandler *factory(); }; #endif diff --git a/resources/ews/test/ewstest.cpp b/resources/ews/test/ewstest.cpp index 0ae09642a..f4cb9f4f2 100644 --- a/resources/ews/test/ewstest.cpp +++ b/resources/ews/test/ewstest.cpp @@ -1,173 +1,173 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 #include #include #include #include #include #include #include "fakeewsserverthread.h" #include "ewssettings.h" #include "ewswallet.h" #include "isolatedtestbase.h" #include "statemonitor.h" class BasicTest : public IsolatedTestBase { Q_OBJECT public: explicit BasicTest(QObject *parent = nullptr); ~BasicTest() override; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testBasic(); }; QTEST_AKONADIMAIN(BasicTest) using namespace Akonadi; constexpr int DesiredStateTimeoutMs = 20000; BasicTest::BasicTest(QObject *parent) : IsolatedTestBase(parent) { } BasicTest::~BasicTest() { } void BasicTest::initTestCase() { init(); } void BasicTest::cleanupTestCase() { cleanup(); } void BasicTest::testBasic() { TestAgentInstance instance(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(mFakeServerThread->portNumber())); QVERIFY(instance.isValid()); static const auto rootId = QStringLiteral("cm9vdA=="); static const auto inboxId = QStringLiteral("aW5ib3g="); FolderList folderList = { {rootId, instance.identifier(), Folder::Root, QString()}, {inboxId, QStringLiteral("Inbox"), Folder::Inbox, rootId}, {QStringLiteral("Y2FsZW5kYXI="), QStringLiteral("Calendar"), Folder::Calendar, rootId}, {QStringLiteral("dGFza3M="), QStringLiteral("Tasks"), Folder::Tasks, rootId}, {QStringLiteral("Y29udGFjdHM="), QStringLiteral("Contacts"), Folder::Contacts, rootId}, {QStringLiteral("b3V0Ym94"), QStringLiteral("Outbox"), Folder::Outbox, rootId}, {QStringLiteral("c2VudCBpdGVtcw=="), QStringLiteral("Sent Items"), Folder::Sent, rootId}, {QStringLiteral("ZGVsZXRlZCBpdGVtcw=="), QStringLiteral("Deleted Items"), Folder::Trash, rootId}, {QStringLiteral("ZHJhZnRz"), QStringLiteral("Drafts"), Folder::Drafts, rootId} }; struct DesiredState { QString parentId; QByteArray specialType; }; QHash desiredStates = { {rootId, {QString(), QByteArray()}}, {inboxId, {rootId, "inbox"}}, {QStringLiteral("Y2FsZW5kYXI="), {rootId, QByteArray()}}, {QStringLiteral("dGFza3M="), {rootId, QByteArray()}}, {QStringLiteral("Y29udGFjdHM="), {rootId, QByteArray()}}, {QStringLiteral("b3V0Ym94"), {rootId, "outbox"}}, {QStringLiteral("c2VudCBpdGVtcw=="), {rootId, "sent-mail"}}, {QStringLiteral("ZGVsZXRlZCBpdGVtcw=="), {rootId, "trash"}}, {QStringLiteral("ZHJhZnRz"), {rootId, "drafts"}} }; FakeEwsServer::DialogEntry::List dialog = { MsgRootInboxDialogEntry(rootId, inboxId, QStringLiteral("GetFolder request for inbox and msgroot")), SubscribedFoldersDialogEntry(folderList, QStringLiteral("GetFolder request for subscribed folders")), ValidateFolderIdsDialogEntry(QStringList() << inboxId << QStringLiteral("Y2FsZW5kYXI=") << QStringLiteral("dGFza3M=") << QStringLiteral("Y29udGFjdHM="), QStringLiteral("GetFolder request for subscribed folder ids")), SpecialFoldersDialogEntry(folderList, QStringLiteral("GetFolder request for special folders")), GetTagsEmptyDialogEntry(rootId, QStringLiteral("GetFolder request for tags")), SubscribeStreamingDialogEntry(QStringLiteral("Subscribe request for streaming events")), SyncFolderHierInitialDialogEntry(folderList, QStringLiteral("bNUUPDWHTvuG9p57NGZdhjREdZXDt48a0E1F22yThko="), QStringLiteral("SyncFolderHierarchy request with empty state")), UnsubscribeDialogEntry(QStringLiteral("Unsubscribe request")) }; bool unknownRequestEncountered = false; mFakeServerThread->setDialog(dialog); - mFakeServerThread->setDefaultReplyCallback([&](const QString & req, QXmlResultItems &, const QXmlNamePool &) { + mFakeServerThread->setDefaultReplyCallback([&](const QString &req, QXmlResultItems &, const QXmlNamePool &) { qDebug() << "Unknown EWS request encountered." << req; unknownRequestEncountered = true; return FakeEwsServer::EmptyResponse; }); QEventLoop loop; CollectionStateMonitor stateMonitor(this, desiredStates, inboxId, [](const Collection &col, const DesiredState &state) { - auto attr = col.attribute(); - QByteArray specialType; - if (attr) { - specialType = attr->collectionType(); - } - return col.parentCollection().remoteId() == state.parentId && specialType == state.specialType; - }, 1000); + auto attr = col.attribute(); + QByteArray specialType; + if (attr) { + specialType = attr->collectionType(); + } + return col.parentCollection().remoteId() == state.parentId && specialType == state.specialType; + }, 1000); stateMonitor.monitor().fetchCollection(true); stateMonitor.monitor().collectionFetchScope().fetchAttribute(); stateMonitor.monitor().collectionFetchScope().setAncestorRetrieval(CollectionFetchScope::Parent); stateMonitor.monitor().setResourceMonitored(instance.identifier().toLatin1(), true); connect(&stateMonitor, &CollectionStateMonitor::stateReached, this, [&]() { loop.exit(0); }); connect(&stateMonitor, &CollectionStateMonitor::errorOccurred, this, [&]() { loop.exit(1); }); QVERIFY(instance.setOnline(true, true)); QTimer timer; timer.setSingleShot(true); connect(&timer, &QTimer::timeout, this, [&]() { qWarning() << "Timeout waiting for desired resource online state."; loop.exit(1); }); timer.start(DesiredStateTimeoutMs); QCOMPARE(loop.exec(), 0); QVERIFY(!unknownRequestEncountered); QVERIFY(instance.setOnline(false, true)); } #include "ewstest.moc" diff --git a/resources/ews/test/fakeserver/fakeewsconnection.cpp b/resources/ews/test/fakeserver/fakeewsconnection.cpp index ca1467de9..d20e1ca7d 100644 --- a/resources/ews/test/fakeserver/fakeewsconnection.cpp +++ b/resources/ews/test/fakeserver/fakeewsconnection.cpp @@ -1,439 +1,443 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "fakeewsconnection.h" #include #include #include #include #include #include #include #include "fakeewsserver.h" #include "fakeewsserver_debug.h" static const QHash responseCodes = { {200, QStringLiteral("OK")}, {400, QStringLiteral("Bad Request")}, {401, QStringLiteral("Unauthorized")}, {403, QStringLiteral("Forbidden")}, {404, QStringLiteral("Not Found")}, {405, QStringLiteral("Method Not Allowed")}, {500, QStringLiteral("Internal Server Error")} }; static Q_CONSTEXPR int streamingEventsHeartbeatIntervalSeconds = 5; -FakeEwsConnection::FakeEwsConnection(QTcpSocket *sock, FakeEwsServer* parent) - : QObject(parent), mSock(sock), mContentLength(0), mKeepAlive(false), mState(Initial), - mAuthenticated(false) +FakeEwsConnection::FakeEwsConnection(QTcpSocket *sock, FakeEwsServer *parent) + : QObject(parent) + , mSock(sock) + , mContentLength(0) + , mKeepAlive(false) + , mState(Initial) + , mAuthenticated(false) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Got new EWS connection."); connect(mSock.data(), &QTcpSocket::disconnected, this, &FakeEwsConnection::disconnected); connect(mSock.data(), &QTcpSocket::readyRead, this, &FakeEwsConnection::dataAvailable); connect(&mDataTimer, &QTimer::timeout, this, &FakeEwsConnection::dataTimeout); connect(&mStreamingRequestHeartbeat, &QTimer::timeout, this, &FakeEwsConnection::streamingRequestHeartbeat); connect(&mStreamingRequestTimeout, &QTimer::timeout, this, &FakeEwsConnection::streamingRequestTimeout); } FakeEwsConnection::~FakeEwsConnection() { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Connection closed."); } void FakeEwsConnection::disconnected() { deleteLater(); } void FakeEwsConnection::dataAvailable() { if (mState == Initial) { QByteArray line = mSock->readLine(); QList tokens = line.split(' '); mKeepAlive = false; if (tokens.size() < 3) { sendError(QStringLiteral("Invalid request header")); return; } if (tokens.at(0) != "POST") { sendError(QStringLiteral("Expected POST request")); return; } if (tokens.at(1) != "/EWS/Exchange.asmx") { sendError(QStringLiteral("Invalid EWS URL")); return; } mState = RequestReceived; } if (mState == RequestReceived) { QByteArray line; do { line = mSock->readLine(); if (line.toLower().startsWith(QByteArray("content-length: "))) { bool ok; mContentLength = line.trimmed().mid(16).toUInt(&ok); if (!ok) { sendError(QStringLiteral("Failed to parse content length.")); return; } } else if (line.toLower().startsWith(QByteArray("authorization: basic "))) { if (line.trimmed().mid(21) == "dGVzdDp0ZXN0") { mAuthenticated = true; } } else if (line.toLower() == "connection: keep-alive\r\n") { mKeepAlive = true; } } while (!line.trimmed().isEmpty()); if (line == "\r\n") { mState = HeadersReceived; } } if (mState == HeadersReceived) { if (mContentLength == 0) { sendError(QStringLiteral("Expected content")); return; } mContent += mSock->read(mContentLength - mContent.size()); if (mContent.size() >= static_cast(mContentLength)) { mDataTimer.stop(); if (!mAuthenticated) { QString codeStr = responseCodes.value(401); QString response(QStringLiteral("HTTP/1.1 %1 %2\r\n" "WWW-Authenticate: Basic realm=\"Fake EWS Server\"\r\n" "Connection: close\r\n" "\r\n").arg(401).arg(codeStr)); response += codeStr; mSock->write(response.toLatin1()); mSock->disconnectFromHost(); return; } FakeEwsServer::DialogEntry::HttpResponse resp = parseRequest(QString::fromUtf8(mContent)); bool chunked = false; if (resp == FakeEwsServer::EmptyResponse) { resp = handleGetEventsRequest(QString::fromUtf8(mContent)); } if (resp == FakeEwsServer::EmptyResponse) { resp = handleGetStreamingEventsRequest(QString::fromUtf8(mContent)); if (resp.second > 1000) { chunked = true; resp.second %= 1000; } } - FakeEwsServer *server = qobject_cast(parent()); + FakeEwsServer *server = qobject_cast(parent()); auto defaultReplyCallback = server->defaultReplyCallback(); if (defaultReplyCallback && (resp == FakeEwsServer::EmptyResponse)) { QXmlResultItems ri; QXmlNamePool namePool; resp = defaultReplyCallback(QString::fromUtf8(mContent), ri, namePool); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning response from default callback ") << resp.second << QStringLiteral(": ") << resp.first; } if (resp == FakeEwsServer::EmptyResponse) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning default response 500."); resp = { QStringLiteral(""), 500 }; } QByteArray buffer; QString codeStr = responseCodes.value(resp.second); QByteArray respContent = resp.first.toUtf8(); buffer += QStringLiteral("HTTP/1.1 %1 %2\r\n").arg(resp.second).arg(codeStr).toLatin1(); if (chunked) { buffer += "Transfer-Encoding: chunked\r\n"; buffer += "\r\n"; buffer += QByteArray::number(respContent.size(), 16) + "\r\n"; buffer += respContent + "\r\n"; } else { buffer += "Content-Length: " + QByteArray::number(respContent.size()) + "\r\n"; buffer += mKeepAlive ? "Connection: Keep-Alive\n" : "Connection: Close\r\n"; buffer += "\r\n"; buffer += respContent; } mSock->write(buffer); if (!mKeepAlive && !chunked) { mSock->disconnectFromHost(); } mContent.clear(); mState = Initial; } else { mDataTimer.start(3000); } } } void FakeEwsConnection::sendError(const QString &msg, ushort code) { qCWarningNC(EWSFAKE_LOG) << msg; QString codeStr = responseCodes.value(code); QByteArray response(QStringLiteral("HTTP/1.1 %1 %2\nConnection: close\n\n").arg(code).arg(codeStr).toLatin1()); response += msg.toLatin1(); mSock->write(response); mSock->disconnectFromHost(); } void FakeEwsConnection::dataTimeout() { qCWarning(EWSFAKE_LOG) << QLatin1String("Timeout waiting for content."); sendError(QStringLiteral("Timeout waiting for content.")); } FakeEwsServer::DialogEntry::HttpResponse FakeEwsConnection::parseRequest(const QString &content) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Got request: ") << content; - FakeEwsServer *server = qobject_cast(parent()); + FakeEwsServer *server = qobject_cast(parent()); FakeEwsServer::DialogEntry::HttpResponse resp = FakeEwsServer::EmptyResponse; Q_FOREACH (const FakeEwsServer::DialogEntry &de, server->dialog()) { QXmlResultItems ri; QByteArray resultBytes; QString result; QBuffer resultBuffer(&resultBytes); resultBuffer.open(QIODevice::WriteOnly); QXmlQuery query; QXmlSerializer xser(query, &resultBuffer); if (!de.xQuery.isNull()) { query.setFocus(content); query.setQuery(de.xQuery); query.evaluateTo(&xser); query.evaluateTo(&ri); if (ri.hasError()) { qCDebugNC(EWSFAKE_LOG) << QStringLiteral("XQuery failed due to errors - skipping"); continue; } result = QString::fromUtf8(resultBytes); } if (!result.trimmed().isEmpty()) { qCDebugNC(EWSFAKE_LOG) << QStringLiteral("Got match for \"") << de.description << QStringLiteral("\""); if (de.replyCallback) { resp = de.replyCallback(content, ri, query.namePool()); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning response from callback ") << resp.second << QStringLiteral(": ") << resp.first; } else { resp = {result.trimmed(), 200}; qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning response from XQuery ") << resp.second << QStringLiteral(": ") << resp.first; } break; } } if (resp == FakeEwsServer::EmptyResponse) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning empty response."); qCInfoNC(EWSFAKE_LOG) << content; } return resp; } FakeEwsServer::DialogEntry::HttpResponse FakeEwsConnection::handleGetEventsRequest(const QString &content) { - const QRegularExpression re(QStringLiteral("].*<\\w*:?SubscriptionId>(?[^<]*)<\\w*:?Watermark>(?[^<]*).*")); + const QRegularExpression re(QStringLiteral( + "].*<\\w*:?SubscriptionId>(?[^<]*)<\\w*:?Watermark>(?[^<]*).*")); QRegularExpressionMatch match = re.match(content); if (!match.hasMatch() || match.hasPartialMatch()) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Not a valid GetEvents request."); return FakeEwsServer::EmptyResponse; } qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Got valid GetEvents request."); QString resp = QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + ""); if (match.captured(QStringLiteral("subid")).isEmpty() || match.captured(QStringLiteral("watermark")).isEmpty()) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Missing subscription id or watermark."); const QString errorResp = QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "Missing subscription id or watermark." - "ErrorInvalidPullSubscriptionId" - "0" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "Missing subscription id or watermark." + "ErrorInvalidPullSubscriptionId" + "0" + "" + "" + "" + "" + ""); return {errorResp, 200}; } resp += QStringLiteral("") + match.captured(QStringLiteral("subid")) + QStringLiteral(""); resp += QStringLiteral("") + match.captured(QStringLiteral("watermark")) + QStringLiteral(""); resp += QStringLiteral("false"); - FakeEwsServer *server = qobject_cast(parent()); + FakeEwsServer *server = qobject_cast(parent()); const QStringList events = server->retrieveEventsXml(); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning %1 events.").arg(events.size()); Q_FOREACH (const QString &eventXml, events) { resp += eventXml; } resp += QStringLiteral("" ""); - return {resp, 200}; } QString FakeEwsConnection::prepareEventsResponse(const QStringList &events) { QString resp = QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "OK"); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "OK"); if (!events.isEmpty()) { resp += QStringLiteral("") + mStreamingSubId + QStringLiteral(""); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Returning %1 events.").arg(events.size()); Q_FOREACH (const QString &eventXml, events) { resp += eventXml; } resp += QStringLiteral(""); } resp += QStringLiteral("" ""); return resp; } FakeEwsServer::DialogEntry::HttpResponse FakeEwsConnection::handleGetStreamingEventsRequest(const QString &content) { - const QRegularExpression re(QStringLiteral("].*<\\w*:?SubscriptionIds><\\w*:?SubscriptionId>(?[^<]*).*<\\w*:?ConnectionTimeout>(?[^<]*).*")); + const QRegularExpression re(QStringLiteral( + "].*<\\w*:?SubscriptionIds><\\w*:?SubscriptionId>(?[^<]*).*<\\w*:?ConnectionTimeout>(?[^<]*).*")); QRegularExpressionMatch match = re.match(content); if (!match.hasMatch() || match.hasPartialMatch()) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Not a valid GetStreamingEvents request."); return FakeEwsServer::EmptyResponse; } qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Got valid GetStreamingEvents request."); if (match.captured(QStringLiteral("subid")).isEmpty() || match.captured(QStringLiteral("timeout")).isEmpty()) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Missing subscription id or timeout."); const QString errorResp = QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "Missing subscription id or timeout." - "ErrorInvalidSubscription" - "0" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "Missing subscription id or timeout." + "ErrorInvalidSubscription" + "0" + "" + "" + "" + "" + ""); return {errorResp, 200}; } mStreamingSubId = match.captured(QStringLiteral("subid")); - FakeEwsServer *server = qobject_cast(parent()); + FakeEwsServer *server = qobject_cast(parent()); const QStringList events = server->retrieveEventsXml(); QString resp = prepareEventsResponse(events); mStreamingRequestTimeout.start(match.captured(QStringLiteral("timeout")).toInt() * 1000 * 60); mStreamingRequestHeartbeat.setSingleShot(false); mStreamingRequestHeartbeat.start(streamingEventsHeartbeatIntervalSeconds * 1000); Q_EMIT streamingRequestStarted(this); return {resp, 1200}; } void FakeEwsConnection::streamingRequestHeartbeat() { sendEvents(QStringList()); } void FakeEwsConnection::streamingRequestTimeout() { mStreamingRequestTimeout.stop(); mStreamingRequestHeartbeat.stop(); mSock->write("0\r\n\r\n"); mSock->disconnectFromHost(); } void FakeEwsConnection::sendEvents(const QStringList &events) { QByteArray resp = prepareEventsResponse(events).toUtf8(); mSock->write(QByteArray::number(resp.size(), 16) + "\r\n" + resp + "\r\n"); } - diff --git a/resources/ews/test/fakeserver/fakeewsserver.cpp b/resources/ews/test/fakeserver/fakeewsserver.cpp index 3b098a852..937cbc060 100644 --- a/resources/ews/test/fakeserver/fakeewsserver.cpp +++ b/resources/ews/test/fakeserver/fakeewsserver.cpp @@ -1,134 +1,136 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "fakeewsserver.h" #include #include "fakeewsconnection.h" #include "fakeewsserver_debug.h" const FakeEwsServer::DialogEntry::HttpResponse FakeEwsServer::EmptyResponse = {QString(), 0}; FakeEwsServer::FakeEwsServer(QObject *parent) - : QTcpServer(parent), mPortNumber(0) + : QTcpServer(parent) + , mPortNumber(0) { } + FakeEwsServer::~FakeEwsServer() { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Stopping fake EWS server."); } bool FakeEwsServer::start() { QMutexLocker lock(&mMutex); int retries = 3; bool ok; do { mPortNumber = (qrand() % 10000) + 10000; qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Starting fake EWS server at 127.0.0.1:%1").arg(mPortNumber); ok = listen(QHostAddress::LocalHost, mPortNumber); if (!ok) { qCWarningNC(EWSFAKE_LOG) << QStringLiteral("Failed to start server"); } } while (!ok && --retries); if (ok) { connect(this, &QTcpServer::newConnection, this, &FakeEwsServer::newConnectionReceived); } return ok; } void FakeEwsServer::setDialog(const DialogEntry::List &dialog) { QMutexLocker lock(&mMutex); mDialog = dialog; } void FakeEwsServer::setDefaultReplyCallback(DialogEntry::ReplyCallback defaultReplyCallback) { QMutexLocker lock(&mMutex); mDefaultReplyCallback = defaultReplyCallback; } void FakeEwsServer::queueEventsXml(const QStringList &events) { if (QThread::currentThread() != thread()) { qCWarningNC(EWSFAKE_LOG) << QStringLiteral("queueEventsXml called from wrong thread " - "(called from ") << QThread::currentThread() << QStringLiteral(", should be ") + "(called from ") << QThread::currentThread() << QStringLiteral(", should be ") << thread() << QStringLiteral(")"); return; } mEventQueue += events; if (mStreamingEventsConnection) { mStreamingEventsConnection->sendEvents(mEventQueue); mEventQueue.clear(); } } QStringList FakeEwsServer::retrieveEventsXml() { QStringList events = mEventQueue; mEventQueue.clear(); return events; } void FakeEwsServer::newConnectionReceived() { QTcpSocket *sock = nextPendingConnection(); FakeEwsConnection *conn = new FakeEwsConnection(sock, this); connect(conn, &FakeEwsConnection::streamingRequestStarted, this, &FakeEwsServer::streamingConnectionStarted); } const FakeEwsServer::DialogEntry::List FakeEwsServer::dialog() const { QMutexLocker lock(&mMutex); return mDialog; } const FakeEwsServer::DialogEntry::ReplyCallback FakeEwsServer::defaultReplyCallback() const { QMutexLocker lock(&mMutex); return mDefaultReplyCallback; } void FakeEwsServer::streamingConnectionStarted(FakeEwsConnection *conn) { if (mStreamingEventsConnection) { qCWarningNC(EWSFAKE_LOG) << QStringLiteral("Got new streaming connection while existing one is active - terminating existing one"); mStreamingEventsConnection->deleteLater(); } mStreamingEventsConnection = conn; } ushort FakeEwsServer::portNumber() const { QMutexLocker lock(&mMutex); return mPortNumber; } diff --git a/resources/ews/test/fakeserver/fakeewsserver_debug.cpp b/resources/ews/test/fakeserver/fakeewsserver_debug.cpp index 29800c29b..b59223047 100644 --- a/resources/ews/test/fakeserver/fakeewsserver_debug.cpp +++ b/resources/ews/test/fakeserver/fakeewsserver_debug.cpp @@ -1,23 +1,22 @@ /* Copyright (C) 2015-2016 Krzysztof Nowicki 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 "fakeewsserver_debug.h" Q_LOGGING_CATEGORY(EWSFAKE_LOG, "log_ews_fakeserver") - diff --git a/resources/ews/test/fakeserver/fakeewsserverthread.cpp b/resources/ews/test/fakeserver/fakeewsserverthread.cpp index 39027ac3b..074503ec7 100644 --- a/resources/ews/test/fakeserver/fakeewsserverthread.cpp +++ b/resources/ews/test/fakeserver/fakeewsserverthread.cpp @@ -1,109 +1,111 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 "fakeewsserverthread.h" #include #include "fakeewsserver.h" #include "fakeewsserver_debug.h" FakeEwsServerThread::FakeEwsServerThread(QObject *parent) - : QThread(parent), mPortNumber(0), mIsRunning(0) + : QThread(parent) + , mPortNumber(0) + , mIsRunning(0) { } FakeEwsServerThread::~FakeEwsServerThread() { } void FakeEwsServerThread::run() { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Starting fake server thread"); mMutex.lock(); mServer.reset(new FakeEwsServer(nullptr)); bool ok = mServer->start(); mMutex.unlock(); if (ok) { qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Fake server thread started."); mPortNumber = mServer->portNumber(); Q_EMIT serverStarted(ok); mIsRunning = 1; exec(); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Fake server thread terminating."); } else { Q_EMIT serverStarted(ok); qCInfoNC(EWSFAKE_LOG) << QStringLiteral("Fake server thread start failed."); } mMutex.lock(); mServer.reset(); mMutex.unlock(); } void FakeEwsServerThread::setDialog(const FakeEwsServer::DialogEntry::List &dialog) { QMutexLocker lock(&mMutex); if (mServer) { mServer->setDialog(dialog); } } void FakeEwsServerThread::setDefaultReplyCallback(FakeEwsServer::DialogEntry::ReplyCallback defaultReplyCallback) { QMutexLocker lock(&mMutex); if (mServer) { mServer->setDefaultReplyCallback(defaultReplyCallback); } } void FakeEwsServerThread::queueEventsXml(const QStringList &events) { QMutexLocker lock(&mMutex); if (mServer) { metaObject()->invokeMethod(this, "doQueueEventsXml", Q_ARG(QStringList, events)); } } void FakeEwsServerThread::doQueueEventsXml(const QStringList &events) { mServer->queueEventsXml(events); } bool FakeEwsServerThread::waitServerStarted() const { QEventLoop loop; { QMutexLocker lock(&mMutex); if (isFinished()) { return false; } if (mIsRunning) { return true; } connect(this, &FakeEwsServerThread::serverStarted, this, [&loop](bool ok) { loop.exit(ok ? 1 : 0); }); } return loop.exec(); } diff --git a/resources/ews/test/fakeserver/fakeewsserverthread.h b/resources/ews/test/fakeserver/fakeewsserverthread.h index 7bf1aa52b..8cd4da140 100644 --- a/resources/ews/test/fakeserver/fakeewsserverthread.h +++ b/resources/ews/test/fakeserver/fakeewsserverthread.h @@ -1,61 +1,63 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 FAKEEWSSERVERTHREAD_H #define FAKEEWSSERVERTHREAD_H #include #include #include "fakeewsserver.h" class Q_DECL_EXPORT FakeEwsServerThread : public QThread { Q_OBJECT public: explicit FakeEwsServerThread(QObject *parent = nullptr); ~FakeEwsServerThread() override; // FakeEwsServer *server() const; ushort portNumber() const { return mPortNumber; } + bool isRunning() const { return mIsRunning == 1; } + void setDialog(const FakeEwsServer::DialogEntry::List &dialog); void setDefaultReplyCallback(FakeEwsServer::DialogEntry::ReplyCallback defaultReplyCallback); void queueEventsXml(const QStringList &events); bool waitServerStarted() const; Q_SIGNALS: void serverStarted(bool ok); protected: void run() override; private Q_SLOTS: void doQueueEventsXml(const QStringList &events); private: QScopedPointer mServer; ushort mPortNumber; QAtomicInt mIsRunning; mutable QMutex mMutex; }; #endif diff --git a/resources/ews/test/fakeserver/test/ewsfakesrv_test.cpp b/resources/ews/test/fakeserver/test/ewsfakesrv_test.cpp index 9f04da44b..5fdb930b7 100644 --- a/resources/ews/test/fakeserver/test/ewsfakesrv_test.cpp +++ b/resources/ews/test/fakeserver/test/ewsfakesrv_test.cpp @@ -1,880 +1,878 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 #include #include #include #include #include #include #include #include #include "fakeewsserver.h" #include "fakeewsserverthread.h" class UtEwsFakeSrvTest : public QObject { Q_OBJECT private Q_SLOTS: void emptyDialog(); void invalidURL(); void invalidMethod(); void emptyRequest(); void defaultCallback(); void simpleResponse(); void callbackResponse(); void multipleResponses(); void emptyResponse(); void getEventsRequest(); void getEventsRequest_data(); void getStreamingEventsRequest(); void serverThread(); void delayedContentSize(); void notAuthenticated(); void badAuthentication(); void xqueryResultsInCallback(); private: - QPair synchronousHttpReq(const QString &content, ushort port, - std::function chunkFn = nullptr); + QPair synchronousHttpReq(const QString &content, ushort port, std::function chunkFn = nullptr); }; void UtEwsFakeSrvTest::emptyDialog() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(srv->portNumber())); url.setUserName(QStringLiteral("test")); url.setPassword(QStringLiteral("test")); QNetworkRequest req(url); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml")); QNetworkReply *reply = nam.post(req, "test"); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp]() { resp += QString::fromUtf8(reply->readAll()); }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); QCOMPARE(respCode, static_cast(500)); } void UtEwsFakeSrvTest::invalidURL() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/some/url").arg(srv->portNumber())); url.setUserName(QStringLiteral("test")); url.setPassword(QStringLiteral("test")); QNetworkRequest req(url); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml")); QNetworkReply *reply = nam.post(req, "test"); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp]() { resp += QString::fromUtf8(reply->readAll()); }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); QCOMPARE(respCode, static_cast(500)); } void UtEwsFakeSrvTest::invalidMethod() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(srv->portNumber())); url.setUserName(QStringLiteral("test")); url.setPassword(QStringLiteral("test")); QNetworkRequest req(url); QNetworkReply *reply = nam.get(req); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp]() { resp += QString::fromUtf8(reply->readAll()); }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); QCOMPARE(respCode, static_cast(500)); } void UtEwsFakeSrvTest::emptyRequest() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.second, static_cast(500)); } void UtEwsFakeSrvTest::defaultCallback() { QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); - srv->setDefaultReplyCallback([&receivedReq](const QString & req, QXmlResultItems &, const QXmlNamePool &) { + srv->setDefaultReplyCallback([&receivedReq](const QString &req, QXmlResultItems &, const QXmlNamePool &) { receivedReq = req; return FakeEwsServer::DialogEntry::HttpResponse(QStringLiteral("testresp"), 200); }); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral("testreq"), srv->portNumber()); QCOMPARE(receivedReq, QStringLiteral("testreq")); QCOMPARE(resp.first, QStringLiteral("testresp")); QCOMPARE(resp.second, static_cast(200)); } void UtEwsFakeSrvTest::simpleResponse() { const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then () else ()"), FakeEwsServer::DialogEntry::ReplyCallback(), QStringLiteral("Sample request 1") } }; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); srv->setDialog(dialog); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); } void UtEwsFakeSrvTest::callbackResponse() { const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then () else ()"), [](const QString &, QXmlResultItems &, const QXmlNamePool &) { return FakeEwsServer::DialogEntry::HttpResponse(QStringLiteral(""), 200); }, QStringLiteral("Sample request 1") } }; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); srv->setDialog(dialog); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); } void UtEwsFakeSrvTest::multipleResponses() { const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = or //test1/b = ) then () else ()"), FakeEwsServer::DialogEntry::ReplyCallback(), QStringLiteral("Sample request 1") }, { QStringLiteral("if (//test1/b = or //test1/c = ) then () else ()"), FakeEwsServer::DialogEntry::ReplyCallback(), QStringLiteral("Sample request 2") } }; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); srv->setDialog(dialog); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.second, static_cast(500)); } void UtEwsFakeSrvTest::emptyResponse() { bool callbackCalled = false; const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then () else ()"), [&callbackCalled](const QString &, QXmlResultItems &, const QXmlNamePool &) { callbackCalled = true; return FakeEwsServer::EmptyResponse; }, QStringLiteral("Sample request 1") } }; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); srv->setDialog(dialog); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.second, static_cast(500)); QCOMPARE(callbackCalled, true); } void UtEwsFakeSrvTest::getEventsRequest() { const FakeEwsServer::DialogEntry::List emptyDialog; const QFETCH(QString, request); const QFETCH(QStringList, events); const QFETCH(ushort, responseCode); const QFETCH(QString, response); QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); srv->queueEventsXml(events); auto resp = synchronousHttpReq(request, srv->portNumber()); QCOMPARE(resp.second, responseCode); if (responseCode == 200) { QCOMPARE(resp.first, response); } } void UtEwsFakeSrvTest::getEventsRequest_data() { QTest::addColumn("request"); QTest::addColumn("events"); QTest::addColumn("responseCode"); QTest::addColumn("response"); QTest::newRow("valid request (MSDN)") << QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "" - "" - "") + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "" + "" + "") << (QStringList() - << QStringLiteral("AAAAAM4GAAAAAAAAAQ==" - "2006-08-22T00:36:29Z" - "" - "" - "") - << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "")) + << QStringLiteral("AAAAAM4GAAAAAAAAAQ==" + "2006-08-22T00:36:29Z" + "" + "" + "") + << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "")) << static_cast(200) << QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "false" - "" - "AAAAAM4GAAAAAAAAAQ==" - "2006-08-22T00:36:29Z" - "" - "" - "" - "" - "AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "" - "" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "false" + "" + "AAAAAM4GAAAAAAAAAQ==" + "2006-08-22T00:36:29Z" + "" + "" + "" + "" + "AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "" + "" + "" + "" + "" + "" + ""); QTest::newRow("valid request (namespaced)") << QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "" - "" - "") + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "" + "" + "") << (QStringList() - << QStringLiteral("AAAAAM4GAAAAAAAAAQ==" - "2006-08-22T00:36:29Z" - "" - "" - "") - << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "")) + << QStringLiteral("AAAAAM4GAAAAAAAAAQ==" + "2006-08-22T00:36:29Z" + "" + "" + "") + << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "")) << static_cast(200) << QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "false" - "" - "AAAAAM4GAAAAAAAAAQ==" - "2006-08-22T00:36:29Z" - "" - "" - "" - "" - "AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "" - "" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "false" + "" + "AAAAAM4GAAAAAAAAAQ==" + "2006-08-22T00:36:29Z" + "" + "" + "" + "" + "AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "" + "" + "" + "" + "" + "" + ""); QTest::newRow("invalid request (missing subscription id)") << QStringLiteral("" - "" - "" - "" - "" - "AAAAAMAGAAAAAAAAAQ==" - "" - "" - "") + "" + "" + "" + "" + "AAAAAMAGAAAAAAAAAQ==" + "" + "" + "") << (QStringList() - << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "")) + << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "")) << static_cast(200) << QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "Missing subscription id or watermark." - "ErrorInvalidPullSubscriptionId" - "0" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "Missing subscription id or watermark." + "ErrorInvalidPullSubscriptionId" + "0" + "" + "" + "" + "" + ""); QTest::newRow("invalid request (missing watermark)") << QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "" - "" - "" - "") + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "" + "" + "" + "") << (QStringList() - << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" - "2006-08-22T01:00:50Z" - "" - "" - "")) + << QStringLiteral("AAAAAOQGAAAAAAAAAQ==" + "2006-08-22T01:00:50Z" + "" + "" + "")) << static_cast(200) << QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "Missing subscription id or watermark." - "ErrorInvalidPullSubscriptionId" - "0" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "Missing subscription id or watermark." + "ErrorInvalidPullSubscriptionId" + "0" + "" + "" + "" + "" + ""); QTest::newRow("valid request (no events)") << QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "" - "" - "") + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "" + "" + "") << QStringList() << static_cast(200) << QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "AAAAAMAGAAAAAAAAAQ==" - "false" - "" - "" - "" - "" - "" - ""); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "AAAAAMAGAAAAAAAAAQ==" + "false" + "" + "" + "" + "" + "" + ""); QTest::newRow("invalid request (not a GetEvents request)") << QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "30" - "" - "" - "") + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "30" + "" + "" + "") << QStringList() << static_cast(500) << QString(); } void UtEwsFakeSrvTest::getStreamingEventsRequest() { bool callbackCalled = false; const FakeEwsServer::DialogEntry::List emptyDialog; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QDateTime startTime = QDateTime::currentDateTime(); const QString event = QStringLiteral("" - "2006-08-22T01:00:10Z" - "" - "" - ""); + "2006-08-22T01:00:10Z" + "" + "" + ""); const QString content = QStringLiteral("" - "" - "" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d" - "1" - "" - "" - ""); + "" + "" + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d" + "1" + "" + "" + ""); srv->queueEventsXml(QStringList() << event); bool unknownRequestEncountered = false; srv->setDefaultReplyCallback([&](const QString &req, QXmlResultItems &, const QXmlNamePool &) { qDebug() << "Unknown EWS request encountered." << req; unknownRequestEncountered = true; return FakeEwsServer::EmptyResponse; }); bool callbackError = false; int responseNo = 0; QDateTime pushEventTime; auto resp = synchronousHttpReq(content, srv->portNumber(), [&](const QString &chunk) { const QString respHead = QStringLiteral("" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "OK"); + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "OK"); const QString respTail = QStringLiteral("" - "" - "" - "" - ""); + "" + "" + "" + ""); const QString eventHead = QStringLiteral("" - "" - "f6bc657d-dde1-4f94-952d-143b95d6483d"); + "" + "f6bc657d-dde1-4f94-952d-143b95d6483d"); const QString eventTail = QStringLiteral(""); const QString event2 = QStringLiteral("" - "2006-08-22T01:00:50Z" - "" - "" - ""); + "2006-08-22T01:00:50Z" + "" + "" + ""); callbackCalled = true; QString expResp = respHead; if (responseNo == 0) { expResp += eventHead + event + eventTail; } else if (responseNo == 2) { expResp += eventHead + event2 + eventTail; } expResp += respTail; if (chunk != expResp) { qWarning() << "Unexpected GetStreamingEventsResponse"; callbackError = true; return false; } if (responseNo == 1) { srv->queueEventsXml(QStringList() << event2); pushEventTime = QDateTime::currentDateTime(); } else if (responseNo == 2) { /* Check if the response to the above event came "immediately" */ QDateTime now = QDateTime::currentDateTime(); if (pushEventTime.msecsTo(now) > 200) { qWarning() << "Push event maximum roundtrip time exceeded"; callbackError = true; return false; } } responseNo++; return true; }); QCOMPARE(callbackCalled, true); QCOMPARE(resp.second, static_cast(200)); QCOMPARE(callbackError, false); QCOMPARE(unknownRequestEncountered, false); QDateTime now = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(now); qDebug() << elapsed; QVERIFY(elapsed >= 1 * 60 * 1000 - 600); QVERIFY(elapsed <= 1 * 60 * 1000 + 600); } void UtEwsFakeSrvTest::serverThread() { const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then () else ()"), FakeEwsServer::DialogEntry::ReplyCallback(), QStringLiteral("Sample request 1") } }; QString receivedReq; FakeEwsServerThread thread; thread.start(); QVERIFY(thread.waitServerStarted()); thread.setDialog(dialog); auto resp = synchronousHttpReq(QStringLiteral(""), thread.portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); thread.exit(); thread.wait(); } void UtEwsFakeSrvTest::delayedContentSize() { /* This test case simulates the behaviour of KIO HTTP, which sends the data in three chunks: * - initial headers * - Content-Length header + end of headers * - content */ const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then () else ()"), FakeEwsServer::DialogEntry::ReplyCallback(), QStringLiteral("Sample request 1") } }; QString receivedReq; FakeEwsServerThread thread; thread.start(); QVERIFY(thread.waitServerStarted()); thread.setDialog(dialog); QTcpSocket sock; sock.connectToHost(QHostAddress(QHostAddress::LocalHost), thread.portNumber()); QVERIFY(sock.waitForConnected(1000)); sock.write(QStringLiteral("POST /EWS/Exchange.asmx HTTP/1.1\r\n" - "Host: 127.0.0.1:%1\r\n" - "Connection: keep-alive\r\n" - "User-Agent: Mozilla/5.0 (X11; Linux x86_64) KHTML/5.26.0 (like Gecko) Konqueror/5.26\r\n" - "Pragma: no-cache\r\n" - "Cache-control: no-cache\r\n" - "Accept: text/html, text/*;q=0.9, image/jpeg;q=0.9, image/png;q=0.9, image/*;q=0.9, */*;q=0.8\r\n" - "Accept-Charset: utf-8,*;q=0.5\r\n" - "Accept-Language: pl-PL,en;q=0.9\r\n" - "Authorization: Basic dGVzdDp0ZXN0\r\n" - "Content-Type: text/xml\r\n").arg(thread.portNumber()).toLatin1()); + "Host: 127.0.0.1:%1\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) KHTML/5.26.0 (like Gecko) Konqueror/5.26\r\n" + "Pragma: no-cache\r\n" + "Cache-control: no-cache\r\n" + "Accept: text/html, text/*;q=0.9, image/jpeg;q=0.9, image/png;q=0.9, image/*;q=0.9, */*;q=0.8\r\n" + "Accept-Charset: utf-8,*;q=0.5\r\n" + "Accept-Language: pl-PL,en;q=0.9\r\n" + "Authorization: Basic dGVzdDp0ZXN0\r\n" + "Content-Type: text/xml\r\n").arg(thread.portNumber()).toLatin1()); sock.waitForBytesWritten(100); QThread::msleep(100); sock.write("Content-Length: 20\r\n\r\n"); sock.waitForBytesWritten(100); QThread::msleep(100); sock.write(""); sock.waitForBytesWritten(100); sock.waitForReadyRead(300); QByteArray data = sock.readAll(); thread.exit(); thread.wait(); QCOMPARE(data, QByteArray("HTTP/1.1 200 OK\r\nContent-Length: 4\r\nConnection: Keep-Alive\n\r\n")); } void UtEwsFakeSrvTest::notAuthenticated() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(srv->portNumber())); QNetworkRequest req(url); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml")); QNetworkReply *reply = nam.post(req, "test"); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp]() { resp += QString::fromUtf8(reply->readAll()); }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); QCOMPARE(respCode, static_cast(401)); } void UtEwsFakeSrvTest::badAuthentication() { QScopedPointer srv(new FakeEwsServer(this)); QVERIFY(srv->start()); QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(srv->portNumber())); url.setUserName(QStringLiteral("foo")); url.setPassword(QStringLiteral("bar")); QNetworkRequest req(url); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml")); QNetworkReply *reply = nam.post(req, "test"); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp]() { resp += QString::fromUtf8(reply->readAll()); }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); QCOMPARE(respCode, static_cast(401)); } void UtEwsFakeSrvTest::xqueryResultsInCallback() { bool callbackOk = false; const FakeEwsServer::DialogEntry::List dialog = { { QStringLiteral("if (//test1/a = ) then (test) else ()"), - [&callbackOk](const QString &, QXmlResultItems & ri, const QXmlNamePool &) + [&callbackOk](const QString &, QXmlResultItems &ri, const QXmlNamePool &) { if (ri.hasError()) { qDebug() << "XQuery result has errors."; return FakeEwsServer::EmptyResponse; } QXmlItem item = ri.next(); if (item.isNull()) { qDebug() << "XQuery result has no items."; return FakeEwsServer::EmptyResponse; } if (!item.isNode()) { qDebug() << "XQuery result is not a XML node."; return FakeEwsServer::EmptyResponse; } QXmlNodeModelIndex index = item.toNodeModelIndex(); if (index.isNull()) { qDebug() << "XQuery XML node is NULL."; return FakeEwsServer::EmptyResponse; } if (index.model()->stringValue(index) != QStringLiteral("test")) { qDebug() << "Unexpected item value:" << index.model()->stringValue(index); return FakeEwsServer::EmptyResponse; } return FakeEwsServer::DialogEntry::HttpResponse(QStringLiteral(""), 200); }, QStringLiteral("Sample request 1") } }; QString receivedReq; QScopedPointer srv(new FakeEwsServer(this)); srv->setDialog(dialog); QVERIFY(srv->start()); auto resp = synchronousHttpReq(QStringLiteral(""), srv->portNumber()); QCOMPARE(resp.first, QStringLiteral("")); QCOMPARE(resp.second, static_cast(200)); } -QPair UtEwsFakeSrvTest::synchronousHttpReq(const QString &content, ushort port, - std::function chunkFn) +QPair UtEwsFakeSrvTest::synchronousHttpReq(const QString &content, ushort port, std::function chunkFn) { QNetworkAccessManager nam(this); QUrl url(QStringLiteral("http://127.0.0.1:%1/EWS/Exchange.asmx").arg(port)); url.setUserName(QStringLiteral("test")); url.setPassword(QStringLiteral("test")); QNetworkRequest req(url); req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/xml")); QNetworkReply *reply = nam.post(req, content.toUtf8()); QEventLoop loop; QString resp; ushort respCode = 0; connect(reply, &QNetworkReply::readyRead, this, [reply, &resp, &chunkFn, &loop]() { QString chunk = QString::fromUtf8(reply->readAll()); if (chunkFn) { bool cont = chunkFn(chunk); if (!cont) { reply->close(); loop.exit(200); return; } } else { resp += chunk; } }); connect(reply, &QNetworkReply::finished, this, [&loop, reply]() { loop.exit(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); }); respCode = loop.exec(); return {resp, respCode}; } QTEST_MAIN(UtEwsFakeSrvTest) #include "ewsfakesrv_test.moc" diff --git a/resources/ews/test/isolatedtestbase.cpp b/resources/ews/test/isolatedtestbase.cpp index 451f7804e..60bcd17f3 100644 --- a/resources/ews/test/isolatedtestbase.cpp +++ b/resources/ews/test/isolatedtestbase.cpp @@ -1,358 +1,350 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 "isolatedtestbase.h" #include #include #include #include #include "ewssettings.h" #include "ewswallet.h" #include "fakeewsserverthread.h" using namespace Akonadi; constexpr int OnlineStateChangeTimeoutMs = 20000; IsolatedTestBase::IsolatedTestBase(QObject *parent) - : QObject(parent), mFakeServerThread(new FakeEwsServerThread(this)) + : QObject(parent) + , mFakeServerThread(new FakeEwsServerThread(this)) { qsrand(QDateTime::currentDateTimeUtc().toTime_t()); } IsolatedTestBase::~IsolatedTestBase() { } void IsolatedTestBase::init() { QVERIFY(Control::start()); /* Switch all resources offline to reduce interference from them */ for (AgentInstance agent : AgentManager::self()->instances()) { agent.setIsOnline(false); } connect(AgentManager::self(), &AgentManager::instanceAdded, this, [](const Akonadi::AgentInstance &instance) { qDebug() << "AgentInstanceAdded" << instance.identifier(); }); mFakeServerThread->start(); QVERIFY(mFakeServerThread->waitServerStarted()); } void IsolatedTestBase::cleanup() { mFakeServerThread->exit(); mFakeServerThread->wait(); } QString IsolatedTestBase::loadResourceAsString(const QString &path) { QFile f(path); if (f.open(QIODevice::ReadOnly)) { return QString::fromUtf8(f.readAll()); } qWarning() << "Resource" << path << "not found"; return QString(); } -template -QDBusReply dBusSetAndWaitReply(std::function()> setFunc, std::function()> getFunc, - const QString &name) +template +QDBusReply dBusSetAndWaitReply(std::function()> setFunc, std::function()> getFunc, const QString &name) { QDBusReply reply; int retryCnt = 4; do { setFunc(); reply = getFunc(); if (!reply.isValid()) { qDebug() << "Failed to set DBus config option" << name << reply.error().message(); QThread::msleep(250); } } while (!reply.isValid() && retryCnt-- > 0); return reply; } TestAgentInstance::TestAgentInstance(const QString &url) { AgentType ewsType = AgentManager::self()->type(QStringLiteral("akonadi_ews_resource")); AgentInstanceCreateJob *agentCreateJob = new AgentInstanceCreateJob(ewsType); QVERIFY(agentCreateJob->exec()); mEwsInstance.reset(new AgentInstance(agentCreateJob->instance())); mIdentifier = mEwsInstance->identifier(); const QString akonadiInstanceIdentifier = QProcessEnvironment::systemEnvironment().value(QStringLiteral("AKONADI_INSTANCE")); mEwsSettingsInterface.reset(new OrgKdeAkonadiEwsSettingsInterface( QStringLiteral("org.freedesktop.Akonadi.Resource.") + mIdentifier + QLatin1Char('.') + akonadiInstanceIdentifier, QStringLiteral("/Settings"), QDBusConnection::sessionBus(), this)); QVERIFY(mEwsSettingsInterface->isValid()); mEwsWalletInterface.reset(new OrgKdeAkonadiEwsWalletInterface( QStringLiteral("org.freedesktop.Akonadi.Resource.") + mIdentifier + QLatin1Char('.') + akonadiInstanceIdentifier, QStringLiteral("/Settings"), QDBusConnection::sessionBus(), this)); QVERIFY(mEwsWalletInterface->isValid()); /* The EWS resource initializes its DBus adapters asynchronously. Therefore it can happen that * due to a race access is attempted prior to their initialization. To fix this retry the DBus * communication a few times before declaring failure. */ const auto baseUrlReply = dBusSetAndWaitReply( std::bind(&OrgKdeAkonadiEwsSettingsInterface::setBaseUrl, mEwsSettingsInterface.data(), url), std::bind(&OrgKdeAkonadiEwsSettingsInterface::baseUrl, mEwsSettingsInterface.data()), QStringLiteral("Base URL")); QVERIFY(baseUrlReply.isValid()); QVERIFY(baseUrlReply.value() == url); const auto hasDomainReply = dBusSetAndWaitReply( std::bind(&OrgKdeAkonadiEwsSettingsInterface::setHasDomain, mEwsSettingsInterface.data(), false), std::bind(&OrgKdeAkonadiEwsSettingsInterface::hasDomain, mEwsSettingsInterface.data()), QStringLiteral("has domain")); QVERIFY(hasDomainReply.isValid()); QVERIFY(hasDomainReply.value() == false); const auto username = QStringLiteral("test"); const auto usernameReply = dBusSetAndWaitReply( std::bind(&OrgKdeAkonadiEwsSettingsInterface::setUsername, mEwsSettingsInterface.data(), username), std::bind(&OrgKdeAkonadiEwsSettingsInterface::username, mEwsSettingsInterface.data()), QStringLiteral("Username")); QVERIFY(usernameReply.isValid()); QVERIFY(usernameReply.value() == username); mEwsWalletInterface->setTestPassword(QStringLiteral("test")); AgentManager::self()->instance(mIdentifier).reconfigure(); } TestAgentInstance::~TestAgentInstance() { if (mEwsInstance) { AgentManager::self()->removeInstance(*mEwsInstance); } } bool TestAgentInstance::isValid() const { return mEwsInstance && mEwsSettingsInterface && mEwsWalletInterface && !mIdentifier.isEmpty(); } const QString &TestAgentInstance::identifier() const { return mIdentifier; } bool TestAgentInstance::setOnline(bool online, bool wait) { if (wait) { QEventLoop loop; auto conn = connect(AgentManager::self(), &AgentManager::instanceOnline, this, - [&](const AgentInstance &instance, bool state) { + [&](const AgentInstance &instance, bool state) { if (instance == *mEwsInstance && state == online) { qDebug() << "is online" << state; loop.exit(0); } }, Qt::QueuedConnection); QTimer timer; timer.setSingleShot(true); connect(&timer, &QTimer::timeout, this, [&]() { qWarning() << "Timeout waiting for desired resource online state."; loop.exit(1); }); timer.start(OnlineStateChangeTimeoutMs); mEwsInstance->setIsOnline(online); bool status = (loop.exec() == 0); disconnect(conn); return status; } else { mEwsInstance->setIsOnline(online); return true; } } - -MsgRootInboxDialogEntry::MsgRootInboxDialogEntry(const QString &rootId, const QString &inboxId, - const QString &descr, const ReplyCallback &callback) +MsgRootInboxDialogEntry::MsgRootInboxDialogEntry(const QString &rootId, const QString &inboxId, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/getfolder-inbox-msgroot")) .arg(rootId).arg(inboxId); description = QStringLiteral("GetFolder request for inbox and msgroot"); } -SubscribedFoldersDialogEntry::SubscribedFoldersDialogEntry(const IsolatedTestBase::FolderList &list, - const QString &descr, const ReplyCallback &callback) +SubscribedFoldersDialogEntry::SubscribedFoldersDialogEntry(const IsolatedTestBase::FolderList &list, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { static const QVector specialFolders = { IsolatedTestBase::Folder::Inbox, IsolatedTestBase::Folder::Calendar, IsolatedTestBase::Folder::Tasks, IsolatedTestBase::Folder::Contacts }; - QHash folderHash; + QHash folderHash; for (const auto &folder : list) { if (specialFolders.contains(folder.type)) { folderHash.insert(folder.type, &folder); } } QString xml; for (auto special : specialFolders) { const IsolatedTestBase::Folder *folder = folderHash[special]; if (QTest::qVerify(folder != nullptr, "folder != nullptr", "", __FILE__, __LINE__)) { xml += QStringLiteral(""); xml += QStringLiteral("NoError"); xml += QStringLiteral(""); xml += QStringLiteral("").arg(folder->id); xml += QStringLiteral(""); xml += QStringLiteral(""); } } xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/getfolder-subscribedfolders")).arg(xml); } -SpecialFoldersDialogEntry::SpecialFoldersDialogEntry(const IsolatedTestBase::FolderList &list, - const QString &descr, const ReplyCallback &callback) +SpecialFoldersDialogEntry::SpecialFoldersDialogEntry(const IsolatedTestBase::FolderList &list, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { static const QVector specialFolders = { IsolatedTestBase::Folder::Inbox, IsolatedTestBase::Folder::Outbox, IsolatedTestBase::Folder::Sent, IsolatedTestBase::Folder::Trash, IsolatedTestBase::Folder::Drafts }; - QHash folderHash; + QHash folderHash; for (const auto &folder : list) { if (specialFolders.contains(folder.type)) { folderHash.insert(folder.type, &folder); } } QString xml; for (auto special : specialFolders) { const IsolatedTestBase::Folder *folder = folderHash[special]; if (QTest::qVerify(folder != nullptr, "folder != nullptr", "", __FILE__, __LINE__)) { xml += QStringLiteral(""); xml += QStringLiteral("NoError"); xml += QStringLiteral(""); xml += QStringLiteral("").arg(folder->id); xml += QStringLiteral(""); xml += QStringLiteral(""); } } xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/getfolder-specialfolders")).arg(xml); } -GetTagsEmptyDialogEntry::GetTagsEmptyDialogEntry(const QString &rootId, const QString &descr, - const ReplyCallback &callback) +GetTagsEmptyDialogEntry::GetTagsEmptyDialogEntry(const QString &rootId, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/getfolder-tags")).arg(rootId); } SubscribeStreamingDialogEntry::SubscribeStreamingDialogEntry(const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/subscribe-streaming")); } -SyncFolderHierInitialDialogEntry::SyncFolderHierInitialDialogEntry(const IsolatedTestBase::FolderList &list, - const QString &syncState, const QString &descr, const ReplyCallback &callback) +SyncFolderHierInitialDialogEntry::SyncFolderHierInitialDialogEntry(const IsolatedTestBase::FolderList &list, const QString &syncState, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { QHash childCount; for (const auto &folder : list) { ++childCount[folder.parentId]; } QString xml; for (const auto &folder : list) { if (folder.type == IsolatedTestBase::Folder::Root) { continue; } xml += QStringLiteral(""); xml += QStringLiteral(""); xml += QStringLiteral("").arg(folder.id); xml += QStringLiteral("").arg(folder.parentId); QString folderClass; QString extraXml; switch (folder.type) { case IsolatedTestBase::Folder::Calendar: folderClass = QStringLiteral("IPF.Calendar"); break; case IsolatedTestBase::Folder::Contacts: folderClass = QStringLiteral("IPF.Contacts"); break; case IsolatedTestBase::Folder::Tasks: folderClass = QStringLiteral("IPF.Tasks"); break; default: folderClass = QStringLiteral("IPF.Note"); extraXml = QStringLiteral("0"); } xml += QStringLiteral("%1").arg(folderClass); xml += QStringLiteral("0"); xml += QStringLiteral("%1").arg(folder.name); xml += QStringLiteral("%1").arg(childCount[folder.id]); xml += extraXml; xml += QStringLiteral(""); xml += QStringLiteral(""); - } xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/syncfolderhierarhy-emptystate")) .arg(syncState).arg(xml); } UnsubscribeDialogEntry::UnsubscribeDialogEntry(const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/unsubscribe")); } -ValidateFolderIdsDialogEntry::ValidateFolderIdsDialogEntry(const QStringList &ids, const QString &descr, - const ReplyCallback &callback) +ValidateFolderIdsDialogEntry::ValidateFolderIdsDialogEntry(const QStringList &ids, const QString &descr, const ReplyCallback &callback) : DialogEntryBase(descr, callback) { QStringList xQueryFolderIds; QString responseXml; int folderIndex = 0; for (auto folderId : ids) { xQueryFolderIds.append(QStringLiteral("//m:GetFolder/m:FolderIds/t:FolderId[position()=%1 and @Id=\"%2\"]") .arg(++folderIndex).arg(folderId)); responseXml += QStringLiteral(""); responseXml += QStringLiteral("NoError"); responseXml += QStringLiteral(""); responseXml += QStringLiteral("").arg(folderId); responseXml += QStringLiteral(""); responseXml += QStringLiteral(""); } xQuery = IsolatedTestBase::loadResourceAsString(QStringLiteral(":/xquery/getfolder-validateids")) - .arg(folderIndex).arg(xQueryFolderIds.join(QStringLiteral(" and "))).arg(responseXml); + .arg(folderIndex).arg(xQueryFolderIds.join(QStringLiteral(" and "))).arg(responseXml); } diff --git a/resources/ews/test/isolatedtestbase.h b/resources/ews/test/isolatedtestbase.h index 4e4936390..2bf709b55 100644 --- a/resources/ews/test/isolatedtestbase.h +++ b/resources/ews/test/isolatedtestbase.h @@ -1,168 +1,154 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 ISOLATEDTESTBASE_H #define ISOLATEDTESTBASE_H #include #include #include #include "fakeewsserver.h" -namespace Akonadi -{ +namespace Akonadi { class AgentInstance; } class FakeEwsServerThread; class OrgKdeAkonadiEwsSettingsInterface; class OrgKdeAkonadiEwsWalletInterface; class IsolatedTestBase : public QObject { Q_OBJECT public: class Folder { public: enum DistinguishedType { None, Root, Inbox, Outbox, Sent, Trash, Drafts, Templates, Calendar, Tasks, Contacts }; QString id; QString name; DistinguishedType type; QString parentId; }; typedef QVector FolderList; explicit IsolatedTestBase(QObject *parent = nullptr); ~IsolatedTestBase() override; static QString loadResourceAsString(const QString &path); protected: virtual void init(); virtual void cleanup(); protected: QScopedPointer mFakeServerThread; }; class TestAgentInstance : public QObject { Q_OBJECT public: TestAgentInstance(const QString &url); ~TestAgentInstance(); const QString &identifier() const; bool setOnline(bool online, bool wait); bool isValid() const; private: QScopedPointer mEwsInstance; QScopedPointer mEwsSettingsInterface; QScopedPointer mEwsWalletInterface; QString mIdentifier; }; class DialogEntryBase : public FakeEwsServer::DialogEntry { public: explicit DialogEntryBase(const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()) { replyCallback = callback; description = descr; } }; class MsgRootInboxDialogEntry : public DialogEntryBase { public: - explicit MsgRootInboxDialogEntry(const QString &rootId, const QString &inboxId, - const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit MsgRootInboxDialogEntry(const QString &rootId, const QString &inboxId, const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class SubscribedFoldersDialogEntry : public DialogEntryBase { public: - explicit SubscribedFoldersDialogEntry(const IsolatedTestBase::FolderList &folders, - const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit SubscribedFoldersDialogEntry(const IsolatedTestBase::FolderList &folders, const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class SpecialFoldersDialogEntry : public DialogEntryBase { public: - explicit SpecialFoldersDialogEntry(const IsolatedTestBase::FolderList &folders, - const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit SpecialFoldersDialogEntry(const IsolatedTestBase::FolderList &folders, const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class GetTagsEmptyDialogEntry : public DialogEntryBase { public: - explicit GetTagsEmptyDialogEntry(const QString &rootId, const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit GetTagsEmptyDialogEntry(const QString &rootId, const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class SubscribeStreamingDialogEntry : public DialogEntryBase { public: - explicit SubscribeStreamingDialogEntry(const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit SubscribeStreamingDialogEntry(const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class SyncFolderHierInitialDialogEntry : public DialogEntryBase { public: - explicit SyncFolderHierInitialDialogEntry(const IsolatedTestBase::FolderList &folders, - const QString &syncState, const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit SyncFolderHierInitialDialogEntry(const IsolatedTestBase::FolderList &folders, const QString &syncState, const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class UnsubscribeDialogEntry : public DialogEntryBase { public: - explicit UnsubscribeDialogEntry(const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit UnsubscribeDialogEntry(const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; class ValidateFolderIdsDialogEntry : public DialogEntryBase { public: - explicit ValidateFolderIdsDialogEntry(const QStringList &ids = QStringList(), - const QString &descr = QString(), - const ReplyCallback &callback = ReplyCallback()); + explicit ValidateFolderIdsDialogEntry(const QStringList &ids = QStringList(), const QString &descr = QString(), const ReplyCallback &callback = ReplyCallback()); }; #endif diff --git a/resources/ews/test/statemonitor.cpp b/resources/ews/test/statemonitor.cpp index b1d05bed8..ac1645972 100644 --- a/resources/ews/test/statemonitor.cpp +++ b/resources/ews/test/statemonitor.cpp @@ -1,21 +1,20 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 "statemonitor.h" - diff --git a/resources/ews/test/statemonitor.h b/resources/ews/test/statemonitor.h index a475780a3..aaa9f887b 100644 --- a/resources/ews/test/statemonitor.h +++ b/resources/ews/test/statemonitor.h @@ -1,123 +1,129 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 TEST_STATEMONITOR_H #define TEST_STATEMONITOR_H #include #include #include #include #include #include class StateMonitorBase : public QObject { Q_OBJECT public: - explicit StateMonitorBase(QObject *parent) : QObject(parent) {} + explicit StateMonitorBase(QObject *parent) : QObject(parent) + { + } + ~StateMonitorBase() override = default; Q_SIGNALS: void stateReached(); void errorOccurred(); }; -template class CollectionStateMonitor : public StateMonitorBase +template class CollectionStateMonitor : public StateMonitorBase { public: - typedef std::function StateComparisonFunc; - CollectionStateMonitor(QObject *parent, const QHash &stateHash, - const QString &inboxId, const StateComparisonFunc &comparisonFunc, int recheckInterval = 0); + typedef std::function StateComparisonFunc; + CollectionStateMonitor(QObject *parent, const QHash &stateHash, const QString &inboxId, const StateComparisonFunc &comparisonFunc, int recheckInterval = 0); ~CollectionStateMonitor() override = default; Akonadi::Monitor &monitor() { return mMonitor; } void forceRecheck(); private: void stateChanged(const Akonadi::Collection &col); Akonadi::Monitor mMonitor; QSet mPending; const QHash &mStateHash; StateComparisonFunc mComparisonFunc; const QString &mInboxId; QTimer mRecheckTimer; }; -template -CollectionStateMonitor::CollectionStateMonitor(QObject *parent, const QHash &stateHash, - const QString &inboxId, const StateComparisonFunc &comparisonFunc, int recheckInterval) - : StateMonitorBase(parent), mMonitor(this), mPending(stateHash.keys().toSet()), mStateHash(stateHash), - mComparisonFunc(comparisonFunc), mInboxId(inboxId), mRecheckTimer(this) +template +CollectionStateMonitor::CollectionStateMonitor(QObject *parent, const QHash &stateHash, const QString &inboxId, const StateComparisonFunc &comparisonFunc, int recheckInterval) + : StateMonitorBase(parent) + , mMonitor(this) + , mPending(stateHash.keys().toSet()) + , mStateHash(stateHash) + , mComparisonFunc(comparisonFunc) + , mInboxId(inboxId) + , mRecheckTimer(this) { connect(&mMonitor, &Akonadi::Monitor::collectionAdded, this, - [this](const Akonadi::Collection & col, const Akonadi::Collection &) { + [this](const Akonadi::Collection &col, const Akonadi::Collection &) { stateChanged(col); }); - connect(&mMonitor, QOverload::of(&Akonadi::Monitor::collectionChanged), this, - [this](const Akonadi::Collection & col) { + connect(&mMonitor, QOverload::of(&Akonadi::Monitor::collectionChanged), this, + [this](const Akonadi::Collection &col) { stateChanged(col); }); if (recheckInterval > 0) { mRecheckTimer.setInterval(recheckInterval); connect(&mRecheckTimer, &QTimer::timeout, this, &CollectionStateMonitor::forceRecheck); mRecheckTimer.start(); } } -template +template void CollectionStateMonitor::stateChanged(const Akonadi::Collection &col) { auto remoteId = col.remoteId(); auto state = mStateHash.find(remoteId); if (state == mStateHash.end()) { qDebug() << "Cannot find state for collection" << remoteId; Q_EMIT errorOccurred(); } if (mComparisonFunc(col, *state)) { mPending.remove(remoteId); } else { mPending.insert(remoteId); } if (mPending.empty()) { Q_EMIT stateReached(); } } -template +template void CollectionStateMonitor::forceRecheck() { auto fetchJob = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this); fetchJob->setFetchScope(mMonitor.collectionFetchScope()); if (fetchJob->exec()) { for (const auto &col : fetchJob->collections()) { const auto remoteId = col.remoteId(); const auto state = mStateHash.find(remoteId); if (state != mStateHash.end()) { stateChanged(col); } } } } #endif diff --git a/resources/ews/test/unittests/ewsattachment_ut.cpp b/resources/ews/test/unittests/ewsattachment_ut.cpp index 842088fe7..45c675de4 100644 --- a/resources/ews/test/unittests/ewsattachment_ut.cpp +++ b/resources/ews/test/unittests/ewsattachment_ut.cpp @@ -1,948 +1,946 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 #include #include "ewsattachment.h" #include "fakehttppost.h" class UtEwsAttachment : public QObject { Q_OBJECT private Q_SLOTS: void read(); void read_data(); void write(); void write_data(); }; Q_DECLARE_METATYPE(EwsAttachment::Type) static const QString xmlMsgNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/services/2006/message"); static const QString xmlTypeNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/services/2006/types"); static const QString xmlHead = QStringLiteral(""); static const QString xmlDocHead = xmlHead + QStringLiteral(""); static const QString xmlDocTail = QStringLiteral(""); static const QString xmlItemAttHead = xmlDocHead + QStringLiteral(""); static const QString xmlItemAttTail = QStringLiteral("") + xmlDocTail; static const QString xmlFileAttHead = xmlDocHead + QStringLiteral(""); static const QString xmlFileAttTail = QStringLiteral("") + xmlDocTail; void UtEwsAttachment::read() { QFETCH(QString, xml); QFETCH(bool, isValid); QXmlStreamReader reader(xml); reader.readNextStartElement(); reader.readNextStartElement(); EwsAttachment att(reader); QCOMPARE(att.isValid(), isValid); if (!isValid) { return; } QFETCH(EwsAttachment::Type, type); QCOMPARE(att.type(), type); QFETCH(bool, hasId); QFETCH(QString, id); QCOMPARE(att.hasId(), hasId); if (hasId) { QCOMPARE(att.id(), id); } QFETCH(bool, hasName); QFETCH(QString, name); QCOMPARE(att.hasName(), hasName); if (hasName) { QCOMPARE(att.name(), name); } QFETCH(bool, hasContentType); QFETCH(QString, contentType); QCOMPARE(att.hasContentType(), hasContentType); if (hasContentType) { QCOMPARE(att.contentType(), contentType); } QFETCH(bool, hasContentId); QFETCH(QString, contentId); QCOMPARE(att.hasContentId(), hasContentId); if (hasContentId) { QCOMPARE(att.contentId(), contentId); } QFETCH(bool, hasContentLocation); QFETCH(QString, contentLocation); QCOMPARE(att.hasContentLocation(), hasContentLocation); if (hasContentLocation) { QCOMPARE(att.contentLocation(), contentLocation); } QFETCH(bool, hasSize); QFETCH(long, size); QCOMPARE(att.hasSize(), hasSize); if (hasSize) { QCOMPARE(att.size(), size); } QFETCH(bool, hasLastModifiedTime); QFETCH(QDateTime, lastModifiedTime); QCOMPARE(att.hasLastModifiedTime(), hasLastModifiedTime); if (hasLastModifiedTime) { QCOMPARE(att.lastModifiedTime(), lastModifiedTime); } QFETCH(bool, hasIsInline); QFETCH(bool, isInline); QCOMPARE(att.hasIsInline(), hasIsInline); if (hasIsInline) { QCOMPARE(att.isInline(), isInline); } QFETCH(bool, hasIsContactPhoto); QFETCH(bool, isContactPhoto); QCOMPARE(att.hasIsContactPhoto(), hasIsContactPhoto); if (hasIsContactPhoto) { QCOMPARE(att.isContactPhoto(), isContactPhoto); } QFETCH(bool, hasContent); QFETCH(QByteArray, content); QCOMPARE(att.hasContent(), hasContent); if (hasContent) { QCOMPARE(att.content(), content); } QFETCH(bool, hasItem); QFETCH(EwsItem, item); QCOMPARE(att.hasItem(), hasItem); if (hasItem) { QCOMPARE(att.item(), item); } reader.skipCurrentElement(); reader.readNextStartElement(); QCOMPARE(reader.name().toString(), QStringLiteral("Test2")); } void UtEwsAttachment::read_data() { QTest::addColumn("xml"); QTest::addColumn("isValid"); QTest::addColumn("type"); QTest::addColumn("hasId"); QTest::addColumn("id"); QTest::addColumn("hasName"); QTest::addColumn("name"); QTest::addColumn("hasContentType"); QTest::addColumn("contentType"); QTest::addColumn("hasContentId"); QTest::addColumn("contentId"); QTest::addColumn("hasContentLocation"); QTest::addColumn("contentLocation"); QTest::addColumn("hasSize"); QTest::addColumn("size"); QTest::addColumn("hasLastModifiedTime"); QTest::addColumn("lastModifiedTime"); QTest::addColumn("hasIsInline"); QTest::addColumn("isInline"); QTest::addColumn("hasIsContactPhoto"); QTest::addColumn("isContactPhoto"); QTest::addColumn("hasContent"); QTest::addColumn("content"); QTest::addColumn("hasItem"); QTest::addColumn("item"); QTest::newRow("invalid namespace") << xmlDocHead + QStringLiteral("") + xmlDocTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid type") << xmlDocHead + QStringLiteral("") + xmlDocTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty file attachment") << xmlDocHead + QStringLiteral("") + xmlDocTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty item attachment") << xmlDocHead + QStringLiteral("") + xmlDocTail << true << EwsAttachment::ItemAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty reference attachment") << xmlDocHead + QStringLiteral("") + xmlDocTail << true << EwsAttachment::ReferenceAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid attachment id - bad namespace") << xmlFileAttHead + QStringLiteral("") + xmlFileAttTail + + QStringLiteral("\" Id=\"JCPhyc4Kg73tIuurR3c0Pw==\" />") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid attachment id - bad attribute") << xmlFileAttHead + QStringLiteral("") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid attachment id") << xmlFileAttHead + QStringLiteral("") + xmlFileAttTail << true << EwsAttachment::FileAttachment << true << QStringLiteral("JCPhyc4Kg73tIuurR3c0Pw==") << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid name") << xmlFileAttHead + QStringLiteral("Test name") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid name") << xmlFileAttHead + QStringLiteral("Test name") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << true << QStringLiteral("Test name") << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid content type") << xmlFileAttHead + QStringLiteral("application/x-test") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << true << QStringLiteral("application/x-test") << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid content id") << xmlFileAttHead + QStringLiteral("FE938BD618330B9DA0C965A6077BB3FF20415531@1") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << true << QStringLiteral("FE938BD618330B9DA0C965A6077BB3FF20415531@1") << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid content location") << xmlFileAttHead + QStringLiteral("file:///foo/bar.txt") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << true << QStringLiteral("file:///foo/bar.txt") << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid size - not a number") << xmlFileAttHead + QStringLiteral("foo") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid size") << xmlFileAttHead + QStringLiteral("123") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << true << 123l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid last modified time - bad time") << xmlFileAttHead + QStringLiteral("2017-01-03T09:74:39") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid last modified time - nested XML element") << xmlFileAttHead + QStringLiteral("2017-01-03T09:74:39") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid last modified time") << xmlFileAttHead + QStringLiteral("2017-01-03T08:24:39Z") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << true << QDateTime::fromSecsSinceEpoch(1483431879, QTimeZone::utc()) << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid is inline - bad value") << xmlFileAttHead + QStringLiteral("fake") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid is inline") << xmlFileAttHead + QStringLiteral("true") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << true << true << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid is contact photo - bad value") << xmlFileAttHead + QStringLiteral("fake") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid is contact photo - inside ItemAttachment") << xmlItemAttHead + QStringLiteral("true") + xmlItemAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid is contact photo") << xmlFileAttHead + QStringLiteral("true") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << true << true << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid content - nested XML element") << xmlFileAttHead + QStringLiteral("djsaoijsa") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid content - inside ItemAttachment") << xmlItemAttHead + QStringLiteral("VGhpcyBpcyBhIHRlc3Q=") + xmlItemAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("valid content") << xmlFileAttHead + QStringLiteral("VGhpcyBpcyBhIHRlc3Q=") + xmlFileAttTail << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << true << QByteArray("This is a test") << false << EwsItem(); QTest::newRow("invalid item - bad data") << xmlItemAttHead + QStringLiteral("") + xmlItemAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("invalid item - inside FileAttachment") << xmlFileAttHead + QStringLiteral("") + xmlFileAttTail << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); EwsItem item1; item1.setType(EwsItemTypeItem); item1.setField(EwsItemFieldItemId, QVariant::fromValue(EwsId(QStringLiteral("VGhpcyBpcyBhIHRlc3Q="), QStringLiteral("muKls0n8pUM=")))); QTest::newRow("valid item") << xmlItemAttHead + QStringLiteral("") + xmlItemAttTail << true << EwsAttachment::ItemAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << true << item1; } void UtEwsAttachment::write() { QFETCH(EwsAttachment::Type, type); QFETCH(bool, isValid); EwsAttachment att; if (isValid) { att.setType(type); QFETCH(bool, hasId); QFETCH(QString, id); if (hasId) { att.setId(id); } QFETCH(bool, hasName); QFETCH(QString, name); if (hasName) { att.setName(name); } QFETCH(bool, hasContentType); QFETCH(QString, contentType); if (hasContentType) { att.setContentType(contentType); } QFETCH(bool, hasContentId); QFETCH(QString, contentId); if (hasContentId) { att.setContentId(contentId); } QFETCH(bool, hasContentLocation); QFETCH(QString, contentLocation); if (hasContentLocation) { att.setContentLocation(contentLocation); } QFETCH(bool, hasSize); QFETCH(long, size); if (hasSize) { att.setSize(size); } QFETCH(bool, hasLastModifiedTime); QFETCH(QDateTime, lastModifiedTime); if (hasLastModifiedTime) { att.setLastModifiedTime(lastModifiedTime); } QFETCH(bool, hasIsInline); QFETCH(bool, isInline); if (hasIsInline) { att.setIsInline(isInline); } QFETCH(bool, hasIsContactPhoto); QFETCH(bool, isContactPhoto); if (hasIsContactPhoto) { att.setIsContactPhoto(isContactPhoto); } QFETCH(bool, hasContent); QFETCH(QByteArray, content); if (hasContent) { att.setContent(content); } QFETCH(bool, hasItem); QFETCH(EwsItem, item); if (hasItem) { att.setItem(item); } } - QFETCH(QString, xml); QString xmlData; QXmlStreamWriter writer(&xmlData); writer.setCodec("UTF-8"); writer.writeStartDocument(); writer.writeDefaultNamespace(xmlTypeNsUri); att.write(writer); writer.writeEndDocument(); QCOMPARE(xmlData.trimmed(), xml); } void UtEwsAttachment::write_data() { QTest::addColumn("xml"); QTest::addColumn("isValid"); QTest::addColumn("type"); QTest::addColumn("hasId"); QTest::addColumn("id"); QTest::addColumn("hasName"); QTest::addColumn("name"); QTest::addColumn("hasContentType"); QTest::addColumn("contentType"); QTest::addColumn("hasContentId"); QTest::addColumn("contentId"); QTest::addColumn("hasContentLocation"); QTest::addColumn("contentLocation"); QTest::addColumn("hasSize"); QTest::addColumn("size"); QTest::addColumn("hasLastModifiedTime"); QTest::addColumn("lastModifiedTime"); QTest::addColumn("hasIsInline"); QTest::addColumn("isInline"); QTest::addColumn("hasIsContactPhoto"); QTest::addColumn("isContactPhoto"); QTest::addColumn("hasContent"); QTest::addColumn("content"); QTest::addColumn("hasItem"); QTest::addColumn("item"); QTest::newRow("invalid") << xmlHead << false << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("unknown type") << xmlHead << true << EwsAttachment::UnknownAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty file attachment") << xmlHead + QStringLiteral("") << true << EwsAttachment::FileAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty item attachment") << xmlHead + QStringLiteral("") << true << EwsAttachment::ItemAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("empty reference attachment") << xmlHead + QStringLiteral("") << true << EwsAttachment::ReferenceAttachment << false << QString() << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); QTest::newRow("non-empty reference attachment") << xmlHead + QStringLiteral("") << true << EwsAttachment::ReferenceAttachment << true << QStringLiteral("5IaIqJVsJzamf2105wg4wQ==") << false << QString() << false << QString() << false << QString() << false << QString() << false << 0l << false << QDateTime() << false << false << false << false << false << QByteArray() << false << EwsItem(); EwsItem item1; item1.setType(EwsItemTypeItem); item1.setField(EwsItemFieldItemId, QVariant::fromValue(EwsId(QStringLiteral("VGhpcyBpcyBhIHRlc3Q="), QStringLiteral("muKls0n8pUM=")))); QDateTime testDt(QDateTime::fromSecsSinceEpoch(1483621243, QTimeZone::utc())); QTest::newRow("non-empty item attachment") << xmlHead + QStringLiteral("" - "" - "application/x-test" - "file:///foo/bar" - "%1" - "" - "").arg(testDt.toString(Qt::ISODate)) + "" + "application/x-test" + "file:///foo/bar" + "%1" + "" + "").arg(testDt.toString(Qt::ISODate)) << true << EwsAttachment::ItemAttachment << true << QStringLiteral("5IaIqJVsJzamf2105wg4wQ==") << false << QStringLiteral("Test attachment") << true << QStringLiteral("application/x-test") << false << QStringLiteral("FE938BD618330B9DA0C965A6077BB3FF20415531@1") << true << QStringLiteral("file:///foo/bar") << false << 123l << true << testDt << false << true << true << true << true << QByteArray("This is another test") << true << item1; QTest::newRow("non-empty file attachment") << xmlHead + QStringLiteral("" - "Test attachment" - "FE938BD618330B9DA0C965A6077BB3FF20415531@1" - "123" - "true" - "true" - "VGhpcyBpcyBhbm90aGVyIHRlc3Q=" - "") + "Test attachment" + "FE938BD618330B9DA0C965A6077BB3FF20415531@1" + "123" + "true" + "true" + "VGhpcyBpcyBhbm90aGVyIHRlc3Q=" + "") << true << EwsAttachment::FileAttachment << false << QStringLiteral("5IaIqJVsJzamf2105wg4wQ==") << true << QStringLiteral("Test attachment") << false << QStringLiteral("application/x-test") << true << QStringLiteral("FE938BD618330B9DA0C965A6077BB3FF20415531@1") << false << QStringLiteral("file:///foo/bar") << true << 123l << false << testDt << true << true << true << true << true << QByteArray("This is another test") << true << item1; - } QTEST_MAIN(UtEwsAttachment) #include "ewsattachment_ut.moc" diff --git a/resources/ews/test/unittests/ewsdeleteitemrequest_ut.cpp b/resources/ews/test/unittests/ewsdeleteitemrequest_ut.cpp index 05126fa56..a85660d66 100644 --- a/resources/ews/test/unittests/ewsdeleteitemrequest_ut.cpp +++ b/resources/ews/test/unittests/ewsdeleteitemrequest_ut.cpp @@ -1,304 +1,302 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 #include "fakehttppost.h" #include "ewsdeleteitemrequest.h" class UtEwsDeleteItemRequest : public QObject { Q_OBJECT private Q_SLOTS: void singleItem(); void twoItems(); void twoItemsOneFailed(); void twoItemsSecondFailed(); private: - void verifier(FakeTransferJob* job, const QByteArray &req, const QByteArray &expReq, - const QByteArray &resp); + void verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &resp); EwsClient mClient; }; void UtEwsDeleteItemRequest::singleItem() { static const QByteArray request = "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsDeleteItemRequest(mClient, this)); EwsId::List ids; ids << EwsId(QStringLiteral("+IRgnMJ8x+J6MQAZ"), QStringLiteral("1iQt/At9")); req->setItemIds(ids); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 1); EwsDeleteItemRequest::Response resp = req->responses().first(); QCOMPARE(resp.responseClass(), EwsResponseSuccess); } void UtEwsDeleteItemRequest::twoItems() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "NoError" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "NoError" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsDeleteItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("9LB1MiL3cOYUjmYy"), QStringLiteral("TBjl3rnU")), EwsId(QStringLiteral("rZ0sc7Gfn9+XHVgv"), QStringLiteral("pHTEe9nY")) }; req->setItemIds(ids); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); - Q_FOREACH(const EwsDeleteItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsDeleteItemRequest::Response &resp, req->responses()) { QCOMPARE(resp.responseClass(), EwsResponseSuccess); } } void UtEwsDeleteItemRequest::twoItemsOneFailed() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "The specified object was not found in the store." - "ErrorItemNotFound" - "0" - "" - "" - "NoError" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "The specified object was not found in the store." + "ErrorItemNotFound" + "0" + "" + "" + "NoError" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsDeleteItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("9LB1MiL3cOYUjmYy"), QStringLiteral("TBjl3rnU")), EwsId(QStringLiteral("rZ0sc7Gfn9+XHVgv"), QStringLiteral("pHTEe9nY")) }; req->setItemIds(ids); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); static const QList respClasses = { EwsResponseError, EwsResponseSuccess }; QList::const_iterator respClassesIt = respClasses.begin(); unsigned i = 0; - Q_FOREACH(const EwsDeleteItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsDeleteItemRequest::Response &resp, req->responses()) { qDebug() << "Verifying response" << i++; QCOMPARE(resp.responseClass(), *respClassesIt); respClassesIt++; } } void UtEwsDeleteItemRequest::twoItemsSecondFailed() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "The specified object was not found in the store." - "ErrorItemNotFound" - "0" - "" - "" - "NoError" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "The specified object was not found in the store." + "ErrorItemNotFound" + "0" + "" + "" + "NoError" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsDeleteItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("9LB1MiL3cOYUjmYy"), QStringLiteral("TBjl3rnU")), EwsId(QStringLiteral("rZ0sc7Gfn9+XHVgv"), QStringLiteral("pHTEe9nY")) }; req->setItemIds(ids); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); static const QList respClasses = { EwsResponseError, EwsResponseSuccess }; QList::const_iterator respClassesIt = respClasses.begin(); unsigned i = 0; - Q_FOREACH(const EwsDeleteItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsDeleteItemRequest::Response &resp, req->responses()) { qDebug() << "Verifying response" << i++; QCOMPARE(resp.responseClass(), *respClassesIt); respClassesIt++; } } -void UtEwsDeleteItemRequest::verifier(FakeTransferJob* job, const QByteArray &req, - const QByteArray &expReq, const QByteArray &response) +void UtEwsDeleteItemRequest::verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &response) { bool fail = true; - auto f = finally([&fail,&job]{ + auto f = finally([&fail, &job] { if (fail) { job->postResponse(""); } }); QCOMPARE(req, expReq); fail = false; job->postResponse(response); } QTEST_MAIN(UtEwsDeleteItemRequest) #include "ewsdeleteitemrequest_ut.moc" diff --git a/resources/ews/test/unittests/ewsgetitemrequest_ut.cpp b/resources/ews/test/unittests/ewsgetitemrequest_ut.cpp index b2bacc4cd..76f9f463b 100644 --- a/resources/ews/test/unittests/ewsgetitemrequest_ut.cpp +++ b/resources/ews/test/unittests/ewsgetitemrequest_ut.cpp @@ -1,147 +1,145 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 #include "fakehttppost.h" #include "ewsgetitemrequest.h" class UtEwsGetItemRequest : public QObject { Q_OBJECT private Q_SLOTS: void twoFailures(); private: - void verifier(FakeTransferJob* job, const QByteArray &req, const QByteArray &expReq, - const QByteArray &resp); + void verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &resp); EwsClient mClient; }; void UtEwsGetItemRequest::twoFailures() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "IdOnly" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "IdOnly" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "NoError" - "NoError" - "NoError" - "NoError" - "NoError" - "NoError" - "The specified object was not found in the store.ErrorItemNotFound0" - "The specified object was not found in the store.ErrorItemNotFound0" - ""; - - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + "" + "" + "" + "" + "" + "" + "" + "NoError" + "NoError" + "NoError" + "NoError" + "NoError" + "NoError" + "NoError" + "The specified object was not found in the store.ErrorItemNotFound0" + "The specified object was not found in the store.ErrorItemNotFound0" + ""; + + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsGetItemRequest(mClient, this)); EwsId::List ids; ids << EwsId(QStringLiteral("DdBTBAvLHI8OyQ3K"), QStringLiteral("6yDDqXl+")); ids << EwsId(QStringLiteral("CgIdfZGT3QJrWZHi"), QStringLiteral("wPjRsOpg")); ids << EwsId(QStringLiteral("Enxw15n4imIERH4w"), QStringLiteral("82pEQQIj")); ids << EwsId(QStringLiteral("yV1OhxPOinZ7mxpK"), QStringLiteral("B22tdkME")); ids << EwsId(QStringLiteral("j1ptydBqXKJLuCiB"), QStringLiteral("z0u+e6/Z")); ids << EwsId(QStringLiteral("ogM0ejAHml/og1tZ"), QStringLiteral("f2t/ou/g")); ids << EwsId(QStringLiteral("ZqDVkG1gIrUkJDGB"), QStringLiteral("0LOh2uE+")); ids << EwsId(QStringLiteral("SFYgXaYm1DK+0TCs"), QStringLiteral("Zbkp+aB4")); ids << EwsId(QStringLiteral("UrNr/v4HynI062u/"), QStringLiteral("WMjq6rUe")); req->setItemIds(ids); req->setItemShape(EwsItemShape(EwsShapeIdOnly)); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), ids.size()); static const QList respClasses = { EwsResponseSuccess, EwsResponseSuccess, EwsResponseSuccess, EwsResponseSuccess, EwsResponseSuccess, EwsResponseSuccess, EwsResponseSuccess, EwsResponseError, EwsResponseError }; QList::const_iterator respClassesIt = respClasses.begin(); EwsId::List::const_iterator idsIt = ids.cbegin(); unsigned i = 0; - Q_FOREACH(const EwsGetItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsGetItemRequest::Response &resp, req->responses()) { qDebug() << "Verifying response" << i++; QCOMPARE(resp.responseClass(), *respClassesIt); if (resp.isSuccess()) { EwsId id = resp.item()[EwsItemFieldItemId].value(); QCOMPARE(id, *idsIt); } idsIt++; respClassesIt++; } } -void UtEwsGetItemRequest::verifier(FakeTransferJob* job, const QByteArray &req, - const QByteArray &expReq, const QByteArray &response) +void UtEwsGetItemRequest::verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &response) { bool fail = true; - auto f = finally([&fail,&job]{ + auto f = finally([&fail, &job] { if (fail) { job->postResponse(""); } }); QCOMPARE(req, expReq); fail = false; job->postResponse(response); } QTEST_MAIN(UtEwsGetItemRequest) #include "ewsgetitemrequest_ut.moc" diff --git a/resources/ews/test/unittests/ewsmoveitemrequest_ut.cpp b/resources/ews/test/unittests/ewsmoveitemrequest_ut.cpp index 371980444..4e4911322 100644 --- a/resources/ews/test/unittests/ewsmoveitemrequest_ut.cpp +++ b/resources/ews/test/unittests/ewsmoveitemrequest_ut.cpp @@ -1,370 +1,366 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 #include "fakehttppost.h" #include "ewsmoveitemrequest.h" class UtEwsMoveItemRequest : public QObject { Q_OBJECT private Q_SLOTS: void singleItem(); void twoItems(); void twoItemsOneFailed(); void twoItemsSecondFailed(); private: - void verifier(FakeTransferJob* job, const QByteArray &req, const QByteArray &expReq, - const QByteArray &resp); + void verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &resp); EwsClient mClient; }; void UtEwsMoveItemRequest::singleItem() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - "" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + "" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsMoveItemRequest(mClient, this)); EwsId::List ids; ids << EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("rqs77HkG")); req->setItemIds(ids); req->setDestinationFolderId(EwsId(QStringLiteral("R70cDGNT1SqOk2pn"), QStringLiteral("1DjfJ3dT"))); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 1); EwsMoveItemRequest::Response resp = req->responses().first(); QCOMPARE(resp.responseClass(), EwsResponseSuccess); QCOMPARE(resp.itemId(), EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("JoFvRwDP"))); } void UtEwsMoveItemRequest::twoItems() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - "" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + "" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsMoveItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("rqs77HkG")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("EDHu5rwK")) }; req->setItemIds(ids); req->setDestinationFolderId(EwsId(QStringLiteral("R70cDGNT1SqOk2pn"), QStringLiteral("1DjfJ3dT"))); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); static const EwsId::List newIds = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("JoFvRwDP")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("4qbAwd3y")) }; EwsId::List::const_iterator newIdsIt = newIds.begin(); - Q_FOREACH(const EwsMoveItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsMoveItemRequest::Response &resp, req->responses()) { QCOMPARE(resp.responseClass(), EwsResponseSuccess); QCOMPARE(resp.itemId(), *newIdsIt); newIdsIt++; } } void UtEwsMoveItemRequest::twoItemsOneFailed() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - "" - "" - "" - "The specified object was not found in the store." - "ErrorItemNotFound" - "0" - "" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + "" + "" + "" + "The specified object was not found in the store." + "ErrorItemNotFound" + "0" + "" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsMoveItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("rqs77HkG")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("EDHu5rwK")) }; req->setItemIds(ids); req->setDestinationFolderId(EwsId(QStringLiteral("R70cDGNT1SqOk2pn"), QStringLiteral("1DjfJ3dT"))); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); static const QList respClasses = { EwsResponseSuccess, EwsResponseError }; static const EwsId::List newIds = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("JoFvRwDP")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("EDHu5rwK")) }; EwsId::List::const_iterator newIdsIt = newIds.begin(); QList::const_iterator respClassesIt = respClasses.begin(); unsigned i = 0; - Q_FOREACH(const EwsMoveItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsMoveItemRequest::Response &resp, req->responses()) { qDebug() << "Verifying response" << i++; QCOMPARE(resp.responseClass(), *respClassesIt); if (resp.isSuccess()) { QCOMPARE(resp.itemId(), *newIdsIt); - } - else { + } else { QCOMPARE(resp.itemId(), EwsId()); } newIdsIt++; respClassesIt++; } } void UtEwsMoveItemRequest::twoItemsSecondFailed() { static const QByteArray request = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "\n"; + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "" - "The specified object was not found in the store." - "ErrorItemNotFound" - "0" - "" - "" - "" - "NoError" - "" - "" - "" - "" - "" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "" + "The specified object was not found in the store." + "ErrorItemNotFound" + "0" + "" + "" + "" + "NoError" + "" + "" + "" + "" + "" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsMoveItemRequest(mClient, this)); static const EwsId::List ids = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("rqs77HkG")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("EDHu5rwK")) }; req->setItemIds(ids); req->setDestinationFolderId(EwsId(QStringLiteral("R70cDGNT1SqOk2pn"), QStringLiteral("1DjfJ3dT"))); req->exec(); QCOMPARE(req->error(), 0); QCOMPARE(req->responses().size(), 2); static const QList respClasses = { EwsResponseError, EwsResponseSuccess }; static const EwsId::List newIds = { EwsId(QStringLiteral("Xnn2DwwaXQUhbn7U"), QStringLiteral("JoFvRwDP")), EwsId(QStringLiteral("ntTNOncESwiyAXog"), QStringLiteral("4qbAwd3y")) }; EwsId::List::const_iterator newIdsIt = newIds.begin(); QList::const_iterator respClassesIt = respClasses.begin(); unsigned i = 0; - Q_FOREACH(const EwsMoveItemRequest::Response &resp, req->responses()) { + Q_FOREACH (const EwsMoveItemRequest::Response &resp, req->responses()) { qDebug() << "Verifying response" << i++; QCOMPARE(resp.responseClass(), *respClassesIt); if (resp.isSuccess()) { QCOMPARE(resp.itemId(), *newIdsIt); - } - else { + } else { QCOMPARE(resp.itemId(), EwsId()); } newIdsIt++; respClassesIt++; } } -void UtEwsMoveItemRequest::verifier(FakeTransferJob* job, const QByteArray &req, - const QByteArray &expReq, const QByteArray &response) +void UtEwsMoveItemRequest::verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &response) { bool fail = true; - auto f = finally([&fail,&job]{ + auto f = finally([&fail, &job] { if (fail) { job->postResponse(""); } }); QCOMPARE(req, expReq); fail = false; job->postResponse(response); } QTEST_MAIN(UtEwsMoveItemRequest) #include "ewsmoveitemrequest_ut.moc" diff --git a/resources/ews/test/unittests/ewsoauth_ut.cpp b/resources/ews/test/unittests/ewsoauth_ut.cpp index 807da93c2..d12bc588f 100644 --- a/resources/ews/test/unittests/ewsoauth_ut.cpp +++ b/resources/ews/test/unittests/ewsoauth_ut.cpp @@ -1,350 +1,346 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 #include #include "auth/ewsoauth.h" #include "ewsoauth_ut_mock.h" static const QString testEmail = QStringLiteral("joe.bloggs@unknown.com"); static const QString testClientId = QStringLiteral("b43c59cd-dd1c-41fd-bb9a-b0a1d5696a93"); static const QString testReturnUri = QStringLiteral("urn:ietf:wg:oauth:2.0:oob"); //static const QString testReturnUriPercent = QUrl::toPercentEncoding(testReturnUri); static const QString testState = QStringLiteral("joidsiuhq"); static const QString resource = QStringLiteral("https://outlook.office365.com/"); //static const QString resourcePercent = QUrl::toPercentEncoding(resource); static const QString authUrl = QStringLiteral("https://login.microsoftonline.com/common/oauth2/authorize"); static const QString tokenUrl = QStringLiteral("https://login.microsoftonline.com/common/oauth2/token"); static const QString accessToken1 = QStringLiteral("IERbOTo5NSdtY5HMntWTH1wgrRt98KmbF7nNloIdZ4SSYOU7pziJJakpHy8r6kxQi+7T9w36mWv9IWLrvEwTsA"); static const QString refreshToken1 = QStringLiteral("YW7lJFWcEISynbraq4NiLLke3rOieFdvoJEDxpjCXorJblIGM56OJSu1PZXMCQL5W3KLxS9ydxqLHxRTSdw"); static const QString idToken1 = QStringLiteral("gz7l0chu9xIi1MMgPkpHGQTmo3W7L1rQbmWAxEL5VSKHeqdIJ7E3K7vmMYTl/C1fWihB5XiLjD2GSVQoOzTfCw"); - class UtEwsOAuth : public QObject { Q_OBJECT private Q_SLOTS: void initialInteractiveSuccessful(); void initialRefreshSuccessful(); void refreshSuccessful(); private: static QString formatJsonSorted(const QVariantMap &map); static int performAuthAction(EwsOAuth &oAuth, int timeout, std::function actionFn); static void setUpAccessFunction(const QString &refreshToken); - static void setUpTokenFunction(const QString &accessToken, const QString &refreshToken, const QString &idToken, - quint64 time, int tokenLifetime, int extTokenLifetime, QString &tokenReplyData); + static void setUpTokenFunction(const QString &accessToken, const QString &refreshToken, const QString &idToken, quint64 time, int tokenLifetime, int extTokenLifetime, QString &tokenReplyData); static void dumpEvents(const QStringList &events, const QStringList &expectedEvents); void setUpOAuth(EwsOAuth &oAuth, QStringList &events, QString password, QMap map); }; void UtEwsOAuth::initialInteractiveSuccessful() { EwsOAuth oAuth(nullptr, testEmail, testClientId, testReturnUri); QVERIFY(Mock::QWebEngineView::instance); QVERIFY(Mock::QOAuth2AuthorizationCodeFlow::instance); QStringList events; setUpOAuth(oAuth, events, QString(), QMap()); - + Mock::QWebEngineView::instance->setRedirectUri(Mock::QOAuth2AuthorizationCodeFlow::instance->redirectUri()); auto time = QDateTime::currentSecsSinceEpoch(); - + constexpr unsigned int tokenLifetime = 86399; constexpr unsigned int extTokenLifetime = 345599; QString tokenReplyData; setUpAccessFunction(refreshToken1); setUpTokenFunction(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, tokenReplyData); Mock::QOAuth2AuthorizationCodeFlow::instance->setState(testState); const auto initStatus = performAuthAction(oAuth, 1000, [](EwsOAuth *oAuth) { - oAuth->init(); - return true; - }); + oAuth->init(); + return true; + }); QVERIFY(initStatus == 1); const auto authStatus = performAuthAction(oAuth, 2000, [](EwsOAuth *oAuth) { - return oAuth->authenticate(true); - }); + return oAuth->authenticate(true); + }); QVERIFY(authStatus == 0); const auto authUrlString = Mock::authUrlString(authUrl, testClientId, testReturnUri, testEmail, resource, testState); const QStringList expectedEvents = { Mock::requestWalletMapString(), Mock::modifyParamsAuthString(testClientId, testReturnUri, testState), Mock::authorizeWithBrowserString(authUrlString), Mock::loadWebPageString(authUrlString), Mock::interceptRequestString(authUrlString), Mock::interceptRequestBlockedString(false), Mock::interceptRequestString(testReturnUri + QStringLiteral("?code=") + QString::fromLatin1(QUrl::toPercentEncoding(refreshToken1))), Mock::interceptRequestBlockedString(true), Mock::authorizationCallbackReceivedString(refreshToken1), Mock::modifyParamsTokenString(testClientId, testReturnUri, refreshToken1), Mock::networkReplyFinishedString(tokenReplyData), Mock::replyDataCallbackString(tokenReplyData), Mock::tokenCallbackString(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, resource) }; dumpEvents(events, expectedEvents); QVERIFY(events == expectedEvents); } void UtEwsOAuth::initialRefreshSuccessful() { EwsOAuth oAuth(nullptr, testEmail, testClientId, testReturnUri); QVERIFY(Mock::QWebEngineView::instance); QVERIFY(Mock::QOAuth2AuthorizationCodeFlow::instance); QStringList events; QMap map = { {QStringLiteral("refresh-token"), refreshToken1} }; - + setUpOAuth(oAuth, events, QString(), map); Mock::QWebEngineView::instance->setRedirectUri(Mock::QOAuth2AuthorizationCodeFlow::instance->redirectUri()); auto time = QDateTime::currentSecsSinceEpoch(); constexpr unsigned int tokenLifetime = 86399; constexpr unsigned int extTokenLifetime = 345599; QString tokenReplyData; setUpAccessFunction(refreshToken1); setUpTokenFunction(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, tokenReplyData); Mock::QOAuth2AuthorizationCodeFlow::instance->setState(testState); const auto initStatus = performAuthAction(oAuth, 1000, [](EwsOAuth *oAuth) { - oAuth->init(); - return true; - }); + oAuth->init(); + return true; + }); QVERIFY(initStatus == 1); const auto authStatus = performAuthAction(oAuth, 2000, [](EwsOAuth *oAuth) { - return oAuth->authenticate(true); - }); + return oAuth->authenticate(true); + }); QVERIFY(authStatus == 0); const auto authUrlString = Mock::authUrlString(authUrl, testClientId, testReturnUri, testEmail, resource, testState); const QStringList expectedEvents = { Mock::requestWalletMapString(), Mock::modifyParamsTokenString(testClientId, testReturnUri, refreshToken1), Mock::networkReplyFinishedString(tokenReplyData), Mock::replyDataCallbackString(tokenReplyData), Mock::tokenCallbackString(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, resource) }; dumpEvents(events, expectedEvents); QVERIFY(events == expectedEvents); } void UtEwsOAuth::refreshSuccessful() { EwsOAuth oAuth(nullptr, testEmail, testClientId, testReturnUri); QVERIFY(Mock::QWebEngineView::instance); QVERIFY(Mock::QOAuth2AuthorizationCodeFlow::instance); QStringList events; setUpOAuth(oAuth, events, QString(), QMap()); - + Mock::QWebEngineView::instance->setRedirectUri(Mock::QOAuth2AuthorizationCodeFlow::instance->redirectUri()); auto time = QDateTime::currentSecsSinceEpoch(); constexpr unsigned int tokenLifetime = 86399; constexpr unsigned int extTokenLifetime = 345599; QString tokenReplyData; setUpAccessFunction(refreshToken1); setUpTokenFunction(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, tokenReplyData); Mock::QOAuth2AuthorizationCodeFlow::instance->setState(testState); const auto initStatus = performAuthAction(oAuth, 1000, [](EwsOAuth *oAuth) { - oAuth->init(); - return true; - }); + oAuth->init(); + return true; + }); QVERIFY(initStatus == 1); const auto authStatus = performAuthAction(oAuth, 2000, [](EwsOAuth *oAuth) { - return oAuth->authenticate(true); - }); + return oAuth->authenticate(true); + }); QVERIFY(authStatus == 0); const auto authUrlString = Mock::authUrlString(authUrl, testClientId, testReturnUri, testEmail, resource, testState); const QStringList expectedEvents = { Mock::requestWalletMapString(), Mock::modifyParamsAuthString(testClientId, testReturnUri, testState), Mock::authorizeWithBrowserString(authUrlString), Mock::loadWebPageString(authUrlString), Mock::interceptRequestString(authUrlString), Mock::interceptRequestBlockedString(false), Mock::interceptRequestString(testReturnUri + QStringLiteral("?code=") + QString::fromLatin1(QUrl::toPercentEncoding(refreshToken1))), Mock::interceptRequestBlockedString(true), Mock::authorizationCallbackReceivedString(refreshToken1), Mock::modifyParamsTokenString(testClientId, testReturnUri, refreshToken1), Mock::networkReplyFinishedString(tokenReplyData), Mock::replyDataCallbackString(tokenReplyData), Mock::tokenCallbackString(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, resource) }; dumpEvents(events, expectedEvents); QVERIFY(events == expectedEvents); events.clear(); - + oAuth.notifyRequestAuthFailed(); const auto reauthStatus = performAuthAction(oAuth, 2000, [](EwsOAuth *oAuth) { - return oAuth->authenticate(false); - }); + return oAuth->authenticate(false); + }); QVERIFY(reauthStatus == 0); const QStringList expectedEventsRefresh = { Mock::modifyParamsTokenString(testClientId, testReturnUri, refreshToken1), Mock::networkReplyFinishedString(tokenReplyData), Mock::replyDataCallbackString(tokenReplyData), Mock::tokenCallbackString(accessToken1, refreshToken1, idToken1, time, tokenLifetime, extTokenLifetime, resource) }; dumpEvents(events, expectedEvents); QVERIFY(events == expectedEventsRefresh); } QString UtEwsOAuth::formatJsonSorted(const QVariantMap &map) { QStringList keys = map.keys(); keys.sort(); QStringList elems; for (const auto &key : keys) { QString val = map[key].toString(); val.replace(QLatin1Char('"'), QStringLiteral("\\\"")); elems.append(QStringLiteral("\"%1\":\"%2\"").arg(key, val)); } return QStringLiteral("{") + elems.join(QLatin1Char(',')) + QStringLiteral("}"); } int UtEwsOAuth::performAuthAction(EwsOAuth &oAuth, int timeout, std::function actionFn) { QEventLoop loop; int status = -1; QTimer timer; connect(&oAuth, &EwsOAuth::authSucceeded, &timer, [&]() { - qDebug() << "succeeded"; - loop.exit(0); - status = 0; - }); + qDebug() << "succeeded"; + loop.exit(0); + status = 0; + }); connect(&oAuth, &EwsOAuth::authFailed, &timer, [&](const QString &msg) { - qDebug() << "failed" << msg; - loop.exit(1); - status = 1; - }); + qDebug() << "failed" << msg; + loop.exit(1); + status = 1; + }); connect(&timer, &QTimer::timeout, &timer, [&]() { - qDebug() << "timeout"; - loop.exit(1); - status = 1; - }); + qDebug() << "timeout"; + loop.exit(1); + status = 1; + }); timer.setSingleShot(true); timer.start(timeout); - if (!actionFn(&oAuth)) - { + if (!actionFn(&oAuth)) { return -1; } - + if (status == -1) { status = loop.exec(); } return status; } void UtEwsOAuth::setUpAccessFunction(const QString &refreshToken) { Mock::QWebEngineView::instance->setAuthFunction([&](const QUrl &, QVariantMap &map){ - map[QStringLiteral("code")] = QUrl::toPercentEncoding(refreshToken); - }); + map[QStringLiteral("code")] = QUrl::toPercentEncoding(refreshToken); + }); } -void UtEwsOAuth::setUpTokenFunction(const QString &accessToken, const QString &refreshToken, const QString &idToken, - quint64 time, int tokenLifetime, int extTokenLifetime, QString &tokenReplyData) +void UtEwsOAuth::setUpTokenFunction(const QString &accessToken, const QString &refreshToken, const QString &idToken, quint64 time, int tokenLifetime, int extTokenLifetime, QString &tokenReplyData) { Mock::QOAuth2AuthorizationCodeFlow::instance->setTokenFunction( - [=, &tokenReplyData] (QString &data, QMap &headers) { - QVariantMap map; - map[QStringLiteral("token_type")] = QStringLiteral("Bearer"); - map[QStringLiteral("scope")] = QStringLiteral("ReadWrite.All"); - map[QStringLiteral("expires_in")] = QString::number(tokenLifetime); - map[QStringLiteral("ext_expires_in")] = QString::number(extTokenLifetime); - map[QStringLiteral("expires_on")] = QString::number(time + tokenLifetime); - map[QStringLiteral("not_before")] = QString::number(time); - map[QStringLiteral("resource")] = resource; - map[QStringLiteral("access_token")] = accessToken; - map[QStringLiteral("refresh_token")] = refreshToken; - map[QStringLiteral("foci")] = QStringLiteral("1"); - map[QStringLiteral("id_token")] = idToken; - tokenReplyData = formatJsonSorted(map); - data = tokenReplyData; - headers[Mock::QNetworkRequest::ContentTypeHeader] = QStringLiteral("application/json; charset=utf-8"); - - return Mock::QNetworkReply::NoError; - }); + [=, &tokenReplyData](QString &data, QMap &headers) { + QVariantMap map; + map[QStringLiteral("token_type")] = QStringLiteral("Bearer"); + map[QStringLiteral("scope")] = QStringLiteral("ReadWrite.All"); + map[QStringLiteral("expires_in")] = QString::number(tokenLifetime); + map[QStringLiteral("ext_expires_in")] = QString::number(extTokenLifetime); + map[QStringLiteral("expires_on")] = QString::number(time + tokenLifetime); + map[QStringLiteral("not_before")] = QString::number(time); + map[QStringLiteral("resource")] = resource; + map[QStringLiteral("access_token")] = accessToken; + map[QStringLiteral("refresh_token")] = refreshToken; + map[QStringLiteral("foci")] = QStringLiteral("1"); + map[QStringLiteral("id_token")] = idToken; + tokenReplyData = formatJsonSorted(map); + data = tokenReplyData; + headers[Mock::QNetworkRequest::ContentTypeHeader] = QStringLiteral("application/json; charset=utf-8"); + + return Mock::QNetworkReply::NoError; + }); } void UtEwsOAuth::dumpEvents(const QStringList &events, const QStringList &expectedEvents) { for (const auto event : events) { qDebug() << "Got event:" << event; } if (events != expectedEvents) { for (const auto event : expectedEvents) { qDebug() << "Expected event:" << event; } } } void UtEwsOAuth::setUpOAuth(EwsOAuth &oAuth, QStringList &events, QString password, QMap map) { connect(Mock::QWebEngineView::instance.data(), &Mock::QWebEngineView::logEvent, this, [&events](const QString &event) { - events.append(event); - }); + events.append(event); + }); connect(Mock::QOAuth2AuthorizationCodeFlow::instance.data(), &Mock::QOAuth2AuthorizationCodeFlow::logEvent, this, [&events](const QString &event) { - events.append(event); - }); + events.append(event); + }); connect(&oAuth, &EwsOAuth::requestWalletPassword, this, [&oAuth, &events, password](bool) { - events.append(QStringLiteral("RequestWalletPassword")); - oAuth.walletPasswordRequestFinished(password); - }); + events.append(QStringLiteral("RequestWalletPassword")); + oAuth.walletPasswordRequestFinished(password); + }); connect(&oAuth, &EwsOAuth::requestWalletMap, this, [&oAuth, &events, map]() { - events.append(QStringLiteral("RequestWalletMap")); - oAuth.walletMapRequestFinished(map); - }); + events.append(QStringLiteral("RequestWalletMap")); + oAuth.walletMapRequestFinished(map); + }); } QTEST_MAIN(UtEwsOAuth) #include "ewsoauth_ut.moc" diff --git a/resources/ews/test/unittests/ewsoauth_ut_mock.cpp b/resources/ews/test/unittests/ewsoauth_ut_mock.cpp index 9dd861b37..5edcd6f74 100644 --- a/resources/ews/test/unittests/ewsoauth_ut_mock.cpp +++ b/resources/ews/test/unittests/ewsoauth_ut_mock.cpp @@ -1,480 +1,481 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 "ewsoauth_ut_mock.h" #include #include Q_LOGGING_CATEGORY(EWSCLI_LOG, "org.kde.pim.ews.client", QtInfoMsg) namespace Mock { - QPointer QWebEngineView::instance; QPointer QOAuth2AuthorizationCodeFlow::instance; QUrl QWebEngineUrlRequestJob::requestUrl() const { return mUrl; } QUrl QWebEngineUrlRequestInfo::requestUrl() const { return mUrl; } void QWebEngineUrlRequestInfo::block(bool) { mBlocked = true; } QWebEngineUrlRequestInterceptor::QWebEngineUrlRequestInterceptor(QObject *parent) : QObject(parent) { } QWebEngineUrlRequestInterceptor::~QWebEngineUrlRequestInterceptor() { } QWebEngineUrlSchemeHandler::QWebEngineUrlSchemeHandler(QObject *parent) : QObject(parent) { } QWebEngineUrlSchemeHandler::~QWebEngineUrlSchemeHandler() { } QWebEngineProfile::QWebEngineProfile(QObject *parent) - : QObject(parent), mInterceptor(nullptr), mHandler(nullptr) + : QObject(parent) + , mInterceptor(nullptr) + , mHandler(nullptr) { } QWebEngineProfile::~QWebEngineProfile() { } void QWebEngineProfile::setHttpUserAgent(const QString &ua) { mUserAgent = ua; } void QWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { mInterceptor = interceptor; } void QWebEngineProfile::installUrlSchemeHandler(QByteArray const &scheme, QWebEngineUrlSchemeHandler *handler) { mScheme = QString::fromLatin1(scheme); mHandler = handler; } QWebEnginePage::QWebEnginePage(QWebEngineProfile *profile, QObject *parent) - : QObject(parent), mProfile(profile) + : QObject(parent) + , mProfile(profile) { connect(profile, &QWebEngineProfile::logEvent, this, &QWebEnginePage::logEvent); } QWebEnginePage::~QWebEnginePage() { } QWebEngineView::QWebEngineView(QWidget *parent) - : QWidget(parent), mPage(nullptr) + : QWidget(parent) + , mPage(nullptr) { if (!instance) { instance = this; } else { qDebug() << "QWebEngineView instance already exists!"; } } QWebEngineView::~QWebEngineView() { } void QWebEngineView::load(const QUrl &url) { Q_EMIT logEvent(QStringLiteral("LoadWebPage:") + url.toString()); simulatePageLoad(url); QVariantMap params; if (mAuthFunction) { mAuthFunction(url, params); simulatePageLoad(QUrl(mRedirectUri + QStringLiteral("?") + QOAuth2AuthorizationCodeFlow::mapToSortedQuery(params).toString())); } else { qWarning() << "No authentication callback defined"; } } void QWebEngineView::simulatePageLoad(const QUrl &url) { if (mPage && mPage->mProfile && mPage->mProfile->mInterceptor) { QWebEngineUrlRequestInfo info(url, this); Q_EMIT logEvent(QStringLiteral("InterceptRequest:") + url.toString()); mPage->mProfile->mInterceptor->interceptRequest(info); Q_EMIT logEvent(QStringLiteral("InterceptRequestBlocked:%1").arg(info.mBlocked)); } else { qWarning() << "Cannot reach to request interceptor"; } } void QWebEngineView::setPage(QWebEnginePage *page) { mPage = page; connect(page, &QWebEnginePage::logEvent, this, &QWebEngineView::logEvent); } void QWebEngineView::stop() { } void QWebEngineView::setAuthFunction(const AuthFunc &func) { mAuthFunction = func; } void QWebEngineView::setRedirectUri(const QString &uri) { mRedirectUri = uri; } QNetworkReply::NetworkError QNetworkReply::error() const { return NoError; } QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const { return mHeaders[header]; } QAbstractOAuthReplyHandler::QAbstractOAuthReplyHandler(QObject *parent) : QObject(parent) { } QAbstractOAuthReplyHandler::~QAbstractOAuthReplyHandler() { } QAbstractOAuth::QAbstractOAuth(QObject *parent) - : QObject(parent), mStatus(Status::NotAuthenticated) + : QObject(parent) + , mStatus(Status::NotAuthenticated) { } void QAbstractOAuth::setReplyHandler(QAbstractOAuthReplyHandler *handler) { mReplyHandler = handler; } void QAbstractOAuth::setAuthorizationUrl(const QUrl &url) { mAuthUrl = url; } void QAbstractOAuth::setClientIdentifier(const QString &identifier) { mClientId = identifier; } -void QAbstractOAuth::setModifyParametersFunction(const std::function*)> &func) +void QAbstractOAuth::setModifyParametersFunction(const std::function *)> &func) { mModifyParamsFunc = func; } QString QAbstractOAuth::token() const { return mToken; } void QAbstractOAuth::setToken(const QString &token) { mToken = token; } QAbstractOAuth::Status QAbstractOAuth::status() const { return mStatus; } QAbstractOAuth2::QAbstractOAuth2(QObject *parent) : QAbstractOAuth(parent) { } QString QAbstractOAuth2::refreshToken() const { return mRefreshToken; } void QAbstractOAuth2::setRefreshToken(const QString &token) { mRefreshToken = token; } QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(QObject *parent) : QAbstractOAuth2(parent) { if (!instance) { instance = this; } else { qDebug() << "QOAuth2AuthorizationCodeFlow instance already exists!"; } } QOAuth2AuthorizationCodeFlow::~QOAuth2AuthorizationCodeFlow() { } void QOAuth2AuthorizationCodeFlow::setAccessTokenUrl(const QUrl &url) { mTokenUrl = url; } void QOAuth2AuthorizationCodeFlow::grant() { QMap map; map[QStringLiteral("response_type")] = QStringLiteral("code"); map[QStringLiteral("client_id")] = QUrl::toPercentEncoding(mClientId); map[QStringLiteral("redirect_uri")] = QUrl::toPercentEncoding(mReplyHandler->callback()); map[QStringLiteral("scope")] = QString(); map[QStringLiteral("state")] = mState; Q_EMIT logEvent(QStringLiteral("ModifyParams:RequestingAuthorization:") + mapToSortedQuery(map).toString()); if (mModifyParamsFunc) { mModifyParamsFunc(Stage::RequestingAuthorization, &map); } mResource = QUrl::fromPercentEncoding(map[QStringLiteral("resource")].toByteArray()); QUrl url(mAuthUrl); url.setQuery(mapToSortedQuery(map)); Q_EMIT logEvent(QStringLiteral("AuthorizeWithBrowser:") + url.toString()); connect(this, &QAbstractOAuth2::authorizationCallbackReceived, this, &QOAuth2AuthorizationCodeFlow::authCallbackReceived, Qt::UniqueConnection); Q_EMIT authorizeWithBrowser(url); } void QOAuth2AuthorizationCodeFlow::refreshAccessToken() { mStatus = Status::RefreshingToken; doRefreshAccessToken(); } void QOAuth2AuthorizationCodeFlow::doRefreshAccessToken() { QMap map; map[QStringLiteral("grant_type")] = QStringLiteral("authorization_code"); map[QStringLiteral("code")] = QUrl::toPercentEncoding(mRefreshToken); map[QStringLiteral("client_id")] = QUrl::toPercentEncoding(mClientId); map[QStringLiteral("redirect_uri")] = QUrl::toPercentEncoding(mReplyHandler->callback()); Q_EMIT logEvent(QStringLiteral("ModifyParams:RequestingAccessToken:") + mapToSortedQuery(map).toString()); if (mModifyParamsFunc) { mModifyParamsFunc(Stage::RequestingAccessToken, &map); } connect(mReplyHandler, &QAbstractOAuthReplyHandler::tokensReceived, this, &QOAuth2AuthorizationCodeFlow::tokenCallbackReceived, Qt::UniqueConnection); connect(mReplyHandler, &QAbstractOAuthReplyHandler::replyDataReceived, this, &QOAuth2AuthorizationCodeFlow::replyDataCallbackReceived, Qt::UniqueConnection); if (mTokenFunc) { QNetworkReply reply(this); QString data; reply.mError = mTokenFunc(data, reply.mHeaders); reply.setData(data.toUtf8()); reply.open(QIODevice::ReadOnly); Q_EMIT logEvent(QStringLiteral("NetworkReplyFinished:") + data); mReplyHandler->networkReplyFinished(&reply); } else { qWarning() << "No token function defined"; } } QUrlQuery QOAuth2AuthorizationCodeFlow::mapToSortedQuery(QMap const &map) { QUrlQuery query; QStringList keys = map.keys(); keys.sort(); for (const auto &key : keys) { query.addQueryItem(key, map[key].toString()); } return query; } void QOAuth2AuthorizationCodeFlow::authCallbackReceived(QMap const ¶ms) { Q_EMIT logEvent(QStringLiteral("AuthorizatioCallbackReceived:") + mapToSortedQuery(params).toString()); mRefreshToken = params[QStringLiteral("code")].toString(); if (!mRefreshToken.isEmpty()) { mStatus = Status::TemporaryCredentialsReceived; doRefreshAccessToken(); } else { Q_EMIT error(QString(), QString(), QUrl()); } } void QOAuth2AuthorizationCodeFlow::tokenCallbackReceived(const QVariantMap &tokens) { Q_EMIT logEvent(QStringLiteral("TokenCallback:") + mapToSortedQuery(tokens).toString()); mToken = tokens[QStringLiteral("access_token")].toString(); mRefreshToken = tokens[QStringLiteral("refresh_token")].toString(); mStatus = Status::Granted; Q_EMIT granted(); } void QOAuth2AuthorizationCodeFlow::replyDataCallbackReceived(const QByteArray &data) { Q_EMIT logEvent(QStringLiteral("ReplyDataCallback:") + QString::fromLatin1(data)); } QString QOAuth2AuthorizationCodeFlow::redirectUri() const { return mReplyHandler->callback(); } void QOAuth2AuthorizationCodeFlow::setTokenFunction(const TokenFunc &func) { mTokenFunc = func; } void QOAuth2AuthorizationCodeFlow::setState(const QString &state) { mState = state; } QString browserDisplayRequestString() { return QStringLiteral("BrowserDisplayRequest"); } QString modifyParamsAuthString(const QString &clientId, const QString &returnUri, const QString &state) { return QStringLiteral("ModifyParams:RequestingAuthorization:client_id=%1&redirect_uri=%2&response_type=code&scope&state=%3") - .arg(QString::fromUtf8(QUrl::toPercentEncoding(clientId)), QString::fromLatin1(QUrl::toPercentEncoding(returnUri)), QString::fromLatin1(QUrl::toPercentEncoding(state))); + .arg(QString::fromUtf8(QUrl::toPercentEncoding(clientId)), QString::fromLatin1(QUrl::toPercentEncoding(returnUri)), QString::fromLatin1(QUrl::toPercentEncoding(state))); } -QString authUrlString(const QString &authUrl, const QString &clientId, const QString &returnUri, - const QString &email, const QString &resource, const QString &state) +QString authUrlString(const QString &authUrl, const QString &clientId, const QString &returnUri, const QString &email, const QString &resource, const QString &state) { return QStringLiteral("%1?client_id=%2&login_hint=%3&prompt=login&redirect_uri=%4&resource=%5&response_type=code&scope&state=%6") - .arg(authUrl, QString::fromLatin1(QUrl::toPercentEncoding(clientId)), email, QString::fromLatin1(QUrl::toPercentEncoding(returnUri)), - QString::fromLatin1(QUrl::toPercentEncoding(resource)), QString::fromLatin1(QUrl::toPercentEncoding(state))); + .arg(authUrl, QString::fromLatin1(QUrl::toPercentEncoding(clientId)), email, QString::fromLatin1(QUrl::toPercentEncoding(returnUri)), + QString::fromLatin1(QUrl::toPercentEncoding(resource)), QString::fromLatin1(QUrl::toPercentEncoding(state))); } QString authorizeWithBrowserString(const QString &url) { return QStringLiteral("AuthorizeWithBrowser:") + url; } QString loadWebPageString(const QString &url) { return QStringLiteral("LoadWebPage:") + url; } QString interceptRequestString(const QString &url) { return QStringLiteral("InterceptRequest:") + url; } QString interceptRequestBlockedString(bool blocked) { return QStringLiteral("InterceptRequestBlocked:%1").arg(blocked); } QString authorizationCallbackReceivedString(const QString &code) { return QStringLiteral("AuthorizatioCallbackReceived:code=%1").arg(code); } QString modifyParamsTokenString(const QString &clientId, const QString &returnUri, const QString &code) { return QStringLiteral("ModifyParams:RequestingAccessToken:client_id=%1&code=%2&grant_type=authorization_code&redirect_uri=%3") - .arg(QString::fromUtf8(QUrl::toPercentEncoding(clientId)), QString::fromLatin1(QUrl::toPercentEncoding(code)), - QString::fromLatin1(QUrl::toPercentEncoding(returnUri))); + .arg(QString::fromUtf8(QUrl::toPercentEncoding(clientId)), QString::fromLatin1(QUrl::toPercentEncoding(code)), + QString::fromLatin1(QUrl::toPercentEncoding(returnUri))); } QString networkReplyFinishedString(const QString &data) { return QStringLiteral("NetworkReplyFinished:") + data; } QString replyDataCallbackString(const QString &data) { return QStringLiteral("ReplyDataCallback:") + data; } -QString tokenCallbackString(const QString &accessToken, const QString &refreshToken, const QString &idToken, - quint64 time, unsigned int tokenLifetime, unsigned int extTokenLifetime, +QString tokenCallbackString(const QString &accessToken, const QString &refreshToken, const QString &idToken, quint64 time, unsigned int tokenLifetime, unsigned int extTokenLifetime, const QString &resource) { - return QStringLiteral("TokenCallback:access_token=%1&expires_in=%2&expires_on=%3&ext_expires_in=%4&foci=1&id_token=%5¬_before=%6&refresh_token=%7&resource=%8&scope=ReadWrite.All&token_type=Bearer") - .arg(accessToken).arg(tokenLifetime).arg(time + tokenLifetime).arg(extTokenLifetime).arg(idToken).arg(time) - .arg(refreshToken).arg(resource); + return QStringLiteral( + "TokenCallback:access_token=%1&expires_in=%2&expires_on=%3&ext_expires_in=%4&foci=1&id_token=%5¬_before=%6&refresh_token=%7&resource=%8&scope=ReadWrite.All&token_type=Bearer") + .arg(accessToken).arg(tokenLifetime).arg(time + tokenLifetime).arg(extTokenLifetime).arg(idToken).arg(time) + .arg(refreshToken).arg(resource); } QString requestWalletMapString() { return QStringLiteral("RequestWalletMap"); } const QString &KJob::errorString() const { static const QString empty; return empty; } -EwsPKeyAuthJob::EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, - QObject *parent) +EwsPKeyAuthJob::EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, QObject *parent) : KJob(parent) { Q_UNUSED(pkeyUri); Q_UNUSED(certFile); Q_UNUSED(keyFile); Q_UNUSED(keyPassword); } const QUrl &EwsPKeyAuthJob::resultUri() const { static const QUrl empty; return empty; } - } diff --git a/resources/ews/test/unittests/ewsoauth_ut_mock.h b/resources/ews/test/unittests/ewsoauth_ut_mock.h index 1e355bcc0..2ff54e5e8 100644 --- a/resources/ews/test/unittests/ewsoauth_ut_mock.h +++ b/resources/ews/test/unittests/ewsoauth_ut_mock.h @@ -1,322 +1,338 @@ /* Copyright (C) 2018 Krzysztof Nowicki 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 EWSOAUTH_UT_MOCK_H #define EWSOAUTH_UT_MOCK_H #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_LOGGING_CATEGORY(EWSCLI_LOG) namespace Mock { - class QWebEngineUrlRequestJob : public QObject { Q_OBJECT public: - explicit QWebEngineUrlRequestJob(const QUrl &url, QObject *parent) : QObject(parent), mUrl(url) {} + explicit QWebEngineUrlRequestJob(const QUrl &url, QObject *parent) : QObject(parent) + , mUrl(url) + { + } + ~QWebEngineUrlRequestJob() override = default; QUrl requestUrl() const; QUrl mUrl; }; class QWebEngineUrlRequestInfo : public QObject { Q_OBJECT public: - explicit QWebEngineUrlRequestInfo(const QUrl &url, QObject *parent) : QObject(parent), mBlocked(false), mUrl(url) {} + explicit QWebEngineUrlRequestInfo(const QUrl &url, QObject *parent) : QObject(parent) + , mBlocked(false) + , mUrl(url) + { + } + ~QWebEngineUrlRequestInfo() override = default; QUrl requestUrl() const; void block(bool shouldBlock); bool mBlocked; QUrl mUrl; }; class QWebEngineUrlRequestInterceptor : public QObject { Q_OBJECT public: explicit QWebEngineUrlRequestInterceptor(QObject *parent); ~QWebEngineUrlRequestInterceptor() override; virtual void interceptRequest(QWebEngineUrlRequestInfo &info) = 0; }; class QWebEngineUrlSchemeHandler : public QObject { Q_OBJECT public: QWebEngineUrlSchemeHandler(QObject *parent); ~QWebEngineUrlSchemeHandler() override; virtual void requestStarted(QWebEngineUrlRequestJob *request) = 0; }; class QWebEngineProfile : public QObject { Q_OBJECT public: explicit QWebEngineProfile(QObject *parent = nullptr); ~QWebEngineProfile() override; void setHttpUserAgent(const QString &ua); void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); void installUrlSchemeHandler(QByteArray const &scheme, QWebEngineUrlSchemeHandler *handler); Q_SIGNALS: void logEvent(const QString &event); public: QString mUserAgent; QWebEngineUrlRequestInterceptor *mInterceptor; QString mScheme; QWebEngineUrlSchemeHandler *mHandler; }; class QWebEnginePage : public QObject { Q_OBJECT public: explicit QWebEnginePage(QWebEngineProfile *profile, QObject *parent = nullptr); ~QWebEnginePage() override; Q_SIGNALS: void logEvent(const QString &event); public: QWebEngineProfile *mProfile; }; class QWebEngineView : public QWidget { Q_OBJECT public: - typedef std::function AuthFunc; + typedef std::function AuthFunc; explicit QWebEngineView(QWidget *parent); ~QWebEngineView() override; void load(const QUrl &url); void setPage(QWebEnginePage *page); void stop(); void setAuthFunction(const AuthFunc &func); void setRedirectUri(const QString &uri); static QPointer instance; Q_SIGNALS: void logEvent(const QString &event); protected: void simulatePageLoad(const QUrl &url); QWebEnginePage *mPage; QString mRedirectUri; AuthFunc mAuthFunction; }; class QNetworkRequest { public: enum KnownHeaders { ContentTypeHeader }; }; class QNetworkReply : public QBuffer { Q_OBJECT public: enum NetworkError { NoError = 0, }; Q_ENUM(NetworkError) - explicit QNetworkReply(QObject *parent) : QBuffer(parent) {} + explicit QNetworkReply(QObject *parent) : QBuffer(parent) + { + } + ~QNetworkReply() override = default; NetworkError error() const; QVariant header(QNetworkRequest::KnownHeaders header) const; QMap mHeaders; NetworkError mError; }; class QAbstractOAuthReplyHandler : public QObject { Q_OBJECT public: QAbstractOAuthReplyHandler(QObject *parent); ~QAbstractOAuthReplyHandler() override; virtual QString callback() const = 0; virtual void networkReplyFinished(QNetworkReply *reply) = 0; Q_SIGNALS: void replyDataReceived(const QByteArray &data); void tokensReceived(const QVariantMap &tokens); }; class QAbstractOAuth : public QObject { Q_OBJECT public: Q_ENUMS(Stage) enum class Stage { RequestingTemporaryCredentials, RequestingAuthorization, RequestingAccessToken, RefreshingAccessToken }; Q_ENUMS(Status) enum class Status { NotAuthenticated, TemporaryCredentialsReceived, Granted, RefreshingToken }; explicit QAbstractOAuth(QObject *parent); ~QAbstractOAuth() override = default; void setReplyHandler(QAbstractOAuthReplyHandler *handler); void setAuthorizationUrl(const QUrl &url); void setClientIdentifier(const QString &identifier); - void setModifyParametersFunction(const std::function*)> &func); + void setModifyParametersFunction(const std::function *)> &func); QString token() const; void setToken(const QString &token); Status status() const; Q_SIGNALS: void authorizeWithBrowser(const QUrl &url); void granted(); void logEvent(const QString &event); protected: QAbstractOAuthReplyHandler *mReplyHandler; QUrl mAuthUrl; QString mClientId; - std::function*)> mModifyParamsFunc; + std::function *)> mModifyParamsFunc; QString mToken; QString mRefreshToken; QUrl mTokenUrl; QString mResource; Status mStatus; }; class QAbstractOAuth2 : public QAbstractOAuth { Q_OBJECT public: explicit QAbstractOAuth2(QObject *parent); ~QAbstractOAuth2() override = default; QString refreshToken() const; void setRefreshToken(const QString &token); Q_SIGNALS: void authorizationCallbackReceived(QMap const ¶ms); void error(const QString &error, const QString &errorDescription, const QUrl &uri); }; class QOAuth2AuthorizationCodeFlow : public QAbstractOAuth2 { Q_OBJECT public: typedef std::function &)> TokenFunc; explicit QOAuth2AuthorizationCodeFlow(QObject *parent = nullptr); ~QOAuth2AuthorizationCodeFlow() override; void setAccessTokenUrl(const QUrl &url); void grant(); void refreshAccessToken(); QString redirectUri() const; void setTokenFunction(const TokenFunc &func); void setState(const QString &state); static QUrlQuery mapToSortedQuery(QMap const &map); static QPointer instance; protected: void authCallbackReceived(QMap const ¶ms); void replyDataCallbackReceived(const QByteArray &data); void tokenCallbackReceived(const QVariantMap &tokens); void doRefreshAccessToken(); TokenFunc mTokenFunc; QString mState; }; class KJob : public QObject { Q_OBJECT public: - explicit KJob(QObject *) {} + explicit KJob(QObject *) + { + } + ~KJob() override = default; - int error() const { return 0; } + int error() const + { + return 0; + } + const QString &errorString() const; Q_SIGNALS: void result(KJob *job); }; class EwsPKeyAuthJob : public KJob { Q_OBJECT public: - explicit EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, - QObject *parent); + explicit EwsPKeyAuthJob(const QUrl &pkeyUri, const QString &certFile, const QString &keyFile, const QString &keyPassword, QObject *parent); ~EwsPKeyAuthJob() override = default; - void start() {} + void start() + { + } const QUrl &resultUri() const; }; QString browserDisplayRequestString(); QString modifyParamsAuthString(const QString &clientId, const QString &returnUri, const QString &state); -QString authUrlString(const QString &authUrl, const QString &clientId, const QString &returnUri, - const QString &email, const QString &resource, const QString &state); +QString authUrlString(const QString &authUrl, const QString &clientId, const QString &returnUri, const QString &email, const QString &resource, const QString &state); QString authorizeWithBrowserString(const QString &url); QString loadWebPageString(const QString &url); QString interceptRequestString(const QString &url); QString interceptRequestBlockedString(bool blocked); QString authorizationCallbackReceivedString(const QString &code); QString modifyParamsTokenString(const QString &clientId, const QString &returnUri, const QString &code); QString networkReplyFinishedString(const QString &data); QString replyDataCallbackString(const QString &data); -QString tokenCallbackString(const QString &accessToken, const QString &refreshToken, const QString &idToken, - quint64 time, unsigned int tokenLifetime, unsigned int extTokenLifetime, +QString tokenCallbackString(const QString &accessToken, const QString &refreshToken, const QString &idToken, quint64 time, unsigned int tokenLifetime, unsigned int extTokenLifetime, const QString &resource); QString requestWalletMapString(); - } #endif /* EWSOAUTH_UT_MOCK_H */ diff --git a/resources/ews/test/unittests/ewssettings_ut.cpp b/resources/ews/test/unittests/ewssettings_ut.cpp index 88e60593e..71c958da6 100644 --- a/resources/ews/test/unittests/ewssettings_ut.cpp +++ b/resources/ews/test/unittests/ewssettings_ut.cpp @@ -1,871 +1,859 @@ /* Copyright (C) 2017-2018 Krzysztof Nowicki 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 #include #include #include "ewssettings.h" #include "ewssettings_ut_mock.h" Q_LOGGING_CATEGORY(EWSRES_LOG, "org.kde.pim.ews", QtInfoMsg) static const QString accessTokenMapKey = QStringLiteral("access-token"); static const QString refreshTokenMapKey = QStringLiteral("refresh-token"); class UtEwsSettings : public QObject { Q_OBJECT private Q_SLOTS: void readNoPassword(); void readNullWallet(); void readTimeout(); void readTimeoutInterrupted(); void readValidPassword(); void writeNullPassword(); void writeNullWallet(); void writeTimeout(); void writeValidPassword(); void readValidMap(); void writeValidMap(); }; namespace KWallet { - class MyWallet : public Wallet { Q_OBJECT public: MyWallet(); ~MyWallet() override; void doOpen(bool success); bool hasFolder(const QString &folder) override; bool setFolder(const QString &folder) override; bool createFolder(const QString &folder) override; int readPassword(const QString &key, QString &value) override; int writePassword(const QString &key, const QString &value) override; int readMap(const QString &key, QMap &value) override; int writeMap(const QString &key, const QMap &value) override; std::function hasFolderCallback; std::function setFolderCallback; std::function createFolderCallback; std::function readPasswordCallback; std::function writePasswordCallback; std::function &)> readMapCallback; std::function &)> writeMapCallback; }; static std::function errorCallback; -static std::function openWalletCallback; +static std::function openWalletCallback; void reportError() { if (errorCallback) { errorCallback(); } } Wallet *openWallet(MyWallet *wallet) { if (openWalletCallback) { return openWalletCallback(wallet); } else { qDebug() << "Wallet open callback not registered!"; errorCallback(); return wallet; } } static const QString networkWallet = QStringLiteral("test_network_wallet"); const QString Wallet::NetworkWallet() { return networkWallet; } Wallet *Wallet::openWallet(const QString &name, WId, OpenType ot) { qDebug() << "intercepted openWallet"; if (name != networkWallet) { qDebug() << "Incorrect wallet name"; reportError(); } if (ot != Asynchronous) { qDebug() << "Unsopported open type"; reportError(); } auto wallet = new MyWallet(); return KWallet::openWallet(wallet); } MyWallet::MyWallet() : Wallet(0, networkWallet) { - } MyWallet::~MyWallet() { } void MyWallet::doOpen(bool success) { Q_EMIT walletOpened(success); } bool MyWallet::hasFolder(const QString &folder) { if (hasFolderCallback) { return hasFolderCallback(folder); } else { qWarning() << "hasFolder() callback not set!"; reportError(); return false; } } bool MyWallet::setFolder(const QString &folder) { if (setFolderCallback) { return setFolderCallback(folder); } else { qWarning() << "setFolder() callback not set!"; reportError(); return false; } } bool MyWallet::createFolder(const QString &folder) { if (createFolderCallback) { return createFolderCallback(folder); } else { qWarning() << "createFolder() callback not set!"; reportError(); return false; } } int MyWallet::readPassword(const QString &key, QString &value) { if (readPasswordCallback) { return readPasswordCallback(key, value); } else { qWarning() << "readPasswordCallback() callback not set!"; reportError(); return 0; } } int MyWallet::writePassword(const QString &key, const QString &value) { if (writePasswordCallback) { return writePasswordCallback(key, value); } else { qWarning() << "writePasswordCallback() callback not set!"; reportError(); return 0; } } int MyWallet::readMap(const QString &key, QMap &value) { if (readMapCallback) { return readMapCallback(key, value); } else { qWarning() << "readMapCallback() callback not set!"; reportError(); return 0; } } int MyWallet::writeMap(const QString &key, const QMap &value) { if (writeMapCallback) { return writeMapCallback(key, value); } else { qWarning() << "writeMapCallback() callback not set!"; reportError(); return 0; } } - } void UtEwsSettings::readNoPassword() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; QString password; EwsSettings settings(0); connect(&settings, &EwsSettings::passwordRequestFinished, this, [&](const QString &p) { password = p; loop.exit(0); }); QTimer::singleShot(100, [&]() { settings.requestPassword(false); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [](const QString &) { - return false; - }; + return false; + }; wallet->doOpen(true); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password.isNull()); QVERIFY(hasFolderCalled); } void UtEwsSettings::readNullWallet() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *) { - return nullptr; - }; + return nullptr; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; QString password; EwsSettings settings(0); connect(&settings, &EwsSettings::passwordRequestFinished, this, [&](const QString &p) { password = p; loop.exit(0); }); QTimer::singleShot(100, [&]() { settings.requestPassword(false); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); - QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password.isNull()); } void UtEwsSettings::readTimeout() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; QString password; EwsSettings settings(0); connect(&settings, &EwsSettings::passwordRequestFinished, this, [&](const QString &p) { password = p; loop.exit(0); }); QTimer::singleShot(100, [&]() { settings.requestPassword(false); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [](const QString &) { - return false; - }; + return false; + }; }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(5000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password.isNull()); QVERIFY(!hasFolderCalled); } void UtEwsSettings::readTimeoutInterrupted() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; QString password; EwsSettings settings(0); bool testSecondSignal = false; connect(&settings, &EwsSettings::passwordRequestFinished, this, [&](const QString &p) { if (!testSecondSignal) { password = p; loop.exit(0); } else { loop.exit(1); } }); QTimer::singleShot(100, [&]() { settings.requestPassword(false); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [](const QString &) { - return false; - }; + return false; + }; }); QTimer::singleShot(1000, [&]() { settings.setTestPassword(QStringLiteral("foo")); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(2); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(5000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password == QStringLiteral("foo")); QVERIFY(!hasFolderCalled); // Check for second passwordRequestFinished signal QVERIFY(loop.exec() == 2); } void UtEwsSettings::readValidPassword() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; bool setFolderCalled = false; QString password; EwsSettings settings(0); connect(&settings, &EwsSettings::passwordRequestFinished, this, [&](const QString &p) { password = p; loop.exit(0); }); QTimer::singleShot(100, [&]() { settings.requestPassword(false); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return true; - }; + hasFolderCalled = true; + return true; + }; wallet->createFolderCallback = [](const QString &) { - return false; - }; + return false; + }; wallet->setFolderCallback = [&setFolderCalled](const QString &) { - setFolderCalled = true; - return true; - }; + setFolderCalled = true; + return true; + }; wallet->readPasswordCallback = [](const QString &, QString &password) { - password = QStringLiteral("foo"); - return true; - }; + password = QStringLiteral("foo"); + return true; + }; wallet->doOpen(true); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password == QStringLiteral("foo")); QVERIFY(hasFolderCalled); QVERIFY(setFolderCalled); } void UtEwsSettings::writeNullPassword() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; EwsSettings settings(0); QTimer::singleShot(100, [&]() { settings.setPassword(QString()); if (wallet) { qDebug() << "Wallet is not null"; loop.exit(1); return; } loop.exit(0); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); } void UtEwsSettings::writeNullWallet() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *) { - return nullptr; - }; + return nullptr; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; EwsSettings settings(0); QTimer::singleShot(100, [&]() { settings.setPassword(QStringLiteral("foo")); if (wallet) { qDebug() << "Wallet is not null"; loop.exit(1); return; } loop.exit(0); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); } void UtEwsSettings::writeTimeout() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; bool createFolderCalled = false; bool setFolderCalled = false; QString password; EwsSettings settings(0); QTimer::singleShot(100, [&]() { settings.setPassword(QStringLiteral("foo")); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [&createFolderCalled](const QString &) { - createFolderCalled = true; - return true; - }; + createFolderCalled = true; + return true; + }; wallet->setFolderCallback = [&setFolderCalled](const QString &) { - setFolderCalled = true; - return false; - }; + setFolderCalled = true; + return false; + }; wallet->writePasswordCallback = [&](const QString &, const QString &p) { - password = p; - loop.exit(0); - return true; - }; + password = p; + loop.exit(0); + return true; + }; }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(2); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(5000); QVERIFY(error != true); QVERIFY(loop.exec() == 2); QVERIFY(password.isNull()); QVERIFY(!hasFolderCalled); QVERIFY(!setFolderCalled); QVERIFY(!createFolderCalled); } void UtEwsSettings::writeValidPassword() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; bool createFolderCalled = false; bool setFolderCalled = false; QString password; EwsSettings settings(0); QTimer::singleShot(100, [&]() { settings.setPassword(QStringLiteral("foo")); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [&createFolderCalled](const QString &) { - createFolderCalled = true; - return true; - }; + createFolderCalled = true; + return true; + }; wallet->setFolderCallback = [&setFolderCalled](const QString &) { - setFolderCalled = true; - return false; - }; + setFolderCalled = true; + return false; + }; wallet->writePasswordCallback = [&](const QString &, const QString &p) { - password = p; - loop.exit(0); - return true; - }; + password = p; + loop.exit(0); + return true; + }; wallet->doOpen(true); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(password == QStringLiteral("foo")); QVERIFY(hasFolderCalled); QVERIFY(setFolderCalled); QVERIFY(createFolderCalled); } void UtEwsSettings::readValidMap() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; bool setFolderCalled = false; QMap map; const QMap expectedMap = { {accessTokenMapKey, QStringLiteral("afoo")}, {refreshTokenMapKey, QStringLiteral("rfoo")} }; EwsSettings settings(0); connect(&settings, &EwsSettings::mapRequestFinished, this, [&](const QMap &m) { map = m; loop.exit(0); }); QTimer::singleShot(100, [&]() { settings.requestMap(); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return true; - }; + hasFolderCalled = true; + return true; + }; wallet->createFolderCallback = [](const QString &) { - return false; - }; + return false; + }; wallet->setFolderCallback = [&setFolderCalled](const QString &) { - setFolderCalled = true; - return true; - }; + setFolderCalled = true; + return true; + }; wallet->readMapCallback = [](const QString &, QMap &map) { - map[accessTokenMapKey] = QStringLiteral("afoo"); - map[refreshTokenMapKey] = QStringLiteral("rfoo"); - return true; - }; + map[accessTokenMapKey] = QStringLiteral("afoo"); + map[refreshTokenMapKey] = QStringLiteral("rfoo"); + return true; + }; wallet->doOpen(true); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(map == expectedMap); QVERIFY(hasFolderCalled); QVERIFY(setFolderCalled); } void UtEwsSettings::writeValidMap() { KWallet::MyWallet *wallet = nullptr; KWallet::openWalletCallback = [&wallet](KWallet::MyWallet *w) { - wallet = w; - return w; - }; + wallet = w; + return w; + }; QEventLoop loop; bool error = false; KWallet::errorCallback = [&]() { - if (loop.isRunning()) { - loop.exit(1); - } else { - error = true; - } - }; - + if (loop.isRunning()) { + loop.exit(1); + } else { + error = true; + } + }; bool hasFolderCalled = false; bool createFolderCalled = false; bool setFolderCalled = false; const QMap expectedMap = { {accessTokenMapKey, QStringLiteral("afoo")}, {refreshTokenMapKey, QStringLiteral("rfoo")} }; QMap map; EwsSettings settings(0); QTimer::singleShot(100, [&]() { settings.setMap(expectedMap); if (!wallet) { qDebug() << "Wallet is null"; loop.exit(1); return; } wallet->hasFolderCallback = [&hasFolderCalled](const QString &) { - hasFolderCalled = true; - return false; - }; + hasFolderCalled = true; + return false; + }; wallet->createFolderCallback = [&createFolderCalled](const QString &) { - createFolderCalled = true; - return true; - }; + createFolderCalled = true; + return true; + }; wallet->setFolderCallback = [&setFolderCalled](const QString &) { - setFolderCalled = true; - return false; - }; + setFolderCalled = true; + return false; + }; wallet->writeMapCallback = [&](const QString &, const QMap &m) { - map = m; - loop.exit(0); - return true; - }; + map = m; + loop.exit(0); + return true; + }; wallet->doOpen(true); }); QTimer timeoutTimer; connect(&timeoutTimer, &QTimer::timeout, this, [&]() { qDebug() << "Test timeout"; loop.exit(1); }); timeoutTimer.setSingleShot(true); timeoutTimer.start(2000); QVERIFY(error != true); QVERIFY(loop.exec() == 0); QVERIFY(map == expectedMap); QVERIFY(hasFolderCalled); QVERIFY(setFolderCalled); QVERIFY(createFolderCalled); } QTEST_MAIN(UtEwsSettings) #include "ewssettings_ut.moc" diff --git a/resources/ews/test/unittests/ewssettings_ut_mock.h b/resources/ews/test/unittests/ewssettings_ut_mock.h index de7a03cd8..63bd284ff 100644 --- a/resources/ews/test/unittests/ewssettings_ut_mock.h +++ b/resources/ews/test/unittests/ewssettings_ut_mock.h @@ -1,47 +1,65 @@ /* Copyright (C) 2017 Krzysztof Nowicki 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 EWSSETTINGS_UT_MOCK_H #define EWSSETTINGS_UT_MOCK_H #include #include #include Q_DECLARE_LOGGING_CATEGORY(EWSRES_LOG) class EwsSettingsBase : public QObject { Q_OBJECT public: class Config { public: - QString name() const { return QStringLiteral("test_resource_name"); } + QString name() const + { + return QStringLiteral("test_resource_name"); + } }; - EwsSettingsBase() {} + EwsSettingsBase() + { + } + virtual ~EwsSettingsBase() = default; - QString username() const { return QStringLiteral("testuser"); } - QString email() const { return QStringLiteral("test@example.com"); } - const Config *config() const { return &mConfig; } + QString username() const + { + return QStringLiteral("testuser"); + } + + QString email() const + { + return QStringLiteral("test@example.com"); + } + + const Config *config() const + { + return &mConfig; + } + Config mConfig; }; #endif /* EWSSETTINGSBASE_H */ diff --git a/resources/ews/test/unittests/ewsunsubscriberequest_ut.cpp b/resources/ews/test/unittests/ewsunsubscriberequest_ut.cpp index 7fd16c2bf..bfdb379a3 100644 --- a/resources/ews/test/unittests/ewsunsubscriberequest_ut.cpp +++ b/resources/ews/test/unittests/ewsunsubscriberequest_ut.cpp @@ -1,96 +1,94 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 #include "fakehttppost.h" #include "ewsunsubscriberequest.h" class UtEwsUnsibscribeRequest : public QObject { Q_OBJECT private Q_SLOTS: void simple(); private: - void verifier(FakeTransferJob* job, const QByteArray &req, const QByteArray &expReq, - const QByteArray &resp); + void verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &resp); EwsClient mClient; }; void UtEwsUnsibscribeRequest::simple() { static const QByteArray request = "" - "" - "" - "" - "" - "dwzVKTlwXxBZtQRMucP5Mg==" - "\n"; + "" + "" + "" + "" + "dwzVKTlwXxBZtQRMucP5Mg==" + "\n"; static const QByteArray response = "" - "" - "" - "" - "" - "" - "" - "" - "NoError" - "" - "" - "" - ""; + "" + "" + "" + "" + "" + "" + "" + "NoError" + "" + "" + "" + ""; - FakeTransferJob::addVerifier(this, [this](FakeTransferJob* job, const QByteArray &req){ + FakeTransferJob::addVerifier(this, [this](FakeTransferJob *job, const QByteArray &req){ verifier(job, req, request, response); }); QScopedPointer req(new EwsUnsubscribeRequest(mClient, this)); req->setSubscriptionId(QStringLiteral("dwzVKTlwXxBZtQRMucP5Mg==")); req->exec(); QCOMPARE(req->error(), 0); } -void UtEwsUnsibscribeRequest::verifier(FakeTransferJob* job, const QByteArray &req, - const QByteArray &expReq, const QByteArray &response) +void UtEwsUnsibscribeRequest::verifier(FakeTransferJob *job, const QByteArray &req, const QByteArray &expReq, const QByteArray &response) { bool fail = true; - auto f = finally([&fail,&job]{ + auto f = finally([&fail, &job] { if (fail) { job->postResponse(""); } }); QCOMPARE(req, expReq); fail = false; job->postResponse(response); } QTEST_MAIN(UtEwsUnsibscribeRequest) #include "ewsunsubscriberequest_ut.moc" diff --git a/resources/ews/test/unittests/fakehttppost.h b/resources/ews/test/unittests/fakehttppost.h index 2cf351abc..e47231a2c 100644 --- a/resources/ews/test/unittests/fakehttppost.h +++ b/resources/ews/test/unittests/fakehttppost.h @@ -1,43 +1,39 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 FAKEHTTPPOST_H #define FAKEHTTPPOST_H - #include #include "faketransferjob.h" -namespace KIO -{ - +namespace KIO { TransferJob *http_post(const QUrl &url, const QByteArray &postData, JobFlags flags) { Q_UNUSED(url); Q_UNUSED(flags); FakeTransferJob::Verifier vfy = FakeTransferJob::getVerifier(); FakeTransferJob *job = new FakeTransferJob(postData, vfy.fn, vfy.object); - return reinterpret_cast(job); + return reinterpret_cast(job); } - } #endif diff --git a/resources/ews/test/unittests/faketransferjob.cpp b/resources/ews/test/unittests/faketransferjob.cpp index f9fac9f4a..ed724fbae 100644 --- a/resources/ews/test/unittests/faketransferjob.cpp +++ b/resources/ews/test/unittests/faketransferjob.cpp @@ -1,74 +1,74 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 "faketransferjob.h" #include #include QQueue FakeTransferJob::mVerifierQueue; FakeTransferJob::FakeTransferJob(const QByteArray &postData, VerifierFn fn, QObject *parent) - : KIO::SpecialJob(QUrl(QStringLiteral("file:///tmp/")), QByteArray()), mPostData(postData), - mVerifier(fn) + : KIO::SpecialJob(QUrl(QStringLiteral("file:///tmp/")), QByteArray()) + , mPostData(postData) + , mVerifier(fn) { Q_UNUSED(parent); metaObject()->invokeMethod(this, "callVerifier", Qt::QueuedConnection); } FakeTransferJob::~FakeTransferJob() { - } void FakeTransferJob::callVerifier() { mVerifier(this, mPostData); } void FakeTransferJob::postResponse(const QByteArray &resp) { mResponse = resp; - qRegisterMetaType(); + qRegisterMetaType(); metaObject()->invokeMethod(this, "doData", Qt::QueuedConnection, Q_ARG(QByteArray, mResponse)); metaObject()->invokeMethod(this, "doEmitResult", Qt::QueuedConnection); } void FakeTransferJob::doData(const QByteArray &resp) { Q_EMIT data(this, resp); } void FakeTransferJob::doEmitResult() { emitResult(); } void FakeTransferJob::addVerifier(QObject *obj, VerifierFn fn) { Verifier vfy = {obj, fn}; mVerifierQueue.enqueue(vfy); } FakeTransferJob::Verifier FakeTransferJob::getVerifier() { return mVerifierQueue.dequeue(); } diff --git a/resources/ews/test/unittests/faketransferjob.h b/resources/ews/test/unittests/faketransferjob.h index 8005a4d5b..136103bb6 100644 --- a/resources/ews/test/unittests/faketransferjob.h +++ b/resources/ews/test/unittests/faketransferjob.h @@ -1,81 +1,84 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki 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 FAKETRANSFERJOB_H #define FAKETRANSFERJOB_H #include #include #include -namespace KIO -{ +namespace KIO { class Job; } -template +template struct Finally { - Finally(F f): cleanupf{f} {} + Finally(F f) : cleanupf{f} + { + } + ~Finally() { cleanupf(); } + F cleanupf; }; -template +template Finally finally(F f) { return Finally(f); } class FakeTransferJob : public KIO::SpecialJob { Q_OBJECT public: - typedef std::function VerifierFn; + typedef std::function VerifierFn; struct Verifier { QObject *object; VerifierFn fn; }; FakeTransferJob(const QByteArray &postData, VerifierFn fn, QObject *parent = nullptr); ~FakeTransferJob(); static void addVerifier(QObject *obj, VerifierFn fn); static Verifier getVerifier(); public Q_SLOTS: void postResponse(const QByteArray &resp); private Q_SLOTS: void callVerifier(); void doEmitResult(); void doData(const QByteArray &resp); Q_SIGNALS: void requestReceived(FakeTransferJob *job, const QByteArray &req); private: QByteArray mPostData; QByteArray mResponse; VerifierFn mVerifier; static QQueue mVerifierQueue; }; #endif diff --git a/resources/facebook/facebooksettingswidget.cpp b/resources/facebook/facebooksettingswidget.cpp index b8f9d1738..b1dad766f 100644 --- a/resources/facebook/facebooksettingswidget.cpp +++ b/resources/facebook/facebooksettingswidget.cpp @@ -1,141 +1,137 @@ /* * Copyright (C) 2017 Daniel Vrátil * Copyright (C) 2018-2019 Laurent Montel * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "facebooksettingswidget.h" #include "ui_facebookagentsettingswidget.h" #include "settings.h" #include "resource.h" #include "tokenjobs.h" #include FacebookSettingsWidget::FacebookSettingsWidget(const KSharedConfigPtr &config, QWidget *parent, const QVariantList &args) : Akonadi::AgentConfigurationBase(config, parent, args) , ui(new Ui::FacebookAgentSettingsWidget) { Settings::instance(config); QWidget *mainWidget = new QWidget(parent); ui->setupUi(mainWidget); parent->layout()->addWidget(mainWidget); auto settings = Settings::self(); ui->attendingReminderChkBox->setChecked(settings->attendingReminders()); ui->maybeAttendingReminderChkBox->setChecked(settings->maybeAttendingReminders()); ui->notAttendingReminderChkBox->setChecked(settings->notAttendingReminders()); ui->notRespondedReminderChkBox->setChecked(settings->notRespondedToReminders()); ui->birthdayReminderChkBox->setChecked(settings->birthdayReminders()); ui->eventReminderHoursSpinBox->setValue(settings->eventReminderHours()); ui->birthdayReminderDaysSpinBox->setValue(settings->birthdayReminderDays()); connect(ui->loginBtn, &QPushButton::clicked, this, &FacebookSettingsWidget::login); connect(ui->logoutBtn, &QPushButton::clicked, this, &FacebookSettingsWidget::logout); - - } FacebookSettingsWidget::~FacebookSettingsWidget() { } - void FacebookSettingsWidget::checkToken() { ui->loginBtn->setVisible(false); ui->logoutBtn->setVisible(false); ui->loginStatusLbl->setText(i18n("Checking login status...")); auto job = new GetTokenJob(identifier(), this); connect(job, &GetTokenJob::result, this, [this, job]() { if (job->error()) { ui->loginStatusLbl->setText(job->errorText()); } else { const auto token = job->token(); if (token.isEmpty()) { ui->loginStatusLbl->setText(i18n("Not logged in")); ui->logoutBtn->setVisible(false); ui->loginBtn->setVisible(true); } else { ui->loginStatusLbl->setText(i18n("Logged in as %1", job->userName())); ui->loginBtn->setVisible(false); ui->logoutBtn->setVisible(true); } } }); job->start(); } void FacebookSettingsWidget::login() { ui->loginBtn->setEnabled(false); auto job = new LoginJob(identifier(), this); connect(job, &LoginJob::result, this, [this](KJob *job) { if (job->error()) { ui->loginStatusLbl->setText(job->errorText()); } else { checkToken(); } }); job->start(); } void FacebookSettingsWidget::logout() { ui->logoutBtn->setEnabled(false); auto job = new LogoutJob(identifier(), this); connect(job, &LogoutJob::result, this, [this](KJob *job) { if (job->error()) { ui->loginStatusLbl->setText(job->errorText()); } else { checkToken(); } }); job->start(); } - void FacebookSettingsWidget::load() { checkToken(); } bool FacebookSettingsWidget::save() const { auto settings = Settings::self(); const bool changed = ui->attendingReminderChkBox->isChecked() != settings->attendingReminders() || ui->maybeAttendingReminderChkBox->isChecked() != settings->maybeAttendingReminders() || ui->notAttendingReminderChkBox->isChecked() != settings->notAttendingReminders() || ui->notRespondedReminderChkBox->isChecked() != settings->notRespondedToReminders() || ui->birthdayReminderChkBox->isChecked() != settings->birthdayReminders() || ui->eventReminderHoursSpinBox->value() != settings->eventReminderHours() || ui->birthdayReminderDaysSpinBox->value() != settings->birthdayReminderDays(); if (changed) { settings->setAttendingReminders(ui->attendingReminderChkBox->isChecked()); settings->setMaybeAttendingReminders(ui->maybeAttendingReminderChkBox->isChecked()); settings->setNotAttendingReminders(ui->notAttendingReminderChkBox->isChecked()); settings->setNotAttendingReminders(ui->notRespondedReminderChkBox->isChecked()); settings->setBirthdayReminders(ui->birthdayReminderChkBox->isChecked()); settings->setEventReminderHours(ui->eventReminderHoursSpinBox->value()); settings->setBirthdayReminderDays(ui->birthdayReminderDaysSpinBox->value()); } return true; } diff --git a/resources/facebook/facebooksettingswidget.h b/resources/facebook/facebooksettingswidget.h index 0c40fe2ea..a10d8c0d2 100644 --- a/resources/facebook/facebooksettingswidget.h +++ b/resources/facebook/facebooksettingswidget.h @@ -1,46 +1,45 @@ /* * Copyright (C) 2017 Daniel Vrátil * Copyright (C) 2018-2019 Laurent Montel * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FACEBOOKSETTINGSWIDGET_H_ #define FACEBOOKSETTINGSWIDGET_H_ #include #include class Ui_FacebookAgentSettingsWidget; class FacebookSettingsWidget : public Akonadi::AgentConfigurationBase { Q_OBJECT public: explicit FacebookSettingsWidget(const KSharedConfigPtr &config, QWidget *parent, const QVariantList &args); ~FacebookSettingsWidget() override; void load() override; bool save() const override; -private Q_SLOT: + private Q_SLOT: void checkToken(); void login(); void logout(); private: QScopedPointer ui; - }; AKONADI_AGENTCONFIG_FACTORY(FacebookSettingsWidgetFactory, "facebookconfig.json", FacebookSettingsWidget) #endif