Add https support to localredirectserver

This commit is contained in:
Jonas Kvinge
2019-04-15 22:17:40 +02:00
parent e9bf04031b
commit 7f23b9b424
12 changed files with 290 additions and 66 deletions

View File

@@ -79,6 +79,7 @@ pkg_check_modules(GIO REQUIRED gio-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0) pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(CDIO libcdio) pkg_check_modules(CDIO libcdio)
find_package(Threads) find_package(Threads)
find_package(OpenSSL)
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)

View File

@@ -5,7 +5,7 @@ run zypper --non-interactive --gpg-auto-import-keys dup -l -y
run zypper --non-interactive --gpg-auto-import-keys install \ run zypper --non-interactive --gpg-auto-import-keys install \
lsb-release git tar make cmake gcc gcc-c++ pkg-config gettext-tools \ lsb-release git tar make cmake gcc gcc-c++ pkg-config gettext-tools \
glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel \ glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel libopenssl-devel \
boost-devel protobuf-devel sqlite3-devel taglib-devel \ boost-devel protobuf-devel sqlite3-devel taglib-devel \
gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \ gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \
libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \ libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \

View File

@@ -43,6 +43,7 @@ include_directories(${CMAKE_BINARY_DIR})
include_directories(${GLIB_INCLUDE_DIRS}) include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GLIBCONFIG_INCLUDE_DIRS}) include_directories(${GLIBCONFIG_INCLUDE_DIRS})
include_directories(${GOBJECT_INCLUDE_DIRS}) include_directories(${GOBJECT_INCLUDE_DIRS})
include_directories(${OPENSSL_INCLUDE_DIR})
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${LIBXML_INCLUDE_DIRS}) include_directories(${LIBXML_INCLUDE_DIRS})
include_directories(${CHROMAPRINT_INCLUDE_DIRS}) include_directories(${CHROMAPRINT_INCLUDE_DIRS})
@@ -929,14 +930,15 @@ add_library(strawberry_lib STATIC
target_link_libraries(strawberry_lib target_link_libraries(strawberry_lib
libstrawberry-common libstrawberry-common
libstrawberry-tagreader libstrawberry-tagreader
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBRARIES} ${GLIB_LIBRARIES}
${GIO_LIBRARIES} ${GIO_LIBRARIES}
${TAGLIB_LIBRARIES}
${GOBJECT_LIBRARIES} ${GOBJECT_LIBRARIES}
${OPENSSL_LIBRARIES}
${QT_LIBRARIES} ${QT_LIBRARIES}
${CHROMAPRINT_LIBRARIES} ${CHROMAPRINT_LIBRARIES}
${SQLITE_LIBRARIES} ${SQLITE_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${TAGLIB_LIBRARIES}
${SINGLEAPPLICATION_LIBRARIES} ${SINGLEAPPLICATION_LIBRARIES}
${SINGLECOREAPPLICATION_LIBRARIES} ${SINGLECOREAPPLICATION_LIBRARIES}
${QOCOA_LIBRARIES} ${QOCOA_LIBRARIES}

View File

@@ -283,4 +283,3 @@ void AlbumCoverFetcherSearch::Cancel() {
} }
} }

View File

