1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "workerinterface_p.h"
9
10#include "commands_p.h"
11#include "connection_p.h"
12#include "hostinfo.h"
13#include "kiocoredebug.h"
14#include "usernotificationhandler_p.h"
15#include "workerbase.h"
16
17#include <KLocalizedString>
18#include <signal.h>
19#include <time.h>
20
21#include <QDataStream>
22#include <QDateTime>
23
24using namespace KIO;
25
26Q_GLOBAL_STATIC(UserNotificationHandler, globalUserNotificationHandler)
27
28WorkerInterface::WorkerInterface(QObject *parent)
29 : QObject(parent)
30{
31 connect(sender: &m_speed_timer, signal: &QTimer::timeout, context: this, slot: &WorkerInterface::calcSpeed);
32}
33
34WorkerInterface::~WorkerInterface()
35{
36 // Note: no Debug() here (scheduler is deleted very late)
37
38 delete m_connection;
39}
40
41static KIO::filesize_t readFilesize_t(QDataStream &stream)
42{
43 KIO::filesize_t result;
44 stream >> result;
45 return result;
46}
47
48bool WorkerInterface::dispatch()
49{
50 Q_ASSERT(m_connection);
51
52 int cmd;
53 QByteArray data;
54
55 int ret = m_connection->read(cmd: &cmd, data);
56 if (ret == -1) {
57 return false;
58 }
59
60 return dispatch(cmd: cmd, data);
61}
62
63void WorkerInterface::calcSpeed()
64{
65 if (m_worker_calcs_speed || !m_connection->isConnected()) { // killing a job results in disconnection but the timer never stops
66 m_speed_timer.stop();
67 return;
68 }
69
70 const qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
71 const qint64 diff = currentTime - m_start_time;
72 if (diff - m_last_time >= 900) {
73 m_last_time = diff;
74 if (m_nums == max_nums) {
75 // let's hope gcc can optimize that well enough
76 // otherwise I'd try memcpy :)
77 for (unsigned int i = 1; i < max_nums; ++i) {
78 m_times[i - 1] = m_times[i];
79 m_sizes[i - 1] = m_sizes[i];
80 }
81 m_nums--;
82 }
83 m_times[m_nums] = diff;
84 m_sizes[m_nums++] = m_filesize - m_offset;
85
86 KIO::filesize_t lspeed = 1000 * (m_sizes[m_nums - 1] - m_sizes[0]) / (m_times[m_nums - 1] - m_times[0]);
87
88 // qDebug() << (long)m_filesize << diff
89 // << long(m_sizes[m_nums-1] - m_sizes[0])
90 // << m_times[m_nums-1] - m_times[0]
91 // << long(lspeed) << double(m_filesize) / diff
92 // << convertSize(lspeed)
93 // << convertSize(long(double(m_filesize) / diff) * 1000);
94
95 if (!lspeed) {
96 m_nums = 1;
97 m_times[0] = diff;
98 m_sizes[0] = m_filesize - m_offset;
99 }
100 Q_EMIT speed(lspeed);
101 }
102}
103
104bool WorkerInterface::dispatch(int _cmd, const QByteArray &rawdata)
105{
106 // qDebug() << "dispatch " << _cmd;
107
108 QDataStream stream(rawdata);
109
110 QString str1;
111 qint32 i;
112 qint8 b;
113 quint32 ul;
114
115 switch (_cmd) {
116 case MSG_DATA:
117 Q_EMIT data(rawdata);
118 break;
119 case MSG_DATA_REQ:
120 Q_EMIT dataReq();
121 break;
122 case MSG_OPENED:
123 Q_EMIT open();
124 break;
125 case MSG_FINISHED:
126 // qDebug() << "Finished [this = " << this << "]";
127 m_offset = 0;
128 m_speed_timer.stop();
129 Q_EMIT finished();
130 break;
131 case MSG_STAT_ENTRY: {
132 UDSEntry entry;
133 stream >> entry;
134 Q_EMIT statEntry(entry);
135 break;
136 }
137 case MSG_LIST_ENTRIES: {
138 UDSEntryList list;
139 UDSEntry entry;
140
141 while (!stream.atEnd()) {
142 stream >> entry;
143 list.append(t: entry);
144 }
145
146 Q_EMIT listEntries(list);
147 break;
148 }
149 case MSG_RESUME: { // From the put job
150 m_offset = readFilesize_t(stream);
151 Q_EMIT canResume(m_offset);
152 break;
153 }
154 case MSG_CANRESUME: // From the get job
155 m_filesize = m_offset;
156 Q_EMIT canResume(0); // the arg doesn't matter
157 break;
158 case MSG_ERROR:
159 stream >> i >> str1;
160 // qDebug() << "error " << i << " " << str1;
161 Q_EMIT error(i, str1);
162 break;
163 case MSG_WORKER_STATUS: {
164 qint64 pid;
165 QByteArray protocol;
166 stream >> pid >> protocol >> str1 >> b;
167 Q_EMIT workerStatus(pid, protocol, str1, (b != 0));
168 break;
169 }
170 case MSG_CONNECTED:
171 Q_EMIT connected();
172 break;
173 case MSG_WRITTEN: {
174 KIO::filesize_t size = readFilesize_t(stream);
175 Q_EMIT written(size);
176 break;
177 }
178 case INF_TOTAL_SIZE: {
179 KIO::filesize_t size = readFilesize_t(stream);
180 m_start_time = QDateTime::currentMSecsSinceEpoch();
181 m_last_time = 0;
182 m_filesize = m_offset;
183 m_sizes[0] = m_filesize - m_offset;
184 m_times[0] = 0;
185 m_nums = 1;
186 m_speed_timer.start(msec: 1000);
187 m_worker_calcs_speed = false;
188 Q_EMIT totalSize(size);
189 break;
190 }
191 case INF_PROCESSED_SIZE: {
192 KIO::filesize_t size = readFilesize_t(stream);
193 Q_EMIT processedSize(size);
194 m_filesize = size;
195 break;
196 }
197 case INF_POSITION: {
198 KIO::filesize_t pos = readFilesize_t(stream);
199 Q_EMIT position(pos);
200 break;
201 }
202 case INF_TRUNCATED: {
203 KIO::filesize_t length = readFilesize_t(stream);
204 Q_EMIT truncated(length);
205 break;
206 }
207 case INF_SPEED:
208 stream >> ul;
209 m_worker_calcs_speed = true;
210 m_speed_timer.stop();
211 Q_EMIT speed(ul);
212 break;
213 case INF_ERROR_PAGE:
214 Q_EMIT errorPage();
215 break;
216 case INF_REDIRECTION: {
217 QUrl url;
218 stream >> url;
219 Q_EMIT redirection(url);
220 break;
221 }
222 case INF_MIME_TYPE:
223 stream >> str1;
224 Q_EMIT mimeType(str1);
225 if (!m_connection->suspended()) {
226 m_connection->sendnow(cmd: CMD_NONE, data: QByteArray());
227 }
228 break;
229 case INF_WARNING:
230 stream >> str1;
231 Q_EMIT warning(str1);
232 break;
233 case INF_MESSAGEBOX: {
234 // qDebug() << "needs a msg box";
235 QString text;
236 QString title;
237 QString primaryActionText;
238 QString secondaryActionText;
239 QString dontAskAgainName;
240 int type;
241 stream >> type >> text >> title >> primaryActionText >> secondaryActionText;
242 if (stream.atEnd()) {
243 messageBox(type, text, title, primaryActionText, secondaryActionText);
244 } else {
245 stream >> dontAskAgainName;
246 messageBox(type, text, title, primaryActionText, secondaryActionText, dontAskAgainName);
247 }
248 break;
249 }
250 case INF_INFOMESSAGE: {
251 QString msg;
252 stream >> msg;
253 Q_EMIT infoMessage(msg);
254 break;
255 }
256 case INF_SSLERROR: {
257 QVariantMap sslErrorData;
258 stream >> sslErrorData;
259 globalUserNotificationHandler->sslError(iface: this, sslErrorData);
260 break;
261 }
262 case INF_META_DATA: {
263 MetaData m;
264 stream >> m;
265 if (auto it = m.constFind(QStringLiteral("privilege_conf_details")); it != m.cend()) {
266 // see WORKER_MESSAGEBOX_DETAILS_HACK
267 m_messageBoxDetails = it.value();
268 }
269 Q_EMIT metaData(m);
270 break;
271 }
272 case MSG_HOST_INFO_REQ: {
273 QString hostName;
274 stream >> hostName;
275 HostInfo::lookupHost(hostName, receiver: this, SLOT(slotHostInfo(QHostInfo)));
276 break;
277 }
278 case MSG_PRIVILEGE_EXEC:
279 Q_EMIT privilegeOperationRequested();
280 break;
281 default:
282 qCWarning(KIO_CORE) << "Worker sends unknown command (" << _cmd << "), dropping worker.";
283 return false;
284 }
285 return true;
286}
287
288void WorkerInterface::setOffset(KIO::filesize_t o)
289{
290 m_offset = o;
291}
292
293KIO::filesize_t WorkerInterface::offset() const
294{
295 return m_offset;
296}
297
298void WorkerInterface::sendResumeAnswer(bool resume)
299{
300 // qDebug() << "ok for resuming:" << resume;
301 m_connection->sendnow(cmd: resume ? CMD_RESUMEANSWER : CMD_NONE, data: QByteArray());
302}
303
304void WorkerInterface::sendMessageBoxAnswer(int result)
305{
306 if (!m_connection) {
307 return;
308 }
309
310 if (m_connection->suspended()) {
311 m_connection->resume();
312 }
313 QByteArray packedArgs;
314 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
315 stream << result;
316 m_connection->sendnow(cmd: CMD_MESSAGEBOXANSWER, data: packedArgs);
317 // qDebug() << "message box answer" << result;
318}
319
320void WorkerInterface::sendSslErrorAnswer(int result)
321{
322 if (!m_connection) {
323 return;
324 }
325
326 if (m_connection->suspended()) {
327 m_connection->resume();
328 }
329 QByteArray packedArgs;
330 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
331 stream << result;
332 m_connection->sendnow(cmd: CMD_SSLERRORANSWER, data: packedArgs);
333 // qDebug() << "message box answer" << result;
334}
335
336void WorkerInterface::messageBox(int type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText)
337{
338 messageBox(type, text, title, primaryActionText, secondaryActionText, dontAskAgainName: QString());
339}
340
341void WorkerInterface::messageBox(int type,
342 const QString &text,
343 const QString &title,
344 const QString &primaryActionText,
345 const QString &secondaryActionText,
346 const QString &dontAskAgainName)
347{
348 if (m_connection) {
349 m_connection->suspend();
350 }
351
352 QHash<UserNotificationHandler::MessageBoxDataType, QVariant> data;
353 data.insert(key: UserNotificationHandler::MSG_TEXT, value: text);
354 data.insert(key: UserNotificationHandler::MSG_TITLE, value: title);
355 data.insert(key: UserNotificationHandler::MSG_PRIMARYACTION_TEXT, value: primaryActionText);
356 data.insert(key: UserNotificationHandler::MSG_SECONDARYACTION_TEXT, value: secondaryActionText);
357 data.insert(key: UserNotificationHandler::MSG_DONT_ASK_AGAIN, value: dontAskAgainName);
358
359 // SMELL: the braindead way to support button icons
360 // TODO: Fix this in KIO::WorkerBase.
361 if (primaryActionText == i18n("&Details")) {
362 data.insert(key: UserNotificationHandler::MSG_PRIMARYACTION_ICON, value: QLatin1String("help-about"));
363 } else if (primaryActionText == i18n("&Forever")) {
364 data.insert(key: UserNotificationHandler::MSG_PRIMARYACTION_ICON, value: QLatin1String("flag-green"));
365 }
366
367 if (secondaryActionText == i18n("Co&ntinue")) {
368 data.insert(key: UserNotificationHandler::MSG_SECONDARYACTION_ICON, value: QLatin1String("arrow-right"));
369 } else if (secondaryActionText == i18n("&Current Session only")) {
370 data.insert(key: UserNotificationHandler::MSG_SECONDARYACTION_ICON, value: QLatin1String("chronometer"));
371 }
372
373 if (type == KIO::WorkerBase::WarningContinueCancelDetailed) { // see WORKER_MESSAGEBOX_DETAILS_HACK
374 data.insert(key: UserNotificationHandler::MSG_DETAILS, value: m_messageBoxDetails);
375 }
376
377 globalUserNotificationHandler()->requestMessageBox(iface: this, type, data);
378}
379
380void WorkerInterface::slotHostInfo(const QHostInfo &info)
381{
382 QByteArray data;
383 QDataStream stream(&data, QIODevice::WriteOnly);
384 stream << info.hostName() << info.addresses() << info.error() << info.errorString();
385 m_connection->send(cmd: CMD_HOST_INFO, arr: data);
386}
387
388#include "moc_workerinterface_p.cpp"
389

source code of kio/src/core/workerinterface.cpp