nextcloud-desktop/src/libsync/propagateupload.h
Jocelyn Turcotte 3c9acdf724 Fix a crash when accessing a dangling UploadDevice pointer #2984
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.
2015-03-23 16:14:54 +01:00

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);
};
}