This commit is contained in:
John Maguire 2013-01-28 15:41:51 +01:00
commit 53608665e4
11 changed files with 186 additions and 22 deletions

View File

@ -5,13 +5,14 @@ enum MsgType {
UNKNOWN = 0;
// Messages generally send from client to server
CONNECT = 1;
DISCONNECT = 2;
REQUEST_PLAYLISTS = 3;
REQUEST_PLAYLIST_SONGS = 4;
CHANGE_SONG = 5;
SET_VOLUME = 6;
SET_TRACK_POSITION = 7;
// Messages send by both
DISCONNECT = 2;
PLAY = 20;
PLAYPAUSE = 21;
PAUSE = 22;
@ -146,20 +147,45 @@ message ResponseUpdateTrackPosition {
optional int32 position = 1;
}
// The connect message containing the authentication code
message RequestConnect {
optional int32 auth_code = 1;
}
// Respone, why the connection was closed
enum ReasonDisconnect {
Server_Shutdown = 1;
Wrong_Auth_Code = 2;
}
message ResponseDisconnect {
optional ReasonDisconnect reason_disconnect = 1;
}
// A client requests a new track position
// position in seconds!
message RequestSetTrackPosition {
optional int32 position = 1;
}
// The message itself
message Message {
optional int32 version = 1 [default=2];
optional MsgType type = 2 [default=UNKNOWN]; // What data is in the message?
optional RequestConnect request_connect = 21;
optional RequestPlaylistSongs request_playlist_songs = 10;
optional RequestChangeSong request_change_song = 11;
optional RequestSetVolume request_set_volume = 12;
optional RequestSetTrackPosition request_set_track_position = 23;
optional Repeat repeat = 13;
optional Shuffle shuffle = 14;
optional ResponseClementineInfo response_clementine_info = 15;
optional ResponseCurrentMetadata response_current_metadata = 16;
optional ResponsePlaylists response_playlists = 17;
optional ResponsePlaylistSongs response_playlist_songs = 18;
optional ResponseEngineStateChanged response_engine_state_changed = 19;
optional ResponseUpdateTrackPosition response_update_track_position = 20;
optional ResponseDisconnect response_disconnect = 22;
}

View File

@ -104,6 +104,10 @@ using boost::scoped_ptr;
# include "devices/wmdmthread.h"
#endif
#ifndef Q_OS_WIN32
#include <signal.h>
#endif
// Load sqlite plugin on windows and mac.
#ifdef HAVE_STATIC_SQLITE
# include <QtPlugin>
@ -406,6 +410,12 @@ int main(int argc, char *argv[]) {
qtsparkle::LoadTranslations(language);
#endif
#ifndef Q_OS_WIN32
// This is needed to prevent SIGPIPE Errors, which occur under some
// circumstances in RemoteClient. They cause a program termination.
signal(SIGPIPE, SIG_IGN);
#endif
// Icons
IconLoader::Init();

View File

@ -42,6 +42,8 @@ IncomingDataParser::IncomingDataParser(Application* app)
app_->player(), SLOT(SetVolume(int)));
connect(this, SIGNAL(PlayAt(int,Engine::TrackChangeFlags,bool)),
app_->player(), SLOT(PlayAt(int,Engine::TrackChangeFlags,bool)));
connect(this, SIGNAL(SeekTo(int)),
app_->player(), SLOT(SeekTo(int)));
// For some connects we have to wait for the playlistmanager
// to be initialized
@ -69,16 +71,9 @@ bool IncomingDataParser::close_connection() {
return close_connection_;
}
void IncomingDataParser::Parse(const QByteArray& data) {
void IncomingDataParser::Parse(const pb::remote::Message& msg) {
close_connection_ = false;
// Parse the incoming data
pb::remote::Message msg;
if (!msg.ParseFromArray(data.constData(), data.size())) {
qLog(Info) << "Couldn't parse data";
return;
}
// Now check what's to do
switch (msg.type()) {
case pb::remote::CONNECT: emit SendClementineInfo();
@ -112,6 +107,9 @@ void IncomingDataParser::Parse(const QByteArray& data) {
break;
case pb::remote::SHUFFLE: SetShuffleMode(msg.shuffle());
break;
case pb::remote::SET_TRACK_POSITION:
emit SeekTo(msg.request_set_track_position().position());
break;
default: break;
}
}

View File

@ -14,7 +14,7 @@ public:
bool close_connection();
public slots:
void Parse(const QByteArray& pb_data);
void Parse(const pb::remote::Message& msg);
signals:
void SendClementineInfo();
@ -34,6 +34,7 @@ signals:
void ShuffleCurrent();
void SetRepeatMode(PlaylistSequence::RepeatMode mode);
void SetShuffleMode(PlaylistSequence::ShuffleMode mode);
void SeekTo(int seconds);
private slots:
void PlaylistManagerInitialized();

View File

@ -65,6 +65,10 @@ void NetworkRemote::SetupServer() {
SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
outgoing_data_creator_.get(),
SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
// Only connect the signals once
connect(server_.get(), SIGNAL(newConnection()), this, SLOT(AcceptConnection()));
connect(server_ipv6_.get(), SIGNAL(newConnection()), this, SLOT(AcceptConnection()));
}
void NetworkRemote::StartServer() {
@ -81,9 +85,6 @@ void NetworkRemote::StartServer() {
qLog(Info) << "Starting network remote";
connect(server_.get(), SIGNAL(newConnection()), this, SLOT(AcceptConnection()));
connect(server_ipv6_.get(), SIGNAL(newConnection()), this, SLOT(AcceptConnection()));
server_->listen(QHostAddress::Any, port_);
server_ipv6_->listen(QHostAddress::AnyIPv6, port_);
@ -97,6 +98,7 @@ void NetworkRemote::StartServer() {
void NetworkRemote::StopServer() {
if (server_->isListening()) {
outgoing_data_creator_.get()->DisconnectAllClients();
server_->close();
server_ipv6_->close();
clients_.clear();
@ -176,7 +178,7 @@ void NetworkRemote::CreateRemoteClient(QTcpSocket *client_socket) {
clients_.push_back(client);
// Connect the signal to parse data
connect(client, SIGNAL(Parse(QByteArray)),
incoming_data_parser_.get(), SLOT(Parse(QByteArray)));
connect(client, SIGNAL(Parse(pb::remote::Message)),
incoming_data_parser_.get(), SLOT(Parse(pb::remote::Message)));
}
}

View File

@ -137,6 +137,9 @@ void OutgoingDataCreator::SendFirstData() {
// then the current volume
VolumeChanged(app_->player()->GetVolume());
// And the current track position
UpdateTrackPosition();
}
void OutgoingDataCreator::CurrentSongChanged(const Song& song, const QString& uri, const QImage& img) {
@ -340,3 +343,10 @@ void OutgoingDataCreator::UpdateTrackPosition() {
SendDataToClients(&msg);
}
void OutgoingDataCreator::DisconnectAllClients() {
pb::remote::Message msg;
msg.set_type(pb::remote::DISCONNECT);
msg.mutable_response_disconnect()->set_reason_disconnect(pb::remote::Server_Shutdown);
SendDataToClients(&msg);
}

View File

@ -37,6 +37,7 @@ public slots:
void SendRepeatMode(PlaylistSequence::RepeatMode mode);
void SendShuffleMode(PlaylistSequence::ShuffleMode mode);
void UpdateTrackPosition();
void DisconnectAllClients();
private:
Application* app_;

View File

@ -18,8 +18,10 @@
#include "core/logging.h"
#include "remoteclient.h"
#include "networkremote.h"
#include <QDataStream>
#include <QSettings>
RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
: app_(app),
@ -32,6 +34,21 @@ RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
// Connect to the slot IncomingData when receiving data
connect(client, SIGNAL(readyRead()), this, SLOT(IncomingData()));
// Connect the signals to see if an error occured or the client
// was disconnected.
connect(client, SIGNAL(disconnected()), this, SLOT(Disconnected()));
connect(client, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(Error(QAbstractSocket::SocketError)));
// Check if we use auth code
QSettings s;
s.beginGroup(NetworkRemote::kSettingsGroup);
use_auth_code_ = s.value("use_auth_code", false).toBool();
auth_code_ = s.value("auth_code", 0).toInt();
s.endGroup();
}
@ -54,7 +71,7 @@ void RemoteClient::IncomingData() {
// Did we get everything?
if (buffer_.size() == expected_length_) {
// Parse the message
emit Parse(buffer_.data());
ParseMessage(buffer_.data());
// Clear the buffer
buffer_.close();
@ -65,19 +82,61 @@ void RemoteClient::IncomingData() {
}
}
void RemoteClient::ParseMessage(const QByteArray &data) {
pb::remote::Message msg;
if (!msg.ParseFromArray(data.constData(), data.size())) {
qLog(Info) << "Couldn't parse data";
return;
}
if (msg.type() == pb::remote::CONNECT && use_auth_code_) {
if (msg.request_connect().auth_code() != auth_code_) {
DisconnectClient();
return;
}
}
// Now parse the other data
emit Parse(msg);
}
void RemoteClient::DisconnectClient() {
pb::remote::Message msg;
msg.set_type(pb::remote::DISCONNECT);
msg.mutable_response_disconnect()->set_reason_disconnect(pb::remote::Wrong_Auth_Code);
SendData(&msg);
// Just close the connection. The next time the outgoing data creator
// sends a keep alive, the client will be deleted
client_->close();
}
void RemoteClient::SendData(pb::remote::Message *msg) {
// Serialize the message
std::string data = msg->SerializeAsString();
// write the length of the data first
QDataStream s(client_);
s << qint32(data.length());
s.writeRawData(data.data(), data.length());
if (client_->isWritable()) {
QDataStream s(client_);
s << qint32(data.length());
s.writeRawData(data.data(), data.length());
// Flush data
client_->flush();
// Flush data
client_->flush();
} else {
client_->close();
}
}
QAbstractSocket::SocketState RemoteClient::State() {
return client_->state();
}
void RemoteClient::Disconnected() {
qLog(Info) << "Client Disconnected";
}
void RemoteClient::Error(QAbstractSocket::SocketError socket_error) {
qLog(Info) << "Client Error:" << socket_error;
client_->close();
}

View File

@ -21,11 +21,19 @@ private slots:
void IncomingData();
signals:
void Parse(const QByteArray& pb_data);
void Parse(const pb::remote::Message& msg);
private:
void ParseMessage(const QByteArray& data);
void DisconnectClient();
void Disconnected();
void Error(QAbstractSocket::SocketError);
Application* app_;
bool use_auth_code_;
int auth_code_;
QTcpSocket* client_;
bool reading_protobuf_;
quint32 expected_length_;

View File

@ -59,6 +59,10 @@ void NetworkRemoteSettingsPage::Load() {
s.setValue("only_non_public_ip", true);
}
// Auth Code, 5 digits
ui_->use_auth_code->setChecked(s.value("use_auth_code", false).toBool());
ui_->auth_code->setValue(s.value("auth_code", qrand() % 100000).toInt());
s.endGroup();
QPixmap android_qr_code(":clementine_remote_qr.png");
@ -72,6 +76,8 @@ void NetworkRemoteSettingsPage::Save() {
s.setValue("port", ui_->remote_port->value());
s.setValue("use_remote", ui_->use_remote->isChecked());
s.setValue("only_non_public_ip", ui_->only_non_public_ip->isChecked());
s.setValue("use_auth_code", ui_->use_auth_code->isChecked());
s.setValue("auth_code", ui_->auth_code->value());
s.endGroup();

View File

@ -83,6 +83,33 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="use_auth_code">
<property name="toolTip">
<string>A client can connect only, if the correct code was entered.</string>
</property>
<property name="text">
<string>Use authentication code</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="auth_code">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -146,5 +173,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>use_auth_code</sender>
<signal>toggled(bool)</signal>
<receiver>auth_code</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>137</x>
<y>124</y>
</hint>
<hint type="destinationlabel">
<x>351</x>
<y>125</y>
</hint>
</hints>
</connection>
</connections>
</ui>