Basic XMPP support using gloox.

This commit is contained in:
John Maguire 2011-02-16 20:49:12 +00:00
parent 7de912d3a1
commit e36a333212
9 changed files with 96 additions and 253 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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
View 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
View 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