req.setRawHeader("Ocs-APIREQUEST", "true");
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
- QIODevice *iodevice = 0;
- setReply(davRequest(_verb, _link, req, iodevice));
- setupConnections(reply());
+ sendRequest(_verb, _link, req);
AbstractNetworkJob::start();
}
queryItems.append(qMakePair(QByteArray("format"), QByteArray("json")));
url.setEncodedQueryItems(queryItems);
- setReply(davRequest(_verb, url, req, buffer));
- setupConnections(reply());
- buffer->setParent(reply());
+ sendRequest(_verb, url, req, buffer);
AbstractNetworkJob::start();
}
void DetermineAuthTypeJob::start()
{
- QNetworkReply *reply = getRequest(account()->davPath());
- setReply(reply);
- setupConnections(reply);
+ sendRequest("GET", account()->davUrl());
AbstractNetworkJob::start();
}
// do a new run
_redirects++;
resetTimeout();
- setReply(getRequest(redirection));
- setupConnections(reply());
+ sendRequest("GET", redirection);
return false; // don't discard
} else {
#ifndef NO_SHIBBOLETH
void ThumbnailJob::start()
{
- setReply(getRequest(path()));
- setupConnections(reply());
+ sendRequest("GET", makeAccountUrl(path()));
AbstractNetworkJob::start();
}
return reply;
}
-QNetworkReply* AbstractNetworkJob::davRequest(const QByteArray &verb, const QString &relPath,
- QNetworkRequest req, QIODevice *data)
+QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUrl &url,
+ QNetworkRequest req, QIODevice *requestBody)
{
- return addTimer(_account->davRequest(verb, relPath, req, data));
-}
-
-QNetworkReply *AbstractNetworkJob::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
-{
- return addTimer(_account->davRequest(verb, url, req, data));
-}
-
-QNetworkReply* AbstractNetworkJob::getRequest(const QString &relPath)
-{
- return addTimer(_account->getRequest(relPath));
-}
-
-QNetworkReply *AbstractNetworkJob::getRequest(const QUrl &url)
-{
- return addTimer(_account->getRequest(url));
+ auto reply = _account->sendRequest(verb, url, req, requestBody);
+ _requestBody = requestBody;
+ if (_requestBody) {
+ _requestBody->setParent(reply);
+ }
+ addTimer(reply);
+ setReply(reply);
+ setupConnections(reply);
+ return reply;
}
-QNetworkReply *AbstractNetworkJob::headRequest(const QString &relPath)
+QUrl AbstractNetworkJob::makeAccountUrl(const QString& relativePath) const
{
- return addTimer(_account->headRequest(relPath));
+ return Utility::concatUrlPath(_account->url(), relativePath);
}
-QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
+QUrl AbstractNetworkJob::makeDavUrl(const QString& relativePath) const
{
- return addTimer(_account->headRequest(url));
+ return Utility::concatUrlPath(_account->davUrl(), relativePath);
}
-QNetworkReply *AbstractNetworkJob::deleteRequest(const QUrl &url)
+QByteArray AbstractNetworkJob::requestVerb(QNetworkReply* reply)
{
- return addTimer(_account->deleteRequest(url));
+ switch (reply->operation()) {
+ case QNetworkAccessManager::HeadOperation:
+ return "HEAD";
+ case QNetworkAccessManager::GetOperation:
+ return "GET";
+ case QNetworkAccessManager::PutOperation:
+ return "PUT";
+ case QNetworkAccessManager::PostOperation:
+ return "POST";
+ case QNetworkAccessManager::DeleteOperation:
+ return "DELETE";
+ case QNetworkAccessManager::CustomOperation:
+ return reply->request().attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
+ case QNetworkAccessManager::UnknownOperation:
+ break;
+ }
+ return QByteArray();
}
void AbstractNetworkJob::slotFinished()
// get the Date timestamp from reply
_responseTimestamp = _reply->rawHeader("Date");
- if (_followRedirects) {
- // ### the qWarnings here should be exported via displayErrors() so they
+ QUrl requestedUrl = reply()->request().url();
+ QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+ if (_followRedirects && !redirectUrl.isEmpty()) {
+ _redirectCount++;
+
+ // ### some of the qWarnings here should be exported via displayErrors() so they
// ### can be presented to the user if the job executor has a GUI
- QUrl requestedUrl = reply()->request().url();
- QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
- if (!redirectUrl.isEmpty()) {
- _redirectCount++;
- if (requestedUrl.scheme() == QLatin1String("https") &&
- redirectUrl.scheme() == QLatin1String("http")) {
- qWarning() << this << "HTTPS->HTTP downgrade detected!";
- } else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
- qWarning() << this << "Redirect loop detected!";
- } else {
- resetTimeout();
- setReply(getRequest(redirectUrl));
- setupConnections(reply());
- return;
+ QByteArray verb = requestVerb(reply());
+ if (requestedUrl.scheme() == QLatin1String("https") &&
+ redirectUrl.scheme() == QLatin1String("http")) {
+ qWarning() << this << "HTTPS->HTTP downgrade detected!";
+ } else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
+ qWarning() << this << "Redirect loop detected!";
+ } else if (_requestBody && _requestBody->isSequential()) {
+ qWarning() << this << "cannot redirect request with sequential body";
+ } else if (verb.isEmpty()) {
+ qWarning() << this << "cannot redirect request: could not detect original verb";
+ } else {
+ // Create the redirected request and send it
+ qDebug() << "Redirecting" << verb << requestedUrl << redirectUrl;
+ resetTimeout();
+ if (_requestBody) {
+ _requestBody->seek(0);
}
+ sendRequest(
+ verb,
+ redirectUrl,
+ reply()->request(),
+ _requestBody);
+ return;
}
}
void networkActivity();
protected:
void setupConnections(QNetworkReply *reply);
- QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
- QNetworkRequest req = QNetworkRequest(),
- QIODevice *data = 0);
- QNetworkReply* davRequest(const QByteArray& verb, const QUrl &url,
- QNetworkRequest req = QNetworkRequest(),
- QIODevice *data = 0);
- QNetworkReply* getRequest(const QString &relPath);
- QNetworkReply* getRequest(const QUrl &url);
- QNetworkReply* headRequest(const QString &relPath);
- QNetworkReply* headRequest(const QUrl &url);
- QNetworkReply* deleteRequest(const QUrl &url);
+
+ /** Initiate a network request, returning a QNetworkReply.
+ *
+ * Calls setReply() and setupConnections() on it.
+ *
+ * Takes ownership of the requestBody (to allow redirects).
+ */
+ QNetworkReply* sendRequest(const QByteArray& verb, const QUrl &url,
+ QNetworkRequest req = QNetworkRequest(),
+ QIODevice *requestBody = 0);
+
+ // sendRequest does not take a relative path instead of an url,
+ // but the old API allowed that. We have this undefined overload
+ // to help catch usage errors
+ QNetworkReply* sendRequest(const QByteArray& verb, const QString &relativePath,
+ QNetworkRequest req = QNetworkRequest(),
+ QIODevice *requestBody = 0);
+
+ /// Creates a url for the account from a relative path
+ QUrl makeAccountUrl(const QString& relativePath) const;
+
+ /// Like makeAccountUrl() but uses the account's dav base path
+ QUrl makeDavUrl(const QString& relativePath) const;
int maxRedirects() const { return 10; }
virtual bool finished() = 0;
// GET requests that don't set up any HTTP body or other flags.
bool _followRedirects;
+ /** Helper to construct the HTTP verb used in the request
+ *
+ * Returns an empty QByteArray for UnknownOperation.
+ */
+ static QByteArray requestVerb(QNetworkReply* reply);
+
private slots:
void slotFinished();
virtual void slotTimeout();
protected:
AccountPtr _account;
+
private:
QNetworkReply* addTimer(QNetworkReply *reply);
bool _ignoreCredentialFailure;
QString _path;
QTimer _timer;
int _redirectCount;
+
+ // Set by the xyzRequest() functions and needed to be able to redirect
+ // requests, should it be required.
+ //
+ // Reparented to the currently running QNetworkReply.
+ QPointer<QIODevice> _requestBody;
};
/**
return _am.data();
}
-QNetworkReply *Account::headRequest(const QString &relPath)
-{
- return headRequest(Utility::concatUrlPath(url(), relPath));
-}
-
-QNetworkReply *Account::headRequest(const QUrl &url)
-{
- QNetworkRequest request(url);
-#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
- request.setSslConfiguration(this->getOrCreateSslConfig());
-#endif
- return _am->head(request);
-}
-
-QNetworkReply *Account::getRequest(const QString &relPath)
-{
- return getRequest(Utility::concatUrlPath(url(), relPath));
-}
-
-QNetworkReply *Account::getRequest(const QUrl &url)
-{
- QNetworkRequest request(url);
-#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
- request.setSslConfiguration(this->getOrCreateSslConfig());
-#endif
- return _am->get(request);
-}
-
-QNetworkReply *Account::deleteRequest( const QUrl &url)
-{
- QNetworkRequest request(url);
-#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
- request.setSslConfiguration(this->getOrCreateSslConfig());
-#endif
- return _am->deleteResource(request);
-}
-
-QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
-{
- return davRequest(verb, Utility::concatUrlPath(davUrl(), relPath), req, data);
-}
-
-QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
+QNetworkReply *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
{
req.setUrl(url);
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
req.setSslConfiguration(this->getOrCreateSslConfig());
#endif
+ if (verb == "HEAD" && !data) {
+ return _am->head(req);
+ } else if (verb == "GET" && !data) {
+ return _am->get(req);
+ } else if (verb == "POST") {
+ return _am->post(req, data);
+ } else if (verb == "PUT") {
+ return _am->put(req, data);
+ } else if (verb == "DELETE" && !data) {
+ return _am->deleteResource(req);
+ }
return _am->sendCustomRequest(req, verb, data);
}
// For creating various network requests
- QNetworkReply* headRequest(const QString &relPath);
- QNetworkReply* headRequest(const QUrl &url);
- QNetworkReply* getRequest(const QString &relPath);
- QNetworkReply* getRequest(const QUrl &url);
- QNetworkReply* deleteRequest( const QUrl &url);
- QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
- QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
-
+ QNetworkReply* sendRequest(const QByteArray &verb,
+ const QUrl &url,
+ QNetworkRequest req = QNetworkRequest(),
+ QIODevice *data = 0);
/** The ssl configuration during the first connection */
QSslConfiguration getOrCreateSslConfig();
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
// assumes ownership
- setReply(davRequest("PROPFIND", path(), req, buf));
- buf->setParent(reply());
- setupConnections(reply());
+ sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
if( reply()->error() != QNetworkReply::NoError ) {
qDebug() << "getting etag: request network error: " << reply()->errorString();
}
// assumes ownership
- QNetworkReply *reply = _url.isValid() ? davRequest("MKCOL", _url, req)
- : davRequest("MKCOL", path(), req);
- setReply(reply);
- setupConnections(reply);
+ if (_url.isValid()) {
+ sendRequest("MKCOL", _url, req);
+ } else {
+ sendRequest("MKCOL", makeDavUrl(path()), req);
+ }
AbstractNetworkJob::start();
}
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
- QNetworkReply *reply = _url.isValid() ? davRequest("PROPFIND", _url, req, buf)
- : davRequest("PROPFIND", path(), req, buf);
- buf->setParent(reply);
- setReply(reply);
- setupConnections(reply);
+ if (_url.isValid()) {
+ sendRequest("PROPFIND", _url, req, buf);
+ } else {
+ sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
+ }
AbstractNetworkJob::start();
}
void CheckServerJob::start()
{
- setReply(getRequest(path()));
- setupConnections(reply());
+ sendRequest("GET", makeAccountUrl(path()));
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
AbstractNetworkJob::start();
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
- setReply(davRequest("PROPFIND", path(), req, buf));
- buf->setParent(reply());
- setupConnections(reply());
+ sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
AbstractNetworkJob::start();
}
QBuffer *buf = new QBuffer(this);
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
- setReply(davRequest("PROPPATCH", path(), req, buf));
- buf->setParent(reply());
- setupConnections(reply());
+ sendRequest("PROPPATCH", makeDavUrl(path()), req, buf);
AbstractNetworkJob::start();
}
void EntityExistsJob::start()
{
- setReply(headRequest(path()));
- setupConnections(reply());
+ sendRequest("HEAD", makeAccountUrl(path()));
AbstractNetworkJob::start();
}
QList<QPair<QString, QString> > params = _additionalParams;
params << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"));
url.setQueryItems(params);
- setReply(davRequest("GET", url, req));
- setupConnections(reply());
+ sendRequest("GET", url, req);
AbstractNetworkJob::start();
}
}
if (_directDownloadUrl.isEmpty()) {
- setReply(davRequest("GET", path(), req));
+ sendRequest("GET", makeDavUrl(path()), req);
} else {
// Use direct URL
- setReply(davRequest("GET", _directDownloadUrl, req));
+ sendRequest("GET", _directDownloadUrl, req);
}
- setupConnections(reply());
reply()->setReadBufferSize(16 * 1024); // keep low so we can easier limit the bandwidth
qDebug() << Q_FUNC_INFO << _bandwidthManager << _bandwidthChoked << _bandwidthLimited;
void DeleteJob::start()
{
QNetworkRequest req;
- setReply(_url.isValid() ? davRequest("DELETE", _url, req) : davRequest("DELETE", path(), req));
- setupConnections(reply());
+ if (_url.isValid()) {
+ sendRequest("DELETE", _url, req);
+ } else {
+ sendRequest("DELETE", makeDavUrl(path()), req);
+ }
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
for(auto it = _extraHeaders.constBegin(); it != _extraHeaders.constEnd(); ++it) {
req.setRawHeader(it.key(), it.value());
}
- setReply(_url.isValid() ? davRequest("MOVE", _url, req) : davRequest("MOVE", path(), req));
- setupConnections(reply());
+ if (_url.isValid()) {
+ sendRequest("MOVE", _url, req);
+ } else {
+ sendRequest("MOVE", makeDavUrl(path()), req);
+ }
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
req.setRawHeader(it.key(), it.value());
}
- setReply(_url.isValid() ? davRequest("PUT", _url, req, _device.data())
- : davRequest("PUT", path(), req, _device.data()));
- setupConnections(reply());
+ if (_url.isValid()) {
+ sendRequest("PUT", _url, req, _device);
+ } else {
+ sendRequest("PUT", makeDavUrl(path()), req, _device);
+ }
if( reply()->error() != QNetworkReply::NoError ) {
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
// (workaround disabled on windows and mac because the binaries we ship have patched qt)
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 7)
if (QLatin1String(qVersion()) < QLatin1String("4.8.7"))
- connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
+ connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
#elif QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 4, 2) && !defined Q_OS_WIN && !defined Q_OS_MAC
if (QLatin1String(qVersion()) < QLatin1String("5.4.2"))
- connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
+ connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
#endif
AbstractNetworkJob::start();
QUrl accountUrl = account()->url();
QUrl finalUrl = QUrl::fromUserInput(accountUrl.scheme() + QLatin1String("://") + accountUrl.authority()
+ (path().startsWith('/') ? QLatin1String("") : QLatin1String("/")) + path());
- setReply(getRequest(finalUrl));
- setupConnections(reply());
+ sendRequest("GET", finalUrl);
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(resetTimeout()));
AbstractNetworkJob::start();
}
Q_OBJECT
private:
- QScopedPointer<QIODevice> _device;
+ QIODevice* _device;
QMap<QByteArray, QByteArray> _headers;
QString _errorString;
QUrl _url;
// 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) {}
+ : AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk)
+ {
+ _device->setParent(this);
+ }
explicit PUTFileJob(AccountPtr account, const QUrl& url, QIODevice *device,
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
: AbstractNetworkJob(account, QString(), parent), _device(device), _headers(headers)
- , _url(url), _chunk(chunk) {}
+ , _url(url), _chunk(chunk)
+ {
+ _device->setParent(this);
+ }
~PUTFileJob();
int _chunk;
if (verb == QLatin1String("PROPFIND"))
// Ignore outgoingData always returning somethign good enough, works for now.
return new FakePropfindReply{info, op, request, this};
- else if (verb == QLatin1String("GET"))
+ else if (verb == QLatin1String("GET") || op == QNetworkAccessManager::GetOperation)
return new FakeGetReply{info, op, request, this};
- else if (verb == QLatin1String("PUT"))
+ else if (verb == QLatin1String("PUT") || op == QNetworkAccessManager::PutOperation)
return new FakePutReply{info, op, request, outgoingData->readAll(), this};
else if (verb == QLatin1String("MKCOL"))
return new FakeMkcolReply{info, op, request, this};
- else if (verb == QLatin1String("DELETE"))
+ else if (verb == QLatin1String("DELETE") || op == QNetworkAccessManager::DeleteOperation)
return new FakeDeleteReply{info, op, request, this};
else if (verb == QLatin1String("MOVE") && !isUpload)
return new FakeMoveReply{info, op, request, this};