2013-10-18 14:24:29 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
|
|
|
* Copyright (C) by Daniel Molkentin <danimo@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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <QNetworkRequest>
|
|
|
|
#include <QNetworkAccessManager>
|
2013-10-28 23:01:59 +04:00
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QNetworkRequest>
|
2013-10-24 02:29:08 +04:00
|
|
|
#include <QSslConfiguration>
|
2013-10-21 23:42:52 +04:00
|
|
|
#include <QBuffer>
|
|
|
|
#include <QXmlStreamReader>
|
|
|
|
#include <QStringList>
|
2013-10-23 16:48:44 +04:00
|
|
|
#include <QStack>
|
2013-11-14 20:02:41 +04:00
|
|
|
#include <QTimer>
|
2013-11-22 17:01:11 +04:00
|
|
|
#include <QMutex>
|
2013-10-21 23:42:52 +04:00
|
|
|
#include <QDebug>
|
2014-01-28 14:06:40 +04:00
|
|
|
#include <QCoreApplication>
|
2013-10-18 14:24:29 +04:00
|
|
|
|
|
|
|
#include "json.h"
|
|
|
|
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "networkjobs.h"
|
|
|
|
#include "account.h"
|
2014-12-10 15:01:36 +03:00
|
|
|
#include "owncloudpropagator.h"
|
2013-10-18 14:24:29 +04:00
|
|
|
|
2013-10-28 23:01:59 +04:00
|
|
|
#include "creds/credentialsfactory.h"
|
2013-11-22 17:01:11 +04:00
|
|
|
#include "creds/abstractcredentials.h"
|
2013-10-28 23:01:59 +04:00
|
|
|
|
2014-02-05 17:16:43 +04:00
|
|
|
Q_DECLARE_METATYPE(QTimer*)
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2013-10-18 14:24:29 +04:00
|
|
|
|
2015-03-10 13:17:35 +03:00
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-21 23:42:52 +04:00
|
|
|
: QObject(parent)
|
2014-11-11 15:19:29 +03:00
|
|
|
, _timedout(false)
|
2014-12-16 13:27:02 +03:00
|
|
|
, _followRedirects(false)
|
2013-11-22 17:01:11 +04:00
|
|
|
, _ignoreCredentialFailure(false)
|
2013-10-21 23:42:52 +04:00
|
|
|
, _reply(0)
|
2013-10-23 16:48:44 +04:00
|
|
|
, _account(account)
|
|
|
|
, _path(path)
|
2014-12-16 13:27:02 +03:00
|
|
|
, _redirectCount(0)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-03-25 13:58:57 +04:00
|
|
|
_timer.setSingleShot(true);
|
2014-12-03 17:22:32 +03:00
|
|
|
_timer.setInterval(OwncloudPropagator::httpTimeout() * 1000); // default to 5 minutes.
|
2014-03-25 13:58:57 +04:00
|
|
|
connect(&_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
|
2014-09-18 16:00:51 +04:00
|
|
|
|
|
|
|
connect(this, SIGNAL(networkActivity()), SLOT(resetTimeout()));
|
|
|
|
|
|
|
|
// Network activity on the propagator jobs (GET/PUT) keeps all requests alive.
|
|
|
|
// This is a workaround for OC instances which only support one
|
|
|
|
// parallel up and download
|
|
|
|
if (_account) {
|
2014-12-18 14:09:48 +03:00
|
|
|
connect(_account.data(), SIGNAL(propagatorNetworkActivity()), SLOT(resetTimeout()));
|
2014-09-18 16:00:51 +04:00
|
|
|
}
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractNetworkJob::setReply(QNetworkReply *reply)
|
|
|
|
{
|
2015-03-23 18:13:52 +03:00
|
|
|
if (reply)
|
|
|
|
reply->setProperty("doNotHandleAuth", true);
|
|
|
|
|
|
|
|
QNetworkReply *old = _reply;
|
2013-10-18 14:24:29 +04:00
|
|
|
_reply = reply;
|
2015-03-23 18:13:52 +03:00
|
|
|
delete old;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-14 20:02:41 +04:00
|
|
|
void AbstractNetworkJob::setTimeout(qint64 msec)
|
|
|
|
{
|
|
|
|
qDebug() << Q_FUNC_INFO << msec;
|
2014-03-25 13:58:57 +04:00
|
|
|
|
|
|
|
_timer.start(msec);
|
2013-11-14 20:23:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractNetworkJob::resetTimeout()
|
|
|
|
{
|
2014-03-25 13:58:57 +04:00
|
|
|
qint64 interval = _timer.interval();
|
|
|
|
_timer.stop();
|
|
|
|
_timer.start(interval);
|
2013-11-14 20:02:41 +04:00
|
|
|
}
|
|
|
|
|
2013-11-22 17:01:11 +04:00
|
|
|
void AbstractNetworkJob::setIgnoreCredentialFailure(bool ignore)
|
|
|
|
{
|
|
|
|
_ignoreCredentialFailure = ignore;
|
|
|
|
}
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
void AbstractNetworkJob::setPath(const QString &path)
|
|
|
|
{
|
|
|
|
_path = path;
|
|
|
|
}
|
|
|
|
|
2013-10-18 14:24:29 +04:00
|
|
|
void AbstractNetworkJob::setupConnections(QNetworkReply *reply)
|
|
|
|
{
|
2013-10-28 23:01:59 +04:00
|
|
|
connect(reply, SIGNAL(finished()), SLOT(slotFinished()));
|
2014-10-30 13:54:58 +03:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
|
|
|
connect(reply, SIGNAL(encrypted()), SIGNAL(networkActivity()));
|
|
|
|
#endif
|
|
|
|
connect(reply->manager(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), SIGNAL(networkActivity()));
|
|
|
|
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SIGNAL(networkActivity()));
|
|
|
|
connect(reply, SIGNAL(metaDataChanged()), SIGNAL(networkActivity()));
|
2014-09-18 16:00:51 +04:00
|
|
|
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(networkActivity()));
|
|
|
|
connect(reply, SIGNAL(uploadProgress(qint64,qint64)), SIGNAL(networkActivity()));
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2014-02-05 17:16:43 +04:00
|
|
|
QNetworkReply* AbstractNetworkJob::addTimer(QNetworkReply *reply)
|
|
|
|
{
|
2014-03-25 13:58:57 +04:00
|
|
|
reply->setProperty("timer", QVariant::fromValue(&_timer));
|
2014-02-05 17:16:43 +04:00
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
QNetworkReply* AbstractNetworkJob::davRequest(const QByteArray &verb, const QString &relPath,
|
|
|
|
QNetworkRequest req, QIODevice *data)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->davRequest(verb, relPath, req, data));
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-10-28 23:01:59 +04:00
|
|
|
QNetworkReply *AbstractNetworkJob::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->davRequest(verb, url, req, data));
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
QNetworkReply* AbstractNetworkJob::getRequest(const QString &relPath)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->getRequest(relPath));
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-10-28 23:01:59 +04:00
|
|
|
QNetworkReply *AbstractNetworkJob::getRequest(const QUrl &url)
|
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->getRequest(url));
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkReply *AbstractNetworkJob::headRequest(const QString &relPath)
|
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->headRequest(relPath));
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
|
|
|
|
{
|
2014-02-05 17:16:43 +04:00
|
|
|
return addTimer(_account->headRequest(url));
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
2013-11-22 17:01:11 +04:00
|
|
|
void AbstractNetworkJob::slotFinished()
|
|
|
|
{
|
2014-03-26 20:58:32 +04:00
|
|
|
_timer.stop();
|
|
|
|
|
2015-03-10 13:17:35 +03:00
|
|
|
if( _reply->error() == QNetworkReply::SslHandshakeFailedError ) {
|
2014-11-18 18:44:14 +03:00
|
|
|
qDebug() << "SslHandshakeFailedError: " << reply()->errorString() << " : can be caused by a webserver wanting SSL client certificates";
|
|
|
|
}
|
|
|
|
|
2015-03-10 13:17:35 +03:00
|
|
|
if( _reply->error() != QNetworkReply::NoError ) {
|
|
|
|
qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString();
|
|
|
|
if (_reply->error() == QNetworkReply::ProxyAuthenticationRequiredError) {
|
2014-03-06 20:45:02 +04:00
|
|
|
qDebug() << Q_FUNC_INFO << _reply->rawHeader("Proxy-Authenticate");
|
|
|
|
}
|
2014-01-28 14:06:40 +04:00
|
|
|
emit networkError(_reply);
|
2014-01-21 15:10:49 +04:00
|
|
|
}
|
2014-03-26 20:58:32 +04:00
|
|
|
|
|
|
|
// get the Date timestamp from reply
|
2015-03-09 17:52:52 +03:00
|
|
|
_responseTimestamp = _reply->rawHeader("Date");
|
2014-03-26 20:58:32 +04:00
|
|
|
_duration = _durationTimer.elapsed();
|
|
|
|
|
2014-12-16 13:27:02 +03:00
|
|
|
if (_followRedirects) {
|
|
|
|
// ### 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractCredentials *creds = _account->credentials();
|
2014-12-11 17:43:48 +03:00
|
|
|
if (!creds->stillValid(_reply) && ! _ignoreCredentialFailure) {
|
|
|
|
_account->handleInvalidCredentials();
|
2013-11-22 17:01:11 +04:00
|
|
|
}
|
2014-12-11 17:43:48 +03:00
|
|
|
|
|
|
|
bool discard = finished();
|
2014-04-14 17:08:43 +04:00
|
|
|
if (discard) {
|
|
|
|
deleteLater();
|
|
|
|
}
|
2013-11-22 17:01:11 +04:00
|
|
|
}
|
|
|
|
|
2014-03-26 20:58:32 +04:00
|
|
|
quint64 AbstractNetworkJob::duration()
|
|
|
|
{
|
|
|
|
return _duration;
|
|
|
|
}
|
|
|
|
|
2015-03-09 17:52:52 +03:00
|
|
|
QByteArray AbstractNetworkJob::responseTimestamp()
|
2014-03-26 20:58:32 +04:00
|
|
|
{
|
|
|
|
return _responseTimestamp;
|
|
|
|
}
|
|
|
|
|
2014-09-18 16:00:51 +04:00
|
|
|
AbstractNetworkJob::~AbstractNetworkJob()
|
|
|
|
{
|
2015-03-23 18:13:52 +03:00
|
|
|
setReply(0);
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-22 17:01:11 +04:00
|
|
|
void AbstractNetworkJob::start()
|
|
|
|
{
|
2014-03-25 13:58:57 +04:00
|
|
|
_timer.start();
|
|
|
|
_durationTimer.start();
|
2014-03-26 20:58:32 +04:00
|
|
|
_duration = 0;
|
2014-03-25 13:58:57 +04:00
|
|
|
|
2015-04-12 13:35:40 +03:00
|
|
|
const QUrl url = account()->url();
|
|
|
|
const QString displayUrl = QString( "%1://%2%3").arg(url.scheme()).arg(url.host()).arg(url.path());
|
|
|
|
|
|
|
|
qDebug() << "!!!" << metaObject()->className() << "created for" << displayUrl << "+" << path();
|
2013-11-22 17:01:11 +04:00
|
|
|
}
|
|
|
|
|
2014-09-12 20:06:29 +04:00
|
|
|
void AbstractNetworkJob::slotTimeout()
|
|
|
|
{
|
2014-11-11 15:19:29 +03:00
|
|
|
_timedout = true;
|
2014-09-26 12:04:36 +04:00
|
|
|
qDebug() << this << "Timeout";
|
|
|
|
if (reply()) {
|
|
|
|
reply()->abort();
|
|
|
|
} else {
|
|
|
|
qDebug() << "reply was NULL";
|
|
|
|
}
|
2014-09-12 20:06:29 +04:00
|
|
|
}
|
|
|
|
|
2013-10-18 14:24:29 +04:00
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
RequestEtagJob::RequestEtagJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-23 16:48:44 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
2013-11-14 22:20:09 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestEtagJob::start()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2013-10-23 16:48:44 +04:00
|
|
|
QNetworkRequest req;
|
2015-03-05 19:49:12 +03:00
|
|
|
// Let's always request all entries inside a directory. There are/were bugs in the server
|
|
|
|
// where a root or root-folder ETag is not updated when its contents change. We work around
|
|
|
|
// this by concatenating the ETags of the root and its contents.
|
|
|
|
req.setRawHeader("Depth", "1");
|
|
|
|
// See https://github.com/owncloud/core/issues/5255 and others
|
|
|
|
|
2013-10-18 14:24:29 +04:00
|
|
|
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
|
|
|
"<d:propfind xmlns:d=\"DAV:\">\n"
|
|
|
|
" <d:prop>\n"
|
2013-10-23 16:48:44 +04:00
|
|
|
" <d:getetag/>\n"
|
2013-10-18 14:24:29 +04:00
|
|
|
" </d:prop>\n"
|
|
|
|
"</d:propfind>\n");
|
2013-11-14 22:20:09 +04:00
|
|
|
QBuffer *buf = new QBuffer(this);
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setData(xml);
|
|
|
|
buf->open(QIODevice::ReadOnly);
|
|
|
|
// assumes ownership
|
2013-11-14 22:20:09 +04:00
|
|
|
setReply(davRequest("PROPFIND", path(), req, buf));
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setParent(reply());
|
|
|
|
setupConnections(reply());
|
|
|
|
|
|
|
|
if( reply()->error() != QNetworkReply::NoError ) {
|
|
|
|
qDebug() << "getting etag: request network error: " << reply()->errorString();
|
|
|
|
}
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool RequestEtagJob::finished()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
|
|
|
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
|
|
|
// Parse DAV response
|
|
|
|
QXmlStreamReader reader(reply());
|
|
|
|
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
|
|
|
QString etag;
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
QXmlStreamReader::TokenType type = reader.readNext();
|
|
|
|
if (type == QXmlStreamReader::StartElement &&
|
|
|
|
reader.namespaceUri() == QLatin1String("DAV:")) {
|
|
|
|
QString name = reader.name().toString();
|
|
|
|
if (name == QLatin1String("getetag")) {
|
|
|
|
etag += reader.readElementText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit etagRetreived(etag);
|
|
|
|
}
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
MkColJob::MkColJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-23 16:48:44 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2013-11-14 22:20:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MkColJob::start()
|
|
|
|
{
|
2015-05-20 15:03:37 +03:00
|
|
|
// add 'Content-Length: 0' header (see https://github.com/owncloud/client/issues/3256)
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("Content-Length", "0");
|
|
|
|
|
|
|
|
// assumes ownership
|
|
|
|
QNetworkReply *reply = davRequest("MKCOL", path(), req);
|
2013-11-14 22:20:09 +04:00
|
|
|
setReply(reply);
|
|
|
|
setupConnections(reply);
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool MkColJob::finished()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2013-10-28 23:01:59 +04:00
|
|
|
emit finished(reply()->error());
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2015-04-14 09:36:17 +03:00
|
|
|
/*********************************************************************************************/
|
|
|
|
// supposed to read <D:collection> when pointing to <D:resourcetype><D:collection></D:resourcetype>..
|
|
|
|
static QString readContentsAsString(QXmlStreamReader &reader) {
|
|
|
|
QString result;
|
|
|
|
int level = 0;
|
|
|
|
do {
|
|
|
|
QXmlStreamReader::TokenType type = reader.readNext();
|
|
|
|
if (type == QXmlStreamReader::StartElement) {
|
|
|
|
level++;
|
|
|
|
result += "<" + reader.name().toString() + ">";
|
|
|
|
} else if (type == QXmlStreamReader::Characters) {
|
|
|
|
result += reader.text();
|
|
|
|
} else if (type == QXmlStreamReader::EndElement) {
|
|
|
|
level--;
|
|
|
|
if (level < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
result += "</" + reader.name().toString() + ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (!reader.atEnd());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LsColXMLParser::LsColXMLParser()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-05-07 18:19:14 +03:00
|
|
|
bool LsColXMLParser::parse( const QByteArray& xml, QHash<QString, qint64> *sizes, const QString& expectedPath)
|
2015-04-14 09:36:17 +03:00
|
|
|
{
|
|
|
|
// Parse DAV response
|
|
|
|
QXmlStreamReader reader(xml);
|
|
|
|
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
|
|
|
|
|
|
|
QStringList folders;
|
|
|
|
QString currentHref;
|
|
|
|
QMap<QString, QString> currentTmpProperties;
|
|
|
|
QMap<QString, QString> currentHttp200Properties;
|
|
|
|
bool currentPropsHaveHttp200 = false;
|
|
|
|
bool insidePropstat = false;
|
|
|
|
bool insideProp = false;
|
2015-04-14 15:56:25 +03:00
|
|
|
bool insideMultiStatus = false;
|
2015-04-14 09:36:17 +03:00
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
QXmlStreamReader::TokenType type = reader.readNext();
|
|
|
|
QString name = reader.name().toString();
|
|
|
|
// Start elements with DAV:
|
|
|
|
if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
|
|
|
|
if (name == QLatin1String("href")) {
|
2015-05-07 18:19:14 +03:00
|
|
|
// We don't use URL encoding in our request URL (which is the expected path) (QNAM will do it for us)
|
|
|
|
// but the result will have URL encoding..
|
|
|
|
QString hrefString = QString::fromUtf8(QByteArray::fromPercentEncoding(reader.readElementText().toUtf8()));
|
|
|
|
if (!hrefString.startsWith(expectedPath)) {
|
|
|
|
qDebug() << "Invalid href" << hrefString << "expected starting with" << expectedPath;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
currentHref = hrefString;
|
2015-04-14 09:36:17 +03:00
|
|
|
} else if (name == QLatin1String("response")) {
|
|
|
|
} else if (name == QLatin1String("propstat")) {
|
|
|
|
insidePropstat = true;
|
|
|
|
} else if (name == QLatin1String("status") && insidePropstat) {
|
|
|
|
QString httpStatus = reader.readElementText();
|
|
|
|
if (httpStatus.startsWith("HTTP/1.1 200")) {
|
|
|
|
currentPropsHaveHttp200 = true;
|
|
|
|
} else {
|
|
|
|
currentPropsHaveHttp200 = false;
|
|
|
|
}
|
|
|
|
} else if (name == QLatin1String("prop")) {
|
|
|
|
insideProp = true;
|
|
|
|
continue;
|
2015-04-14 15:56:25 +03:00
|
|
|
} else if (name == QLatin1String("multistatus")) {
|
|
|
|
insideMultiStatus = true;
|
|
|
|
continue;
|
2015-04-14 09:36:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == QXmlStreamReader::StartElement && insidePropstat && insideProp) {
|
|
|
|
// All those elements are properties
|
|
|
|
QString propertyContent = readContentsAsString(reader);
|
|
|
|
if (name == QLatin1String("resourcetype") && propertyContent.contains("collection")) {
|
|
|
|
folders.append(currentHref);
|
|
|
|
} else if (name == QLatin1String("quota-used-bytes")) {
|
|
|
|
bool ok = false;
|
|
|
|
auto s = propertyContent.toLongLong(&ok);
|
|
|
|
if (ok && sizes) {
|
|
|
|
sizes->insert(currentHref, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentTmpProperties.insert(reader.name().toString(), propertyContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// End elements with DAV:
|
|
|
|
if (type == QXmlStreamReader::EndElement) {
|
|
|
|
if (reader.namespaceUri() == QLatin1String("DAV:")) {
|
|
|
|
if (reader.name() == "response") {
|
|
|
|
if (currentHref.endsWith('/')) {
|
|
|
|
currentHref.chop(1);
|
|
|
|
}
|
|
|
|
emit directoryListingIterated(currentHref, currentHttp200Properties);
|
|
|
|
currentHref.clear();
|
|
|
|
currentHttp200Properties.clear();
|
|
|
|
} else if (reader.name() == "propstat") {
|
|
|
|
insidePropstat = false;
|
|
|
|
if (currentPropsHaveHttp200) {
|
|
|
|
currentHttp200Properties = QMap<QString,QString>(currentTmpProperties);
|
|
|
|
}
|
|
|
|
currentTmpProperties.clear();
|
|
|
|
currentPropsHaveHttp200 = false;
|
|
|
|
} else if (reader.name() == "prop") {
|
|
|
|
insideProp = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reader.hasError()) {
|
|
|
|
// XML Parser error? Whatever had been emitted before will come as directoryListingIterated
|
2015-04-14 15:56:25 +03:00
|
|
|
qDebug() << "ERROR" << reader.errorString() << xml;
|
|
|
|
return false;
|
|
|
|
} else if (!insideMultiStatus) {
|
|
|
|
qDebug() << "ERROR no WebDAV response?" << xml;
|
2015-04-14 09:36:17 +03:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
emit directoryListingSubfolders(folders);
|
|
|
|
emit finishedWithoutError();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-10-18 14:24:29 +04:00
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
LsColJob::LsColJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-23 16:48:44 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
2013-11-14 22:20:09 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-03-02 13:00:37 +03:00
|
|
|
void LsColJob::setProperties(QList<QByteArray> properties)
|
|
|
|
{
|
|
|
|
_properties = properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QByteArray> LsColJob::properties() const
|
|
|
|
{
|
|
|
|
return _properties;
|
|
|
|
}
|
|
|
|
|
2013-11-14 22:20:09 +04:00
|
|
|
void LsColJob::start()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2015-03-02 13:00:37 +03:00
|
|
|
QList<QByteArray> properties = _properties;
|
|
|
|
|
|
|
|
if (properties.isEmpty()) {
|
|
|
|
qWarning() << "Propfind with no properties!";
|
|
|
|
}
|
|
|
|
QByteArray propStr;
|
|
|
|
foreach (const QByteArray &prop, properties) {
|
|
|
|
if (prop.contains(':')) {
|
|
|
|
int colIdx = prop.lastIndexOf(":");
|
|
|
|
auto ns = prop.left(colIdx);
|
|
|
|
if (ns == "http://owncloud.org/ns") {
|
|
|
|
propStr += " <oc:" + prop.mid(colIdx+1) + " />\n";
|
|
|
|
} else {
|
|
|
|
propStr += " <" + prop.mid(colIdx+1) + " xmlns=\"" + ns + "\" />\n";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
propStr += " <d:" + prop + " />\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
QNetworkRequest req;
|
2013-10-18 14:24:29 +04:00
|
|
|
req.setRawHeader("Depth", "1");
|
|
|
|
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
2014-12-02 14:25:51 +03:00
|
|
|
"<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\">\n"
|
2013-10-18 14:24:29 +04:00
|
|
|
" <d:prop>\n"
|
2015-03-02 13:00:37 +03:00
|
|
|
+ propStr +
|
2013-10-18 14:24:29 +04:00
|
|
|
" </d:prop>\n"
|
|
|
|
"</d:propfind>\n");
|
2013-11-14 22:20:09 +04:00
|
|
|
QBuffer *buf = new QBuffer(this);
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setData(xml);
|
|
|
|
buf->open(QIODevice::ReadOnly);
|
2013-11-14 22:20:09 +04:00
|
|
|
QNetworkReply *reply = davRequest("PROPFIND", path(), req, buf);
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setParent(reply);
|
|
|
|
setReply(reply);
|
|
|
|
setupConnections(reply);
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2015-04-13 17:04:24 +03:00
|
|
|
// TODO: Instead of doing all in this slot, we should iteratively parse in readyRead(). This
|
|
|
|
// would allow us to be more asynchronous in processing while data is coming from the network,
|
|
|
|
// not in all in one big blobb at the end.
|
2014-04-14 17:08:43 +04:00
|
|
|
bool LsColJob::finished()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-12-02 14:25:51 +03:00
|
|
|
QString contentType = reply()->header(QNetworkRequest::ContentTypeHeader).toString();
|
|
|
|
int httpCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
if (httpCode == 207 && contentType.contains("application/xml; charset=utf-8")) {
|
2015-04-14 09:36:17 +03:00
|
|
|
LsColXMLParser parser;
|
|
|
|
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
|
|
|
this, SIGNAL(directoryListingSubfolders(const QStringList&)) );
|
|
|
|
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
|
|
|
this, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
|
|
|
connect( &parser, SIGNAL(finishedWithError(QNetworkReply *)),
|
|
|
|
this, SIGNAL(finishedWithError(QNetworkReply *)) );
|
|
|
|
connect( &parser, SIGNAL(finishedWithoutError()),
|
|
|
|
this, SIGNAL(finishedWithoutError()) );
|
|
|
|
|
2015-05-07 18:19:14 +03:00
|
|
|
QString expectedPath = reply()->request().url().path(); // something like "/owncloud/remote.php/webdav/folder"
|
|
|
|
if( !parser.parse( reply()->readAll(), &_sizes, expectedPath ) ) {
|
2015-04-14 09:36:17 +03:00
|
|
|
// XML parse error
|
2015-04-13 17:04:24 +03:00
|
|
|
emit finishedWithError(reply());
|
|
|
|
}
|
2014-12-02 14:25:51 +03:00
|
|
|
} else if (httpCode == 207) {
|
|
|
|
// wrong content type
|
|
|
|
emit finishedWithError(reply());
|
|
|
|
} else {
|
2015-04-13 17:04:24 +03:00
|
|
|
// wrong HTTP code or any other network error
|
2014-12-02 14:25:51 +03:00
|
|
|
emit finishedWithError(reply());
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
2015-04-14 09:36:17 +03:00
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-04-14 17:10:08 +04:00
|
|
|
namespace {
|
|
|
|
const char statusphpC[] = "status.php";
|
|
|
|
const char owncloudDirC[] = "owncloud/";
|
|
|
|
}
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent)
|
2014-04-14 17:10:08 +04:00
|
|
|
: AbstractNetworkJob(account, QLatin1String(statusphpC) , parent)
|
|
|
|
, _subdirFallback(false)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-12-16 13:27:02 +03:00
|
|
|
_followRedirects = true;
|
2014-03-03 19:37:29 +04:00
|
|
|
setIgnoreCredentialFailure(true);
|
2013-11-14 22:20:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckServerJob::start()
|
|
|
|
{
|
2013-10-23 16:48:44 +04:00
|
|
|
setReply(getRequest(path()));
|
2013-10-18 14:24:29 +04:00
|
|
|
setupConnections(reply());
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-14 20:02:41 +04:00
|
|
|
void CheckServerJob::slotTimeout()
|
|
|
|
{
|
|
|
|
qDebug() << "TIMEOUT" << Q_FUNC_INFO;
|
2014-10-29 11:37:52 +03:00
|
|
|
if (reply() && reply()->isRunning()) {
|
2013-11-14 20:02:41 +04:00
|
|
|
emit timeout(reply()->url());
|
2014-10-29 11:37:52 +03:00
|
|
|
} else if (!reply()) {
|
|
|
|
qDebug() << Q_FUNC_INFO << "Timeout even there was no reply?";
|
|
|
|
}
|
2014-09-12 19:58:01 +04:00
|
|
|
deleteLater();
|
2013-11-14 20:02:41 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
QString CheckServerJob::version(const QVariantMap &info)
|
|
|
|
{
|
|
|
|
return info.value(QLatin1String("version")).toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString CheckServerJob::versionString(const QVariantMap &info)
|
|
|
|
{
|
|
|
|
return info.value(QLatin1String("versionstring")).toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckServerJob::installed(const QVariantMap &info)
|
|
|
|
{
|
|
|
|
return info.value(QLatin1String("installed")).toBool();
|
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool CheckServerJob::finished()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2014-01-21 04:45:02 +04:00
|
|
|
account()->setSslConfiguration(reply()->sslConfiguration());
|
2013-10-24 02:29:08 +04:00
|
|
|
|
2015-05-12 18:19:17 +03:00
|
|
|
#if QT_VERSION > QT_VERSION_CHECK(5, 2, 0)
|
2015-05-08 15:21:27 +03:00
|
|
|
if (reply()->request().url().scheme() == QLatin1String("https")
|
|
|
|
&& reply()->sslConfiguration().sessionTicket().isEmpty()) {
|
|
|
|
qDebug() << "No SSL session identifier / session ticket is used, this might impact sync performance negatively.";
|
|
|
|
}
|
2015-05-12 18:19:17 +03:00
|
|
|
#endif
|
2015-05-08 15:21:27 +03:00
|
|
|
|
2014-04-14 17:10:08 +04:00
|
|
|
// The serverInstalls to /owncloud. Let's try that if the file wasn't found
|
|
|
|
// at the original location
|
|
|
|
if ((reply()->error() == QNetworkReply::ContentNotFoundError) && (!_subdirFallback)) {
|
|
|
|
_subdirFallback = true;
|
|
|
|
setPath(QLatin1String(owncloudDirC)+QLatin1String(statusphpC));
|
|
|
|
start();
|
|
|
|
qDebug() << "Retrying with" << reply()->url();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-18 14:24:29 +04:00
|
|
|
bool success = false;
|
2014-06-24 17:17:33 +04:00
|
|
|
QByteArray body = reply()->readAll();
|
2014-10-09 13:05:04 +04:00
|
|
|
int httpStatus = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
if( body.isEmpty() || httpStatus != 200) {
|
|
|
|
qDebug() << "error: status.php replied " << httpStatus << body;
|
2014-04-14 17:10:08 +04:00
|
|
|
emit instanceNotFound(reply());
|
2014-06-24 17:17:33 +04:00
|
|
|
} else {
|
|
|
|
QVariantMap status = QtJson::parse(QString::fromUtf8(body), success).toMap();
|
|
|
|
// empty or invalid response
|
|
|
|
if (!success || status.isEmpty()) {
|
|
|
|
qDebug() << "status.php from server is not valid JSON!";
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
|
|
|
|
if( status.contains("installed")
|
|
|
|
&& status.contains("version")
|
|
|
|
&& status.contains("versionstring") ) {
|
2014-10-20 15:34:09 +04:00
|
|
|
|
2014-06-24 17:17:33 +04:00
|
|
|
emit instanceFound(reply()->url(), status);
|
|
|
|
} else {
|
2014-12-16 13:27:02 +03:00
|
|
|
qDebug() << "No proper answer on " << reply()->url();
|
2014-06-24 17:17:33 +04:00
|
|
|
emit instanceNotFound(reply());
|
|
|
|
}
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-04 19:36:23 +04:00
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
PropfindJob::PropfindJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-23 16:48:44 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
2013-11-14 22:20:09 +04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropfindJob::start()
|
|
|
|
{
|
|
|
|
QList<QByteArray> properties = _properties;
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
if (properties.isEmpty()) {
|
2015-02-26 14:36:11 +03:00
|
|
|
qWarning() << "Propfind with no properties!";
|
2013-10-23 16:48:44 +04:00
|
|
|
}
|
|
|
|
QNetworkRequest req;
|
2013-10-18 14:24:29 +04:00
|
|
|
req.setRawHeader("Depth", "0");
|
2013-10-23 16:48:44 +04:00
|
|
|
QByteArray propStr;
|
|
|
|
foreach (const QByteArray &prop, properties) {
|
2014-11-13 20:57:07 +03:00
|
|
|
if (prop.contains(':')) {
|
2014-11-14 17:03:48 +03:00
|
|
|
int colIdx = prop.lastIndexOf(":");
|
|
|
|
propStr += " <" + prop.mid(colIdx+1) + " xmlns=\"" + prop.left(colIdx) + "\" />\n";
|
2014-11-13 20:57:07 +03:00
|
|
|
} else {
|
|
|
|
propStr += " <d:" + prop + " />\n";
|
|
|
|
}
|
2013-10-23 16:48:44 +04:00
|
|
|
}
|
|
|
|
QByteArray xml = "<?xml version=\"1.0\" ?>\n"
|
|
|
|
"<d:propfind xmlns:d=\"DAV:\">\n"
|
|
|
|
" <d:prop>\n"
|
|
|
|
+ propStr +
|
|
|
|
" </d:prop>\n"
|
|
|
|
"</d:propfind>\n";
|
|
|
|
|
2013-11-14 22:20:09 +04:00
|
|
|
QBuffer *buf = new QBuffer(this);
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setData(xml);
|
|
|
|
buf->open(QIODevice::ReadOnly);
|
2013-11-14 22:20:09 +04:00
|
|
|
setReply(davRequest("PROPFIND", path(), req, buf));
|
2013-10-18 14:24:29 +04:00
|
|
|
buf->setParent(reply());
|
|
|
|
setupConnections(reply());
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-14 22:20:09 +04:00
|
|
|
void PropfindJob::setProperties(QList<QByteArray> properties)
|
|
|
|
{
|
|
|
|
_properties = properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QByteArray> PropfindJob::properties() const
|
|
|
|
{
|
|
|
|
return _properties;
|
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool PropfindJob::finished()
|
2013-10-18 14:24:29 +04:00
|
|
|
{
|
|
|
|
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
|
|
|
|
if (http_result_code == 207) {
|
|
|
|
// Parse DAV response
|
|
|
|
QXmlStreamReader reader(reply());
|
|
|
|
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
QVariantMap items;
|
|
|
|
// introduced to nesting is ignored
|
|
|
|
QStack<QString> curElement;
|
2013-10-18 14:24:29 +04:00
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
QXmlStreamReader::TokenType type = reader.readNext();
|
2014-11-13 20:57:07 +03:00
|
|
|
if (type == QXmlStreamReader::StartElement) {
|
2014-11-14 17:03:48 +03:00
|
|
|
if (!curElement.isEmpty() && curElement.top() == QLatin1String("prop")) {
|
|
|
|
items.insert(reader.name().toString(), reader.readElementText());
|
2015-04-07 11:35:27 +03:00
|
|
|
} else {
|
|
|
|
curElement.push(reader.name().toString());
|
2013-10-23 16:48:44 +04:00
|
|
|
}
|
|
|
|
}
|
2014-11-13 20:57:07 +03:00
|
|
|
if (type == QXmlStreamReader::EndElement) {
|
2013-10-23 16:48:44 +04:00
|
|
|
if(curElement.top() == reader.name()) {
|
|
|
|
curElement.pop();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-23 16:48:44 +04:00
|
|
|
}
|
|
|
|
emit result(items);
|
2013-10-18 14:24:29 +04:00
|
|
|
} else {
|
2014-02-24 18:20:36 +04:00
|
|
|
qDebug() << "Quota request *not* successful, http result code is" << http_result_code
|
|
|
|
<< (http_result_code == 302 ? reply()->header(QNetworkRequest::LocationHeader).toString() : QLatin1String(""));
|
2014-11-13 20:57:07 +03:00
|
|
|
emit finishedWithError();
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-18 14:24:29 +04:00
|
|
|
}
|
|
|
|
|
2013-11-04 19:36:23 +04:00
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
EntityExistsJob::EntityExistsJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-10-28 23:01:59 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
|
|
|
{
|
2013-11-14 22:20:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void EntityExistsJob::start()
|
|
|
|
{
|
|
|
|
setReply(headRequest(path()));
|
2013-10-28 23:01:59 +04:00
|
|
|
setupConnections(reply());
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool EntityExistsJob::finished()
|
2013-10-28 23:01:59 +04:00
|
|
|
{
|
|
|
|
emit exists(reply());
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-10-28 23:01:59 +04:00
|
|
|
}
|
|
|
|
|
2013-11-04 19:36:23 +04:00
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
CheckQuotaJob::CheckQuotaJob(AccountPtr account, const QString &path, QObject *parent)
|
2013-11-04 19:36:23 +04:00
|
|
|
: AbstractNetworkJob(account, path, parent)
|
2013-11-14 22:20:09 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckQuotaJob::start()
|
2013-11-04 19:36:23 +04:00
|
|
|
{
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("Depth", "0");
|
|
|
|
QByteArray xml("<?xml version=\"1.0\" ?>\n"
|
|
|
|
"<d:propfind xmlns:d=\"DAV:\">\n"
|
|
|
|
" <d:prop>\n"
|
|
|
|
" <d:quota-available-bytes/>\n"
|
|
|
|
" <d:quota-used-bytes/>\n"
|
|
|
|
" </d:prop>\n"
|
|
|
|
"</d:propfind>\n");
|
2013-11-14 22:20:09 +04:00
|
|
|
QBuffer *buf = new QBuffer(this);
|
2013-11-04 19:36:23 +04:00
|
|
|
buf->setData(xml);
|
|
|
|
buf->open(QIODevice::ReadOnly);
|
|
|
|
// assumes ownership
|
2013-11-14 22:20:09 +04:00
|
|
|
setReply(davRequest("PROPFIND", path(), req, buf));
|
2013-11-04 19:36:23 +04:00
|
|
|
buf->setParent(reply());
|
|
|
|
setupConnections(reply());
|
2013-11-22 17:01:11 +04:00
|
|
|
AbstractNetworkJob::start();
|
2013-11-04 19:36:23 +04:00
|
|
|
}
|
|
|
|
|
2014-04-14 17:08:43 +04:00
|
|
|
bool CheckQuotaJob::finished()
|
2013-11-04 19:36:23 +04:00
|
|
|
{
|
|
|
|
if (reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
|
|
|
|
// Parse DAV response
|
|
|
|
QXmlStreamReader reader(reply());
|
|
|
|
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
|
|
|
qint64 quotaAvailableBytes = 0;
|
|
|
|
qint64 quotaUsedBytes = 0;
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
QXmlStreamReader::TokenType type = reader.readNext();
|
|
|
|
if (type == QXmlStreamReader::StartElement &&
|
|
|
|
reader.namespaceUri() == QLatin1String("DAV:")) {
|
|
|
|
QString name = reader.name().toString();
|
|
|
|
if (name == QLatin1String("quota-available-bytes")) {
|
2014-03-17 13:37:06 +04:00
|
|
|
// I have seen the server returning frational bytes:
|
|
|
|
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
|
|
|
|
quotaAvailableBytes = reader.readElementText().toDouble();
|
2013-11-04 19:36:23 +04:00
|
|
|
} else if (name == QLatin1String("quota-used-bytes")) {
|
2014-03-17 13:37:06 +04:00
|
|
|
quotaUsedBytes = reader.readElementText().toDouble();
|
2013-11-04 19:36:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qint64 total = quotaUsedBytes + quotaAvailableBytes;
|
|
|
|
emit quotaRetrieved(total, quotaUsedBytes);
|
|
|
|
}
|
2014-04-14 17:08:43 +04:00
|
|
|
return true;
|
2013-11-04 19:36:23 +04:00
|
|
|
}
|
|
|
|
|
2014-02-05 17:16:43 +04:00
|
|
|
NetworkJobTimeoutPauser::NetworkJobTimeoutPauser(QNetworkReply *reply)
|
|
|
|
{
|
|
|
|
_timer = reply->property("timer").value<QTimer*>();
|
|
|
|
if(!_timer.isNull()) {
|
|
|
|
_timer->stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NetworkJobTimeoutPauser::~NetworkJobTimeoutPauser()
|
|
|
|
{
|
|
|
|
if(!_timer.isNull()) {
|
|
|
|
_timer->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-26 17:33:00 +03:00
|
|
|
JsonApiJob::JsonApiJob(const AccountPtr &account, const QString& path, QObject* parent): AbstractNetworkJob(account, path, parent)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
void JsonApiJob::start()
|
|
|
|
{
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("OCS-APIREQUEST", "true");
|
|
|
|
QUrl url = Account::concatUrlPath(account()->url(), path());
|
|
|
|
url.setQueryItems(QList<QPair<QString, QString> >() << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json")));
|
|
|
|
setReply(davRequest("GET", url, req));
|
|
|
|
setupConnections(reply());
|
|
|
|
AbstractNetworkJob::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JsonApiJob::finished()
|
|
|
|
{
|
|
|
|
if (reply()->error() != QNetworkReply::NoError) {
|
|
|
|
qWarning() << "Network error: " << path() << reply()->errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
|
|
|
emit jsonRecieved(QVariantMap());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool success = false;
|
|
|
|
QString jsonStr = QString::fromUtf8(reply()->readAll());
|
|
|
|
QVariantMap json = QtJson::parse(jsonStr, success).toMap();
|
|
|
|
// empty or invalid response
|
|
|
|
if (!success || json.isEmpty()) {
|
|
|
|
qWarning() << "invalid JSON!" << jsonStr;
|
|
|
|
emit jsonRecieved(QVariantMap());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit jsonRecieved(json);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
} // namespace OCC
|