Basic XMPP support using gloox.
This commit is contained in:
parent
7de912d3a1
commit
e36a333212
@ -60,8 +60,8 @@ pkg_check_modules(PLIST libplist)
|
||||
pkg_check_modules(USBMUXD libusbmuxd)
|
||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(INDICATEQT indicate-qt)
|
||||
pkg_check_modules(QJSON QJson)
|
||||
pkg_check_modules(ARCHIVE libarchive)
|
||||
pkg_check_modules(GLOOX gloox>=1.0)
|
||||
|
||||
if (WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@ -235,9 +235,9 @@ endif(ENABLE_VISUALISATIONS)
|
||||
# need from it are private.
|
||||
option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON)
|
||||
|
||||
if(ENABLE_REMOTE AND QJSON_LIBRARIES AND QJSON_INCLUDE_DIRS)
|
||||
if(ENABLE_REMOTE AND GLOOX_LIBRARIES)
|
||||
set(HAVE_REMOTE ON)
|
||||
endif(ENABLE_REMOTE AND QJSON_LIBRARIES AND QJSON_INCLUDE_DIRS)
|
||||
endif(ENABLE_REMOTE AND GLOOX_LIBRARIES)
|
||||
|
||||
set(HAVE_STATIC_SQLITE ${STATIC_SQLITE})
|
||||
if(STATIC_SQLITE)
|
||||
|
@ -788,12 +788,10 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||
endif(HAVE_SCRIPTING_PYTHON)
|
||||
|
||||
if(HAVE_REMOTE)
|
||||
include_directories(${QJSON_INCLUDE_DIRS})
|
||||
link_directories(${QJSON_LIBRARY_DIRS})
|
||||
list(APPEND HEADERS remote/httpserver.h)
|
||||
list(APPEND HEADERS remote/httpconnection.h)
|
||||
list(APPEND SOURCES remote/httpserver.cpp)
|
||||
list(APPEND SOURCES remote/httpconnection.cpp)
|
||||
include_directories(${GLOOX_INCLUDE_DIRS})
|
||||
link_directories(${GLOOX_LIBRARY_DIRS})
|
||||
list(APPEND HEADERS remote/xmpp.h)
|
||||
list(APPEND SOURCES remote/xmpp.cpp)
|
||||
list(APPEND SOURCES remote/zeroconf.cpp)
|
||||
|
||||
if(APPLE)
|
||||
@ -965,7 +963,7 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||
endif(HAVE_SCRIPTING_PYTHON)
|
||||
|
||||
if(HAVE_REMOTE)
|
||||
target_link_libraries(clementine_lib ${QJSON_LIBRARIES})
|
||||
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
|
||||
endif(HAVE_REMOTE)
|
||||
|
||||
if (APPLE)
|
||||
|
10
src/main.cpp
10
src/main.cpp
@ -93,7 +93,7 @@ using boost::scoped_ptr;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
#include "remote/httpserver.h"
|
||||
#include "remote/xmpp.h"
|
||||
#include "remote/zeroconf.h"
|
||||
#endif
|
||||
|
||||
@ -333,12 +333,10 @@ int main(int argc, char *argv[]) {
|
||||
#ifdef HAVE_REMOTE
|
||||
Zeroconf* zeroconf = Zeroconf::GetZeroconf();
|
||||
if (zeroconf) {
|
||||
HttpServer* server = new HttpServer(&player);
|
||||
int port = 12345;
|
||||
while (!server->Listen(QHostAddress::Any, port)) { ++port; }
|
||||
|
||||
zeroconf->Publish("local", "_clementine._tcp", "Clementine", port);
|
||||
zeroconf->Publish("local", "_clementine._tcp", "Clementine", 12345);
|
||||
}
|
||||
XMPP xmpp;
|
||||
xmpp.Connect("timetabletest2@googlemail.com", "timetabletestpassword");
|
||||
#endif
|
||||
|
||||
// Window
|
||||
|
@ -1,142 +0,0 @@
|
||||
#include "httpconnection.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QStringList>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include <qjson/serializer.h>
|
||||
|
||||
#include "core/player.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
|
||||
HttpConnection::HttpConnection(Player* player, QTcpSocket* socket)
|
||||
: QObject(socket),
|
||||
socket_(socket),
|
||||
state_(WaitingForRequest),
|
||||
player_(player),
|
||||
cover_loader_(new AlbumCoverLoader(this)) {
|
||||
connect(socket_, SIGNAL(readyRead()), SLOT(ReadyRead()));
|
||||
connect(cover_loader_, SIGNAL(ImageLoaded(quint64, const QImage&)),
|
||||
SLOT(ImageReady(quint64, const QImage&)));
|
||||
connect(socket_, SIGNAL(disconnected()), socket_, SLOT(deleteLater()));
|
||||
|
||||
cover_loader_->SetDesiredHeight(400);
|
||||
cover_loader_->SetScaleOutputImage(true);
|
||||
}
|
||||
|
||||
void HttpConnection::ReadyRead() {
|
||||
switch (state_) {
|
||||
case WaitingForRequest:
|
||||
if (socket_->canReadLine()) {
|
||||
QString line = socket_->readLine();
|
||||
if (ParseRequest(line)) {
|
||||
state_ = WaitingForHeaders;
|
||||
} else {
|
||||
state_ = Error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WaitingForHeaders:
|
||||
while (socket_->canReadLine()) {
|
||||
QByteArray line = socket_->readLine();
|
||||
if (line == "\r\n") {
|
||||
DoRequest();
|
||||
state_ = Finished;
|
||||
} else {
|
||||
// TODO: Parse headers.
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state_ == Error) {
|
||||
socket_->close();
|
||||
socket_->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket_->canReadLine()) {
|
||||
ReadyRead();
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpConnection::ParseRequest(const QString& line) {
|
||||
QStringList tokens = line.split(' ', QString::SkipEmptyParts);
|
||||
if (tokens.length() != 3) {
|
||||
return false;
|
||||
}
|
||||
const QString& method = tokens[0];
|
||||
if (method != "GET") {
|
||||
return false;
|
||||
}
|
||||
|
||||
method_ = QNetworkAccessManager::GetOperation;
|
||||
path_ = tokens[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpConnection::DoRequest() {
|
||||
PlaylistItemPtr current_item = player_->GetCurrentItem();
|
||||
if (current_item) {
|
||||
quint64 id = cover_loader_->LoadImageAsync(current_item->Metadata());
|
||||
pending_tasks_[id] = current_item->Metadata();
|
||||
return;
|
||||
}
|
||||
|
||||
SendHeaders();
|
||||
|
||||
QVariantMap state = GetState();
|
||||
|
||||
QJson::Serializer serializer;
|
||||
QString output = serializer.serialize(state);
|
||||
socket_->write(output.toUtf8());
|
||||
|
||||
socket_->disconnectFromHost();
|
||||
}
|
||||
|
||||
void HttpConnection::ImageReady(quint64 id, const QImage& image) {
|
||||
Song song = pending_tasks_.take(id);
|
||||
SendSongInfo(song, image);
|
||||
}
|
||||
|
||||
void HttpConnection::SendHeaders() {
|
||||
socket_->write("HTTP/1.0 200 OK\r\n");
|
||||
socket_->write("Content-type: application/json\r\n");
|
||||
socket_->write("\r\n");
|
||||
}
|
||||
|
||||
QVariantMap HttpConnection::GetState() {
|
||||
QVariantMap json;
|
||||
json.insert("state", player_->GetState());
|
||||
json.insert("volume", player_->GetVolume());
|
||||
return json;
|
||||
}
|
||||
|
||||
void HttpConnection::SendSongInfo(Song song, const QImage& cover) {
|
||||
SendHeaders();
|
||||
|
||||
QVariantMap metadata;
|
||||
metadata.insert("title", song.title());
|
||||
metadata.insert("album", song.album());
|
||||
metadata.insert("artist", song.artist());
|
||||
if (!cover.isNull()) {
|
||||
QByteArray cover_data;
|
||||
{
|
||||
QBuffer cover_buffer(&cover_data);
|
||||
cover_buffer.open(QIODevice::WriteOnly);
|
||||
cover.save(&cover_buffer, "PNG");
|
||||
}
|
||||
metadata.insert("cover", cover_data.toBase64());
|
||||
}
|
||||
|
||||
QVariantMap state = GetState();
|
||||
state.insert("song", metadata);
|
||||
|
||||
QJson::Serializer serializer;
|
||||
QString output = serializer.serialize(state);
|
||||
socket_->write(output.toUtf8());
|
||||
socket_->disconnectFromHost();
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#ifndef HTTPCONNECTION_H
|
||||
#define HTTPCONNECTION_H
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
class AlbumCoverLoader;
|
||||
class Player;
|
||||
|
||||
class HttpConnection : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HttpConnection(Player* player, QTcpSocket* sock);
|
||||
|
||||
private slots:
|
||||
void ReadyRead();
|
||||
void ImageReady(quint64 id, const QImage& image);
|
||||
|
||||
private:
|
||||
void DoRequest();
|
||||
bool ParseRequest(const QString& line);
|
||||
void SendHeaders();
|
||||
QVariantMap GetState();
|
||||
void SendSongInfo(Song song, const QImage& cover);
|
||||
|
||||
QTcpSocket* socket_;
|
||||
|
||||
enum State {
|
||||
WaitingForRequest,
|
||||
WaitingForHeaders,
|
||||
WaitingForRequestFinish,
|
||||
Finished,
|
||||
Error,
|
||||
} state_;
|
||||
|
||||
QNetworkAccessManager::Operation method_;
|
||||
QString path_;
|
||||
|
||||
Player* player_;
|
||||
AlbumCoverLoader* cover_loader_;
|
||||
|
||||
QMap<quint64, Song> pending_tasks_;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
#include "httpserver.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "httpconnection.h"
|
||||
|
||||
HttpServer::HttpServer(Player* player, QObject* parent)
|
||||
: QObject(parent),
|
||||
player_(player) {
|
||||
connect(&server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||
}
|
||||
|
||||
bool HttpServer::Listen(const QHostAddress& addr, quint16 port) {
|
||||
return server_.listen(addr, port);
|
||||
}
|
||||
|
||||
void HttpServer::NewConnection() {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QTcpSocket* socket = server_.nextPendingConnection();
|
||||
new HttpConnection(player_, socket);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#ifndef HTTPSERVER_H
|
||||
#define HTTPSERVER_H
|
||||
|
||||
#include <QTcpServer>
|
||||
|
||||
class Player;
|
||||
|
||||
class HttpServer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HttpServer(Player* player, QObject* parent = 0);
|
||||
bool Listen(const QHostAddress& addr, quint16 port);
|
||||
|
||||
private slots:
|
||||
void NewConnection();
|
||||
|
||||
private:
|
||||
QTcpServer server_;
|
||||
|
||||
Player* player_;
|
||||
};
|
||||
|
||||
#endif
|
52
src/remote/xmpp.cpp
Normal file
52
src/remote/xmpp.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "xmpp.h"
|
||||
|
||||
#include <gloox/connectiontcpclient.h>
|
||||
#include <gloox/message.h>
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
using gloox::Client;
|
||||
using gloox::ConnectionTCPClient;
|
||||
using gloox::JID;
|
||||
using gloox::Message;
|
||||
using gloox::MessageSession;
|
||||
|
||||
XMPP::XMPP() {
|
||||
|
||||
}
|
||||
|
||||
XMPP::~XMPP() {
|
||||
|
||||
}
|
||||
|
||||
bool XMPP::Connect(const QString& jid, const QString& password) {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// TODO: Generate <256 char resource.
|
||||
JID j(jid.toUtf8().constData());
|
||||
qDebug() << "Resource:" << j.resource().c_str();
|
||||
client_.reset(new Client(j, password.toUtf8().constData()));
|
||||
client_->registerMessageHandler(this);
|
||||
client_->setServer("talk.google.com");
|
||||
client_->connect(false);
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
int fd = static_cast<ConnectionTCPClient*>(client_->connectionImpl())->socket();
|
||||
|
||||
notifier_.reset(new QSocketNotifier(fd, QSocketNotifier::Read));
|
||||
connect(notifier_.get(), SIGNAL(activated(int)), SLOT(Receive()));
|
||||
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
return true;
|
||||
}
|
||||
|
||||
void XMPP::handleMessage(const Message& stanza, MessageSession* session) {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
qDebug() << stanza.tag()->xml().c_str();
|
||||
qDebug() << "resource:" << client_->resource().c_str();
|
||||
Message reply(Message::Chat, stanza.from(), "Hello World!");
|
||||
client_->send(reply);
|
||||
}
|
||||
|
||||
void XMPP::Receive() {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
client_->recv();
|
||||
}
|
32
src/remote/xmpp.h
Normal file
32
src/remote/xmpp.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef XMPP_H
|
||||
#define XMPP_H
|
||||
|
||||
#include <gloox/client.h>
|
||||
#include <gloox/messagehandler.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QString>
|
||||
|
||||
class XMPP : public QObject, public gloox::MessageHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
XMPP();
|
||||
virtual ~XMPP();
|
||||
|
||||
bool Connect(const QString& jid, const QString& password);
|
||||
|
||||
private slots:
|
||||
void Receive();
|
||||
|
||||
private:
|
||||
// gloox::MessageHandler
|
||||
virtual void handleMessage(const gloox::Message& stanza,
|
||||
gloox::MessageSession* session = 0);
|
||||
|
||||
boost::scoped_ptr<gloox::Client> client_;
|
||||
boost::scoped_ptr<QSocketNotifier> notifier_;
|
||||
};
|
||||
|
||||
#endif // XMPP_H
|
Loading…
x
Reference in New Issue
Block a user