diff --git a/ext/libclementine-remote/remotecontrolmessages.proto b/ext/libclementine-remote/remotecontrolmessages.proto index eaa43574e..3cbf6bde6 100644 --- a/ext/libclementine-remote/remotecontrolmessages.proto +++ b/ext/libclementine-remote/remotecontrolmessages.proto @@ -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; } diff --git a/src/networkremote/incomingdataparser.cpp b/src/networkremote/incomingdataparser.cpp index af39290b6..b02e8d13b 100644 --- a/src/networkremote/incomingdataparser.cpp +++ b/src/networkremote/incomingdataparser.cpp @@ -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; } } diff --git a/src/networkremote/incomingdataparser.h b/src/networkremote/incomingdataparser.h index 9312cfeca..bea80d3b3 100644 --- a/src/networkremote/incomingdataparser.h +++ b/src/networkremote/incomingdataparser.h @@ -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(); diff --git a/src/networkremote/networkremote.cpp b/src/networkremote/networkremote.cpp index bdc31c677..a41d1fad1 100644 --- a/src/networkremote/networkremote.cpp +++ b/src/networkremote/networkremote.cpp @@ -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))); } } diff --git a/src/networkremote/outgoingdatacreator.cpp b/src/networkremote/outgoingdatacreator.cpp index 8453f7d36..d7c921527 100644 --- a/src/networkremote/outgoingdatacreator.cpp +++ b/src/networkremote/outgoingdatacreator.cpp @@ -340,3 +340,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); +} diff --git a/src/networkremote/outgoingdatacreator.h b/src/networkremote/outgoingdatacreator.h index 21f604033..31fb7ec5d 100644 --- a/src/networkremote/outgoingdatacreator.h +++ b/src/networkremote/outgoingdatacreator.h @@ -37,6 +37,7 @@ public slots: void SendRepeatMode(PlaylistSequence::RepeatMode mode); void SendShuffleMode(PlaylistSequence::ShuffleMode mode); void UpdateTrackPosition(); + void DisconnectAllClients(); private: Application* app_; diff --git a/src/networkremote/remoteclient.cpp b/src/networkremote/remoteclient.cpp index 0732395bd..7cd37eb94 100644 --- a/src/networkremote/remoteclient.cpp +++ b/src/networkremote/remoteclient.cpp @@ -18,8 +18,10 @@ #include "core/logging.h" #include "remoteclient.h" +#include "networkremote.h" #include +#include RemoteClient::RemoteClient(Application* app, QTcpSocket* client) : app_(app), @@ -32,6 +34,15 @@ RemoteClient::RemoteClient(Application* app, QTcpSocket* client) // Connect to the slot IncomingData when receiving data connect(client, SIGNAL(readyRead()), this, SLOT(IncomingData())); + + // 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 +65,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,6 +76,35 @@ 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(); diff --git a/src/networkremote/remoteclient.h b/src/networkremote/remoteclient.h index f283b4145..dada3d91a 100644 --- a/src/networkremote/remoteclient.h +++ b/src/networkremote/remoteclient.h @@ -21,11 +21,17 @@ 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(); + Application* app_; + bool use_auth_code_; + int auth_code_; + QTcpSocket* client_; bool reading_protobuf_; quint32 expected_length_; diff --git a/src/ui/networkremotesettingspage.cpp b/src/ui/networkremotesettingspage.cpp index cd2c870d1..ebef17489 100644 --- a/src/ui/networkremotesettingspage.cpp +++ b/src/ui/networkremotesettingspage.cpp @@ -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(); diff --git a/src/ui/networkremotesettingspage.ui b/src/ui/networkremotesettingspage.ui index b56284626..992cf4973 100644 --- a/src/ui/networkremotesettingspage.ui +++ b/src/ui/networkremotesettingspage.ui @@ -83,6 +83,33 @@ + + + + + + A client can connect only, if the correct code was entered. + + + Use authentication code + + + + + + + false + + + + + + 99999 + + + + + @@ -146,5 +173,21 @@ + + use_auth_code + toggled(bool) + auth_code + setEnabled(bool) + + + 137 + 124 + + + 351 + 125 + + +