diff --git a/data/data.qrc b/data/data.qrc index 164f9421e..edda37da4 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -294,6 +294,7 @@ providers/somafm.png providers/songkick.png providers/soundcloud.png + providers/ubuntuone.png providers/wikipedia.png sample.mood schema/device-schema.sql diff --git a/data/providers/podcast16.png b/data/providers/podcast16.png old mode 100755 new mode 100644 diff --git a/data/providers/podcast32.png b/data/providers/podcast32.png old mode 100755 new mode 100644 diff --git a/data/providers/ubuntuone.png b/data/providers/ubuntuone.png new file mode 100644 index 000000000..4513269f8 Binary files /dev/null and b/data/providers/ubuntuone.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 78b2e11e4..14ac63fc9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -192,6 +192,7 @@ set(SOURCES internet/soundcloudservice.cpp internet/ubuntuoneauthenticator.cpp internet/ubuntuoneservice.cpp + internet/ubuntuonesettingspage.cpp internet/ubuntuoneurlhandler.cpp library/groupbydialog.cpp @@ -469,6 +470,7 @@ set(HEADERS internet/soundcloudservice.h internet/ubuntuoneauthenticator.h internet/ubuntuoneservice.h + internet/ubuntuonesettingspage.h internet/ubuntuoneurlhandler.h library/groupbydialog.h @@ -645,6 +647,7 @@ set(UI internet/magnatunesettingspage.ui internet/searchboxwidget.ui internet/spotifysettingspage.ui + internet/ubuntuonesettingspage.ui library/groupbydialog.ui library/libraryfilterwidget.ui diff --git a/src/internet/googledrivesettingspage.h b/src/internet/googledrivesettingspage.h index 77de3d08c..cbf39a417 100644 --- a/src/internet/googledrivesettingspage.h +++ b/src/internet/googledrivesettingspage.h @@ -1,16 +1,16 @@ /* This file is part of Clementine. Copyright 2012, David Sansome - + Clementine 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 3 of the License, or (at your option) any later version. - + Clementine 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. - + You should have received a copy of the GNU General Public License along with Clementine. If not, see . */ @@ -26,11 +26,9 @@ class GoogleDriveService; class Ui_GoogleDriveSettingsPage; -class QSortFilterProxyModel; - class GoogleDriveSettingsPage : public SettingsPage { Q_OBJECT - + public: GoogleDriveSettingsPage(SettingsDialog* parent = 0); ~GoogleDriveSettingsPage(); diff --git a/src/internet/ubuntuoneauthenticator.cpp b/src/internet/ubuntuoneauthenticator.cpp index 729a03828..6f0540d4c 100644 --- a/src/internet/ubuntuoneauthenticator.cpp +++ b/src/internet/ubuntuoneauthenticator.cpp @@ -1,18 +1,24 @@ #include "ubuntuoneauthenticator.h" #include +#include #include +#include #include #include "core/closure.h" #include "core/logging.h" #include "core/network.h" +#include "core/timeconstants.h" namespace { static const char* kUbuntuOneEndpoint = "https://login.ubuntu.com/api/1.0/authentications"; static const char* kTokenNameTemplate = "Ubuntu One @ %1 [%2]"; +static const char* kOAuthSSOFinishedEndpoint = + "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"; +static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", "; } UbuntuOneAuthenticator::UbuntuOneAuthenticator(QObject* parent) @@ -66,5 +72,67 @@ void UbuntuOneAuthenticator::AuthorisationFinished(QNetworkReply* reply) { token_ = auth_info["token"].toString(); token_secret_ = auth_info["token_secret"].toString(); + CopySSOTokens(); +} + +QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader( + const QString& consumer_key, + const QString& consumer_secret, + const QString& token, + const QString& token_secret) { + typedef QPair Param; + QString timestamp = QString::number( + QDateTime::currentMSecsSinceEpoch() / kMsecPerSec); + QList parameters; + parameters << Param("oauth_nonce", QString::number(qrand())) + << Param("oauth_timestamp", timestamp) + << Param("oauth_version", "1.0") + << Param("oauth_consumer_key", consumer_key) + << Param("oauth_token", token) + << Param("oauth_signature_method", "PLAINTEXT"); + qSort(parameters.begin(), parameters.end()); + QStringList encoded_params; + for (const Param& p : parameters) { + encoded_params << QString("%1=%2").arg(p.first, p.second); + } + + QString signing_key = + consumer_secret + "&" + token_secret; + QByteArray signature = QUrl::toPercentEncoding(signing_key); + + // Construct authorisation header + parameters << Param("oauth_signature", signature); + QStringList header_params; + for (const Param& p : parameters) { + header_params << QString("%1=\"%2\"").arg(p.first, p.second); + } + QString authorisation_header = header_params.join(", "); + authorisation_header.prepend(kOAuthHeaderPrefix); + + return authorisation_header.toAscii(); +} + +QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader() { + return GenerateAuthorisationHeader( + consumer_key_, + consumer_secret_, + token_, + token_secret_); +} + +void UbuntuOneAuthenticator::CopySSOTokens() { + QUrl url(kOAuthSSOFinishedEndpoint); + QNetworkRequest request(url); + request.setRawHeader("Authorization", GenerateAuthorisationHeader()); + request.setRawHeader("Accept", "application/json"); + + QNetworkReply* reply = network_->get(request); + NewClosure(reply, SIGNAL(finished()), + this, SLOT(CopySSOTokensFinished(QNetworkReply*)), reply); +} + +void UbuntuOneAuthenticator::CopySSOTokensFinished(QNetworkReply* reply) { + reply->deleteLater(); + qLog(Debug) << reply->readAll(); emit Finished(); } diff --git a/src/internet/ubuntuoneauthenticator.h b/src/internet/ubuntuoneauthenticator.h index eb853d5b8..053dbe5ae 100644 --- a/src/internet/ubuntuoneauthenticator.h +++ b/src/internet/ubuntuoneauthenticator.h @@ -17,11 +17,22 @@ class UbuntuOneAuthenticator : public QObject { QString token() const { return token_; } QString token_secret() const { return token_secret_; } + static QByteArray GenerateAuthorisationHeader( + const QString& consumer_key, + const QString& consumer_secret, + const QString& token, + const QString& token_secret); + signals: void Finished(); private slots: void AuthorisationFinished(QNetworkReply* reply); + void CopySSOTokensFinished(QNetworkReply* reply); + + private: + void CopySSOTokens(); + QByteArray GenerateAuthorisationHeader(); private: NetworkAccessManager* network_; diff --git a/src/internet/ubuntuoneservice.cpp b/src/internet/ubuntuoneservice.cpp index bba1ac362..8cc78ef0e 100644 --- a/src/internet/ubuntuoneservice.cpp +++ b/src/internet/ubuntuoneservice.cpp @@ -1,6 +1,7 @@ #include "ubuntuoneservice.h" #include +#include #include @@ -21,10 +22,7 @@ const char* UbuntuOneService::kSettingsGroup = "Ubuntu One"; namespace { static const char* kFileStorageEndpoint = "https://one.ubuntu.com/api/file_storage/v1/~/Ubuntu One/"; -static const char* kOAuthSSOFinishedEndpoint = - "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"; static const char* kContentRoot = "https://files.one.ubuntu.com"; -static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", "; } UbuntuOneService::UbuntuOneService(Application* app, InternetModel* parent) @@ -52,6 +50,18 @@ void UbuntuOneService::LazyPopulate(QStandardItem* item) { } void UbuntuOneService::Connect() { + QSettings s; + s.beginGroup(kSettingsGroup); + if (s.contains("consumer_key")) { + consumer_key_ = s.value("consumer_key").toString(); + consumer_secret_ = s.value("consumer_secret").toString(); + token_ = s.value("token").toString(); + token_secret_ = s.value("token_secret").toString(); + + RequestFileList(); + return; + } + UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator; authenticator->StartAuthorisation( "Username", @@ -62,36 +72,11 @@ void UbuntuOneService::Connect() { } QByteArray UbuntuOneService::GenerateAuthorisationHeader() { - typedef QPair Param; - QString timestamp = QString::number( - QDateTime::currentMSecsSinceEpoch() / kMsecPerSec); - QList parameters; - parameters << Param("oauth_nonce", QString::number(qrand())) - << Param("oauth_timestamp", timestamp) - << Param("oauth_version", "1.0") - << Param("oauth_consumer_key", consumer_key_) - << Param("oauth_token", token_) - << Param("oauth_signature_method", "PLAINTEXT"); - qSort(parameters.begin(), parameters.end()); - QStringList encoded_params; - for (const Param& p : parameters) { - encoded_params << QString("%1=%2").arg(p.first, p.second); - } - - QString signing_key = - consumer_secret_ + "&" + token_secret_; - QByteArray signature = QUrl::toPercentEncoding(signing_key); - - // Construct authorisation header - parameters << Param("oauth_signature", signature); - QStringList header_params; - for (const Param& p : parameters) { - header_params << QString("%1=\"%2\"").arg(p.first, p.second); - } - QString authorisation_header = header_params.join(", "); - authorisation_header.prepend(kOAuthHeaderPrefix); - - return authorisation_header.toAscii(); + return UbuntuOneAuthenticator::GenerateAuthorisationHeader( + consumer_key_, + consumer_secret_, + token_, + token_secret_); } void UbuntuOneService::AuthenticationFinished( @@ -103,19 +88,17 @@ void UbuntuOneService::AuthenticationFinished( token_ = authenticator->token(); token_secret_ = authenticator->token_secret(); - QUrl sso_url(kOAuthSSOFinishedEndpoint); - QNetworkRequest request(sso_url); - request.setRawHeader("Authorization", GenerateAuthorisationHeader()); - request.setRawHeader("Accept", "application/json"); + QSettings s; + s.beginGroup(kSettingsGroup); + s.setValue("consumer_key", consumer_key_); + s.setValue("consumer_secret", consumer_secret_); + s.setValue("token", token_); + s.setValue("token_secret", token_secret_); - qLog(Debug) << "Sending SSO copy request"; - QNetworkReply* reply = network_->get(request); - NewClosure(reply, SIGNAL(finished()), - this, SLOT(SSORequestFinished(QNetworkReply*)), reply); + RequestFileList(); } -void UbuntuOneService::SSORequestFinished(QNetworkReply* reply) { - qLog(Debug) << Q_FUNC_INFO; +void UbuntuOneService::RequestFileList() { QUrl files_url(kFileStorageEndpoint); files_url.addQueryItem("include_children", "true"); QNetworkRequest request(files_url); diff --git a/src/internet/ubuntuoneservice.h b/src/internet/ubuntuoneservice.h index 731e36ef1..756f8f51f 100644 --- a/src/internet/ubuntuoneservice.h +++ b/src/internet/ubuntuoneservice.h @@ -23,11 +23,11 @@ class UbuntuOneService : public InternetService { private slots: void AuthenticationFinished(UbuntuOneAuthenticator* authenticator); - void SSORequestFinished(QNetworkReply* reply); void FileListRequestFinished(QNetworkReply* reply); private: void Connect(); + void RequestFileList(); private: QByteArray GenerateAuthorisationHeader(); diff --git a/src/internet/ubuntuonesettingspage.cpp b/src/internet/ubuntuonesettingspage.cpp new file mode 100644 index 000000000..cb12e5d34 --- /dev/null +++ b/src/internet/ubuntuonesettingspage.cpp @@ -0,0 +1,74 @@ +#include "ubuntuonesettingspage.h" + +#include "ui_ubuntuonesettingspage.h" + +#include "core/application.h" +#include "core/closure.h" +#include "core/logging.h" +#include "internet/internetmodel.h" +#include "internet/ubuntuoneauthenticator.h" +#include "internet/ubuntuoneservice.h" + +UbuntuOneSettingsPage::UbuntuOneSettingsPage(SettingsDialog* parent) + : SettingsPage(parent), + ui_(new Ui::UbuntuOneSettingsPage), + service_(dialog()->app()->internet_model()->Service()), + authenticated_(false) { + ui_->setupUi(this); + ui_->login_state->AddCredentialGroup(ui_->login_container); + connect(ui_->login_button, SIGNAL(clicked()), SLOT(LoginClicked())); + + dialog()->installEventFilter(this); +} + +void UbuntuOneSettingsPage::Load() { + QSettings s; + s.beginGroup(UbuntuOneService::kSettingsGroup); + + const QString user_email = s.value("user_email").toString(); + if (!user_email.isEmpty()) { + ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, user_email); + ui_->username->setText(user_email); + } +} + +void UbuntuOneSettingsPage::Save() { + QSettings s; + s.beginGroup(UbuntuOneService::kSettingsGroup); + + if (authenticated_) { + s.setValue("user_email", ui_->username->text()); + } +} + +void UbuntuOneSettingsPage::LoginClicked() { + ui_->login_button->setEnabled(false); + QString username = ui_->username->text(); + QString password = ui_->password->text(); + + UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator; + authenticator->StartAuthorisation(username, password); + NewClosure(authenticator, SIGNAL(Finished()), + this, SLOT(Connected(UbuntuOneAuthenticator*)), authenticator); + NewClosure(authenticator, SIGNAL(Finished()), + service_, SLOT(AuthenticationFinished(UbuntuOneAuthenticator*)), + authenticator); +} + +void UbuntuOneSettingsPage::LogoutClicked() { + +} + +void UbuntuOneSettingsPage::Connected(UbuntuOneAuthenticator* authenticator) { + ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, ui_->username->text()); + authenticated_ = true; +} + +bool UbuntuOneSettingsPage::eventFilter(QObject* object, QEvent* event) { + if (object == dialog() && event->type() == QEvent::Enter) { + ui_->login_button->setEnabled(true); + return false; + } + + return SettingsPage::eventFilter(object, event); +} diff --git a/src/internet/ubuntuonesettingspage.h b/src/internet/ubuntuonesettingspage.h new file mode 100644 index 000000000..855fc733e --- /dev/null +++ b/src/internet/ubuntuonesettingspage.h @@ -0,0 +1,33 @@ +#ifndef UBUNTUONESETTINGSPAGE_H +#define UBUNTUONESETTINGSPAGE_H + +#include "ui/settingspage.h" + +class UbuntuOneAuthenticator; +class UbuntuOneService; +class Ui_UbuntuOneSettingsPage; + +class UbuntuOneSettingsPage : public SettingsPage { + Q_OBJECT + public: + UbuntuOneSettingsPage(SettingsDialog* parent = 0); + + void Load(); + void Save(); + + // QObject + bool eventFilter(QObject* object, QEvent* event); + + private slots: + void LoginClicked(); + void LogoutClicked(); + void Connected(UbuntuOneAuthenticator* authenticator); + + private: + Ui_UbuntuOneSettingsPage* ui_; + UbuntuOneService* service_; + + bool authenticated_; +}; + +#endif // UBUNTUONESETTINGSPAGE_H diff --git a/src/internet/ubuntuonesettingspage.ui b/src/internet/ubuntuonesettingspage.ui new file mode 100644 index 000000000..be681d142 --- /dev/null +++ b/src/internet/ubuntuonesettingspage.ui @@ -0,0 +1,117 @@ + + + UbuntuOneSettingsPage + + + + 0 + 0 + 569 + 491 + + + + Ubuntu One + + + + :/providers/ubuntuone.png:/providers/ubuntuone.png + + + + + + Clementine can play music that you have uploaded to Ubuntu One + + + true + + + + + + + + + + + 28 + + + 0 + + + 0 + + + + + + + Login + + + + + + + Email address + + + + + + + Password + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 357 + + + + + + + + + LoginStateWidget + QWidget +
widgets/loginstatewidget.h
+ 1 +
+
+ + + + +
diff --git a/src/ui/settingsdialog.cpp b/src/ui/settingsdialog.cpp index 868d06452..e465867d9 100644 --- a/src/ui/settingsdialog.cpp +++ b/src/ui/settingsdialog.cpp @@ -37,6 +37,7 @@ #include "internet/digitallyimportedsettingspage.h" #include "internet/groovesharksettingspage.h" #include "internet/magnatunesettingspage.h" +#include "internet/ubuntuonesettingspage.h" #include "library/librarysettingspage.h" #include "playlist/playlistview.h" #include "podcasts/podcastsettingspage.h" @@ -147,6 +148,8 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams, QWi AddPage(Page_GoogleDrive, new GoogleDriveSettingsPage(this), providers); #endif + AddPage(Page_UbuntuOne, new UbuntuOneSettingsPage(this), providers); + #ifdef HAVE_SPOTIFY AddPage(Page_Spotify, new SpotifySettingsPage(this), providers); #endif diff --git a/src/ui/settingsdialog.h b/src/ui/settingsdialog.h index 3d73b5691..0e8d19498 100644 --- a/src/ui/settingsdialog.h +++ b/src/ui/settingsdialog.h @@ -77,6 +77,7 @@ public: Page_Wiimotedev, Page_Podcasts, Page_GoogleDrive, + Page_UbuntuOne, }; enum Role {