File indexing completed on 2024-12-22 04:57:55
0001 /* 0002 SPDX-FileCopyrightText: 2016 Stefan Stäglich <sstaeglich@kdemail.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "tomboynotesresource.h" 0008 #include "debug.h" 0009 #include "settings.h" 0010 #include "settingsadaptor.h" 0011 #include "tomboycollectionsdownloadjob.h" 0012 #include "tomboyitemdownloadjob.h" 0013 #include "tomboyitemsdownloadjob.h" 0014 #include "tomboyitemuploadjob.h" 0015 #include "tomboyserverauthenticatejob.h" 0016 #include <Akonadi/ChangeRecorder> 0017 #include <Akonadi/ItemFetchScope> 0018 #include <KLocalizedString> 0019 #include <QDBusConnection> 0020 #include <QSslCipher> 0021 0022 using namespace Akonadi; 0023 0024 TomboyNotesResource::TomboyNotesResource(const QString &id) 0025 : ResourceBase(id) 0026 { 0027 Settings::instance(KSharedConfig::openConfig()); 0028 new SettingsAdaptor(Settings::self()); 0029 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), Settings::self(), QDBusConnection::ExportAdaptors); 0030 0031 // Akonadi:Item should always provide the payload 0032 changeRecorder()->itemFetchScope().fetchFullPayload(true); 0033 0034 // Status message stuff 0035 mStatusMessageTimer = new QTimer(this); 0036 mStatusMessageTimer->setSingleShot(true); 0037 connect(mStatusMessageTimer, &QTimer::timeout, [this]() { 0038 Q_EMIT status(Akonadi::AgentBase::Idle, QString()); 0039 }); 0040 connect(this, &AgentBase::error, this, &TomboyNotesResource::showError); 0041 0042 mUploadJobProcessRunning = false; 0043 0044 mManager = new QNetworkAccessManager(this); 0045 mManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); 0046 mManager->setStrictTransportSecurityEnabled(true); 0047 mManager->enableStrictTransportSecurityStore(true); 0048 connect(mManager, &QNetworkAccessManager::sslErrors, this, &TomboyNotesResource::onSslError); 0049 0050 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Resource started"; 0051 connect(this, &TomboyNotesResource::reloadConfiguration, this, &TomboyNotesResource::slotReloadConfig); 0052 } 0053 0054 TomboyNotesResource::~TomboyNotesResource() = default; 0055 0056 void TomboyNotesResource::retrieveCollections() 0057 { 0058 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving collections started"; 0059 0060 if (configurationNotValid()) { 0061 cancelTask(i18n("Resource configuration is not valid")); 0062 return; 0063 } 0064 0065 int refreshInterval = Settings::self()->refreshInterval(); 0066 if (refreshInterval == 0) { 0067 refreshInterval = -1; 0068 } 0069 0070 auto job = new TomboyCollectionsDownloadJob(Settings::collectionName(), mManager, refreshInterval, this); 0071 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0072 job->setServerURL(Settings::serverURL(), Settings::userURL()); 0073 // connect to its result() signal 0074 connect(job, &KJob::result, this, &TomboyNotesResource::onCollectionsRetrieved); 0075 job->start(); 0076 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving collections job started"; 0077 } 0078 0079 void TomboyNotesResource::retrieveItems(const Akonadi::Collection &collection) 0080 { 0081 if (configurationNotValid()) { 0082 cancelTask(i18n("Resource configuration is not valid")); 0083 return; 0084 } 0085 0086 // create the job 0087 auto job = new TomboyItemsDownloadJob(collection.id(), mManager, this); 0088 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0089 job->setServerURL(Settings::serverURL(), Settings::contentURL()); 0090 // connect to its result() signal 0091 connect(job, &KJob::result, this, &TomboyNotesResource::onItemsRetrieved); 0092 job->start(); 0093 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving items job started"; 0094 } 0095 0096 bool TomboyNotesResource::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts) 0097 { 0098 Q_UNUSED(parts) 0099 0100 if (configurationNotValid()) { 0101 cancelTask(i18n("Resource configuration is not valid")); 0102 return false; 0103 } 0104 0105 // this method is called when Akonadi wants more data for a given item. 0106 auto job = new TomboyItemDownloadJob(item, mManager, this); 0107 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0108 job->setServerURL(Settings::serverURL(), Settings::contentURL()); 0109 // connect to its result() signal 0110 connect(job, &KJob::result, this, &TomboyNotesResource::onItemRetrieved); 0111 job->start(); 0112 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving item data job started"; 0113 0114 return true; 0115 } 0116 0117 void TomboyNotesResource::onAuthorizationFinished(KJob *kjob) 0118 { 0119 // Saves the received client authentication data in the settings 0120 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Authorization job finished"; 0121 auto job = qobject_cast<TomboyServerAuthenticateJob *>(kjob); 0122 if (job->error() == TomboyJobError::NoError) { 0123 Settings::setRequestToken(job->getRequestToken()); 0124 Settings::setRequestTokenSecret(job->getRequestTokenSecret()); 0125 Settings::setContentURL(job->getContentUrl()); 0126 Settings::setUserURL(job->getUserURL()); 0127 Settings::self()->save(); 0128 synchronizeCollectionTree(); 0129 synchronize(); 0130 } else { 0131 showError(job->errorText()); 0132 } 0133 } 0134 0135 void TomboyNotesResource::onCollectionsRetrieved(KJob *kjob) 0136 { 0137 auto job = qobject_cast<TomboyCollectionsDownloadJob *>(kjob); 0138 if (job->error() != TomboyJobError::NoError) { 0139 cancelTask(); 0140 showError(job->errorText()); 0141 return; 0142 } 0143 0144 collectionsRetrieved(job->collections()); 0145 } 0146 0147 void TomboyNotesResource::onItemChangeCommitted(KJob *kjob) 0148 { 0149 auto job = qobject_cast<TomboyItemUploadJob *>(kjob); 0150 mUploadJobProcessRunning = false; 0151 switch (job->error()) { 0152 case TomboyJobError::PermanentError: 0153 cancelTask(); 0154 showError(job->errorText()); 0155 return; 0156 case TomboyJobError::TemporaryError: 0157 retryAfterFailure(job->errorString()); 0158 return; 0159 case TomboyJobError::NoError: 0160 changeCommitted(job->item()); 0161 // The data should be actualized for the next UploadJob 0162 synchronize(); 0163 return; 0164 } 0165 } 0166 0167 void TomboyNotesResource::onItemRetrieved(KJob *kjob) 0168 { 0169 auto job = qobject_cast<TomboyItemDownloadJob *>(kjob); 0170 0171 if (job->error() != TomboyJobError::NoError) { 0172 cancelTask(); 0173 showError(job->errorText()); 0174 return; 0175 } 0176 0177 itemRetrieved(job->item()); 0178 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving item data job with remoteId " << job->item().remoteId() << " finished"; 0179 } 0180 0181 void TomboyNotesResource::onItemsRetrieved(KJob *kjob) 0182 { 0183 auto job = qobject_cast<TomboyItemsDownloadJob *>(kjob); 0184 if (job->error() != TomboyJobError::NoError) { 0185 cancelTask(); 0186 showError(job->errorText()); 0187 return; 0188 } 0189 0190 itemsRetrieved(job->items()); 0191 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Retrieving items job finished"; 0192 } 0193 0194 void TomboyNotesResource::onSslError(QNetworkReply *reply, const QList<QSslError> &errors) 0195 { 0196 Q_UNUSED(errors) 0197 if (Settings::ignoreSslErrors()) { 0198 reply->ignoreSslErrors(); 0199 } 0200 } 0201 0202 void TomboyNotesResource::aboutToQuit() 0203 { 0204 // TODO: any cleanup you need to do while there is still an active 0205 // event loop. The resource will terminate after this method returns 0206 } 0207 0208 void TomboyNotesResource::slotReloadConfig() 0209 { 0210 setAgentName(Settings::collectionName()); 0211 0212 if (configurationNotValid()) { 0213 auto job = new TomboyServerAuthenticateJob(mManager, this); 0214 job->setServerURL(Settings::serverURL(), QString()); 0215 connect(job, &KJob::result, this, &TomboyNotesResource::onAuthorizationFinished); 0216 job->start(); 0217 qCDebug(TOMBOYNOTESRESOURCE_LOG) << "Authorization job started"; 0218 } else { 0219 synchronize(); 0220 } 0221 } 0222 0223 void TomboyNotesResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) 0224 { 0225 Q_UNUSED(collection) 0226 if (Settings::readOnly() || configurationNotValid()) { 0227 cancelTask(i18n("Resource is read-only")); 0228 return; 0229 } 0230 0231 if (mUploadJobProcessRunning) { 0232 retryAfterFailure(QString()); 0233 return; 0234 } 0235 0236 auto job = new TomboyItemUploadJob(item, JobType::AddItem, mManager, this); 0237 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0238 job->setServerURL(Settings::serverURL(), Settings::contentURL()); 0239 connect(job, &KJob::result, this, &TomboyNotesResource::onItemChangeCommitted); 0240 mUploadJobProcessRunning = true; 0241 job->start(); 0242 } 0243 0244 void TomboyNotesResource::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &parts) 0245 { 0246 Q_UNUSED(parts) 0247 if (Settings::readOnly() || configurationNotValid()) { 0248 cancelTask(i18n("Resource is read-only")); 0249 return; 0250 } 0251 0252 if (mUploadJobProcessRunning) { 0253 retryAfterFailure(QString()); 0254 return; 0255 } 0256 0257 auto job = new TomboyItemUploadJob(item, JobType::ModifyItem, mManager, this); 0258 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0259 job->setServerURL(Settings::serverURL(), Settings::contentURL()); 0260 connect(job, &KJob::result, this, &TomboyNotesResource::onItemChangeCommitted); 0261 mUploadJobProcessRunning = true; 0262 job->start(); 0263 } 0264 0265 void TomboyNotesResource::itemRemoved(const Akonadi::Item &item) 0266 { 0267 if (Settings::readOnly() || configurationNotValid()) { 0268 cancelTask(i18n("Resource is read-only")); 0269 return; 0270 } 0271 0272 if (mUploadJobProcessRunning) { 0273 retryAfterFailure(QString()); 0274 return; 0275 } 0276 0277 auto job = new TomboyItemUploadJob(item, JobType::DeleteItem, mManager, this); 0278 job->setAuthentication(Settings::requestToken(), Settings::requestTokenSecret()); 0279 job->setServerURL(Settings::serverURL(), Settings::contentURL()); 0280 connect(job, &KJob::result, this, &TomboyNotesResource::onItemChangeCommitted); 0281 mUploadJobProcessRunning = true; 0282 job->start(); 0283 } 0284 0285 bool TomboyNotesResource::configurationNotValid() const 0286 { 0287 return Settings::requestToken().isEmpty() || Settings::contentURL().isEmpty(); 0288 } 0289 0290 void TomboyNotesResource::retryAfterFailure(const QString &errorMessage) 0291 { 0292 Q_EMIT status(Broken, errorMessage); 0293 deferTask(); 0294 setTemporaryOffline(Settings::self()->refreshInterval() <= 0 ? 300 : Settings::self()->refreshInterval() * 60); 0295 } 0296 0297 void TomboyNotesResource::showError(const QString &errorText) 0298 { 0299 Q_EMIT status(Akonadi::AgentBase::Idle, errorText); 0300 mStatusMessageTimer->start(1000 * 10); 0301 } 0302 0303 AKONADI_RESOURCE_MAIN(TomboyNotesResource) 0304 0305 #include "moc_tomboynotesresource.cpp"