1 /* This file is part of Clementine.
2 Copyright 2013, Andreas Muttscheller <asfa194@gmail.com>
3
4 Clementine is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 Clementine is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Clementine. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "core/logging.h"
19
20 #include "remoteclient.h"
21 #include "networkremote.h"
22
23 #include <QDataStream>
24 #include <QSettings>
25
RemoteClient(Application * app,QTcpSocket * client)26 RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
27 : app_(app),
28 downloader_(false),
29 client_(client),
30 song_sender_(new SongSender(app, this)) {
31 // Open the buffer
32 buffer_.setData(QByteArray());
33 buffer_.open(QIODevice::ReadWrite);
34 reading_protobuf_ = false;
35
36 // Connect to the slot IncomingData when receiving data
37 connect(client, SIGNAL(readyRead()), this, SLOT(IncomingData()));
38
39 // Check if we use auth code
40 QSettings s;
41
42 s.beginGroup(NetworkRemote::kSettingsGroup);
43 use_auth_code_ = s.value("use_auth_code", false).toBool();
44 auth_code_ = s.value("auth_code", 0).toInt();
45 allow_downloads_ = s.value("allow_downloads", false).toBool();
46
47 s.endGroup();
48
49 // If we don't use an auth code, we don't need to authenticate the client.
50 authenticated_ = !use_auth_code_;
51 }
52
~RemoteClient()53 RemoteClient::~RemoteClient() {
54 client_->close();
55 if (client_->state() == QAbstractSocket::ConnectedState)
56 client_->waitForDisconnected(2000);
57
58 song_sender_->deleteLater();
59 client_->deleteLater();
60 }
61
setDownloader(bool downloader)62 void RemoteClient::setDownloader(bool downloader) { downloader_ = downloader; }
63
IncomingData()64 void RemoteClient::IncomingData() {
65 while (client_->bytesAvailable()) {
66 if (!reading_protobuf_) {
67 // If we have less than 4 byte, we cannot read the length. Wait for more data
68 if (client_->bytesAvailable() < 4) {
69 break;
70 }
71 // Read the length of the next message
72 QDataStream s(client_);
73 s >> expected_length_;
74
75 // Receiving more than 128mb is very unlikely
76 // Flush the data and disconnect the client
77 if (expected_length_ > 134217728) {
78 qLog(Debug) << "Received invalid data, disconnect client";
79 qLog(Debug) << "expected_length_ =" << expected_length_;
80 client_->close();
81 return;
82 }
83
84 reading_protobuf_ = true;
85 }
86
87 // Read some of the message
88 buffer_.write(client_->read(expected_length_ - buffer_.size()));
89
90 // Did we get everything?
91 if (buffer_.size() == expected_length_) {
92 // Parse the message
93 ParseMessage(buffer_.data());
94
95 // Clear the buffer
96 buffer_.close();
97 buffer_.setData(QByteArray());
98 buffer_.open(QIODevice::ReadWrite);
99 reading_protobuf_ = false;
100 }
101 }
102 }
103
ParseMessage(const QByteArray & data)104 void RemoteClient::ParseMessage(const QByteArray& data) {
105 pb::remote::Message msg;
106 if (!msg.ParseFromArray(data.constData(), data.size())) {
107 qLog(Info) << "Couldn't parse data";
108 return;
109 }
110
111 if (msg.type() == pb::remote::CONNECT && use_auth_code_) {
112 if (msg.request_connect().auth_code() != auth_code_) {
113 DisconnectClient(pb::remote::Wrong_Auth_Code);
114 return;
115 } else {
116 authenticated_ = true;
117 }
118 }
119
120 if (msg.type() == pb::remote::CONNECT) {
121 setDownloader(msg.request_connect().downloader());
122 qDebug() << "Downloader" << downloader_;
123 }
124
125 // Check if downloads are allowed
126 if (msg.type() == pb::remote::DOWNLOAD_SONGS && !allow_downloads_) {
127 DisconnectClient(pb::remote::Download_Forbidden);
128 return;
129 }
130
131 if (msg.type() == pb::remote::DISCONNECT) {
132 client_->abort();
133 qDebug() << "Client disconnected";
134 return;
135 }
136
137 // Check if the client has sent the correct auth code
138 if (!authenticated_) {
139 DisconnectClient(pb::remote::Not_Authenticated);
140 return;
141 }
142
143 // Now parse the other data
144 emit Parse(msg);
145 }
146
DisconnectClient(pb::remote::ReasonDisconnect reason)147 void RemoteClient::DisconnectClient(pb::remote::ReasonDisconnect reason) {
148 pb::remote::Message msg;
149 msg.set_type(pb::remote::DISCONNECT);
150
151 msg.mutable_response_disconnect()->set_reason_disconnect(reason);
152 SendDataToClient(&msg);
153
154 // Just close the connection. The next time the outgoing data creator
155 // sends a keep alive, the client will be deleted
156 client_->close();
157 }
158
159 // Sends data to client without check if authenticated
SendDataToClient(pb::remote::Message * msg)160 void RemoteClient::SendDataToClient(pb::remote::Message* msg) {
161 // Set the default version
162 msg->set_version(msg->default_instance().version());
163
164 // Check if we are still connected
165 if (client_->state() == QTcpSocket::ConnectedState) {
166 // Serialize the message
167 std::string data = msg->SerializeAsString();
168
169 // write the length of the data first
170 QDataStream s(client_);
171 s << qint32(data.length());
172 if (downloader_) {
173 // Don't use QDataSteam for large files
174 client_->write(data.data(), data.length());
175 } else {
176 s.writeRawData(data.data(), data.length());
177 }
178
179 // Do NOT flush data here! If the client is already disconnected, it
180 // causes a SIGPIPE termination!!!
181 } else {
182 qDebug() << "Closed";
183 client_->close();
184 }
185 }
186
SendData(pb::remote::Message * msg)187 void RemoteClient::SendData(pb::remote::Message* msg) {
188 // Check if client is authenticated before sending the data
189 if (authenticated_) {
190 SendDataToClient(msg);
191 }
192 }
193
State()194 QAbstractSocket::SocketState RemoteClient::State() { return client_->state(); }
195