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(USBMUXD libusbmuxd)
|
||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
pkg_check_modules(INDICATEQT indicate-qt)
|
pkg_check_modules(INDICATEQT indicate-qt)
|
||||||
pkg_check_modules(QJSON QJson)
|
|
||||||
pkg_check_modules(ARCHIVE libarchive)
|
pkg_check_modules(ARCHIVE libarchive)
|
||||||
|
pkg_check_modules(GLOOX gloox>=1.0)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
@ -235,9 +235,9 @@ endif(ENABLE_VISUALISATIONS)
|
||||||
# need from it are private.
|
# need from it are private.
|
||||||
option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON)
|
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)
|
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})
|
set(HAVE_STATIC_SQLITE ${STATIC_SQLITE})
|
||||||
if(STATIC_SQLITE)
|
if(STATIC_SQLITE)
|
||||||
|
|
|
@ -788,12 +788,10 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||||
endif(HAVE_SCRIPTING_PYTHON)
|
endif(HAVE_SCRIPTING_PYTHON)
|
||||||
|
|
||||||
if(HAVE_REMOTE)
|
if(HAVE_REMOTE)
|
||||||
include_directories(${QJSON_INCLUDE_DIRS})
|
include_directories(${GLOOX_INCLUDE_DIRS})
|
||||||
link_directories(${QJSON_LIBRARY_DIRS})
|
link_directories(${GLOOX_LIBRARY_DIRS})
|
||||||
list(APPEND HEADERS remote/httpserver.h)
|
list(APPEND HEADERS remote/xmpp.h)
|
||||||
list(APPEND HEADERS remote/httpconnection.h)
|
list(APPEND SOURCES remote/xmpp.cpp)
|
||||||
list(APPEND SOURCES remote/httpserver.cpp)
|
|
||||||
list(APPEND SOURCES remote/httpconnection.cpp)
|
|
||||||
list(APPEND SOURCES remote/zeroconf.cpp)
|
list(APPEND SOURCES remote/zeroconf.cpp)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
@ -965,7 +963,7 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||||
endif(HAVE_SCRIPTING_PYTHON)
|
endif(HAVE_SCRIPTING_PYTHON)
|
||||||
|
|
||||||
if(HAVE_REMOTE)
|
if(HAVE_REMOTE)
|
||||||
target_link_libraries(clementine_lib ${QJSON_LIBRARIES})
|
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
|
||||||
endif(HAVE_REMOTE)
|
endif(HAVE_REMOTE)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -93,7 +93,7 @@ using boost::scoped_ptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_REMOTE
|
#ifdef HAVE_REMOTE
|
||||||
#include "remote/httpserver.h"
|
#include "remote/xmpp.h"
|
||||||
#include "remote/zeroconf.h"
|
#include "remote/zeroconf.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -333,12 +333,10 @@ int main(int argc, char *argv[]) {
|
||||||
#ifdef HAVE_REMOTE
|
#ifdef HAVE_REMOTE
|
||||||
Zeroconf* zeroconf = Zeroconf::GetZeroconf();
|
Zeroconf* zeroconf = Zeroconf::GetZeroconf();
|
||||||
if (zeroconf) {
|
if (zeroconf) {
|
||||||
HttpServer* server = new HttpServer(&player);
|
zeroconf->Publish("local", "_clementine._tcp", "Clementine", 12345);
|
||||||
int port = 12345;
|
|
||||||
while (!server->Listen(QHostAddress::Any, port)) { ++port; }
|
|
||||||
|
|
||||||
zeroconf->Publish("local", "_clementine._tcp", "Clementine", port);
|
|
||||||
}
|
}
|
||||||
|
XMPP xmpp;
|
||||||
|
xmpp.Connect("timetabletest2@googlemail.com", "timetabletestpassword");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Window
|
// 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
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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…
Reference in New Issue