return;
}
- int resultCode =0;
- if( reply ) {
- resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- }
+ int resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
endNotificationRequest(job->widget(), resultCode);
qDebug() << Q_FUNC_INFO << "Server notify job failed with code " << resultCode;
void FolderWizardRemotePath::slotHandleMkdirNetworkError(QNetworkReply *reply)
{
qDebug() << "** webdav mkdir request failed:" << reply->error();
- if( reply && !_account->credentials()->stillValid(reply) ) {
+ if( !_account->credentials()->stillValid(reply) ) {
showWarn(tr("Authentication failed accessing %1").arg(Theme::instance()->appNameGUI()));
} else {
showWarn(tr("Failed to create the folder on %1. Please check manually.")
}
}
-void FolderWizardRemotePath::slotHandleLsColNetworkError(QNetworkReply *reply)
+void FolderWizardRemotePath::slotHandleLsColNetworkError(QNetworkReply */*reply*/)
{
+ auto job = qobject_cast<MkColJob *>(sender());
showWarn(tr("Failed to list a folder. Error: %1")
- .arg(errorMessage(reply->errorString(), reply->readAll())));
+ .arg(job->errorStringParsingBody()));
}
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
void OwncloudSetupWizard::slotNoOwnCloudFoundAuth(QNetworkReply *reply)
{
+ auto job = qobject_cast<CheckServerJob *>(sender());
int resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
msg = tr("Failed to connect to %1 at %2:<br/>%3")
.arg(Utility::escape(Theme::instance()->appNameGUI()),
Utility::escape(reply->url().toString()),
- Utility::escape(reply->errorString()));
+ Utility::escape(job->errorString()));
}
bool isDowngradeAdvised = checkDowngradeAdvised(reply);
"<a href=\"%1\">click here</a> to access the service with your browser.")
.arg(Utility::escape(_ocWizard->account()->url().toString()));
} else {
- errorMsg = errorMessage(reply->errorString(), reply->readAll());
+ errorMsg = job->errorStringParsingBody();
}
// Something else went wrong, maybe the response was 200 but with invalid data.
// ### TODO move into EntityExistsJob once we decide if/how to return gui strings from jobs
void OwncloudSetupWizard::slotRemoteFolderExists(QNetworkReply *reply)
{
+ auto job = qobject_cast<EntityExistsJob *>(sender());
bool ok = true;
QString error;
QNetworkReply::NetworkError errId = reply->error();
createRemoteFolder();
}
} else {
- error = tr("Error: %1").arg(reply->errorString());
+ error = tr("Error: %1").arg(job->errorString());
ok = false;
}
return Utility::concatUrlPath(_account->davUrl(), relativePath);
}
-QByteArray AbstractNetworkJob::requestVerb(QNetworkReply* reply)
-{
- 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()
{
_timer.stop();
if( _reply->error() == QNetworkReply::SslHandshakeFailedError ) {
- qDebug() << "SslHandshakeFailedError: " << reply()->errorString() << " : can be caused by a webserver wanting SSL client certificates";
+ qDebug() << "SslHandshakeFailedError: " << errorString() << " : can be caused by a webserver wanting SSL client certificates";
}
if( _reply->error() != QNetworkReply::NoError ) {
- qDebug() << Q_FUNC_INFO << _reply->error() << _reply->errorString()
+ qDebug() << Q_FUNC_INFO << _reply->error() << errorString()
<< _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (_reply->error() == QNetworkReply::ProxyAuthenticationRequiredError) {
qDebug() << Q_FUNC_INFO << _reply->rawHeader("Proxy-Authenticate");
// ### 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
- QByteArray verb = requestVerb(reply());
+ QByteArray verb = requestVerb(*reply());
if (requestedUrl.scheme() == QLatin1String("https") &&
redirectUrl.scheme() == QLatin1String("http")) {
qWarning() << this << "HTTPS->HTTP downgrade detected!";
return _responseTimestamp;
}
+QString AbstractNetworkJob::errorString() const
+{
+ if (_timedout) {
+ return tr("Connection timed out");
+ } else if (!reply()) {
+ return tr("Unknown error: network reply was deleted");
+ } else if (reply()->hasRawHeader("OC-ErrorString")) {
+ return reply()->rawHeader("OC-ErrorString");
+ } else {
+ return networkReplyErrorString(*reply());
+ }
+}
+
+QString AbstractNetworkJob::errorStringParsingBody(QByteArray* body)
+{
+ QString base = errorString();
+ if (base.isEmpty() || !reply()) {
+ return QString();
+ }
+
+ QByteArray replyBody = reply()->readAll();
+ if (body) {
+ *body = replyBody;
+ }
+
+ QString extra = extractErrorMessage(replyBody);
+ // Don't append the XML error message to a OC-ErrorString message.
+ if (!extra.isEmpty() && !reply()->hasRawHeader("OC-ErrorString")) {
+ return QString::fromLatin1("%1 (%2)").arg(base, extra);
+ }
+
+ return base;
+}
+
AbstractNetworkJob::~AbstractNetworkJob()
{
setReply(0);
void AbstractNetworkJob::slotTimeout()
{
_timedout = true;
+ qDebug() << this << "Timeout" << (reply() ? reply()->request().url() : path());
+ onTimedOut();
+}
+
+void AbstractNetworkJob::onTimedOut()
+{
if (reply()) {
- qDebug() << Q_FUNC_INFO << this << "Timeout" << reply()->request().url();
reply()->abort();
} else {
- qDebug() << Q_FUNC_INFO << this << "Timeout reply was NULL";
deleteLater();
}
}
return msg;
}
+QByteArray requestVerb(const QNetworkReply& reply)
+{
+ 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();
+}
+
+QString networkReplyErrorString(const QNetworkReply& reply)
+{
+ QString base = reply.errorString();
+ int httpStatus = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ QString httpReason = reply.attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+
+ // Only adjust HTTP error messages of the expected format.
+ if (httpReason.isEmpty() || httpStatus == 0 || !base.contains(httpReason)) {
+ return base;
+ }
+
+ return AbstractNetworkJob::tr("Server replied \"%1 %2\" to \"%3 %4\"").arg(
+ QString::number(httpStatus),
+ httpReason,
+ requestVerb(reply),
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ reply.request().url().toString()
+#else
+ reply.request().url().toDisplayString()
+#endif
+ );
+}
+
} // namespace OCC
QByteArray responseTimestamp();
- qint64 timeoutMsec() { return _timer.interval(); }
+ qint64 timeoutMsec() const { return _timer.interval(); }
+ bool timedOut() const { return _timedout; }
+
+ /** Returns an error message, if any. */
+ QString errorString() const;
+
+ /** Like errorString, but also checking the reply body for information.
+ *
+ * Specifically, sometimes xml bodies have extra error information.
+ * This function reads the body of the reply and parses out the
+ * error information, if possible.
+ *
+ * \a body is optinally filled with the reply body.
+ *
+ * Warning: Needs to call reply()->readAll().
+ */
+ QString errorStringParsingBody(QByteArray* body = 0);
public slots:
void setTimeout(qint64 msec);
void resetTimeout();
signals:
+ /** Emitted on network error.
+ *
+ * \a reply is never null
+ */
void networkError(QNetworkReply *reply);
void networkActivity();
protected:
QUrl makeDavUrl(const QString& relativePath) const;
int maxRedirects() const { return 10; }
+
+ /** Called at the end of QNetworkReply::finished processing.
+ *
+ * Returning true triggers a deleteLater() of this job.
+ */
virtual bool finished() = 0;
+
+ /** Called on timeout.
+ *
+ * The default implementation aborts the reply.
+ */
+ virtual void onTimedOut();
+
QByteArray _responseTimestamp;
bool _timedout; // set to true when the timeout slot is received
// 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();
+ void slotTimeout();
protected:
AccountPtr _account;
/** Builds a error message based on the error and the reply body. */
QString OWNCLOUDSYNC_EXPORT errorMessage(const QString& baseError, const QByteArray& body);
+/** Helper to construct the HTTP verb used in the request
+ *
+ * Returns an empty QByteArray for UnknownOperation.
+ */
+QByteArray OWNCLOUDSYNC_EXPORT requestVerb(const QNetworkReply& reply);
+
+/** Nicer errorString() for QNetworkReply
+ *
+ * By default QNetworkReply::errorString() often produces messages like
+ * "Error downloading <url> - server replied: <reason>"
+ * but the "downloading" part invariably confuses people since the
+ * error might very well have been produced by a PUT request.
+ *
+ * This function produces clearer error messages for HTTP errors.
+ */
+QString OWNCLOUDSYNC_EXPORT networkReplyErrorString(const QNetworkReply& reply);
+
} // namespace OCC
// status.php could not be loaded (network or server issue!).
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
{
- qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString() << reply->peek(1024);
- if (reply && !_account->credentials()->ready()) {
+ auto job = qobject_cast<CheckServerJob *>(sender());
+ qDebug() << Q_FUNC_INFO << reply->error() << job->errorString() << reply->peek(1024);
+ if (!_account->credentials()->ready()) {
// This could be needed for SSL client certificates
// We need to load them from keychain and try
reportResult( CredentialsMissingOrWrong );
- } else
- if( reply && ! _account->credentials()->stillValid(reply)) {
+ } else if (! _account->credentials()->stillValid(reply)) {
_errors.append(tr("Authentication error: Either username or password are wrong."));
- } else {
+ } else {
//_errors.append(tr("Unable to connect to %1").arg(_account->url().toString()));
- _errors.append( reply->errorString() );
+ _errors.append( job->errorString() );
}
reportResult( StatusNotFound );
}
void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
{
+ auto job = qobject_cast<PropfindJob *>(sender());
Status stat = Timeout;
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
!_account->credentials()->stillValid(reply)) {
- qDebug() << reply->error() << reply->errorString();
+ qDebug() << reply->error() << job->errorString();
qDebug() << "******** Password is wrong!";
_errors << tr("The provided credentials are not correct");
stat = CredentialsMissingOrWrong;
} else if( reply->error() != QNetworkReply::NoError ) {
- _errors << errorMessage(reply->errorString(), reply->readAll());
+ _errors << job->errorStringParsingBody();
const int httpStatus =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
AbstractNetworkJob::start();
}
-void CheckServerJob::slotTimeout()
+void CheckServerJob::onTimedOut()
{
qDebug() << "TIMEOUT" << Q_FUNC_INFO;
if (reply() && reply()->isRunning()) {
int statusCode = 0;
if (reply()->error() != QNetworkReply::NoError) {
- qWarning() << "Network error: " << path() << reply()->errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+ qWarning() << "Network error: " << path() << errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute);
emit jsonReceived(QVariantMap(), statusCode);
return true;
}
signals:
void instanceFound(const QUrl&url, const QVariantMap &info);
+
+ /** Emitted on invalid status.php reply.
+ *
+ * \a reply is never null
+ */
void instanceNotFound(QNetworkReply *reply);
void timeout(const QUrl&url);
+private:
+ bool finished() Q_DECL_OVERRIDE;
+ void onTimedOut() Q_DECL_OVERRIDE;
private slots:
- virtual bool finished() Q_DECL_OVERRIDE;
- virtual void slotTimeout() Q_DECL_OVERRIDE;
virtual void metaDataChangedSlot();
virtual void encryptedSlot();
}
if( reply()->error() != QNetworkReply::NoError ) {
- qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
+ qWarning() << Q_FUNC_INFO << " Network error: " << errorString();
}
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(slotMetaDataChanged()));
qint64 r = reply()->read(buffer.data(), toRead);
if (r < 0) {
- _errorString = reply()->errorString();
+ _errorString = networkReplyErrorString(*reply());
_errorStatus = SyncFileItem::NormalError;
qDebug() << "Error while reading from device: " << _errorString;
reply()->abort();
}
}
-void GETFileJob::slotTimeout()
+void GETFileJob::onTimedOut()
{
qDebug() << "Timeout" << (reply() ? reply()->request().url() : path());
if (!reply())
{
if (!_errorString.isEmpty()) {
return _errorString;
- } else if (reply()->hasRawHeader("OC-ErrorString")) {
- return reply()->rawHeader("OC-ErrorString");
- } else {
- return reply()->errorString();
}
+ return AbstractNetworkJob::errorString();
}
void PropagateDownloadFile::start()
qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS"
<< job->reply()->error()
- << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString())
+ << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->errorString())
<< _item->_httpErrorCode
<< _tmpFile.size() << _item->_size << job->resumeStart()
<< job->reply()->rawHeader("Content-Range") << job->reply()->rawHeader("Content-Length");
SyncFileItem::Status errorStatus() { return _errorStatus; }
void setErrorStatus(const SyncFileItem::Status & s) { _errorStatus = s; }
- virtual void slotTimeout() Q_DECL_OVERRIDE;
+ void onTimedOut() Q_DECL_OVERRIDE;
QByteArray &etag() { return _etag; }
quint64 resumeStart() { return _resumeStart; }
AbstractNetworkJob::start();
}
-
-QString DeleteJob::errorString()
-{
- if (_timedout) {
- return tr("Connection timed out");
- } else if (reply()->hasRawHeader("OC-ErrorString")) {
- return reply()->rawHeader("OC-ErrorString");
- } else {
- return reply()->errorString();
- }
-}
-
bool DeleteJob::finished()
{
emit finishedSignal();
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
- << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->reply()->errorString());
+ << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->errorString());
QNetworkReply::NetworkError err = _job->reply()->error();
const int httpStatus = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
void start() Q_DECL_OVERRIDE;
bool finished() Q_DECL_OVERRIDE;
- QString errorString();
- bool timedOut() { return _timedout; }
-
signals:
void finishedSignal();
};
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
- << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->reply()->errorString());
+ << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->errorString());
QNetworkReply::NetworkError err = _job->reply()->error();
_item->_httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
} else if (err != QNetworkReply::NoError) {
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
&propagator()->_anotherSyncNeeded);
- auto errorString = _job->reply()->errorString();
- if (_job->reply()->hasRawHeader("OC-ErrorString")) {
- errorString = _job->reply()->rawHeader("OC-ErrorString");
- }
- done(status, errorString);
+ done(status, _job->errorString());
return;
} else if (_item->_httpErrorCode != 201) {
// Normally we expect "201 Created"
}
-QString MoveJob::errorString()
-{
- if (_timedout) {
- return tr("Connection timed out");
- } else if (reply()->hasRawHeader("OC-ErrorString")) {
- return reply()->rawHeader("OC-ErrorString");
- } else {
- return reply()->errorString();
- }
-}
-
bool MoveJob::finished()
{
emit finishedSignal();
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
- << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->reply()->errorString());
+ << (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->errorString());
QNetworkReply::NetworkError err = _job->reply()->error();
_item->_httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
void start() Q_DECL_OVERRIDE;
bool finished() Q_DECL_OVERRIDE;
- QString errorString();
- bool timedOut() { return _timedout; }
-
signals:
void finishedSignal();
};
AbstractNetworkJob::start();
}
-void PUTFileJob::slotTimeout() {
- qDebug() << "Timeout" << (reply() ? reply()->request().url() : path());
- if (!reply())
- return;
- _errorString = tr("Connection Timeout");
- reply()->abort();
-}
-
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
void PUTFileJob::slotSoftAbort() {
reply()->setProperty(owncloudShouldSoftCancelPropertyName, true);
if (err != QNetworkReply::NoError) {
_item->_httpErrorCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_status = classifyError(err, _item->_httpErrorCode);
- _item->_errorString = reply()->errorString();
-
- if (reply()->hasRawHeader("OC-ErrorString")) {
- _item->_errorString = reply()->rawHeader("OC-ErrorString");
- }
+ _item->_errorString = errorString();
if (_item->_status == SyncFileItem::FatalError || _item->_httpErrorCode >= 400) {
if (_item->_status != SyncFileItem::FatalError
}
QString errorString() {
- return _errorString.isEmpty() ? reply()->errorString() : _errorString;
+ return _errorString.isEmpty() ? AbstractNetworkJob::errorString() : _errorString;
}
- virtual void slotTimeout() Q_DECL_OVERRIDE;
-
quint64 msSinceStart() const {
return _requestTimer.elapsed();
}
-
signals:
void finishedSignal();
void uploadProgress(qint64,qint64);
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();
auto status = classifyError(err, httpErrorCode, &propagator()->_anotherSyncNeeded);
if (status == SyncFileItem::FatalError) {
propagator()->_activeJobList.removeOne(this);
- QString errorString = errorMessage(job->reply()->errorString(), job->reply()->readAll());
- abortWithError(status, errorString);
+ abortWithError(status, job->errorStringParsingBody());
return;
}
startNewUpload();
if (err != QNetworkReply::NoError || _item->_httpErrorCode != 201) {
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
&propagator()->_anotherSyncNeeded);
- QString errorString = errorMessage(job->reply()->errorString(), job->reply()->readAll());
- if (job->reply()->hasRawHeader("OC-ErrorString")) {
- errorString = job->reply()->rawHeader("OC-ErrorString");
- }
- abortWithError(status, errorString);
+ abortWithError(status, job->errorStringParsingBody());
return;
}
startNextChunk();
qDebug() << job->reply()->request().url() << "FINISHED WITH STATUS"
<< job->reply()->error()
- << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString())
+ << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->errorString())
<< job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute)
<< job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
if (err != QNetworkReply::NoError) {
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- QByteArray replyContent = job->reply()->readAll();
+ QByteArray replyContent;
+ QString errorString = job->errorStringParsingBody(&replyContent);
qDebug() << replyContent; // display the XML error in the debug
- QString errorString = errorMessage(job->errorString(), replyContent);
-
- if (job->reply()->hasRawHeader("OC-ErrorString")) {
- errorString = job->reply()->rawHeader("OC-ErrorString");
- }
// Ensure errors that should eventually reset the chunked upload are tracked.
checkResettingErrors();
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
&propagator()->_anotherSyncNeeded);
- QString errorString = errorMessage(job->errorString(), job->reply()->readAll());
- abortWithError(status, errorString);
+ abortWithError(status, job->errorStringParsingBody());
return;
}
if (_item->_httpErrorCode != 201 && _item->_httpErrorCode != 204) {
qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS"
<< job->reply()->error()
- << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString())
+ << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->errorString())
<< job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute)
<< job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
"It is restored and your edit is in the conflict file."))) {
return;
}
- QByteArray replyContent = job->reply()->readAll();
+ QByteArray replyContent;
+ QString errorString = job->errorStringParsingBody(&replyContent);
qDebug() << replyContent; // display the XML error in the debug
- QString errorString = errorMessage(job->errorString(), replyContent);
-
- if (job->reply()->hasRawHeader("OC-ErrorString")) {
- errorString = job->reply()->rawHeader("OC-ErrorString");
- }
if (_item->_httpErrorCode == 412) {
// Precondition Failed: Either an etag or a checksum mismatch.