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