diff --git a/CMakeLists.txt b/CMakeLists.txt
index 490c4ba22..6c26bfcdd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -202,6 +202,11 @@ optional_component(DROPBOX ON "Dropbox support"
DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
)
+optional_component(SKYDRIVE ON "Skydrive support"
+ DEPENDS "Google sparsehash" SPARSEHASH_INCLUDE_DIRS
+ DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
+)
+
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND
)
diff --git a/data/data.qrc b/data/data.qrc
index a0f1bef23..d68a182b9 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -291,6 +291,7 @@
providers/podcast16.png
providers/podcast32.png
providers/rockradio.png
+ providers/skydrive.png
providers/skyfm.png
providers/somafm.png
providers/songkick.png
diff --git a/data/providers/skydrive.png b/data/providers/skydrive.png
new file mode 100644
index 000000000..d76c6f935
Binary files /dev/null and b/data/providers/skydrive.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c670584c3..a1e8720d5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1072,6 +1072,14 @@ optional_source(HAVE_DROPBOX
internet/dropboxsettingspage.ui
)
+# Skydrive support
+optional_source(HAVE_SKYDRIVE
+ SOURCES
+ internet/skydriveservice.cpp
+ HEADERS
+ internet/skydriveservice.h
+)
+
# Hack to add Clementine to the Unity system tray whitelist
optional_source(LINUX
SOURCES core/ubuntuunityhack.cpp
diff --git a/src/config.h.in b/src/config.h.in
index 52b87e75b..d92ffd904 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -36,6 +36,7 @@
#cmakedefine HAVE_LIBMTP
#cmakedefine HAVE_MOODBAR
#cmakedefine HAVE_QCA
+#cmakedefine HAVE_SKYDRIVE
#cmakedefine HAVE_SPARKLE
#cmakedefine HAVE_SPOTIFY
#cmakedefine HAVE_SPOTIFY_DOWNLOADER
diff --git a/src/internet/googledriveclient.cpp b/src/internet/googledriveclient.cpp
index fad73c2de..fed872450 100644
--- a/src/internet/googledriveclient.cpp
+++ b/src/internet/googledriveclient.cpp
@@ -89,7 +89,8 @@ Client::Client(QObject* parent)
ConnectResponse* Client::Connect(const QString& refresh_token) {
ConnectResponse* ret = new ConnectResponse(this);
- OAuthenticator* oauth = new OAuthenticator(kClientId, kClientSecret, this);
+ OAuthenticator* oauth = new OAuthenticator(
+ kClientId, kClientSecret, OAuthenticator::RedirectStyle::LOCALHOST, this);
if (refresh_token.isEmpty()) {
oauth->StartAuthorisation(
diff --git a/src/internet/internetmodel.cpp b/src/internet/internetmodel.cpp
index 17f382a3e..34e9f2c76 100644
--- a/src/internet/internetmodel.cpp
+++ b/src/internet/internetmodel.cpp
@@ -51,6 +51,9 @@
#ifdef HAVE_DROPBOX
#include "dropboxservice.h"
#endif
+#ifdef HAVE_SKYDRIVE
+ #include "skydriveservice.h"
+#endif
using smart_playlists::Generator;
using smart_playlists::GeneratorMimeData;
@@ -97,6 +100,9 @@ InternetModel::InternetModel(Application* app, QObject* parent)
#ifdef HAVE_DROPBOX
AddService(new DropboxService(app, this));
#endif
+#ifdef HAVE_SKYDRIVE
+ AddService(new SkydriveService(app, this));
+#endif
}
void InternetModel::AddService(InternetService *service) {
diff --git a/src/internet/oauthenticator.cpp b/src/internet/oauthenticator.cpp
index dfdfa330c..f8cb4a3bd 100644
--- a/src/internet/oauthenticator.cpp
+++ b/src/internet/oauthenticator.cpp
@@ -13,10 +13,12 @@
OAuthenticator::OAuthenticator(
const QString& client_id,
const QString& client_secret,
+ RedirectStyle redirect,
QObject* parent)
: QObject(parent),
client_id_(client_id),
- client_secret_(client_secret) {
+ client_secret_(client_secret),
+ redirect_style_(redirect) {
}
void OAuthenticator::StartAuthorisation(
@@ -27,25 +29,35 @@ void OAuthenticator::StartAuthorisation(
LocalRedirectServer* server = new LocalRedirectServer(this);
server->Listen();
- NewClosure(server, SIGNAL(Finished()),
- this, SLOT(RedirectArrived(LocalRedirectServer*)), server);
-
QUrl url = QUrl(oauth_endpoint);
url.addQueryItem("response_type", "code");
url.addQueryItem("client_id", client_id_);
- url.addQueryItem("redirect_uri", server->url().toString());
+ QUrl redirect_url;
+ if (redirect_style_ == RedirectStyle::REMOTE) {
+ const int port = server->url().port();
+ redirect_url = QUrl(
+ QString("http://data.clementine-player.org/skydrive?port=%1").arg(port));
+ } else {
+ redirect_url = server->url();
+ }
+ url.addQueryItem("redirect_uri", redirect_url.toString());
url.addQueryItem("scope", scope);
+ NewClosure(server, SIGNAL(Finished()),
+ this, SLOT(RedirectArrived(LocalRedirectServer*,QUrl)),
+ server, redirect_url);
+
QDesktopServices::openUrl(url);
}
-void OAuthenticator::RedirectArrived(LocalRedirectServer* server) {
+void OAuthenticator::RedirectArrived(
+ LocalRedirectServer* server, QUrl url) {
server->deleteLater();
QUrl request_url = server->request_url();
qLog(Debug) << Q_FUNC_INFO << request_url;
RequestAccessToken(
request_url.queryItemValue("code").toUtf8(),
- server->url());
+ url);
}
QByteArray OAuthenticator::ParseHttpRequest(const QByteArray& request) const {
diff --git a/src/internet/oauthenticator.h b/src/internet/oauthenticator.h
index 05a0d5081..8c229bcd5 100644
--- a/src/internet/oauthenticator.h
+++ b/src/internet/oauthenticator.h
@@ -12,9 +12,18 @@ class QTcpSocket;
class OAuthenticator : public QObject {
Q_OBJECT
public:
+ enum class RedirectStyle {
+ // Redirect to localhost immediately.
+ LOCALHOST = 0,
+ // Redirect via data.clementine-player.org for when localhost is
+ // unsupported (eg. Skydrive).
+ REMOTE = 1,
+ };
+
OAuthenticator(
const QString& client_id,
const QString& client_secret,
+ RedirectStyle redirect,
QObject* parent = 0);
void StartAuthorisation(
const QString& oauth_endpoint,
@@ -31,13 +40,12 @@ class OAuthenticator : public QObject {
const QString& refresh_token() const { return refresh_token_; }
const QDateTime& expiry_time() const { return expiry_time_; }
- const QString& user_email() const { return user_email_; }
signals:
void Finished();
private slots:
- void RedirectArrived(LocalRedirectServer* server);
+ void RedirectArrived(LocalRedirectServer* server, QUrl url);
void FetchAccessTokenFinished(QNetworkReply* reply);
void RefreshAccessTokenFinished(QNetworkReply* reply);
@@ -51,11 +59,11 @@ class OAuthenticator : public QObject {
QString access_token_;
QString refresh_token_;
QDateTime expiry_time_;
- QString user_email_;
const QString client_id_;
const QString client_secret_;
QUrl token_endpoint_;
+ RedirectStyle redirect_style_;
};
#endif
diff --git a/src/internet/skydriveservice.cpp b/src/internet/skydriveservice.cpp
new file mode 100644
index 000000000..6f490e60a
--- /dev/null
+++ b/src/internet/skydriveservice.cpp
@@ -0,0 +1,61 @@
+#include "skydriveservice.h"
+
+#include "oauthenticator.h"
+
+namespace {
+
+static const char* kServiceName = "Skydrive";
+static const char* kServiceId = "skydrive";
+static const char* kSettingsGroup = "Skydrive";
+
+static const char* kClientId = "00000000400E7C78";
+static const char* kClientSecret = "B0KLZjEgC5SpW0KknrsBFwlaKmGThaAk";
+
+static const char* kOAuthEndpoint =
+ "https://login.live.com/oauth20_authorize.srf";
+static const char* kOAuthTokenEndpoint =
+ "https://login.live.com/oauth20_token.srf";
+static const char* kOAuthScope = "wl.basic wl.skydrive wl.offline_access";
+
+} // namespace
+
+SkydriveService::SkydriveService(
+ Application* app,
+ InternetModel* parent)
+ : CloudFileService(
+ app, parent, kServiceName, kServiceId,
+ QIcon(":providers/skydrive.png"), SettingsDialog::Page_Skydrive) {
+}
+
+bool SkydriveService::has_credentials() const {
+ return true;
+}
+
+void SkydriveService::Connect() {
+ OAuthenticator* oauth = new OAuthenticator(
+ kClientId, kClientSecret, OAuthenticator::RedirectStyle::REMOTE, this);
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ if (s.contains("refresh_token")) {
+ oauth->RefreshAuthorisation(
+ kOAuthTokenEndpoint, s.value("refresh_token").toString());
+ } else {
+ oauth->StartAuthorisation(
+ kOAuthEndpoint,
+ kOAuthTokenEndpoint,
+ kOAuthScope);
+ }
+
+ NewClosure(oauth, SIGNAL(Finished()),
+ this, SLOT(ConnectFinished(OAuthenticator*)), oauth);
+}
+
+void SkydriveService::ConnectFinished(OAuthenticator* oauth) {
+ qLog(Debug) << oauth->access_token()
+ << oauth->refresh_token();
+
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+
+ s.setValue("refresh_token", oauth->refresh_token());
+}
diff --git a/src/internet/skydriveservice.h b/src/internet/skydriveservice.h
new file mode 100644
index 000000000..190ecc457
--- /dev/null
+++ b/src/internet/skydriveservice.h
@@ -0,0 +1,26 @@
+#ifndef SKYDRIVESERVICE_H
+#define SKYDRIVESERVICE_H
+
+#include "cloudfileservice.h"
+
+class OAuthenticator;
+
+class SkydriveService : public CloudFileService {
+ Q_OBJECT
+
+ public:
+ SkydriveService(
+ Application* app,
+ InternetModel* parent);
+
+ protected:
+ // CloudFileService
+ virtual bool has_credentials() const;
+ virtual void Connect();
+
+ private slots:
+ void ConnectFinished(OAuthenticator* oauth);
+
+};
+
+#endif // SKYDRIVESERVICE_H
diff --git a/src/ui/settingsdialog.h b/src/ui/settingsdialog.h
index 643a225eb..3f874a6bb 100644
--- a/src/ui/settingsdialog.h
+++ b/src/ui/settingsdialog.h
@@ -79,6 +79,7 @@ public:
Page_GoogleDrive,
Page_UbuntuOne,
Page_Dropbox,
+ Page_Skydrive,
};
enum Role {