diff --git a/src/internet/lastfm/lastfmservice.cpp b/src/internet/lastfm/lastfmservice.cpp index 2c53d6aa9..ece2db58b 100644 --- a/src/internet/lastfm/lastfmservice.cpp +++ b/src/internet/lastfm/lastfmservice.cpp @@ -39,7 +39,10 @@ #include "lastfmservice.h" +#include +#include #include +#include #include #ifdef HAVE_LIBLASTFM1 @@ -49,14 +52,16 @@ #endif #include "lastfmcompat.h" -#include "internet/core/internetmodel.h" -#include "internet/core/internetplaylistitem.h" #include "core/application.h" #include "core/closure.h" #include "core/logging.h" +#include "core/network.h" #include "core/player.h" #include "core/song.h" #include "core/taskmanager.h" +#include "internet/core/internetmodel.h" +#include "internet/core/internetplaylistitem.h" +#include "internet/core/localredirectserver.h" #include "covers/coverproviders.h" #include "covers/lastfmcoverprovider.h" #include "ui/iconloader.h" @@ -82,7 +87,8 @@ LastFMService::LastFMService(Application* app, QObject* parent) already_scrobbled_(false), scrobbling_enabled_(false), connection_problems_(false), - app_(app) { + app_(app), + network_(new NetworkAccessManager) { #ifdef HAVE_LIBLASTFM1 lastfm::ws::setScheme(lastfm::ws::Https); #endif @@ -130,37 +136,50 @@ bool LastFMService::IsSubscriber() const { return settings.value("Subscriber", false).toBool(); } -void LastFMService::GetToken() { - QMap params; - params["method"] = "auth.getToken"; - QNetworkReply* reply = lastfm::ws::post(params); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(GetTokenReplyFinished(QNetworkReply*)), reply); -} - -void LastFMService::GetTokenReplyFinished(QNetworkReply* reply) { - reply->deleteLater(); - - // Parse the reply - lastfm::XmlQuery lfm(lastfm::compat::EmptyXmlQuery()); - if (lastfm::compat::ParseQuery(reply->readAll(), &lfm)) { - QString token = lfm["token"].text(); - - emit TokenReceived(true, token); - } else { - emit TokenReceived(false, lfm["error"].text().trimmed()); +namespace { +QByteArray SignApiRequest(QList> params) { + qSort(params); + QString to_sign; + for (const auto& p : params) { + to_sign += p.first; + to_sign += p.second; } + to_sign += LastFMService::kSecret; + return QCryptographicHash::hash(to_sign.toUtf8(), QCryptographicHash::Md5).toHex(); } +} // namespace -void LastFMService::Authenticate(const QString& token) { - QMap params; - params["method"] = "auth.getSession"; - params["token"] = token; +void LastFMService::Authenticate() { + QUrl url("https://www.last.fm/api/auth/"); + url.addQueryItem("api_key", kApiKey); - QNetworkReply* reply = lastfm::ws::post(params); - NewClosure(reply, SIGNAL(finished()), this, - SLOT(AuthenticateReplyFinished(QNetworkReply*)), reply); - // If we need more detailed error reporting, handle error(NetworkError) signal + LocalRedirectServer* server = new LocalRedirectServer(this); + server->Listen(); + + url.addQueryItem("cb", server->url().toString()); + + NewClosure(server, SIGNAL(Finished()), [this, server]() { + server->deleteLater(); + + const QUrl& url = server->request_url(); + QString token = url.queryItemValue("token"); + + QUrl session_url("https://ws.audioscrobbler.com/2.0/"); + session_url.addQueryItem("api_key", kApiKey); + session_url.addQueryItem("method", "auth.getSession"); + session_url.addQueryItem("token", token); + session_url.addQueryItem("api_sig", SignApiRequest(session_url.queryItems())); + + QNetworkReply* reply = network_->get(QNetworkRequest(session_url)); + NewClosure(reply, SIGNAL(finished()), this, SLOT(AuthenticateReplyFinished(QNetworkReply*)), reply); + }); + + if (!QDesktopServices::openUrl(url)) { + QMessageBox box(QMessageBox::NoIcon, tr("Last.fm Authentication"), tr("Please open this url in your browser: %1").arg(url.toString()), QMessageBox::Ok); + box.setTextFormat(Qt::RichText); + qLog(Debug) << "Last.fm authentication URL: " << url.toString(); + box.exec(); + } } void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) { @@ -181,14 +200,14 @@ void LastFMService::AuthenticateReplyFinished(QNetworkReply* reply) { settings.setValue("Session", lastfm::ws::SessionKey); settings.setValue("Subscriber", is_subscriber); } else { - emit AuthenticationComplete(false, lfm["error"].text().trimmed()); + emit AuthenticationComplete(false); return; } // Invalidate the scrobbler - it will get recreated later scrobbler_.reset(nullptr); - emit AuthenticationComplete(true, QString()); + emit AuthenticationComplete(true); } void LastFMService::SignOut() { diff --git a/src/internet/lastfm/lastfmservice.h b/src/internet/lastfm/lastfmservice.h index b18394169..c9ea52ee3 100644 --- a/src/internet/lastfm/lastfmservice.h +++ b/src/internet/lastfm/lastfmservice.h @@ -38,8 +38,8 @@ uint qHash(const lastfm::Track& track); class Application; class LastFMUrlHandler; +class NetworkAccessManager; class QAction; -class QNetworkAccessManager; class Song; class LastFMService : public Scrobbler { @@ -69,8 +69,7 @@ class LastFMService : public Scrobbler { bool PreferAlbumArtist() const { return prefer_albumartist_; } bool HasConnectionProblems() const { return connection_problems_; } - void GetToken(); - void Authenticate(const QString& token); + void Authenticate(); void SignOut(); void UpdateSubscriberStatus(); @@ -83,8 +82,7 @@ class LastFMService : public Scrobbler { void ToggleScrobbling(); signals: - void TokenReceived(bool success, const QString& token); - void AuthenticationComplete(bool success, const QString& error_message); + void AuthenticationComplete(bool success); void ScrobblingEnabledChanged(bool value); void ButtonVisibilityChanged(bool value); void ScrobbleButtonVisibilityChanged(bool value); @@ -97,7 +95,6 @@ signals: void SavedItemsChanged(); private slots: - void GetTokenReplyFinished(QNetworkReply* reply); void AuthenticateReplyFinished(QNetworkReply* reply); void UpdateSubscriberStatusFinished(QNetworkReply* reply); @@ -129,6 +126,7 @@ signals: bool connection_problems_; Application* app_; + std::unique_ptr network_; }; #endif // INTERNET_LASTFM_LASTFMSERVICE_H_ diff --git a/src/internet/lastfm/lastfmsettingspage.cpp b/src/internet/lastfm/lastfmsettingspage.cpp index 7e79bad2b..533ac9fc5 100644 --- a/src/internet/lastfm/lastfmsettingspage.cpp +++ b/src/internet/lastfm/lastfmsettingspage.cpp @@ -43,10 +43,8 @@ LastFMSettingsPage::LastFMSettingsPage(SettingsDialog* dialog) // Icons setWindowIcon(IconLoader::Load("lastfm", IconLoader::Provider)); - connect(service_, SIGNAL(TokenReceived(bool,QString)), - SLOT(TokenReceived(bool,QString))); - connect(service_, SIGNAL(AuthenticationComplete(bool, QString)), - SLOT(AuthenticationComplete(bool, QString))); + connect(service_, SIGNAL(AuthenticationComplete(bool)), + SLOT(AuthenticationComplete(bool))); connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout())); connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login())); connect(ui_->login, SIGNAL(clicked()), SLOT(Login())); @@ -62,26 +60,10 @@ void LastFMSettingsPage::Login() { waiting_for_auth_ = true; ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress); - service_->GetToken(); + service_->Authenticate(); } -void LastFMSettingsPage::TokenReceived(bool success, const QString &token) { - if (!success) { - QMessageBox::warning(this, tr("Last.fm authentication failed"), token); - return; - } - - QString url = QString(LastFMService::kAuthLoginUrl).arg(LastFMService::kApiKey, token); - QDesktopServices::openUrl(QUrl(url)); - - QMessageBox::information(this, tr("Last.fm authentication"), - tr("Click Ok once you authenticated Clementine in your last.fm account.")); - - service_->Authenticate(token); -} - -void LastFMSettingsPage::AuthenticationComplete(bool success, - const QString& message) { +void LastFMSettingsPage::AuthenticationComplete(bool success) { if (!waiting_for_auth_) return; // Wasn't us that was waiting for auth waiting_for_auth_ = false; @@ -90,10 +72,7 @@ void LastFMSettingsPage::AuthenticationComplete(bool success, // Save settings Save(); } else { - QString dialog_text = tr("Your Last.fm credentials were incorrect"); - if (!message.isEmpty()) { - dialog_text = message; - } + QString dialog_text = tr("Failed to login to last.fm. Please try again."); QMessageBox::warning(this, tr("Authentication failed"), dialog_text); } diff --git a/src/internet/lastfm/lastfmsettingspage.h b/src/internet/lastfm/lastfmsettingspage.h index 225cb407c..6e48258af 100644 --- a/src/internet/lastfm/lastfmsettingspage.h +++ b/src/internet/lastfm/lastfmsettingspage.h @@ -38,8 +38,7 @@ class LastFMSettingsPage : public SettingsPage { private slots: void Login(); - void TokenReceived(bool success, const QString& token); - void AuthenticationComplete(bool success, const QString& error_message); + void AuthenticationComplete(bool success); void Logout(); private: