Add https support to localredirectserver
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -283,4 +283,3 @@ void AlbumCoverFetcherSearch::Cancel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user