1 /*
2 SPDX-FileCopyrightText: 2015-2016 Krzysztof Nowicki <krissn@op.pl>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "ewseventrequestbase.h"
8 #include "ewsclient_debug.h"
9 #include "ewsxml.h"
10
11 enum NotificationElementType {
12 SubscriptionId,
13 PreviousWatermark,
14 MoreEvents,
15 Events,
16 };
17 typedef EwsXml<NotificationElementType> NotificationReader;
18
19 enum EventElementType {
20 Watermark,
21 Timestamp,
22 ItemId,
23 FolderId,
24 ParentFolderId,
25 OldItemId,
26 OldFolderId,
27 OldParentFolderId,
28 UnreadCount,
29 };
30 typedef EwsXml<EventElementType> EventReader;
31
EwsEventRequestBase(EwsClient & client,const QString & reqName,QObject * parent)32 EwsEventRequestBase::EwsEventRequestBase(EwsClient &client, const QString &reqName, QObject *parent)
33 : EwsRequest(client, parent)
34 , mReqName(reqName)
35 {
36 qRegisterMetaType<EwsEventRequestBase::Event::List>();
37 }
38
~EwsEventRequestBase()39 EwsEventRequestBase::~EwsEventRequestBase()
40 {
41 }
42
parseResult(QXmlStreamReader & reader)43 bool EwsEventRequestBase::parseResult(QXmlStreamReader &reader)
44 {
45 return parseResponseMessage(reader, mReqName, [this](QXmlStreamReader &reader) {
46 return parseNotificationsResponse(reader);
47 });
48 }
49
parseNotificationsResponse(QXmlStreamReader & reader)50 bool EwsEventRequestBase::parseNotificationsResponse(QXmlStreamReader &reader)
51 {
52 Response resp(reader);
53 if (resp.responseClass() == EwsResponseUnknown) {
54 return false;
55 }
56
57 if (EWSCLI_REQUEST_LOG().isDebugEnabled()) {
58 if (resp.isSuccess()) {
59 int numEv = 0;
60 const auto notifications = resp.notifications();
61 for (const Notification &nfy : notifications) {
62 numEv += nfy.events().size();
63 }
64 qCDebugNC(EWSCLI_REQUEST_LOG)
65 << QStringLiteral("Got %1 response (%2 notifications, %3 events)").arg(mReqName).arg(resp.notifications().size()).arg(numEv);
66 } else {
67 qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got %1 response - %2").arg(mReqName, resp.responseMessage());
68 }
69 }
70
71 mResponses.append(resp);
72 return true;
73 }
74
Response(QXmlStreamReader & reader)75 EwsEventRequestBase::Response::Response(QXmlStreamReader &reader)
76 : EwsRequest::Response(reader)
77 {
78 if (mClass == EwsResponseParseError) {
79 return;
80 }
81
82 while (reader.readNextStartElement()) {
83 if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) {
84 setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2").arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString()));
85 return;
86 }
87
88 if (reader.name() == QLatin1String("Notification")) {
89 Notification nfy(reader);
90 if (!nfy.isValid()) {
91 setErrorMsg(QStringLiteral("Failed to process notification."));
92 reader.skipCurrentElement();
93 return;
94 }
95 mNotifications.append(nfy);
96 } else if (reader.name() == QLatin1String("Notifications")) {
97 while (reader.readNextStartElement()) {
98 if (reader.name() == QLatin1String("Notification")) {
99 Notification nfy(reader);
100 if (!nfy.isValid()) {
101 setErrorMsg(QStringLiteral("Failed to process notification."));
102 reader.skipCurrentElement();
103 return;
104 }
105 mNotifications.append(nfy);
106 } else {
107 setErrorMsg(QStringLiteral("Failed to read EWS request - expected Notification inside Notifications"));
108 }
109 }
110 } else if (reader.name() == QLatin1String("ConnectionStatus")) {
111 reader.skipCurrentElement();
112 } else if (!readResponseElement(reader)) {
113 setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element '%1'").arg(reader.name().toString()));
114 return;
115 }
116 }
117 }
118
Notification(QXmlStreamReader & reader)119 EwsEventRequestBase::Notification::Notification(QXmlStreamReader &reader)
120 {
121 static const QVector<NotificationReader::Item> items = {
122 {SubscriptionId, QStringLiteral("SubscriptionId"), &ewsXmlTextReader},
123 {PreviousWatermark, QStringLiteral("PreviousWatermark"), &ewsXmlTextReader},
124 {MoreEvents, QStringLiteral("MoreEvents"), &ewsXmlBoolReader},
125 {Events, QStringLiteral("CopiedEvent"), &eventsReader},
126 {Events, QStringLiteral("CreatedEvent"), &eventsReader},
127 {Events, QStringLiteral("DeletedEvent"), &eventsReader},
128 {Events, QStringLiteral("ModifiedEvent"), &eventsReader},
129 {Events, QStringLiteral("MovedEvent"), &eventsReader},
130 {Events, QStringLiteral("NewMailEvent"), &eventsReader},
131 {Events, QStringLiteral("FreeBusyChangeEvent"), &eventsReader},
132 {Events, QStringLiteral("StatusEvent"), &eventsReader},
133 };
134 static const NotificationReader staticReader(items);
135
136 NotificationReader ewsreader(staticReader);
137
138 if (!ewsreader.readItems(reader, ewsTypeNsUri)) {
139 return;
140 }
141
142 QHash<NotificationElementType, QVariant> values = ewsreader.values();
143
144 mSubscriptionId = values[SubscriptionId].toString();
145 mWatermark = values[PreviousWatermark].toString();
146 mMoreEvents = values[MoreEvents].toBool();
147 mEvents = values[Events].value<Event::List>();
148 }
149
eventsReader(QXmlStreamReader & reader,QVariant & val)150 bool EwsEventRequestBase::Notification::eventsReader(QXmlStreamReader &reader, QVariant &val)
151 {
152 Event::List events = val.value<Event::List>();
153 const QString elmName(reader.name().toString());
154
155 Event event(reader);
156 if (!event.isValid()) {
157 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element").arg(elmName);
158 return false;
159 }
160
161 events.append(event);
162
163 val = QVariant::fromValue<Event::List>(events);
164 return true;
165 }
166
Event(QXmlStreamReader & reader)167 EwsEventRequestBase::Event::Event(QXmlStreamReader &reader)
168 : mType(EwsUnknownEvent)
169 {
170 static const QVector<EventReader::Item> items = {
171 {Watermark, QStringLiteral("Watermark"), &ewsXmlTextReader},
172 {Timestamp, QStringLiteral("TimeStamp"), &ewsXmlDateTimeReader},
173 {FolderId, QStringLiteral("FolderId"), &ewsXmlIdReader},
174 {ItemId, QStringLiteral("ItemId"), &ewsXmlIdReader},
175 {ParentFolderId, QStringLiteral("ParentFolderId"), &ewsXmlIdReader},
176 {OldFolderId, QStringLiteral("OldFolderId"), &ewsXmlIdReader},
177 {OldItemId, QStringLiteral("OldItemId"), &ewsXmlIdReader},
178 {OldParentFolderId, QStringLiteral("OldParentFolderId"), &ewsXmlIdReader},
179 {UnreadCount, QStringLiteral("UnreadCount"), &ewsXmlUIntReader},
180 };
181 static const EventReader staticReader(items);
182
183 EventReader ewsreader(staticReader);
184
185 QStringRef elmName = reader.name();
186 if (elmName == QLatin1String("CopiedEvent")) {
187 mType = EwsCopiedEvent;
188 } else if (elmName == QLatin1String("CreatedEvent")) {
189 mType = EwsCreatedEvent;
190 } else if (elmName == QLatin1String("DeletedEvent")) {
191 mType = EwsDeletedEvent;
192 } else if (elmName == QLatin1String("ModifiedEvent")) {
193 mType = EwsModifiedEvent;
194 } else if (elmName == QLatin1String("MovedEvent")) {
195 mType = EwsMovedEvent;
196 } else if (elmName == QLatin1String("NewMailEvent")) {
197 mType = EwsNewMailEvent;
198 } else if (elmName == QLatin1String("StatusEvent")) {
199 mType = EwsStatusEvent;
200 } else if (elmName == QLatin1String("FreeBusyChangedEvent")) {
201 mType = EwsFreeBusyChangedEvent;
202 } else {
203 qCWarning(EWSCLI_LOG) << QStringLiteral("Unknown notification event type: %1").arg(elmName.toString());
204 return;
205 }
206
207 if (!ewsreader.readItems(reader, ewsTypeNsUri)) {
208 mType = EwsUnknownEvent;
209 return;
210 }
211
212 QHash<EventElementType, QVariant> values = ewsreader.values();
213
214 mWatermark = values[Watermark].toString();
215 mTimestamp = values[Timestamp].toDateTime();
216 if (values.contains(ItemId)) {
217 mId = values[ItemId].value<EwsId>();
218 mOldId = values[OldItemId].value<EwsId>();
219 mIsFolder = false;
220 } else {
221 mId = values[FolderId].value<EwsId>();
222 mOldId = values[OldFolderId].value<EwsId>();
223 mIsFolder = true;
224 }
225 mParentFolderId = values[ParentFolderId].value<EwsId>();
226 mOldParentFolderId = values[OldParentFolderId].value<EwsId>();
227 mUnreadCount = values[UnreadCount].toUInt();
228
229 if (mType == EwsStatusEvent) {
230 qCDebugNCS(EWSCLI_LOG) << QStringLiteral(" %1").arg(elmName.toString());
231 } else {
232 qCDebugNCS(EWSCLI_LOG) << QStringLiteral(" %1, %2, parent: ").arg(elmName.toString()).arg(mIsFolder ? 'F' : 'I') << mParentFolderId
233 << QStringLiteral(", id: ") << mId;
234 }
235 }
236
operator ==(const Response & other) const237 bool EwsEventRequestBase::Response::operator==(const Response &other) const
238 {
239 return mNotifications == other.mNotifications;
240 }
241
operator ==(const Notification & other) const242 bool EwsEventRequestBase::Notification::operator==(const Notification &other) const
243 {
244 return (mSubscriptionId == other.mSubscriptionId) && (mWatermark == other.mWatermark) && (mMoreEvents == other.mMoreEvents) && (mEvents == other.mEvents);
245 }
246
operator ==(const Event & other) const247 bool EwsEventRequestBase::Event::operator==(const Event &other) const
248 {
249 return (mType == other.mType) && (mWatermark == other.mWatermark) && (mTimestamp == other.mTimestamp) && (mId == other.mId)
250 && (mParentFolderId == other.mParentFolderId) && (mUnreadCount == other.mUnreadCount) && (mOldId == other.mOldId)
251 && (mOldParentFolderId == other.mOldParentFolderId) && (mIsFolder == other.mIsFolder);
252 }
253