@@ -2,6 +2,7 @@
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2012, 2014, John Maguire <john.maguire@gmail.com> * Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
* Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com> * Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -20,53 +21,225 @@
#include "localredirectserver.h" #include "localredirectserver.h"
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <QApplication> #include <QApplication>
#include <QBuffer> #include <QBuffer>
#include <QFile> #include <QFile>
#include <QRegExp> #include <QRegExp>
#include <QStyle> #include <QStyle>
#include <QSslKey>
#include <QSslCertificate>
#include <QTcpServer> #include <QTcpServer>
#include <QAbstractSocket>
#include <QTcpSocket> #include <QTcpSocket>
#include <QSslSocket>
#include <QSslConfiguration>
#include "core/logging.h"
#include "core/closure.h" #include "core/closure.h"
LocalRedirectServer::LocalRedirectServer(QObject* parent) LocalRedirectServer::LocalRedirectServer(const bool https, QObject *parent)
: QObject(parent), server_(new QTcpServer(this)) {} : QTcpServer(parent),
https_(https),
socket_(nullptr)
{}
void LocalRedirectServer::Listen() { LocalRedirectServer::~LocalRedirectServer() {}
server_->listen(QHostAddress::LocalHost); bool LocalRedirectServer::GenerateCertificate() {
// We have to calculate this and store it now as the server port is cleared once we close the socket.
url_.setScheme("http"); EVP_PKEY *pkey = nullptr;
RSA *rsa = nullptr;
X509 *x509 = nullptr;
X509_NAME *name = nullptr;
BIO *bp_public = nullptr, *bp_private = nullptr;
const char *buffer = nullptr;
long size = 0;
pkey = EVP_PKEY_new();
q_check_ptr(pkey);
rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
q_check_ptr(rsa);
EVP_PKEY_assign_RSA(pkey, rsa);
x509 = X509_new();
q_check_ptr(x509);
ASN1_INTEGER_set(X509_get_serialNumber(x509), static_cast<uint64_t>(9999999 + qrand() % 1000000));
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
X509_set_pubkey(x509, pkey);
name = X509_get_subject_name(x509);
q_check_ptr(name);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *) "US", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *) "Strawberry Music Player", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *) "localhost", -1, -1, 0);
X509_set_issuer_name(x509, name);
X509_sign(x509, pkey, EVP_sha1());
bp_private = BIO_new(BIO_s_mem());
q_check_ptr(bp_private);
if (PEM_write_bio_PrivateKey(bp_private, pkey, nullptr, nullptr, 0, nullptr, nullptr) != 1) {
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_private);
error_ = "PEM_write_bio_PrivateKey() failed.";
return false;
}
bp_public = BIO_new(BIO_s_mem());
q_check_ptr(bp_public);
if (PEM_write_bio_X509(bp_public, x509) != 1) {
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
error_ = "PEM_write_bio_X509() failed.";
return false;
}
size = BIO_get_mem_data(bp_public, &buffer);
q_check_ptr(buffer);
QSslCertificate ssl_certificate(QByteArray(buffer, size));
if (ssl_certificate.isNull()) {
error_ = "Failed to generate a random client certificate.";
return false;
}
size = BIO_get_mem_data(bp_private, &buffer);
q_check_ptr(buffer);
QSslKey ssl_key(QByteArray(buffer, size), QSsl::Rsa);
if (ssl_key.isNull()) {
error_ = "Failed to generate a random private key.";
return false;
}
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
ssl_certificate_ = ssl_certificate;
ssl_key_ = ssl_key;
return true;
}
bool LocalRedirectServer::Listen() {
if (https_) {
if (!GenerateCertificate()) return false;
}
if (!listen(QHostAddress::LocalHost)) {
error_ = errorString();
return false;
}
if (https_) url_.setScheme("https");
else url_.setScheme("http");
url_.setHost("localhost"); url_.setHost("localhost");
url_.setPort(server_->serverPort()); url_.setPort(serverPort());
url_.setPath("/"); url_.setPath("/");
connect(server_, SIGNAL(newConnection()), SLOT(NewConnection())); connect(this, SIGNAL(newConnection()), this, SLOT(NewConnection()));
return true;
} }
void LocalRedirectServer::NewConnection() { void LocalRedirectServer::NewConnection() {
QTcpSocket* socket = server_->nextPendingConnection();
server_->close();
QByteArray buffer; while (hasPendingConnections()) {
NewClosure(socket, SIGNAL(readyRead()), this, SLOT(ReadyRead(QTcpSocket*, QByteArray)), socket, buffer); incomingConnection(nextPendingConnection()->socketDescriptor());
}
} }
void LocalRedirectServer::ReadyRead(QTcpSocket* socket, QByteArray buffer) { void LocalRedirectServer::incomingConnection(qintptr socket_descriptor) {
buffer.append(socket->readAll());
if (socket->atEnd() || buffer.endsWith("\r\n\r\n")) { if (socket_) {
WriteTemplate(socket); if (socket_->state() == QAbstractSocket::ConnectedState) socket_->close();
socket->deleteLater(); socket_->deleteLater();
request_url_ = ParseUrlFromRequest(buffer); socket_ = nullptr;
}
buffer_.clear();
if (https_) {
QSslSocket *ssl_socket = new QSslSocket(this);
if (!ssl_socket->setSocketDescriptor(socket_descriptor)) {
delete ssl_socket;
error_ = "Unable to set socket descriptor";
emit Finished();
return;
}
ssl_socket->ignoreSslErrors({QSslError::SelfSignedCertificate});
ssl_socket->setPrivateKey(ssl_key_);
ssl_socket->setLocalCertificate(ssl_certificate_);
ssl_socket->setProtocol(QSsl::TlsV1_2);
ssl_socket->startServerEncryption();
connect(ssl_socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(SSLErrors(QList<QSslError>)));
connect(ssl_socket, SIGNAL(encrypted()), this, SLOT(Encrypted(QSslSocket*)));
socket_ = ssl_socket;
}
else {
QTcpSocket *tcp_socket = new QTcpSocket(this);
if (!tcp_socket->setSocketDescriptor(socket_descriptor)) {
delete tcp_socket;
error_ = "Unable to set socket descriptor";
emit Finished();
return;
}
socket_ = tcp_socket;
}
connect(socket_, SIGNAL(connected()), this, SLOT(Connected()));
connect(socket_, SIGNAL(disconnected()), this, SLOT(Disconnected()));
connect(socket_, SIGNAL(readyRead()), this, SLOT(ReadyRead()));
}
void LocalRedirectServer::SSLErrors(const QList<QSslError> &errors) {}
void LocalRedirectServer::Encrypted() {}
void LocalRedirectServer::Connected() {}
void LocalRedirectServer::Disconnected() {}
void LocalRedirectServer::ReadyRead() {
buffer_.append(socket_->readAll());
if (socket_->atEnd() || buffer_.endsWith("\r\n\r\n")) {
WriteTemplate();
socket_->close();
socket_->deleteLater();
socket_ = nullptr;
request_url_ = ParseUrlFromRequest(buffer_);
close();
emit Finished(); emit Finished();
} }
else { else {
NewClosure(socket, SIGNAL(readyRead()), this, SLOT(ReadyReady(QTcpSocket*, QByteArray)), socket, buffer); connect(socket_, SIGNAL(readyRead()), this, SLOT(ReadyRead()));
} }
} }
void LocalRedirectServer::WriteTemplate(QTcpSocket* socket) const { void LocalRedirectServer::WriteTemplate() const {
QFile page_file(":/html/oauthsuccess.html"); QFile page_file(":/html/oauthsuccess.html");
page_file.open(QIODevice::ReadOnly); page_file.open(QIODevice::ReadOnly);
@@ -93,19 +266,21 @@ void LocalRedirectServer::WriteTemplate(QTcpSocket* socket) const {
.save(&image_buffer, "PNG"); .save(&image_buffer, "PNG");
page_data.replace("@IMAGE_DATA@", image_buffer.data().toBase64()); page_data.replace("@IMAGE_DATA@", image_buffer.data().toBase64());
socket->write("HTTP/1.0 200 OK\r\n"); socket_->write("HTTP/1.0 200 OK\r\n");
socket->write("Content-type: text/html;charset=UTF-8\r\n"); socket_->write("Content-type: text/html;charset=UTF-8\r\n");
socket->write("\r\n\r\n"); socket_->write("\r\n\r\n");
socket->write(page_data.toUtf8()); socket_->write(page_data.toUtf8());
socket->flush(); socket_->flush();
} }
QUrl LocalRedirectServer::ParseUrlFromRequest(const QByteArray& request) const { QUrl LocalRedirectServer::ParseUrlFromRequest(const QByteArray &request) const {
QList<QByteArray> lines = request.split('\r'); QList<QByteArray> lines = request.split('\r');
const QByteArray& request_line = lines[0]; const QByteArray &request_line = lines[0];
QByteArray path = request_line.split(' ')[1]; QByteArray path = request_line.split(' ')[1];
QUrl base_url = url(); QUrl base_url = url_;
QUrl request_url(base_url.toString() + path.mid(1), QUrl::StrictMode); QUrl request_url(base_url.toString() + path.mid(1), QUrl::StrictMode);
return request_url; return request_url;
} }

View File

@@ -2,6 +2,7 @@
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2012, 2014, John Maguire <john.maguire@gmail.com> * Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
* Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com> * Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -23,41 +24,54 @@
#include <QByteArray> #include <QByteArray>
#include <QObject> #include <QObject>
#include <QSslCertificate>
#include <QSslKey>
#include <QTcpServer>
#include <QAbstractSocket>
#include <QTcpSocket>
#include <QSslSocket>
#include <QString>
#include <QUrl> #include <QUrl>
class QTcpServer; class LocalRedirectServer : public QTcpServer {
class QTcpSocket;
class LocalRedirectServer : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit LocalRedirectServer(QObject* parent = nullptr); explicit LocalRedirectServer(const bool https, QObject* parent = nullptr);
~LocalRedirectServer();
// Causes the server to listen for _one_ request. bool Listen();
void Listen(); const QUrl &url() const { return url_; }
const QUrl &request_url() const { return request_url_; }
// Returns the HTTP URL of this server. const QString &error() const { return error_; }
const QUrl& url() const { return url_; }
// Returns the URL requested by the OAuth redirect.
const QUrl& request_url() const { return request_url_; }
signals: signals:
void Finished(); void Finished(QString error = QString());
private slots: public slots:
void NewConnection(); void NewConnection();
void ReadyRead(QTcpSocket* socket, QByteArray buffer); void incomingConnection(qintptr socket_descriptor);
void SSLErrors(const QList<QSslError> &errors);
void Encrypted();
void Connected();
void Disconnected();
void ReadyRead();
private: private:
void WriteTemplate(QTcpSocket* socket) const; bool GenerateCertificate();
QUrl ParseUrlFromRequest(const QByteArray& request) const; void WriteTemplate() const;
QUrl ParseUrlFromRequest(const QByteArray &request) const;
private: private:
QTcpServer* server_; bool https_;
QUrl url_; QUrl url_;
QUrl request_url_; QUrl request_url_;
QSslCertificate ssl_certificate_;
QSslKey ssl_key_;
QAbstractSocket *socket_;
QByteArray buffer_;
QString error_;
}; };
#endif #endif

