1 /* IcyWebAccess.cpp */
2
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4 *
5 * This file is part of sayonara player
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "IcyWebAccess.h"
22 #include "Utils/Logger/Logger.h"
23 #include "Utils/Macros.h"
24
25 #include <QTcpSocket>
26 #include <QUrl>
27
28 struct IcyWebAccess::Private
29 {
30 IcyWebAccess::Status status;
31 QTcpSocket* tcp=nullptr;
32 QString hostname;
33 QString directory;
34 QString filename;
35 int port;
36
PrivateIcyWebAccess::Private37 Private()
38 {
39 status = IcyWebAccess::Status::Success;
40 port = 80;
41 }
42
close_tcpIcyWebAccess::Private43 void close_tcp()
44 {
45 if(tcp->isOpen()){
46 tcp->close();
47 }
48
49 tcp->deleteLater();
50 }
51
concat_dir_and_filenameIcyWebAccess::Private52 QString concat_dir_and_filename() const
53 {
54 QString ret = directory + "/" + filename;
55 while(ret.contains("//")){
56 ret.replace("//", "/");
57 }
58
59 if(!ret.startsWith("/")){
60 ret.prepend("/");
61 }
62
63 return ret;
64 }
65 };
66
IcyWebAccess(QObject * parent)67 IcyWebAccess::IcyWebAccess(QObject* parent) :
68 QObject(parent)
69 {
70 m = Pimpl::make<Private>();
71
72 }
73
~IcyWebAccess()74 IcyWebAccess::~IcyWebAccess() {}
75
check(const QUrl & url)76 void IcyWebAccess::check(const QUrl& url)
77 {
78 m->tcp = new QTcpSocket(nullptr);
79 m->hostname = url.host(QUrl::PrettyDecoded);
80 m->port = url.port(80);
81 m->directory = url.path();
82 m->filename = url.fileName();
83 m->status = IcyWebAccess::Status::NotExecuted;
84
85 connect(m->tcp, &QTcpSocket::connected, this, &IcyWebAccess::connected);
86 connect(m->tcp, &QTcpSocket::disconnected, this, &IcyWebAccess::disconnected);
87 connect(m->tcp, &QTcpSocket::readyRead, this, &IcyWebAccess::dataAvailable);
88
89 connect(m->tcp, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorReceived(QAbstractSocket::SocketError)));
90
91 m->tcp->connectToHost(m->hostname,
92 m->port,
93 QTcpSocket::ReadWrite,
94 QAbstractSocket::AnyIPProtocol
95 );
96
97 spLog(Log::Develop, this) << "Start ICY Request";
98 }
99
stop()100 void IcyWebAccess::stop()
101 {
102 if(m->tcp && m->tcp->isOpen() && m->tcp->isValid()){
103 m->tcp->abort();
104 m->tcp->close();
105 }
106 }
107
status() const108 IcyWebAccess::Status IcyWebAccess::status() const
109 {
110 return m->status;
111 }
112
113
connected()114 void IcyWebAccess::connected()
115 {
116 QString user_agent = QString("Sayonara/") + SAYONARA_VERSION;
117 QByteArray data(
118 "GET " + m->concat_dir_and_filename().toLocal8Bit() + " HTTP/1.1\r\n"
119 "User-Agent: " + user_agent.toLocal8Bit() + "\r\n"
120 "Connection: Keep-Alive\r\n"
121 "Accept-Encoding: gzip, deflate\r\n"
122 "Accept-Language: en-US,*\r\n"
123 "Host: " +
124 m->hostname.toLocal8Bit() + ":" +
125 QString::number(m->port).toLocal8Bit() + "\r\n\r\n"
126 );
127
128 spLog(Log::Develop, this) << data;
129
130 int64_t bytes_written = m->tcp->write(data.data(), data.size());
131 if(bytes_written != data.size())
132 {
133 spLog(Log::Warning, this) << "Could only write " << bytes_written << " bytes";
134 m->status = IcyWebAccess::Status::WriteError;
135 emit sigFinished();
136 m->close_tcp();
137 }
138 }
139
disconnected()140 void IcyWebAccess::disconnected()
141 {
142 spLog(Log::Develop, this) << "Disconnected";
143 if(m->status == IcyWebAccess::Status::NotExecuted) {
144 m->status = IcyWebAccess::Status::OtherError;
145 emit sigFinished();
146 }
147
148 m->close_tcp();
149
150 sender()->deleteLater();
151 }
152
errorReceived(QAbstractSocket::SocketError socket_state)153 void IcyWebAccess::errorReceived(QAbstractSocket::SocketError socket_state)
154 {
155 Q_UNUSED(socket_state)
156
157 spLog(Log::Warning, this) << "Icy Webaccess Error: " << m->tcp->errorString();
158
159 m->status = IcyWebAccess::Status::OtherError;
160 m->close_tcp();
161
162 emit sigFinished();
163 }
164
dataAvailable()165 void IcyWebAccess::dataAvailable()
166 {
167 QByteArray arr = m->tcp->read(20);
168 if(arr.contains("ICY 200 OK")){
169 m->status = IcyWebAccess::Status::Success;
170 }
171
172 else {
173 spLog(Log::Warning, this) << "Icy Answer Error: " << arr;
174 m->status = IcyWebAccess::Status::WrongAnswer;
175 }
176
177 m->close_tcp();
178
179 emit sigFinished();
180 }
181