}
}
+void GETFileJob::cancel()
+{
+ if (reply()->isRunning()) {
+ reply()->abort();
+ }
+
+ emit canceled();
+}
+
void GETFileJob::onTimedOut()
{
qCWarning(lcGetJob) << "Timeout" << (reply() ? reply()->request().url() : path());
}
}
+ void cancel();
+
void newReplyHook(QNetworkReply *reply) override;
void setBandwidthManager(BandwidthManager *bwm);
void setExpectedContentLength(qint64 size) { _expectedContentLength = size; }
signals:
+ void canceled();
void finishedSignal();
void downloadProgress(qint64, qint64);
private slots:
}
}
-
void CALLBACK cfApiFetchDataCallback(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS *callbackParameters)
{
qDebug(lcCfApiWrapper) << "Fetch data callback called. File size:" << callbackInfo->FileSize.QuadPart;
}
}
+void CALLBACK cfApiCancelFetchData(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS * /*callbackParameters*/)
+{
+ const auto path = QString(QString::fromWCharArray(callbackInfo->VolumeDosName) + QString::fromWCharArray(callbackInfo->NormalizedPath));
+
+ qInfo(lcCfApiWrapper) << "Cancel fetch data of" << path;
+
+ auto vfs = reinterpret_cast<OCC::VfsCfApi *>(callbackInfo->CallbackContext);
+ Q_ASSERT(vfs->metaObject()->className() == QByteArrayLiteral("OCC::VfsCfApi"));
+ const auto requestId = QString::number(callbackInfo->TransferKey.QuadPart, 16);
+
+ const auto invokeResult = QMetaObject::invokeMethod(
+ vfs, [=] { vfs->cancelHydration(requestId, path); }, Qt::QueuedConnection);
+ if (!invokeResult) {
+ qCritical(lcCfApiWrapper) << "Failed to cancel hydration for" << path << requestId;
+ }
+}
+
+
CF_CALLBACK_REGISTRATION cfApiCallbacks[] = {
{ CF_CALLBACK_TYPE_FETCH_DATA, cfApiFetchDataCallback },
+ { CF_CALLBACK_TYPE_CANCEL_FETCH_DATA, cfApiCancelFetchData },
CF_CALLBACK_REGISTRATION_END
};
connect(_server, &QLocalServer::newConnection, this, &HydrationJob::onNewConnection);
}
+void OCC::HydrationJob::cancel()
+{
+ if (!_job) {
+ return;
+ }
+
+ _job->cancel();
+}
+
void OCC::HydrationJob::emitFinished(Status status)
{
_status = status;
}
}
+void OCC::HydrationJob::emitCanceled()
+{
+ connect(_socket, &QLocalSocket::disconnected, this, [=] {
+ _socket->close();
+ });
+ _socket->disconnectFromServer();
+
+ emit canceled(this);
+}
+
void OCC::HydrationJob::onNewConnection()
{
Q_ASSERT(!_socket);
_socket = _server->nextPendingConnection();
_job = new GETFileJob(_account, _remotePath + _folderPath, _socket, {}, {}, 0, this);
connect(_job, &GETFileJob::finishedSignal, this, &HydrationJob::onGetFinished);
+ connect(_job, &GETFileJob::canceled, this, &HydrationJob::onGetCanceled);
_job->start();
}
+void OCC::HydrationJob::onGetCanceled()
+{
+ qCInfo(lcHydration) << "GETFileJob canceled" << _requestId << _folderPath << _job->reply()->error();
+ emitCanceled();
+}
+
void OCC::HydrationJob::onGetFinished()
{
qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->reply()->error();
Status status() const;
void start();
+ void cancel();
signals:
void finished(HydrationJob *job);
+ void canceled(HydrationJob *job);
private:
void emitFinished(Status status);
+ void emitCanceled();
void onNewConnection();
void onGetFinished();
+ void onGetCanceled();
AccountPtr _account;
QString _remotePath;
return AvailabilityError::NoSuchItem;
}
+void VfsCfApi::cancelHydration(const QString &requestId, const QString & /*path*/)
+{
+ // Find matching hydration job for request id
+ const auto hydrationJobsIter = std::find_if(d->hydrationJobs.cbegin(), d->hydrationJobs.cend(), [&](const HydrationJob *job) {
+ return job->requestId() == requestId;
+ });
+
+ // If found, cancel it
+ if (hydrationJobsIter != d->hydrationJobs.cend()) {
+ (*hydrationJobsIter)->cancel();
+ }
+}
+
void VfsCfApi::requestHydration(const QString &requestId, const QString &path)
{
qCInfo(lcCfApi) << "Received request to hydrate" << path << requestId;
job->setRequestId(requestId);
job->setFolderPath(folderPath);
connect(job, &HydrationJob::finished, this, &VfsCfApi::onHydrationJobFinished);
+ connect(job, &HydrationJob::canceled, this, &VfsCfApi::onHydrationJobCanceled);
d->hydrationJobs << job;
job->start();
emit hydrationRequestReady(requestId);
}
}
+void VfsCfApi::onHydrationJobCanceled(HydrationJob *job)
+{
+ const auto folderPath = job->localPath();
+ const auto folderRelativePath = job->folderPath();
+
+ // Remove placeholder file because there might be already pumped
+ // some data into it
+ QFile::remove(folderPath + folderRelativePath);
+
+ // Create a new placeholder file
+ SyncJournalFileRecord record;
+ params().journal->getFileRecord(folderRelativePath, &record);
+ const auto item = SyncFileItem::fromSyncJournalFileRecord(record);
+ createPlaceholder(*item);
+}
+
VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinStates(const QString &folderPath, const Optional<PinState> &basePinState)
{
Q_ASSERT(!folderPath.endsWith('/'));
Optional<PinState> pinState(const QString &folderPath) override;
AvailabilityResult availability(const QString &folderPath) override;
+ void cancelHydration(const QString &requestId, const QString &path);
+
public slots:
void requestHydration(const QString &requestId, const QString &path);
void fileStatusChanged(const QString &systemFileName, SyncFileStatus fileStatus) override;
private:
void scheduleHydrationJob(const QString &requestId, const QString &folderPath);
void onHydrationJobFinished(HydrationJob *job);
+ void onHydrationJobCanceled(HydrationJob *job);
struct HasHydratedDehydrated {
bool hasHydrated = false;