View File

@@ -121,12 +121,16 @@ void ListenBrainzScrobbler::Logout() {
} }
void ListenBrainzScrobbler::Authenticate() { void ListenBrainzScrobbler::Authenticate(const bool https) {
QUrl url(kAuthUrl); QUrl url(kAuthUrl);
LocalRedirectServer *server = new LocalRedirectServer(this); LocalRedirectServer *server = new LocalRedirectServer(https, this);
server->Listen(); if (!server->Listen()) {
AuthError(server->error());
delete server;
return;
}
NewClosure(server, SIGNAL(Finished()), this, &ListenBrainzScrobbler::RedirectArrived, server); NewClosure(server, SIGNAL(Finished()), this, &ListenBrainzScrobbler::RedirectArrived, server);
QUrl redirect_url(kRedirectUrl); QUrl redirect_url(kRedirectUrl);

View File

@@ -60,7 +60,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
void Submitted() { submitted_ = true; } void Submitted() { submitted_ = true; }
QString user_token() const { return user_token_; } QString user_token() const { return user_token_; }
void Authenticate(); void Authenticate(const bool https = false);
void Logout(); void Logout();
void ShowConfig(); void ShowConfig();
void Submit(); void Submit();

View File

@@ -83,6 +83,7 @@ void ScrobblingAPI20::ReloadSettings() {
QSettings s; QSettings s;
s.beginGroup(settings_group_); s.beginGroup(settings_group_);
enabled_ = s.value("enabled", false).toBool(); enabled_ = s.value("enabled", false).toBool();
https_ = s.value("https", false).toBool();
s.endGroup(); s.endGroup();
} }
@@ -113,13 +114,17 @@ void ScrobblingAPI20::Logout() {
} }
void ScrobblingAPI20::Authenticate() { void ScrobblingAPI20::Authenticate(const bool https) {
QUrl url(auth_url_); QUrl url(auth_url_);
LocalRedirectServer *server = new LocalRedirectServer(this); LocalRedirectServer *server = new LocalRedirectServer(https, this);
server->Listen(); if (!server->Listen()) {
NewClosure(server, SIGNAL(Finished()), this, &ScrobblingAPI20::RedirectArrived, server); AuthError(server->error());
delete server;
return;
}
NewClosure(server, SIGNAL(Finished(QString)), this, &ScrobblingAPI20::RedirectArrived, server);
QUrl redirect_url(kRedirectUrl); QUrl redirect_url(kRedirectUrl);
QUrlQuery redirect_url_query; QUrlQuery redirect_url_query;
@@ -129,7 +134,8 @@ void ScrobblingAPI20::Authenticate() {
QUrlQuery url_query; QUrlQuery url_query;
url_query.addQueryItem("api_key", kApiKey); url_query.addQueryItem("api_key", kApiKey);
url_query.addQueryItem("cb", redirect_url.toString()); url_query.addQueryItem("cb", QUrl::toPercentEncoding(redirect_url.toString()));
qLog(Debug) << QUrl::toPercentEncoding(redirect_url.toString());
url.setQuery(url_query); url.setQuery(url_query);
QMessageBox messagebox(QMessageBox::Information, tr("%1 Scrobbler Authentication").arg(name_), tr("Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to copy the URL to clipboard and manually open it in a web browser.").arg(url.toString()), QMessageBox::Open|QMessageBox::Save|QMessageBox::Cancel); QMessageBox messagebox(QMessageBox::Information, tr("%1 Scrobbler Authentication").arg(name_), tr("Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to copy the URL to clipboard and manually open it in a web browser.").arg(url.toString()), QMessageBox::Open|QMessageBox::Save|QMessageBox::Cancel);
@@ -149,7 +155,9 @@ void ScrobblingAPI20::Authenticate() {
QApplication::clipboard()->setText(url.toString()); QApplication::clipboard()->setText(url.toString());
break; break;
case QMessageBox::Cancel: case QMessageBox::Cancel:
AuthError(tr("Authentication was cancelled.")); server->close();
server->deleteLater();
emit AuthenticationComplete(false);
break; break;
default: default:
break; break;
@@ -162,7 +170,17 @@ void ScrobblingAPI20::RedirectArrived(LocalRedirectServer *server) {
server->deleteLater(); server->deleteLater();
QUrl url = server->request_url(); QUrl url = server->request_url();
RequestSession(QUrlQuery(url).queryItemValue("token").toUtf8()); if (!url.isValid()) {
AuthError(tr("Invalid reply from web browser. Try using Chromium or Chrome instead."));
return;
}
QUrlQuery url_query(url);
QString token = url_query.queryItemValue("token").toUtf8();
if (token.isEmpty()) {
AuthError(tr("Invalid reply from web browser. Missing token."));
return;
}
RequestSession(token);
} }
@@ -373,7 +391,7 @@ QByteArray ScrobblingAPI20::GetReplyData(QNetworkReply *reply) {
} }
return data; return data;
} }
void ScrobblingAPI20::UpdateNowPlaying(const Song &song) { void ScrobblingAPI20::UpdateNowPlaying(const Song &song) {

View File

@@ -59,13 +59,14 @@ class ScrobblingAPI20 : public ScrobblerService {
virtual ScrobblerCache *cache() = 0; virtual ScrobblerCache *cache() = 0;
bool IsEnabled() const { return enabled_; } bool IsEnabled() const { return enabled_; }
bool IsUseHTTPS() const { return https_; }
bool IsAuthenticated() const { return !username_.isEmpty() && !session_key_.isEmpty(); } bool IsAuthenticated() const { return !username_.isEmpty() && !session_key_.isEmpty(); }
bool IsSubscriber() const { return subscriber_; } bool IsSubscriber() const { return subscriber_; }
bool IsSubmitted() const { return submitted_; } bool IsSubmitted() const { return submitted_; }
void Submitted() { submitted_ = true; } void Submitted() { submitted_ = true; }
QString username() const { return username_; } QString username() const { return username_; }
void Authenticate(); void Authenticate(const bool https = false);
void Logout(); void Logout();
void UpdateNowPlaying(const Song &song); void UpdateNowPlaying(const Song &song);
void Scrobble(const Song &song); void Scrobble(const Song &song);
@@ -143,6 +144,7 @@ class ScrobblingAPI20 : public ScrobblerService {
Application *app_; Application *app_;
bool enabled_; bool enabled_;
bool https_;
bool subscriber_; bool subscriber_;
QString username_; QString username_;

View File

@@ -85,6 +85,7 @@ void ScrobblerSettingsPage::Load() {
ui_->spinbox_submit->setValue(scrobbler_->SubmitDelay()); ui_->spinbox_submit->setValue(scrobbler_->SubmitDelay());
ui_->checkbox_lastfm_enable->setChecked(lastfmscrobbler_->IsEnabled()); ui_->checkbox_lastfm_enable->setChecked(lastfmscrobbler_->IsEnabled());
ui_->checkbox_lastfm_https->setChecked(lastfmscrobbler_->IsUseHTTPS());
LastFM_RefreshControls(lastfmscrobbler_->IsAuthenticated()); LastFM_RefreshControls(lastfmscrobbler_->IsAuthenticated());
ui_->checkbox_librefm_enable->setChecked(librefmscrobbler_->IsEnabled()); ui_->checkbox_librefm_enable->setChecked(librefmscrobbler_->IsEnabled());
@@ -109,6 +110,7 @@ void ScrobblerSettingsPage::Save() {
s.beginGroup(LastFMScrobbler::kSettingsGroup); s.beginGroup(LastFMScrobbler::kSettingsGroup);
s.setValue("enabled", ui_->checkbox_lastfm_enable->isChecked()); s.setValue("enabled", ui_->checkbox_lastfm_enable->isChecked());
s.setValue("https", ui_->checkbox_lastfm_https->isChecked());
s.endGroup(); s.endGroup();
s.beginGroup(LibreFMScrobbler::kSettingsGroup); s.beginGroup(LibreFMScrobbler::kSettingsGroup);
@@ -128,7 +130,7 @@ void ScrobblerSettingsPage::LastFM_Login() {
lastfm_waiting_for_auth_ = true; lastfm_waiting_for_auth_ = true;
ui_->widget_lastfm_login_state->SetLoggedIn(LoginStateWidget::LoginInProgress); ui_->widget_lastfm_login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
lastfmscrobbler_->Authenticate(); lastfmscrobbler_->Authenticate(ui_->checkbox_lastfm_https->isChecked());
} }
@@ -148,7 +150,7 @@ void ScrobblerSettingsPage::LastFM_AuthenticationComplete(const bool success, QS
Save(); Save();
} }
else { else {
QMessageBox::warning(this, "Authentication failed", error); if (!error.isEmpty()) QMessageBox::warning(this, "Authentication failed", error);
} }
LastFM_RefreshControls(success); LastFM_RefreshControls(success);

View File

@@ -115,6 +115,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="checkbox_lastfm_https">
<property name="text">
<string>Use HTTPS for local redirectserver to bypass login problems</string>
</property>
</widget>
</item>
<item> <item>
<widget class="LoginStateWidget" name="widget_lastfm_login_state" native="true"/> <widget class="LoginStateWidget" name="widget_lastfm_login_state" native="true"/>
</item> </item>