diff --git a/src/core/network.cpp b/src/core/network.cpp index 30d29b0b2..89b566665 100644 --- a/src/core/network.cpp +++ b/src/core/network.cpp @@ -112,7 +112,7 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR QByteArray user_agent = QString("%1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()).toUtf8(); if (request.hasRawHeader("User-Agent")) { - // Append the existing user-agent set by a client collection (such as libmygpo-qt). + // Append the existing user-agent set by a client library. user_agent += " " + request.rawHeader("User-Agent"); } diff --git a/src/subsonic/subsonicbaserequest.cpp b/src/subsonic/subsonicbaserequest.cpp index 9c0985732..2a0ffb552 100644 --- a/src/subsonic/subsonicbaserequest.cpp +++ b/src/subsonic/subsonicbaserequest.cpp @@ -19,6 +19,9 @@ #include "config.h" +#include +#include + #include #include #include @@ -27,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -36,14 +40,13 @@ #include #include "core/logging.h" -#include "core/network.h" #include "subsonicservice.h" #include "subsonicbaserequest.h" -SubsonicBaseRequest::SubsonicBaseRequest(SubsonicService *service, NetworkAccessManager *network, QObject *parent) : +SubsonicBaseRequest::SubsonicBaseRequest(SubsonicService *service, QObject *parent) : QObject(parent), service_(service), - network_(network) + network_(new QNetworkAccessManager) {} SubsonicBaseRequest::~SubsonicBaseRequest() {} diff --git a/src/subsonic/subsonicbaserequest.h b/src/subsonic/subsonicbaserequest.h index a594cd0fd..bbb5f40c6 100644 --- a/src/subsonic/subsonicbaserequest.h +++ b/src/subsonic/subsonicbaserequest.h @@ -22,16 +22,17 @@ #include "config.h" +#include +#include + #include #include #include #include #include #include -#include +#include #include -#include -#include #include "core/song.h" #include "internet/internetservices.h" @@ -39,7 +40,9 @@ #include "internet/internetsearch.h" #include "subsonicservice.h" -class NetworkAccessManager; +class QNetworkAccessManager; +class QNetworkReply; + class SubsonicUrlHandler; class CollectionBackend; class CollectionModel; @@ -49,7 +52,7 @@ class SubsonicBaseRequest : public QObject { public: - SubsonicBaseRequest(SubsonicService *service, NetworkAccessManager *network, QObject *parent); + SubsonicBaseRequest(SubsonicService *service, QObject *parent); ~SubsonicBaseRequest(); typedef QPair Param; @@ -78,9 +81,8 @@ class SubsonicBaseRequest : public QObject { void HandleSSLErrors(QList ssl_errors); private: - SubsonicService *service_; - NetworkAccessManager *network_; + std::unique_ptr network_; }; diff --git a/src/subsonic/subsonicrequest.cpp b/src/subsonic/subsonicrequest.cpp index 42ad85541..a5b7b8c56 100644 --- a/src/subsonic/subsonicrequest.cpp +++ b/src/subsonic/subsonicrequest.cpp @@ -19,7 +19,8 @@ #include "config.h" -#include +#include +#include #include #include @@ -27,8 +28,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -38,7 +39,6 @@ #include "core/application.h" #include "core/closure.h" #include "core/logging.h" -#include "core/network.h" #include "core/song.h" #include "core/timeconstants.h" #include "covermanager/albumcoverloader.h" @@ -50,12 +50,12 @@ const int SubsonicRequest::kMaxConcurrentAlbumsRequests = 3; const int SubsonicRequest::kMaxConcurrentAlbumSongsRequests = 3; const int SubsonicRequest::kMaxConcurrentAlbumCoverRequests = 1; -SubsonicRequest::SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QObject *parent) - : SubsonicBaseRequest(service, network, parent), +SubsonicRequest::SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent) + : SubsonicBaseRequest(service, parent), service_(service), url_handler_(url_handler), app_(app), - network_(network), + network_(new QNetworkAccessManager), finished_(false), albums_requests_active_(0), album_songs_requests_active_(0), diff --git a/src/subsonic/subsonicrequest.h b/src/subsonic/subsonicrequest.h index d20db3968..d778cc97c 100644 --- a/src/subsonic/subsonicrequest.h +++ b/src/subsonic/subsonicrequest.h @@ -22,6 +22,9 @@ #include "config.h" +#include +#include + #include #include #include @@ -48,7 +51,7 @@ class SubsonicRequest : public SubsonicBaseRequest { public: - SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QObject *parent); + SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent); ~SubsonicRequest(); void ReloadSettings(); @@ -114,7 +117,7 @@ class SubsonicRequest : public SubsonicBaseRequest { SubsonicService *service_; SubsonicUrlHandler *url_handler_; Application *app_; - NetworkAccessManager *network_; + std::unique_ptr network_; bool finished_; diff --git a/src/subsonic/subsonicservice.cpp b/src/subsonic/subsonicservice.cpp index 381415f8f..75cff7d70 100644 --- a/src/subsonic/subsonicservice.cpp +++ b/src/subsonic/subsonicservice.cpp @@ -60,17 +60,19 @@ const char *SubsonicService::kClientName = "Strawberry"; const char *SubsonicService::kApiVersion = "1.15.0"; const char *SubsonicService::kSongsTable = "subsonic_songs"; const char *SubsonicService::kSongsFtsTable = "subsonic_songs_fts"; +const int SubsonicService::kMaxRedirects = 3; SubsonicService::SubsonicService(Application *app, QObject *parent) : InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", app, parent), app_(app), - network_(new NetworkAccessManager(this)), + network_(new QNetworkAccessManager), url_handler_(new SubsonicUrlHandler(app, this)), collection_backend_(nullptr), collection_model_(nullptr), collection_sort_model_(new QSortFilterProxyModel(this)), verify_certificate_(false), - download_album_covers_(true) + download_album_covers_(true), + ping_redirects_(0) { app->player()->RegisterUrlHandler(url_handler_); @@ -131,7 +133,12 @@ void SubsonicService::SendPing() { SendPing(server_url_, username_, password_); } -void SubsonicService::SendPing(QUrl url, const QString &username, const QString &password) { +void SubsonicService::SendPing(QUrl url, const QString &username, const QString &password, const bool redirect) { + + if (!redirect) { + network_.reset(new QNetworkAccessManager); + ping_redirects_ = 0; + } const ParamList params = ParamList() << Param("c", kClientName) << Param("v", kApiVersion) @@ -139,17 +146,21 @@ void SubsonicService::SendPing(QUrl url, const QString &username, const QString << Param("u", username) << Param("p", QString("enc:" + password.toUtf8().toHex())); - QUrlQuery url_query; + QUrlQuery url_query(url.query()); for (const Param ¶m : params) { EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); - url_query.addQueryItem(encoded_param.first, encoded_param.second); + if (!url_query.hasQueryItem(encoded_param.first)) { + url_query.addQueryItem(encoded_param.first, encoded_param.second); + } } - if (!url.path().isEmpty() && url.path().right(1) == "/") { - url.setPath(url.path() + QString("rest/ping.view")); + if (!redirect) { + if (!url.path().isEmpty() && url.path().right(1) == "/") { + url.setPath(url.path() + QString("rest/ping.view")); + } + else + url.setPath(url.path() + QString("/rest/ping.view")); } - else - url.setPath(url.path() + QString("/rest/ping.view")); url.setQuery(url_query); @@ -166,7 +177,7 @@ void SubsonicService::SendPing(QUrl url, const QString &username, const QString errors_.clear(); QNetworkReply *reply = network_->get(req); connect(reply, SIGNAL(sslErrors(QList)), this, SLOT(HandlePingSSLErrors(QList))); - NewClosure(reply, SIGNAL(finished()), this, SLOT(HandlePingReply(QNetworkReply*)), reply); + NewClosure(reply, SIGNAL(finished()), this, SLOT(HandlePingReply(QNetworkReply*, QUrl, QString, QString)), reply, url, username, password); //qLog(Debug) << "Subsonic: Sending request" << url << query; @@ -180,7 +191,7 @@ void SubsonicService::HandlePingSSLErrors(QList ssl_errors) { } -void SubsonicService::HandlePingReply(QNetworkReply *reply) { +void SubsonicService::HandlePingReply(QNetworkReply *reply, const QUrl &url, const QString &username, const QString &password) { reply->deleteLater(); @@ -191,6 +202,27 @@ void SubsonicService::HandlePingReply(QNetworkReply *reply) { return; } else { + + // Check for a valid redirect first. + if ( + ( + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 301 || + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 302 || + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 307 + ) + && + ping_redirects_ <= kMaxRedirects + ) + { + QUrl redirect_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if (!redirect_url.isEmpty()) { + ++ping_redirects_; + qLog(Debug) << "Redirecting ping request to" << redirect_url.toString(QUrl::RemoveQuery); + SendPing(redirect_url, username, password, true); + return; + } + } + // See if there is Json data containing "error" - then use that instead. QByteArray data = reply->readAll(); QJsonParseError parse_error; @@ -345,7 +377,7 @@ void SubsonicService::GetSongs() { } ResetSongsRequest(); - songs_request_.reset(new SubsonicRequest(this, url_handler_, app_, network_, this)); + songs_request_.reset(new SubsonicRequest(this, url_handler_, app_, this)); connect(songs_request_.get(), SIGNAL(Results(const SongList&, const QString&)), SLOT(SongsResultsReceived(const SongList&, const QString&))); connect(songs_request_.get(), SIGNAL(UpdateStatus(const QString&)), SIGNAL(SongsUpdateStatus(const QString&))); connect(songs_request_.get(), SIGNAL(ProgressSetMaximum(const int)), SIGNAL(SongsProgressSetMaximum(const int))); diff --git a/src/subsonic/subsonicservice.h b/src/subsonic/subsonicservice.h index f449a66c1..3eb278e40 100644 --- a/src/subsonic/subsonicservice.h +++ b/src/subsonic/subsonicservice.h @@ -32,8 +32,8 @@ #include #include #include -#include #include +#include #include "core/song.h" #include "internet/internetservice.h" @@ -41,8 +41,10 @@ #include "settings/subsonicsettingspage.h" class QSortFilterProxyModel; +class QNetworkAccessManager; +class QNetworkReply; + class Application; -class NetworkAccessManager; class SubsonicUrlHandler; class SubsonicRequest; class CollectionBackend; @@ -87,14 +89,13 @@ class SubsonicService : public InternetService { public slots: void ShowConfig(); void SendPing(); - void SendPing(QUrl url, const QString &username, const QString &password); + void SendPing(QUrl url, const QString &username, const QString &password, const bool redirect = false); void GetSongs(); void ResetSongsRequest(); private slots: - //void HandlePingSSLErrors(QNetworkReply *reply, QList ssl_errors); void HandlePingSSLErrors(QList ssl_errors); - void HandlePingReply(QNetworkReply *reply); + void HandlePingReply(QNetworkReply *reply, const QUrl &url, const QString &username, const QString &password); void SongsResultsReceived(const SongList &songs, const QString &error); private: @@ -110,9 +111,10 @@ class SubsonicService : public InternetService { static const char *kApiVersion; static const char *kSongsTable; static const char *kSongsFtsTable; + static const int kMaxRedirects; Application *app_; - NetworkAccessManager *network_; + std::unique_ptr network_; SubsonicUrlHandler *url_handler_; CollectionBackend *collection_backend_; @@ -128,6 +130,7 @@ class SubsonicService : public InternetService { bool download_album_covers_; QStringList errors_; + int ping_redirects_; };