1 /*
2     SPDX-FileCopyrightText: 2015-2017 Krzysztof Nowicki <krissn@op.pl>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "ewssyncfolderitemsrequest.h"
8 
9 #include <memory>
10 
11 #include <QXmlStreamWriter>
12 
13 #include "ewsclient_debug.h"
14 #include "ewsxml.h"
15 
16 enum SyncFolderItemsResponseElementType {
17     SyncState,
18     IncludesLastItemInRange,
19     Changes,
20 };
21 
22 enum SyncFolderItemsChangeElementType {
23     Item,
24     ItemId,
25     IsRead,
26 };
27 
28 class EwsSyncFolderItemsRequest::Response : public EwsRequest::Response
29 {
30 public:
31     Response(QXmlStreamReader &reader);
32 
33     static bool changeReader(QXmlStreamReader &reader, QVariant &val);
34 
35     EwsSyncFolderItemsRequest::Change::List mChanges;
36     bool mIncludesLastItem;
37     QString mSyncState;
38 };
39 
EwsSyncFolderItemsRequest(EwsClient & client,QObject * parent)40 EwsSyncFolderItemsRequest::EwsSyncFolderItemsRequest(EwsClient &client, QObject *parent)
41     : EwsRequest(client, parent)
42     , mMaxChanges(100)
43     , mIncludesLastItem(false)
44 {
45     qRegisterMetaType<EwsSyncFolderItemsRequest::Change::List>();
46     qRegisterMetaType<EwsItem>();
47 }
48 
~EwsSyncFolderItemsRequest()49 EwsSyncFolderItemsRequest::~EwsSyncFolderItemsRequest()
50 {
51 }
52 
setFolderId(const EwsId & id)53 void EwsSyncFolderItemsRequest::setFolderId(const EwsId &id)
54 {
55     mFolderId = id;
56 }
57 
setItemShape(const EwsItemShape & shape)58 void EwsSyncFolderItemsRequest::setItemShape(const EwsItemShape &shape)
59 {
60     mShape = shape;
61 }
62 
setSyncState(const QString & state)63 void EwsSyncFolderItemsRequest::setSyncState(const QString &state)
64 {
65     mSyncState = state;
66 }
67 
setMaxChanges(uint max)68 void EwsSyncFolderItemsRequest::setMaxChanges(uint max)
69 {
70     mMaxChanges = max;
71 }
72 
start()73 void EwsSyncFolderItemsRequest::start()
74 {
75     QString reqString;
76     QXmlStreamWriter writer(&reqString);
77 
78     startSoapDocument(writer);
79 
80     writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderItems"));
81 
82     mShape.write(writer);
83 
84     writer.writeStartElement(ewsMsgNsUri, QStringLiteral("SyncFolderId"));
85     mFolderId.writeFolderIds(writer);
86     writer.writeEndElement();
87 
88     if (!mSyncState.isNull()) {
89         writer.writeTextElement(ewsMsgNsUri, QStringLiteral("SyncState"), mSyncState);
90     }
91 
92     writer.writeTextElement(ewsMsgNsUri, QStringLiteral("MaxChangesReturned"), QString::number(mMaxChanges));
93 
94     writer.writeEndElement();
95 
96     endSoapDocument(writer);
97 
98     qCDebug(EWSCLI_PROTO_LOG) << reqString;
99 
100     if (EWSCLI_REQUEST_LOG().isDebugEnabled()) {
101         QString st = mSyncState.isNull() ? QStringLiteral("none") : ewsHash(mSyncState);
102         qCDebugNCS(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting SyncFolderItems request (folder: ") << mFolderId << QStringLiteral(", state: %1").arg(st);
103     }
104 
105     prepare(reqString);
106 
107     doSend();
108 }
109 
parseResult(QXmlStreamReader & reader)110 bool EwsSyncFolderItemsRequest::parseResult(QXmlStreamReader &reader)
111 {
112     return parseResponseMessage(reader, QStringLiteral("SyncFolderItems"), [this](QXmlStreamReader &reader) {
113         return parseItemsResponse(reader);
114     });
115 }
116 
parseItemsResponse(QXmlStreamReader & reader)117 bool EwsSyncFolderItemsRequest::parseItemsResponse(QXmlStreamReader &reader)
118 {
119     QScopedPointer<EwsSyncFolderItemsRequest::Response> resp(new EwsSyncFolderItemsRequest::Response(reader));
120     if (resp->responseClass() == EwsResponseUnknown) {
121         return false;
122     }
123 
124     mChanges = resp->mChanges;
125     mIncludesLastItem = resp->mIncludesLastItem;
126     mSyncState = resp->mSyncState;
127 
128     if (EWSCLI_REQUEST_LOG().isDebugEnabled()) {
129         if (resp->isSuccess()) {
130             qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderItems response (%1 changes, last included: %2, state: %3)")
131                                                  .arg(mChanges.size())
132                                                  .arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false"))
133                                                  .arg(qHash(mSyncState), 0, 36);
134         } else {
135             qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got SyncFolderItems response - %1").arg(resp->responseMessage());
136         }
137     }
138 
139     return true;
140 }
141 
Response(QXmlStreamReader & reader)142 EwsSyncFolderItemsRequest::Response::Response(QXmlStreamReader &reader)
143     : EwsRequest::Response(reader)
144 {
145     if (mClass == EwsResponseParseError) {
146         return;
147     }
148 
149     static const QVector<EwsXml<SyncFolderItemsResponseElementType>::Item> items = {
150         {SyncState, QStringLiteral("SyncState"), &ewsXmlTextReader},
151         {IncludesLastItemInRange, QStringLiteral("IncludesLastItemInRange"), &ewsXmlBoolReader},
152         {Changes, QStringLiteral("Changes"), &EwsSyncFolderItemsRequest::Response::changeReader},
153     };
154     static const EwsXml<SyncFolderItemsResponseElementType> staticReader(items);
155 
156     EwsXml<SyncFolderItemsResponseElementType> ewsReader(staticReader);
157 
158     if (!ewsReader.readItems(reader, ewsMsgNsUri, [this](QXmlStreamReader &reader, const QString &) {
159             if (!readResponseElement(reader)) {
160                 setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element."));
161                 return false;
162             }
163             return true;
164         })) {
165         mClass = EwsResponseParseError;
166         return;
167     }
168 
169     QHash<SyncFolderItemsResponseElementType, QVariant> values = ewsReader.values();
170 
171     mSyncState = values[SyncState].toString();
172     mIncludesLastItem = values[IncludesLastItemInRange].toBool();
173     mChanges = values[Changes].value<Change::List>();
174 }
175 
changeReader(QXmlStreamReader & reader,QVariant & val)176 bool EwsSyncFolderItemsRequest::Response::changeReader(QXmlStreamReader &reader, QVariant &val)
177 {
178     Change::List changes;
179     QString elmName(reader.name().toString());
180 
181     while (reader.readNextStartElement()) {
182         Change change(reader);
183         if (!change.isValid()) {
184             qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element").arg(elmName);
185             return false;
186         }
187         changes.append(change);
188     }
189 
190     val = QVariant::fromValue<Change::List>(changes);
191     return true;
192 }
193 
Change(QXmlStreamReader & reader)194 EwsSyncFolderItemsRequest::Change::Change(QXmlStreamReader &reader)
195 {
196     static const QVector<EwsXml<SyncFolderItemsChangeElementType>::Item> items = {
197         {Item, QStringLiteral("Item"), &ewsXmlItemReader},
198         {Item, QStringLiteral("Message"), &ewsXmlItemReader},
199         {Item, QStringLiteral("CalendarItem"), &ewsXmlItemReader},
200         {Item, QStringLiteral("Contact"), &ewsXmlItemReader},
201         {Item, QStringLiteral("DistributionList"), &ewsXmlItemReader},
202         {Item, QStringLiteral("MeetingMessage"), &ewsXmlItemReader},
203         {Item, QStringLiteral("MeetingRequest"), &ewsXmlItemReader},
204         {Item, QStringLiteral("MeetingResponse"), &ewsXmlItemReader},
205         {Item, QStringLiteral("MeetingCancellation"), &ewsXmlItemReader},
206         {Item, QStringLiteral("Task"), &ewsXmlItemReader},
207         {ItemId, QStringLiteral("ItemId"), &ewsXmlIdReader},
208         {IsRead, QStringLiteral("IsRead"), &ewsXmlBoolReader},
209     };
210     static const EwsXml<SyncFolderItemsChangeElementType> staticReader(items);
211 
212     EwsXml<SyncFolderItemsChangeElementType> ewsReader(staticReader);
213 
214     if (reader.name() == QLatin1String("Create")) {
215         mType = Create;
216     } else if (reader.name() == QLatin1String("Update")) {
217         mType = Update;
218     } else if (reader.name() == QLatin1String("Delete")) {
219         mType = Delete;
220     } else if (reader.name() == QLatin1String("ReadFlagChange")) {
221         mType = ReadFlagChange;
222     }
223     if (!ewsReader.readItems(reader, ewsTypeNsUri)) {
224         return;
225     }
226 
227     QHash<SyncFolderItemsChangeElementType, QVariant> values = ewsReader.values();
228 
229     switch (mType) {
230     case Create:
231     case Update:
232         mItem = values[Item].value<EwsItem>();
233         mId = mItem[EwsItemFieldItemId].value<EwsId>();
234         break;
235     case ReadFlagChange:
236         mIsRead = values[IsRead].toBool();
237         /* fall through */
238         Q_FALLTHROUGH();
239     case Delete:
240         mId = values[ItemId].value<EwsId>();
241         break;
242     default:
243         break;
244     }
245 }
246