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