Update server url in case of permanent redirection #5972

This is the first time the account url may update outside of
account setup.

Summary of redirection handling:
1. During account setup (wizard)
   - status.php gets permanently redirected -> adjust url
   - authed PROPFIND gets *any* redirection -> adjust url
2. During connectivity ping (ConnectionValidator)
   - status.php gets permanently redirected -> adjust url (new!)

All other redirections should be followed transparently and
don't update the account url in the settings.
This commit is contained in:
Christian Kamm 2017-09-08 11:59:45 +02:00 committed by Roeland Jago Douma
parent 1c0d80c20d
commit b810ce7768
No known key found for this signature in database
GPG key ID: F941078878347C0C
6 changed files with 65 additions and 8 deletions

View file

@ -196,13 +196,10 @@ void OwncloudSetupWizard::slotOwnCloudFoundAuth(const QUrl &url, const QJsonObje
// https://github.com/owncloud/core/pull/27473/files // https://github.com/owncloud/core/pull/27473/files
_ocWizard->account()->setServerVersion(serverVersion); _ocWizard->account()->setServerVersion(serverVersion);
QString p = url.path(); if (url != _ocWizard->account()->url()) {
if (p.endsWith("/status.php")) {
// We might be redirected, update the account // We might be redirected, update the account
QUrl redirectedUrl = url; _ocWizard->account()->setUrl(url);
redirectedUrl.setPath(url.path().left(url.path().length() - 11)); qCInfo(lcWizard) << " was redirected to" << url.toString();
_ocWizard->account()->setUrl(redirectedUrl);
qCInfo(lcWizard) << " was redirected to" << redirectedUrl.toString();
} }
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_ocWizard->account(), this); DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_ocWizard->account(), this);

View file

@ -182,6 +182,8 @@ void AbstractNetworkJob::slotFinished()
} else if (verb.isEmpty()) { } else if (verb.isEmpty()) {
qCWarning(lcNetworkJob) << this << "cannot redirect request: could not detect original verb"; qCWarning(lcNetworkJob) << this << "cannot redirect request: could not detect original verb";
} else { } else {
emit redirected(_reply, redirectUrl, _redirectCount - 1);
// Create the redirected request and send it // Create the redirected request and send it
qCInfo(lcNetworkJob) << "Redirecting" << verb << requestedUrl << redirectUrl; qCInfo(lcNetworkJob) << "Redirecting" << verb << requestedUrl << redirectUrl;
resetTimeout(); resetTimeout();

View file

@ -98,6 +98,14 @@ signals:
void networkError(QNetworkReply *reply); void networkError(QNetworkReply *reply);
void networkActivity(); void networkActivity();
/** Emitted when a redirect is followed.
*
* \a reply The "please redirect" reply
* \a targetUrl Where to redirect to
* \a redirectCount Counts redirect hops, first is 0.
*/
void redirected(QNetworkReply *reply, const QUrl &targetUrl, int redirectCount);
protected: protected:
void setupConnections(QNetworkReply *reply); void setupConnections(QNetworkReply *reply);

View file

@ -135,6 +135,13 @@ void ConnectionValidator::slotStatusFound(const QUrl &url, const QJsonObject &in
<< CheckServerJob::versionString(info) << CheckServerJob::versionString(info)
<< "(" << serverVersion << ")"; << "(" << serverVersion << ")";
// Update server url in case of redirection
if (_account->url() != url) {
qCInfo(lcConnectionValidator()) << "status.php was redirected to" << url.toString();
_account->setUrl(url);
_account->wantsAccountSaved(_account.data());
}
if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) { if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) {
return; return;
} }

View file

