Clementine-audio-player-Mac.../src/networkremote/remoteclient.cpp

195 lines
5.6 KiB
C++

/* This file is part of Clementine.
Copyright 2013, Andreas Muttscheller <asfa194@gmail.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core/logging.h"
#include "remoteclient.h"
#include "networkremote.h"
#include <QDataStream>
#include <QSettings>
RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
: app_(app),
downloader_(false),
client_(client),
song_sender_(new SongSender(app, this)) {
// Open the buffer
buffer_.setData(QByteArray());
buffer_.open(QIODevice::ReadWrite);
reading_protobuf_ = false;
// 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();
allow_downloads_ = s.value("allow_downloads", false).toBool();
s.endGroup();
// If we don't use an auth code, we don't need to authenticate the client.
authenticated_ = !use_auth_code_;
}
RemoteClient::~RemoteClient() {
client_->close();
if (client_->state() == QAbstractSocket::ConnectedState)
client_->waitForDisconnected(2000);
song_sender_->deleteLater();
client_->deleteLater();
}
void RemoteClient::setDownloader(bool downloader) { downloader_ = downloader; }
void RemoteClient::IncomingData() {
while (client_->bytesAvailable()) {
if (!reading_protobuf_) {
// If we have less than 4 byte, we cannot read the length. Wait for more data
if (client_->bytesAvailable() < 4) {
break;
}
// Read the length of the next message
QDataStream s(client_);
s >> expected_length_;
// Receiving more than 128mb is very unlikely
// Flush the data and disconnect the client
if (expected_length_ > 134217728) {
qLog(Debug) << "Received invalid data, disconnect client";
qLog(Debug) << "expected_length_ =" << expected_length_;
client_->close();
return;
}
reading_protobuf_ = true;
}
// Read some of the message
buffer_.write(client_->read(expected_length_ - buffer_.size()));
// Did we get everything?
if (buffer_.size() == expected_length_) {
// Parse the message
ParseMessage(buffer_.data());
// Clear the buffer
buffer_.close();
buffer_.setData(QByteArray());
buffer_.open(QIODevice::ReadWrite);
reading_protobuf_ = false;
}
}
}
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(pb::remote::Wrong_Auth_Code);
return;
} else {
authenticated_ = true;
}
}
if (msg.type() == pb::remote::CONNECT) {
setDownloader(msg.request_connect().downloader());
qDebug() << "Downloader" << downloader_;
}
// Check if downloads are allowed
if (msg.type() == pb::remote::DOWNLOAD_SONGS && !allow_downloads_) {
DisconnectClient(pb::remote::Download_Forbidden);
return;
}
if (msg.type() == pb::remote::DISCONNECT) {
client_->abort();
qDebug() << "Client disconnected";
return;
}
// Check if the client has sent the correct auth code
if (!authenticated_) {
DisconnectClient(pb::remote::Not_Authenticated);
return;
}
// Now parse the other data
emit Parse(msg);
}
void RemoteClient::DisconnectClient(pb::remote::ReasonDisconnect reason) {
pb::remote::Message msg;
msg.set_type(pb::remote::DISCONNECT);
msg.mutable_response_disconnect()->set_reason_disconnect(reason);
SendDataToClient(&msg);
// Just close the connection. The next time the outgoing data creator
// sends a keep alive, the client will be deleted
client_->close();
}
// Sends data to client without check if authenticated
void RemoteClient::SendDataToClient(pb::remote::Message* msg) {
// Set the default version
msg->set_version(msg->default_instance().version());
// Check if we are still connected
if (client_->state() == QTcpSocket::ConnectedState) {
// Serialize the message
std::string data = msg->SerializeAsString();
// write the length of the data first
QDataStream s(client_);
s << qint32(data.length());
if (downloader_) {
// Don't use QDataSteam for large files
client_->write(data.data(), data.length());
} else {
s.writeRawData(data.data(), data.length());
}
// Do NOT flush data here! If the client is already disconnected, it
// causes a SIGPIPE termination!!!
} else {
qDebug() << "Closed";
client_->close();
}
}
void RemoteClient::SendData(pb::remote::Message* msg) {
// Check if client is authenticated before sending the data
if (authenticated_) {
SendDataToClient(msg);
}
}
QAbstractSocket::SocketState RemoteClient::State() { return client_->state(); }