File indexing completed on 2024-04-28 03:55:19

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "workerinterface_p.h"
0009 
0010 #include "commands_p.h"
0011 #include "connection_p.h"
0012 #include "hostinfo.h"
0013 #include "kiocoredebug.h"
0014 #include "usernotificationhandler_p.h"
0015 #include "workerbase.h"
0016 
0017 #include <KLocalizedString>
0018 #include <signal.h>
0019 #include <time.h>
0020 
0021 #include <QDataStream>
0022 #include <QDateTime>
0023 
0024 using namespace KIO;
0025 
0026 Q_GLOBAL_STATIC(UserNotificationHandler, globalUserNotificationHandler)
0027 
0028 WorkerInterface::WorkerInterface(QObject *parent)
0029     : QObject(parent)
0030 {
0031     connect(&m_speed_timer, &QTimer::timeout, this, &WorkerInterface::calcSpeed);
0032 }
0033 
0034 WorkerInterface::~WorkerInterface()
0035 {
0036     // Note: no Debug() here (scheduler is deleted very late)
0037 
0038     delete m_connection;
0039 }
0040 
0041 static KIO::filesize_t readFilesize_t(QDataStream &stream)
0042 {
0043     KIO::filesize_t result;
0044     stream >> result;
0045     return result;
0046 }
0047 
0048 bool WorkerInterface::dispatch()
0049 {
0050     Q_ASSERT(m_connection);
0051 
0052     int cmd;
0053     QByteArray data;
0054 
0055     int ret = m_connection->read(&cmd, data);
0056     if (ret == -1) {
0057         return false;
0058     }
0059 
0060     return dispatch(cmd, data);
0061 }
0062 
0063 void WorkerInterface::calcSpeed()
0064 {
0065     if (m_worker_calcs_speed || !m_connection->isConnected()) { // killing a job results in disconnection but the timer never stops
0066         m_speed_timer.stop();
0067         return;
0068     }
0069 
0070     const qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
0071     const qint64 diff = currentTime - m_start_time;
0072     if (diff - m_last_time >= 900) {
0073         m_last_time = diff;
0074         if (m_nums == max_nums) {
0075             // let's hope gcc can optimize that well enough
0076             // otherwise I'd try memcpy :)
0077             for (unsigned int i = 1; i < max_nums; ++i) {
0078                 m_times[i - 1] = m_times[i];
0079                 m_sizes[i - 1] = m_sizes[i];
0080             }
0081             m_nums--;
0082         }
0083         m_times[m_nums] = diff;
0084         m_sizes[m_nums++] = m_filesize - m_offset;
0085 
0086         KIO::filesize_t lspeed = 1000 * (m_sizes[m_nums - 1] - m_sizes[0]) / (m_times[m_nums - 1] - m_times[0]);
0087 
0088         // qDebug() << (long)m_filesize << diff
0089         //          << long(m_sizes[m_nums-1] - m_sizes[0])
0090         //          << m_times[m_nums-1] - m_times[0]
0091         //          << long(lspeed) << double(m_filesize) / diff
0092         //          << convertSize(lspeed)
0093         //          << convertSize(long(double(m_filesize) / diff) * 1000);
0094 
0095         if (!lspeed) {
0096             m_nums = 1;
0097             m_times[0] = diff;
0098             m_sizes[0] = m_filesize - m_offset;
0099         }
0100         Q_EMIT speed(lspeed);
0101     }
0102 }
0103 
0104 bool WorkerInterface::dispatch(int _cmd, const QByteArray &rawdata)
0105 {
0106     // qDebug() << "dispatch " << _cmd;
0107 
0108     QDataStream stream(rawdata);
0109 
0110     QString str1;
0111     qint32 i;
0112     qint8 b;
0113     quint32 ul;
0114 
0115     switch (_cmd) {
0116     case MSG_DATA:
0117         Q_EMIT data(rawdata);
0118         break;
0119     case MSG_DATA_REQ:
0120         Q_EMIT dataReq();
0121         break;
0122     case MSG_OPENED:
0123         Q_EMIT open();
0124         break;
0125     case MSG_FINISHED:
0126         // qDebug() << "Finished [this = " << this << "]";
0127         m_offset = 0;
0128         m_speed_timer.stop();
0129         Q_EMIT finished();
0130         break;
0131     case MSG_STAT_ENTRY: {
0132         UDSEntry entry;
0133         stream >> entry;
0134         Q_EMIT statEntry(entry);
0135         break;
0136     }
0137     case MSG_LIST_ENTRIES: {
0138         UDSEntryList list;
0139         UDSEntry entry;
0140 
0141         while (!stream.atEnd()) {
0142             stream >> entry;
0143             list.append(entry);
0144         }
0145 
0146         Q_EMIT listEntries(list);
0147         break;
0148     }
0149     case MSG_RESUME: { // From the put job
0150         m_offset = readFilesize_t(stream);
0151         Q_EMIT canResume(m_offset);
0152         break;
0153     }
0154     case MSG_CANRESUME: // From the get job
0155         m_filesize = m_offset;
0156         Q_EMIT canResume(0); // the arg doesn't matter
0157         break;
0158     case MSG_ERROR:
0159         stream >> i >> str1;
0160         // qDebug() << "error " << i << " " << str1;
0161         Q_EMIT error(i, str1);
0162         break;
0163     case MSG_WORKER_STATUS: {
0164         qint64 pid;
0165         QByteArray protocol;
0166         stream >> pid >> protocol >> str1 >> b;
0167         Q_EMIT workerStatus(pid, protocol, str1, (b != 0));
0168         break;
0169     }
0170     case MSG_CONNECTED:
0171         Q_EMIT connected();
0172         break;
0173     case MSG_WRITTEN: {
0174         KIO::filesize_t size = readFilesize_t(stream);
0175         Q_EMIT written(size);
0176         break;
0177     }
0178     case INF_TOTAL_SIZE: {
0179         KIO::filesize_t size = readFilesize_t(stream);
0180         m_start_time = QDateTime::currentMSecsSinceEpoch();
0181         m_last_time = 0;
0182         m_filesize = m_offset;
0183         m_sizes[0] = m_filesize - m_offset;
0184         m_times[0] = 0;
0185         m_nums = 1;
0186         m_speed_timer.start(1000);
0187         m_worker_calcs_speed = false;
0188         Q_EMIT totalSize(size);
0189         break;
0190     }
0191     case INF_PROCESSED_SIZE: {
0192         KIO::filesize_t size = readFilesize_t(stream);
0193         Q_EMIT processedSize(size);
0194         m_filesize = size;
0195         break;
0196     }
0197     case INF_POSITION: {
0198         KIO::filesize_t pos = readFilesize_t(stream);
0199         Q_EMIT position(pos);
0200         break;
0201     }
0202     case INF_TRUNCATED: {
0203         KIO::filesize_t length = readFilesize_t(stream);
0204         Q_EMIT truncated(length);
0205         break;
0206     }
0207     case INF_SPEED:
0208         stream >> ul;
0209         m_worker_calcs_speed = true;
0210         m_speed_timer.stop();
0211         Q_EMIT speed(ul);
0212         break;
0213     case INF_ERROR_PAGE:
0214         Q_EMIT errorPage();
0215         break;
0216     case INF_REDIRECTION: {
0217         QUrl url;
0218         stream >> url;
0219         Q_EMIT redirection(url);
0220         break;
0221     }
0222     case INF_MIME_TYPE:
0223         stream >> str1;
0224         Q_EMIT mimeType(str1);
0225         if (!m_connection->suspended()) {
0226             m_connection->sendnow(CMD_NONE, QByteArray());
0227         }
0228         break;
0229     case INF_WARNING:
0230         stream >> str1;
0231         Q_EMIT warning(str1);
0232         break;
0233     case INF_MESSAGEBOX: {
0234         // qDebug() << "needs a msg box";
0235         QString text;
0236         QString title;
0237         QString primaryActionText;
0238         QString secondaryActionText;
0239         QString dontAskAgainName;
0240         int type;
0241         stream >> type >> text >> title >> primaryActionText >> secondaryActionText;
0242         if (stream.atEnd()) {
0243             messageBox(type, text, title, primaryActionText, secondaryActionText);
0244         } else {
0245             stream >> dontAskAgainName;
0246             messageBox(type, text, title, primaryActionText, secondaryActionText, dontAskAgainName);
0247         }
0248         break;
0249     }
0250     case INF_INFOMESSAGE: {
0251         QString msg;
0252         stream >> msg;
0253         Q_EMIT infoMessage(msg);
0254         break;
0255     }
0256     case INF_SSLERROR: {
0257         QVariantMap sslErrorData;
0258         stream >> sslErrorData;
0259         globalUserNotificationHandler->sslError(this, sslErrorData);
0260         break;
0261     }
0262     case INF_META_DATA: {
0263         MetaData m;
0264         stream >> m;
0265         if (auto it = m.constFind(QStringLiteral("privilege_conf_details")); it != m.cend()) {
0266             // see WORKER_MESSAGEBOX_DETAILS_HACK
0267             m_messageBoxDetails = it.value();
0268         }
0269         Q_EMIT metaData(m);
0270         break;
0271     }
0272     case MSG_HOST_INFO_REQ: {
0273         QString hostName;
0274         stream >> hostName;
0275         HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo)));
0276         break;
0277     }
0278     case MSG_PRIVILEGE_EXEC:
0279         Q_EMIT privilegeOperationRequested();
0280         break;
0281     default:
0282         qCWarning(KIO_CORE) << "Worker sends unknown command (" << _cmd << "), dropping worker.";
0283         return false;
0284     }
0285     return true;
0286 }
0287 
0288 void WorkerInterface::setOffset(KIO::filesize_t o)
0289 {
0290     m_offset = o;
0291 }
0292 
0293 KIO::filesize_t WorkerInterface::offset() const
0294 {
0295     return m_offset;
0296 }
0297 
0298 void WorkerInterface::sendResumeAnswer(bool resume)
0299 {
0300     // qDebug() << "ok for resuming:" << resume;
0301     m_connection->sendnow(resume ? CMD_RESUMEANSWER : CMD_NONE, QByteArray());
0302 }
0303 
0304 void WorkerInterface::sendMessageBoxAnswer(int result)
0305 {
0306     if (!m_connection) {
0307         return;
0308     }
0309 
0310     if (m_connection->suspended()) {
0311         m_connection->resume();
0312     }
0313     QByteArray packedArgs;
0314     QDataStream stream(&packedArgs, QIODevice::WriteOnly);
0315     stream << result;
0316     m_connection->sendnow(CMD_MESSAGEBOXANSWER, packedArgs);
0317     // qDebug() << "message box answer" << result;
0318 }
0319 
0320 void WorkerInterface::sendSslErrorAnswer(int result)
0321 {
0322     if (!m_connection) {
0323         return;
0324     }
0325 
0326     if (m_connection->suspended()) {
0327         m_connection->resume();
0328     }
0329     QByteArray packedArgs;
0330     QDataStream stream(&packedArgs, QIODevice::WriteOnly);
0331     stream << result;
0332     m_connection->sendnow(CMD_SSLERRORANSWER, packedArgs);
0333     // qDebug() << "message box answer" << result;
0334 }
0335 
0336 void WorkerInterface::messageBox(int type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText)
0337 {
0338     messageBox(type, text, title, primaryActionText, secondaryActionText, QString());
0339 }
0340 
0341 void WorkerInterface::messageBox(int type,
0342                                  const QString &text,
0343                                  const QString &title,
0344                                  const QString &primaryActionText,
0345                                  const QString &secondaryActionText,
0346                                  const QString &dontAskAgainName)
0347 {
0348     if (m_connection) {
0349         m_connection->suspend();
0350     }
0351 
0352     QHash<UserNotificationHandler::MessageBoxDataType, QVariant> data;
0353     data.insert(UserNotificationHandler::MSG_TEXT, text);
0354     data.insert(UserNotificationHandler::MSG_TITLE, title);
0355     data.insert(UserNotificationHandler::MSG_PRIMARYACTION_TEXT, primaryActionText);
0356     data.insert(UserNotificationHandler::MSG_SECONDARYACTION_TEXT, secondaryActionText);
0357     data.insert(UserNotificationHandler::MSG_DONT_ASK_AGAIN, dontAskAgainName);
0358 
0359     // SMELL: the braindead way to support button icons
0360     // TODO: Fix this in KIO::WorkerBase.
0361     if (primaryActionText == i18n("&Details")) {
0362         data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("help-about"));
0363     } else if (primaryActionText == i18n("&Forever")) {
0364         data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("flag-green"));
0365     }
0366 
0367     if (secondaryActionText == i18n("Co&ntinue")) {
0368         data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("arrow-right"));
0369     } else if (secondaryActionText == i18n("&Current Session only")) {
0370         data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("chronometer"));
0371     }
0372 
0373     if (type == KIO::WorkerBase::WarningContinueCancelDetailed) { // see WORKER_MESSAGEBOX_DETAILS_HACK
0374         data.insert(UserNotificationHandler::MSG_DETAILS, m_messageBoxDetails);
0375     }
0376 
0377     globalUserNotificationHandler()->requestMessageBox(this, type, data);
0378 }
0379 
0380 void WorkerInterface::slotHostInfo(const QHostInfo &info)
0381 {
0382     QByteArray data;
0383     QDataStream stream(&data, QIODevice::WriteOnly);
0384     stream << info.hostName() << info.addresses() << info.error() << info.errorString();
0385     m_connection->send(CMD_HOST_INFO, data);
0386 }
0387 
0388 #include "moc_workerinterface_p.cpp"