@ -397,13 +397,17 @@ namespace {
CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent) CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent)
: AbstractNetworkJob(account, QLatin1String(statusphpC), parent) : AbstractNetworkJob(account, QLatin1String(statusphpC), parent)
, _subdirFallback(false) , _subdirFallback(false)
, _permanentRedirects(0)
{ {
setIgnoreCredentialFailure(true); setIgnoreCredentialFailure(true);
connect(this, SIGNAL(redirected(QNetworkReply *, QUrl, int)),
SLOT(slotRedirected(QNetworkReply *, QUrl, int)));
} }
void CheckServerJob::start() void CheckServerJob::start()
{ {
sendRequest("GET", makeAccountUrl(path())); _serverUrl = account()->url();
sendRequest("GET", Utility::concatUrlPath(_serverUrl, path()));
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot())); connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot())); connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
AbstractNetworkJob::start(); AbstractNetworkJob::start();
@ -455,6 +459,24 @@ void CheckServerJob::encryptedSlot()
mergeSslConfigurationForSslButton(reply()->sslConfiguration(), account()); mergeSslConfigurationForSslButton(reply()->sslConfiguration(), account());
} }
void CheckServerJob::slotRedirected(QNetworkReply *reply, const QUrl &targetUrl, int redirectCount)
{
QByteArray slashStatusPhp("/");
slashStatusPhp.append(statusphpC);
int httpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString path = targetUrl.path();
if ((httpCode == 301 || httpCode == 308) // permanent redirection
&& redirectCount == _permanentRedirects // don't apply permanent redirects after a temporary one
&& path.endsWith(slashStatusPhp)) {
_serverUrl = targetUrl;
_serverUrl.setPath(path.left(path.size() - slashStatusPhp.size()));
qCInfo(lcCheckServerJob) << "status.php was permanently redirected to"
<< targetUrl << "new server url is" << _serverUrl;
++_permanentRedirects;
}
}
void CheckServerJob::metaDataChangedSlot() void CheckServerJob::metaDataChangedSlot()
{ {
account()->setSslConfiguration(reply()->sslConfiguration()); account()->setSslConfiguration(reply()->sslConfiguration());
@ -499,7 +521,7 @@ bool CheckServerJob::finished()
qCInfo(lcCheckServerJob) << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply(); qCInfo(lcCheckServerJob) << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
if (status.object().contains("installed")) { if (status.object().contains("installed")) {
emit instanceFound(reply()->url(), status.object()); emit instanceFound(_serverUrl, status.object());
} else { } else {
qCWarning(lcCheckServerJob) << "No proper answer on " << reply()->url(); qCWarning(lcCheckServerJob) << "No proper answer on " << reply()->url();
emit instanceNotFound(reply()); emit instanceNotFound(reply());

View file

@ -241,6 +241,11 @@ public:
static bool installed(const QJsonObject &info); static bool installed(const QJsonObject &info);
signals: signals:
/** Emitted when a status.php was successfully read.
*
* \a url see _serverStatusUrl (does not include "/status.php")
* \a info The status.php reply information
*/
void instanceFound(const QUrl &url, const QJsonObject &info); void instanceFound(const QUrl &url, const QJsonObject &info);
/** Emitted on invalid status.php reply. /** Emitted on invalid status.php reply.
@ -248,6 +253,11 @@ signals:
* \a reply is never null * \a reply is never null
*/ */
void instanceNotFound(QNetworkReply *reply); void instanceNotFound(QNetworkReply *reply);
/** A timeout occurred.
*
* \a url The specific url where the timeout happened.
*/
void timeout(const QUrl &url); void timeout(const QUrl &url);
private: private:
@ -256,9 +266,20 @@ private:
private slots: private slots:
virtual void metaDataChangedSlot(); virtual void metaDataChangedSlot();
virtual void encryptedSlot(); virtual void encryptedSlot();
void slotRedirected(QNetworkReply *reply, const QUrl &targetUrl, int redirectCount);
private: private:
bool _subdirFallback; bool _subdirFallback;
/** The permanent-redirect adjusted account url.
*
* Note that temporary redirects or a permanent redirect behind a temporary
* one do not affect this url.
*/
QUrl _serverUrl;
/** Keep track of how many permanent redirect were applied. */
int _permanentRedirects;
}; };