mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-28 07:54:49 +03:00
3c9acdf724
This is largely a guess, but this is the only place where we use a QIODevice to push data through QNAM and that the QIODevice isn't a direct child of the QNetworkReply. Fix the issue by making sure that we don't go back to the event loop and possibly handle network events between the destruction of the upload QIODevice and the QNetworkReply, which might lead to QNAM dereferencing a dangling QIODevice pointer.
169 lines
5.4 KiB
C++
169 lines
5.4 KiB
C++
/*
|
|
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
#pragma once
|
|
|
|
#include "owncloudpropagator.h"
|
|
#include "networkjobs.h"
|
|
|
|
#include <QBuffer>
|
|
#include <QFile>
|
|
#include <QDebug>
|
|
|
|
namespace OCC {
|
|
class BandwidthManager;
|
|
|
|
class UploadDevice : public QIODevice {
|
|
Q_OBJECT
|
|
public:
|
|
UploadDevice(BandwidthManager *bwm);
|
|
~UploadDevice();
|
|
|
|
/** Reads the data from the file and opens the device */
|
|
bool prepareAndOpen(const QString& fileName, qint64 start, qint64 size);
|
|
|
|
qint64 writeData(const char* , qint64 ) Q_DECL_OVERRIDE;
|
|
qint64 readData(char* data, qint64 maxlen) Q_DECL_OVERRIDE;
|
|
bool atEnd() const Q_DECL_OVERRIDE;
|
|
qint64 size() const Q_DECL_OVERRIDE;
|
|
qint64 bytesAvailable() const Q_DECL_OVERRIDE;
|
|
bool isSequential() const Q_DECL_OVERRIDE;
|
|
bool seek ( qint64 pos ) Q_DECL_OVERRIDE;
|
|
|
|
void setBandwidthLimited(bool);
|
|
bool isBandwidthLimited() { return _bandwidthLimited; }
|
|
void setChoked(bool);
|
|
bool isChoked() { return _choked; }
|
|
void giveBandwidthQuota(qint64 bwq);
|
|
private:
|
|
|
|
// The file data
|
|
QByteArray _data;
|
|
// Position in the data
|
|
qint64 _read;
|
|
|
|
// Bandwidth manager related
|
|
QPointer<BandwidthManager> _bandwidthManager;
|
|
qint64 _bandwidthQuota;
|
|
qint64 _readWithProgress;
|
|
bool _bandwidthLimited; // if _bandwidthQuota will be used
|
|
bool _choked; // if upload is paused (readData() will return 0)
|
|
friend class BandwidthManager;
|
|
protected slots:
|
|
void slotJobUploadProgress(qint64 sent, qint64 t);
|
|
};
|
|
|
|
class PUTFileJob : public AbstractNetworkJob {
|
|
Q_OBJECT
|
|
QScopedPointer<QIODevice> _device;
|
|
QMap<QByteArray, QByteArray> _headers;
|
|
QString _errorString;
|
|
|
|
public:
|
|
// Takes ownership of the device
|
|
explicit PUTFileJob(AccountPtr account, const QString& path, QIODevice *device,
|
|
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
|
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk) {}
|
|
~PUTFileJob();
|
|
|
|
int _chunk;
|
|
|
|
virtual void start() Q_DECL_OVERRIDE;
|
|
|
|
virtual bool finished() Q_DECL_OVERRIDE {
|
|
emit finishedSignal();
|
|
return true;
|
|
}
|
|
|
|
QString errorString() {
|
|
return _errorString.isEmpty() ? reply()->errorString() : _errorString;
|
|
};
|
|
|
|
virtual void slotTimeout() Q_DECL_OVERRIDE;
|
|
|
|
|
|
signals:
|
|
void finishedSignal();
|
|
void uploadProgress(qint64,qint64);
|
|
};
|
|
|
|
/**
|
|
* This job implements the assynchronous PUT
|
|
* If the server replies to a PUT with a OC-Finish-Poll url, we will query this url until the server
|
|
* replies with an etag
|
|
* https://github.com/owncloud/core/issues/12097
|
|
*/
|
|
class PollJob : public AbstractNetworkJob {
|
|
Q_OBJECT
|
|
SyncJournalDb *_journal;
|
|
QString _localPath;
|
|
public:
|
|
SyncFileItem _item;
|
|
// Takes ownership of the device
|
|
explicit PollJob(AccountPtr account, const QString &path, const SyncFileItem &item,
|
|
SyncJournalDb *journal, const QString &localPath, QObject *parent)
|
|
: AbstractNetworkJob(account, path, parent), _journal(journal), _localPath(localPath), _item(item) {}
|
|
|
|
void start() Q_DECL_OVERRIDE;
|
|
bool finished() Q_DECL_OVERRIDE;
|
|
void slotTimeout() Q_DECL_OVERRIDE {
|
|
// emit finishedSignal(false);
|
|
// deleteLater();
|
|
qDebug() << Q_FUNC_INFO;
|
|
reply()->abort();
|
|
}
|
|
|
|
signals:
|
|
void finishedSignal();
|
|
};
|
|
|
|
|
|
class PropagateUploadFileQNAM : public PropagateItemJob {
|
|
Q_OBJECT
|
|
|
|
/**
|
|
* That's the start chunk that was stored in the database for resuming.
|
|
* In the non-resuming case it is 0.
|
|
* If we are resuming, this is the first chunk we need to send
|
|
*/
|
|
int _startChunk;
|
|
/**
|
|
* This is the next chunk that we need to send. Starting from 0 even if _startChunk != 0
|
|
* (In other words, _startChunk + _currentChunk is really the number of the chunk we need to send next)
|
|
* (In other words, _currentChunk is the number of chunk that we already sent or start sending)
|
|
*/
|
|
int _currentChunk;
|
|
int _chunkCount; /// Total number of chunks for this file
|
|
int _transferId; /// transfer id (part of the url)
|
|
QElapsedTimer _duration;
|
|
QVector<PUTFileJob*> _jobs; /// network jobs that are currently in transit
|
|
bool _finished; // Tells that all the jobs have been finished
|
|
public:
|
|
PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
|
|
: PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {}
|
|
void start() Q_DECL_OVERRIDE;
|
|
private slots:
|
|
void slotPutFinished();
|
|
void slotPollFinished();
|
|
void slotUploadProgress(qint64,qint64);
|
|
void abort() Q_DECL_OVERRIDE;
|
|
void startNextChunk();
|
|
void finalize(const SyncFileItem&);
|
|
void slotJobDestroyed(QObject *job);
|
|
private:
|
|
void startPollJob(const QString& path);
|
|
void abortWithError(SyncFileItem::Status status, const QString &error);
|
|
};
|
|
|
|
}
|
|
|