File indexing completed on 2025-02-16 04:50:11
0001 /* 0002 SPDX-FileCopyrightText: 2015-2020 Krzysztof Nowicki <krissn@op.pl> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ewssubscriptionmanager.h" 0008 0009 #include "ewsgeteventsrequest.h" 0010 #include "ewsgetstreamingeventsrequest.h" 0011 #include "ewsresource_debug.h" 0012 #include "ewssettings.h" 0013 #include "ewssubscribedfoldersjob.h" 0014 #include "ewssubscriberequest.h" 0015 #include "ewsunsubscriberequest.h" 0016 #include <QPointer> 0017 0018 // TODO: Allow customization 0019 static constexpr uint pollInterval = 10; /* seconds */ 0020 0021 static constexpr uint streamingTimeout = 30; /* minutes */ 0022 0023 static constexpr uint streamingConnTimeout = 60; /* seconds */ 0024 0025 EwsSubscriptionManager::EwsSubscriptionManager(EwsClient &client, const EwsId &rootId, EwsSettings *settings, QObject *parent) 0026 : QObject(parent) 0027 , mEwsClient(client) 0028 , mPollTimer(this) 0029 , mMsgRootId(rootId) 0030 , mFolderTreeChanged(false) 0031 , mSettings(settings) 0032 { 0033 mStreamingEvents = mEwsClient.serverVersion().supports(EwsServerVersion::StreamingSubscription); 0034 mStreamingTimer.setInterval(streamingConnTimeout * 1000); 0035 mStreamingTimer.setSingleShot(true); 0036 connect(&mStreamingTimer, &QTimer::timeout, this, &EwsSubscriptionManager::streamingConnectionTimeout); 0037 } 0038 0039 EwsSubscriptionManager::~EwsSubscriptionManager() 0040 { 0041 cancelSubscription(); 0042 } 0043 0044 void EwsSubscriptionManager::start() 0045 { 0046 // Set-up change notification subscription (if needed) 0047 if (mSettings->eventSubscriptionId().isEmpty()) { 0048 setupSubscription(); 0049 } else { 0050 reset(); 0051 } 0052 0053 if (!mStreamingEvents) { 0054 mPollTimer.setInterval(pollInterval * 1000); 0055 mPollTimer.setSingleShot(false); 0056 connect(&mPollTimer, &QTimer::timeout, this, &EwsSubscriptionManager::getEvents); 0057 } 0058 } 0059 0060 void EwsSubscriptionManager::cancelSubscription() 0061 { 0062 if (!mSettings->eventSubscriptionId().isEmpty()) { 0063 QPointer<EwsUnsubscribeRequest> req = new EwsUnsubscribeRequest(mEwsClient, this); 0064 req->setSubscriptionId(mSettings->eventSubscriptionId()); 0065 req->exec(); 0066 mSettings->setEventSubscriptionId(QString()); 0067 mSettings->setEventSubscriptionWatermark(QString()); 0068 mSettings->save(); 0069 } 0070 } 0071 0072 void EwsSubscriptionManager::setupSubscription() 0073 { 0074 auto job = new EwsSubscribedFoldersJob(mEwsClient, mSettings, this); 0075 connect(job, &EwsRequest::result, this, &EwsSubscriptionManager::verifySubFoldersRequestFinished); 0076 job->start(); 0077 } 0078 0079 void EwsSubscriptionManager::verifySubFoldersRequestFinished(KJob *job) 0080 { 0081 if (!job->error()) { 0082 auto folderJob = qobject_cast<EwsSubscribedFoldersJob *>(job); 0083 Q_ASSERT(folderJob); 0084 0085 setupSubscriptionReq(folderJob->folders()); 0086 } else { 0087 Q_EMIT connectionError(); 0088 } 0089 } 0090 0091 void EwsSubscriptionManager::setupSubscriptionReq(const EwsId::List &ids) 0092 { 0093 auto req = new EwsSubscribeRequest(mEwsClient, this); 0094 // req->setAllFolders(true); 0095 QList<EwsEventType> events; 0096 events << EwsNewMailEvent; 0097 events << EwsMovedEvent; 0098 events << EwsCopiedEvent; 0099 events << EwsModifiedEvent; 0100 events << EwsDeletedEvent; 0101 events << EwsCreatedEvent; 0102 req->setEventTypes(events); 0103 if (mStreamingEvents) { 0104 req->setType(EwsSubscribeRequest::StreamingSubscription); 0105 } else { 0106 req->setType(EwsSubscribeRequest::PullSubscription); 0107 } 0108 req->setFolderIds(ids); 0109 req->setAllFolders(false); 0110 connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::subscribeRequestFinished); 0111 req->start(); 0112 } 0113 0114 void EwsSubscriptionManager::reset() 0115 { 0116 mPollTimer.stop(); 0117 getEvents(); 0118 if (!mStreamingEvents) { 0119 mPollTimer.start(); 0120 } 0121 } 0122 0123 void EwsSubscriptionManager::resetSubscription() 0124 { 0125 mPollTimer.stop(); 0126 cancelSubscription(); 0127 setupSubscription(); 0128 } 0129 0130 void EwsSubscriptionManager::subscribeRequestFinished(KJob *job) 0131 { 0132 if (!job->error()) { 0133 auto req = qobject_cast<EwsSubscribeRequest *>(job); 0134 if (req) { 0135 mSettings->setEventSubscriptionId(req->response().subscriptionId()); 0136 if (mStreamingEvents) { 0137 getEvents(); 0138 } else { 0139 mSettings->setEventSubscriptionWatermark(req->response().watermark()); 0140 getEvents(); 0141 mPollTimer.start(); 0142 } 0143 mSettings->save(); 0144 } 0145 } else { 0146 Q_EMIT connectionError(); 0147 } 0148 } 0149 0150 void EwsSubscriptionManager::getEvents() 0151 { 0152 if (mStreamingEvents) { 0153 auto req = new EwsGetStreamingEventsRequest(mEwsClient, this); 0154 req->setSubscriptionId(mSettings->eventSubscriptionId()); 0155 req->setTimeout(streamingTimeout); 0156 connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); 0157 connect(req, &EwsGetStreamingEventsRequest::eventsReceived, this, &EwsSubscriptionManager::streamingEventsReceived); 0158 req->start(); 0159 mEventReq = req; 0160 mStreamingTimer.start(); 0161 } else { 0162 auto req = new EwsGetEventsRequest(mEwsClient, this); 0163 req->setSubscriptionId(mSettings->eventSubscriptionId()); 0164 req->setWatermark(mSettings->eventSubscriptionWatermark()); 0165 connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); 0166 req->start(); 0167 mEventReq = req; 0168 } 0169 } 0170 0171 void EwsSubscriptionManager::getEventsRequestFinished(KJob *job) 0172 { 0173 mStreamingTimer.stop(); 0174 0175 mEventReq->deleteLater(); 0176 mEventReq = nullptr; 0177 0178 auto req = qobject_cast<EwsEventRequestBase *>(job); 0179 if (!req) { 0180 qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); 0181 reset(); 0182 return; 0183 } 0184 0185 if ((!req->responses().isEmpty()) 0186 && ((req->responses()[0].responseCode() == QLatin1StringView("ErrorInvalidSubscription")) 0187 || (req->responses()[0].responseCode() == QLatin1StringView("ErrorSubscriptionNotFound")))) { 0188 mSettings->setEventSubscriptionId(QString()); 0189 mSettings->setEventSubscriptionWatermark(QString()); 0190 mSettings->save(); 0191 resetSubscription(); 0192 return; 0193 } 0194 0195 if (!job->error()) { 0196 processEvents(req, true); 0197 if (mStreamingEvents) { 0198 getEvents(); 0199 } 0200 } else { 0201 reset(); 0202 } 0203 } 0204 0205 void EwsSubscriptionManager::streamingEventsReceived(KJob *job) 0206 { 0207 mStreamingTimer.stop(); 0208 0209 auto req = qobject_cast<EwsEventRequestBase *>(job); 0210 if (!req) { 0211 qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); 0212 reset(); 0213 return; 0214 } 0215 0216 if (!job->error()) { 0217 processEvents(req, false); 0218 mStreamingTimer.start(); 0219 } 0220 } 0221 0222 void EwsSubscriptionManager::streamingConnectionTimeout() 0223 { 0224 if (mEventReq) { 0225 qCWarningNC(EWSRES_LOG) << QStringLiteral("Streaming request timeout - restarting"); 0226 mEventReq->deleteLater(); 0227 mEventReq = nullptr; 0228 getEvents(); 0229 } 0230 } 0231 0232 void EwsSubscriptionManager::processEvents(EwsEventRequestBase *req, bool finished) 0233 { 0234 bool moreEvents = false; 0235 0236 const auto responses{req->responses()}; 0237 for (const EwsGetEventsRequest::Response &resp : responses) { 0238 const auto notifications{resp.notifications()}; 0239 for (const EwsGetEventsRequest::Notification &nfy : notifications) { 0240 const auto nfyEvents{nfy.events()}; 0241 for (const EwsGetEventsRequest::Event &event : nfyEvents) { 0242 mSettings->setEventSubscriptionWatermark(event.watermark()); 0243 switch (event.type()) { 0244 case EwsCopiedEvent: 0245 case EwsMovedEvent: 0246 if (!event.itemIsFolder()) { 0247 mUpdatedFolderIds.insert(event.oldParentFolderId()); 0248 } 0249 /* fall through */ 0250 case EwsCreatedEvent: 0251 case EwsDeletedEvent: 0252 case EwsModifiedEvent: 0253 case EwsNewMailEvent: 0254 if (event.itemIsFolder()) { 0255 mFolderTreeChanged = true; 0256 } else { 0257 mUpdatedFolderIds.insert(event.parentFolderId()); 0258 } 0259 break; 0260 case EwsStatusEvent: 0261 // Do nothing 0262 break; 0263 default: 0264 break; 0265 } 0266 } 0267 if (nfy.hasMoreEvents()) { 0268 moreEvents = true; 0269 } 0270 } 0271 if (mStreamingEvents) { 0272 auto req2 = qobject_cast<EwsGetStreamingEventsRequest *>(req); 0273 if (req2) { 0274 req2->eventsProcessed(resp); 0275 } 0276 } 0277 } 0278 0279 if (moreEvents && finished) { 0280 getEvents(); 0281 } else { 0282 if (mFolderTreeChanged) { 0283 qCDebugNC(EWSRES_LOG) << QStringLiteral("Found modified folder tree"); 0284 Q_EMIT folderTreeModified(); 0285 mFolderTreeChanged = false; 0286 } 0287 if (!mUpdatedFolderIds.isEmpty()) { 0288 qCDebugNC(EWSRES_LOG) << QStringLiteral("Found %1 modified folders").arg(mUpdatedFolderIds.size()); 0289 const auto updated = mUpdatedFolderIds.values(); 0290 Q_EMIT foldersModified(EwsId::List(updated.cbegin(), updated.cend())); 0291 mUpdatedFolderIds.clear(); 0292 } 0293 } 0294 } 0295 0296 #include "moc_ewssubscriptionmanager.cpp"