Factor out oauth redirect & HTTP server logic.
This commit is contained in:
parent
847e4ce121
commit
e68a6f9eb8
|
@ -180,6 +180,7 @@ set(SOURCES
|
|||
internet/jamendodynamicplaylist.cpp
|
||||
internet/jamendoplaylistitem.cpp
|
||||
internet/jamendoservice.cpp
|
||||
internet/localredirectserver.cpp
|
||||
internet/magnatunedownloaddialog.cpp
|
||||
internet/magnatuneplaylistitem.cpp
|
||||
internet/magnatunesettingspage.cpp
|
||||
|
@ -457,6 +458,7 @@ set(HEADERS
|
|||
internet/internetviewcontainer.h
|
||||
internet/jamendodynamicplaylist.h
|
||||
internet/jamendoservice.h
|
||||
internet/localredirectserver.h
|
||||
internet/magnatunedownloaddialog.h
|
||||
internet/magnatunesettingspage.h
|
||||
internet/magnatuneservice.h
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "core/closure.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "internet/localredirectserver.h"
|
||||
|
||||
namespace {
|
||||
static const char* kAppKey = "qh6ca27eclt9p2k";
|
||||
|
@ -75,37 +76,25 @@ void DropboxAuthenticator::RequestTokenFinished(QNetworkReply* reply) {
|
|||
}
|
||||
|
||||
void DropboxAuthenticator::Authorise() {
|
||||
server_.listen(QHostAddress::LocalHost);
|
||||
const quint16 port = server_.serverPort();
|
||||
LocalRedirectServer* server = new LocalRedirectServer(this);
|
||||
server->Listen();
|
||||
|
||||
NewClosure(&server_, SIGNAL(newConnection()), this, SLOT(NewConnection()));
|
||||
NewClosure(server, SIGNAL(Finished()),
|
||||
this, SLOT(RedirectArrived(LocalRedirectServer*)), server);
|
||||
|
||||
QUrl url(kAuthoriseEndpoint);
|
||||
url.addQueryItem("oauth_token", token_);
|
||||
url.addQueryItem("oauth_callback", QString("http://localhost:%1").arg(port));
|
||||
url.addQueryItem("oauth_callback", server->url().toString());
|
||||
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::NewConnection() {
|
||||
QTcpSocket* socket = server_.nextPendingConnection();
|
||||
server_.close();
|
||||
|
||||
QByteArray buffer;
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(RedirectArrived(QTcpSocket*, QByteArray)), socket, buffer);
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RedirectArrived(QTcpSocket* socket, QByteArray buffer) {
|
||||
buffer.append(socket->readAll());
|
||||
if (socket->atEnd() || buffer.endsWith("\r\n\r\n")) {
|
||||
socket->deleteLater();
|
||||
QList<QByteArray> split = buffer.split('\r');
|
||||
const QByteArray& request_line = split[0];
|
||||
QUrl url(QString::fromAscii(request_line.split(' ')[1]));
|
||||
uid_ = url.queryItemValue("uid");
|
||||
RequestAccessToken();
|
||||
}
|
||||
void DropboxAuthenticator::RedirectArrived(LocalRedirectServer* server) {
|
||||
server->deleteLater();
|
||||
QUrl request_url = server->request_url();
|
||||
qLog(Debug) << Q_FUNC_INFO << request_url;
|
||||
uid_ = request_url.queryItemValue("uid");
|
||||
RequestAccessToken();
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RequestAccessToken() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QObject>
|
||||
#include <QTcpServer>
|
||||
|
||||
class LocalRedirectServer;
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
|
@ -26,8 +27,7 @@ class DropboxAuthenticator : public QObject {
|
|||
|
||||
private slots:
|
||||
void RequestTokenFinished(QNetworkReply* reply);
|
||||
void RedirectArrived(QTcpSocket* socket, QByteArray buffer);
|
||||
void NewConnection();
|
||||
void RedirectArrived(LocalRedirectServer* server);
|
||||
void RequestAccessTokenFinished(QNetworkReply* reply);
|
||||
void RequestAccountInformationFinished(QNetworkReply* reply);
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
#include "localredirectserver.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
#include <QStyle>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include "core/closure.h"
|
||||
|
||||
LocalRedirectServer::LocalRedirectServer(QObject* parent)
|
||||
: QObject(parent),
|
||||
server_(new QTcpServer(this)) {
|
||||
}
|
||||
|
||||
void LocalRedirectServer::Listen() {
|
||||
server_->listen(QHostAddress::LocalHost);
|
||||
// We have to calculate this and store it now as the server port is cleared
|
||||
// once we close the socket.
|
||||
url_.setScheme("http");
|
||||
url_.setHost("localhost");
|
||||
url_.setPort(server_->serverPort());
|
||||
url_.setPath("/");
|
||||
connect(server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||
}
|
||||
|
||||
void LocalRedirectServer::NewConnection() {
|
||||
QTcpSocket* socket = server_->nextPendingConnection();
|
||||
server_->close();
|
||||
|
||||
QByteArray buffer;
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(ReadyRead(QTcpSocket*,QByteArray)), socket, buffer);
|
||||
}
|
||||
|
||||
void LocalRedirectServer::ReadyRead(QTcpSocket* socket, QByteArray buffer) {
|
||||
buffer.append(socket->readAll());
|
||||
if (socket->atEnd() || buffer.endsWith("\r\n\r\n")) {
|
||||
WriteTemplate(socket);
|
||||
socket->deleteLater();
|
||||
request_url_ = ParseUrlFromRequest(buffer);
|
||||
emit Finished();
|
||||
} else {
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(ReadyReady(QTcpSocket*,QByteArray)), socket, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalRedirectServer::WriteTemplate(QTcpSocket* socket) const {
|
||||
QFile page_file(":oauthsuccess.html");
|
||||
page_file.open(QIODevice::ReadOnly);
|
||||
QString page_data = QString::fromUtf8(page_file.readAll());
|
||||
|
||||
QRegExp tr_regexp("tr\\(\"([^\"]+)\"\\)");
|
||||
int offset = 0;
|
||||
forever {
|
||||
offset = tr_regexp.indexIn(page_data, offset);
|
||||
if (offset == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
page_data.replace(offset, tr_regexp.matchedLength(),
|
||||
tr(tr_regexp.cap(1).toUtf8()));
|
||||
offset += tr_regexp.matchedLength();
|
||||
}
|
||||
|
||||
QBuffer image_buffer;
|
||||
image_buffer.open(QIODevice::ReadWrite);
|
||||
QApplication::style()->standardIcon(QStyle::SP_DialogOkButton)
|
||||
.pixmap(16).toImage().save(&image_buffer, "PNG");
|
||||
page_data.replace("@IMAGE_DATA@", image_buffer.data().toBase64());
|
||||
|
||||
socket->write("HTTP/1.0 200 OK\r\n");
|
||||
socket->write("Content-type: text/html;charset=UTF-8\r\n");
|
||||
socket->write("\r\n\r\n");
|
||||
socket->write(page_data.toUtf8());
|
||||
socket->flush();
|
||||
}
|
||||
|
||||
QUrl LocalRedirectServer::ParseUrlFromRequest(const QByteArray& request) const {
|
||||
QList<QByteArray> lines = request.split('\r');
|
||||
const QByteArray& request_line = lines[0];
|
||||
QByteArray path = request_line.split(' ')[1];
|
||||
QUrl base_url = url();
|
||||
QUrl request_url(base_url.toString() + path.mid(1), QUrl::StrictMode);
|
||||
return request_url;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef LOCALREDIRECTSERVER_H
|
||||
#define LOCALREDIRECTSERVER_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
|
||||
class LocalRedirectServer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LocalRedirectServer(QObject* parent = 0);
|
||||
|
||||
// Causes the server to listen for _one_ request.
|
||||
void Listen();
|
||||
|
||||
// Returns the HTTP URL of this server.
|
||||
const QUrl& url() const { return url_; }
|
||||
|
||||
// Returns the URL requested by the OAuth redirect.
|
||||
const QUrl& request_url() const { return request_url_; }
|
||||
|
||||
|
||||
signals:
|
||||
void Finished();
|
||||
|
||||
private slots:
|
||||
void NewConnection();
|
||||
void ReadyRead(QTcpSocket* socket, QByteArray buffer);
|
||||
|
||||
private:
|
||||
void WriteTemplate(QTcpSocket* socket) const;
|
||||
QUrl ParseUrlFromRequest(const QByteArray& request) const;
|
||||
|
||||
private:
|
||||
QTcpServer* server_;
|
||||
QUrl url_;
|
||||
QUrl request_url_;
|
||||
};
|
||||
|
||||
#endif // LOCALREDIRECTSERVER_H
|
|
@ -1,18 +1,14 @@
|
|||
#include "oauthenticator.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QDesktopServices>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QStyle>
|
||||
#include <QTcpSocket>
|
||||
#include <QUrl>
|
||||
|
||||
#include <qjson/parser.h>
|
||||
|
||||
#include "core/closure.h"
|
||||
#include "core/logging.h"
|
||||
#include "internet/localredirectserver.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -35,75 +31,28 @@ OAuthenticator::OAuthenticator(QObject* parent)
|
|||
}
|
||||
|
||||
void OAuthenticator::StartAuthorisation() {
|
||||
server_.listen(QHostAddress::LocalHost);
|
||||
const quint16 port = server_.serverPort();
|
||||
LocalRedirectServer* server = new LocalRedirectServer(this);
|
||||
server->Listen();
|
||||
|
||||
NewClosure(&server_, SIGNAL(newConnection()), this, SLOT(NewConnection()));
|
||||
NewClosure(server, SIGNAL(Finished()),
|
||||
this, SLOT(RedirectArrived(LocalRedirectServer*)), server);
|
||||
|
||||
QUrl url = QUrl(kGoogleOAuthEndpoint);
|
||||
url.addQueryItem("response_type", "code");
|
||||
url.addQueryItem("client_id", kClientId);
|
||||
url.addQueryItem("redirect_uri", QString("http://localhost:%1").arg(port));
|
||||
url.addQueryItem("redirect_uri", server->url().toString());
|
||||
url.addQueryItem("scope", kGoogleOAuthScope);
|
||||
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
void OAuthenticator::NewConnection() {
|
||||
QTcpSocket* socket = server_.nextPendingConnection();
|
||||
server_.close();
|
||||
|
||||
QByteArray buffer;
|
||||
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(RedirectArrived(QTcpSocket*, QByteArray)), socket, buffer);
|
||||
|
||||
// Everything is bon. Prepare and display the success page.
|
||||
QFile page_file(":oauthsuccess.html");
|
||||
page_file.open(QIODevice::ReadOnly);
|
||||
QString page_data = QString::fromLatin1(page_file.readAll());
|
||||
|
||||
// Translate the strings inside
|
||||
QRegExp tr_regexp("tr\\(\"([^\"]+)\"\\)");
|
||||
int offset = 0;
|
||||
forever {
|
||||
offset = tr_regexp.indexIn(page_data, offset);
|
||||
if (offset == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
page_data.replace(offset, tr_regexp.matchedLength(),
|
||||
tr(tr_regexp.cap(1).toAscii()));
|
||||
offset += tr_regexp.matchedLength();
|
||||
}
|
||||
|
||||
// Add the tick image.
|
||||
QBuffer image_buffer;
|
||||
image_buffer.open(QIODevice::ReadWrite);
|
||||
QApplication::style()->standardIcon(QStyle::SP_DialogOkButton)
|
||||
.pixmap(16).toImage().save(&image_buffer, "PNG");
|
||||
|
||||
page_data.replace("@IMAGE_DATA@", image_buffer.data().toBase64());
|
||||
|
||||
socket->write("HTTP/1.0 200 OK\r\n");
|
||||
socket->write("Content-type: text/html;charset=UTF-8\r\n");
|
||||
socket->write("\r\n\r\n");
|
||||
socket->write(page_data.toUtf8());
|
||||
socket->flush();
|
||||
}
|
||||
|
||||
void OAuthenticator::RedirectArrived(QTcpSocket* socket, QByteArray buffer) {
|
||||
buffer.append(socket->readAll());
|
||||
|
||||
if (socket->atEnd() || buffer.endsWith("\r\n\r\n")) {
|
||||
socket->deleteLater();
|
||||
const QByteArray& code = ParseHttpRequest(buffer);
|
||||
qLog(Debug) << "Code:" << code;
|
||||
RequestAccessToken(code, socket->localPort());
|
||||
} else {
|
||||
NewClosure(socket, SIGNAL(readyReady()),
|
||||
this, SLOT(RedirectArrived(QTcpSocket*, QByteArray)), socket, buffer);
|
||||
}
|
||||
void OAuthenticator::RedirectArrived(LocalRedirectServer* server) {
|
||||
server->deleteLater();
|
||||
QUrl request_url = server->request_url();
|
||||
qLog(Debug) << Q_FUNC_INFO << request_url;
|
||||
RequestAccessToken(
|
||||
request_url.queryItemValue("code").toUtf8(),
|
||||
server->url());
|
||||
}
|
||||
|
||||
QByteArray OAuthenticator::ParseHttpRequest(const QByteArray& request) const {
|
||||
|
@ -115,7 +64,7 @@ QByteArray OAuthenticator::ParseHttpRequest(const QByteArray& request) const {
|
|||
return code;
|
||||
}
|
||||
|
||||
void OAuthenticator::RequestAccessToken(const QByteArray& code, quint16 port) {
|
||||
void OAuthenticator::RequestAccessToken(const QByteArray& code, const QUrl& url) {
|
||||
typedef QPair<QString, QString> Param;
|
||||
QList<Param> parameters;
|
||||
parameters << Param("code", code)
|
||||
|
@ -124,7 +73,7 @@ void OAuthenticator::RequestAccessToken(const QByteArray& code, quint16 port) {
|
|||
<< Param("grant_type", "authorization_code")
|
||||
// Even though we don't use this URI anymore, it must match the
|
||||
// original one.
|
||||
<< Param("redirect_uri", QString("http://localhost:%1").arg(port));
|
||||
<< Param("redirect_uri", url.toString());
|
||||
|
||||
QStringList params;
|
||||
foreach (const Param& p, parameters) {
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include "core/network.h"
|
||||
|
||||
class LocalRedirectServer;
|
||||
class QTcpSocket;
|
||||
|
||||
class OAuthenticator : public QObject {
|
||||
|
@ -29,18 +29,16 @@ class OAuthenticator : public QObject {
|
|||
void Finished();
|
||||
|
||||
private slots:
|
||||
void NewConnection();
|
||||
void RedirectArrived(QTcpSocket* socket, QByteArray buffer);
|
||||
void RedirectArrived(LocalRedirectServer* server);
|
||||
void FetchAccessTokenFinished(QNetworkReply* reply);
|
||||
void RefreshAccessTokenFinished(QNetworkReply* reply);
|
||||
void FetchUserInfoFinished(QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
QByteArray ParseHttpRequest(const QByteArray& request) const;
|
||||
void RequestAccessToken(const QByteArray& code, quint16 port);
|
||||
void RequestAccessToken(const QByteArray& code, const QUrl& url);
|
||||
void SetExpiryTime(int expires_in_seconds);
|
||||
|
||||
QTcpServer server_;
|
||||
NetworkAccessManager network_;
|
||||
|
||||
QString access_token_;
|
||||
|
|
Loading…
Reference in New Issue