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