File indexing completed on 2024-04-28 15:26:34

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 "slaveinterface.h"
0009 #include "slaveinterface_p.h"
0010 #include "usernotificationhandler_p.h"
0011 
0012 #include "commands_p.h"
0013 #include "connection_p.h"
0014 #include "hostinfo.h"
0015 #include "workerbase.h"
0016 #include <KLocalizedString>
0017 #include <signal.h>
0018 #include <time.h>
0019 
0020 #include <QDataStream>
0021 #include <QDateTime>
0022 #include <QDebug>
0023 
0024 using namespace KIO;
0025 
0026 Q_GLOBAL_STATIC(UserNotificationHandler, globalUserNotificationHandler)
0027 
0028 SlaveInterface::SlaveInterface(SlaveInterfacePrivate &dd, QObject *parent)
0029     : QObject(parent)
0030     , d_ptr(&dd)
0031 {
0032     connect(&d_ptr->speed_timer, &QTimer::timeout, this, &SlaveInterface::calcSpeed);
0033 }
0034 
0035 SlaveInterface::~SlaveInterface()
0036 {
0037     // Note: no Debug() here (scheduler is deleted very late)
0038 
0039     delete d_ptr;
0040 }
0041 
0042 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0043 void SlaveInterface::setConnection(Connection *connection)
0044 {
0045     Q_D(SlaveInterface);
0046     d->connection = connection;
0047 }
0048 #endif
0049 
0050 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0051 Connection *SlaveInterface::connection() const
0052 {
0053     const Q_D(SlaveInterface);
0054     return d->connection;
0055 }
0056 #endif
0057 
0058 static KIO::filesize_t readFilesize_t(QDataStream &stream)
0059 {
0060     KIO::filesize_t result;
0061     stream >> result;
0062     return result;
0063 }
0064 
0065 bool SlaveInterface::dispatch()
0066 {
0067     Q_D(SlaveInterface);
0068     Q_ASSERT(d->connection);
0069 
0070     int cmd;
0071     QByteArray data;
0072 
0073     int ret = d->connection->read(&cmd, data);
0074     if (ret == -1) {
0075         return false;
0076     }
0077 
0078     return dispatch(cmd, data);
0079 }
0080 
0081 void SlaveInterface::calcSpeed()
0082 {
0083     Q_D(SlaveInterface);
0084     if (d->slave_calcs_speed || !d->connection->isConnected()) { // killing a job results in disconnection but the timer never stops
0085         d->speed_timer.stop();
0086         return;
0087     }
0088 
0089     const qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
0090     const qint64 diff = currentTime - d->start_time;
0091     if (diff - d->last_time >= 900) {
0092         d->last_time = diff;
0093         if (d->nums == max_nums) {
0094             // let's hope gcc can optimize that well enough
0095             // otherwise I'd try memcpy :)
0096             for (unsigned int i = 1; i < max_nums; ++i) {
0097                 d->times[i - 1] = d->times[i];
0098                 d->sizes[i - 1] = d->sizes[i];
0099             }
0100             d->nums--;
0101         }
0102         d->times[d->nums] = diff;
0103         d->sizes[d->nums++] = d->filesize - d->offset;
0104 
0105         KIO::filesize_t lspeed = 1000 * (d->sizes[d->nums - 1] - d->sizes[0]) / (d->times[d->nums - 1] - d->times[0]);
0106 
0107         // qDebug() << (long)d->filesize << diff
0108         //          << long(d->sizes[d->nums-1] - d->sizes[0])
0109         //          << d->times[d->nums-1] - d->times[0]
0110         //          << long(lspeed) << double(d->filesize) / diff
0111         //          << convertSize(lspeed)
0112         //          << convertSize(long(double(d->filesize) / diff) * 1000);
0113 
0114         if (!lspeed) {
0115             d->nums = 1;
0116             d->times[0] = diff;
0117             d->sizes[0] = d->filesize - d->offset;
0118         }
0119         Q_EMIT speed(lspeed);
0120     }
0121 }
0122 
0123 bool SlaveInterface::dispatch(int _cmd, const QByteArray &rawdata)
0124 {
0125     Q_D(SlaveInterface);
0126     // qDebug() << "dispatch " << _cmd;
0127 
0128     QDataStream stream(rawdata);
0129 
0130     QString str1;
0131     qint32 i;
0132     qint8 b;
0133     quint32 ul;
0134 
0135     switch (_cmd) {
0136     case MSG_DATA:
0137         Q_EMIT data(rawdata);
0138         break;
0139     case MSG_DATA_REQ:
0140         Q_EMIT dataReq();
0141         break;
0142     case MSG_OPENED:
0143         Q_EMIT open();
0144         break;
0145     case MSG_FINISHED:
0146         // qDebug() << "Finished [this = " << this << "]";
0147         d->offset = 0;
0148         d->speed_timer.stop();
0149         Q_EMIT finished();
0150         break;
0151     case MSG_STAT_ENTRY: {
0152         UDSEntry entry;
0153         stream >> entry;
0154         Q_EMIT statEntry(entry);
0155         break;
0156     }
0157     case MSG_LIST_ENTRIES: {
0158         UDSEntryList list;
0159         UDSEntry entry;
0160 
0161         while (!stream.atEnd()) {
0162             stream >> entry;
0163             list.append(entry);
0164         }
0165 
0166         Q_EMIT listEntries(list);
0167         break;
0168     }
0169     case MSG_RESUME: { // From the put job
0170         d->offset = readFilesize_t(stream);
0171         Q_EMIT canResume(d->offset);
0172         break;
0173     }
0174     case MSG_CANRESUME: // From the get job
0175         d->filesize = d->offset;
0176         Q_EMIT canResume(0); // the arg doesn't matter
0177         break;
0178     case MSG_ERROR:
0179         stream >> i >> str1;
0180         // qDebug() << "error " << i << " " << str1;
0181         Q_EMIT error(i, str1);
0182         break;
0183 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 45)
0184     case MSG_SLAVE_STATUS:
0185 #endif
0186     case MSG_SLAVE_STATUS_V2: {
0187         qint64 pid;
0188         QByteArray protocol;
0189         stream >> pid >> protocol >> str1 >> b;
0190         Q_EMIT slaveStatus(pid, protocol, str1, (b != 0));
0191         break;
0192     }
0193     case MSG_CONNECTED:
0194         Q_EMIT connected();
0195         break;
0196     case MSG_WRITTEN: {
0197         KIO::filesize_t size = readFilesize_t(stream);
0198         Q_EMIT written(size);
0199         break;
0200     }
0201     case INF_TOTAL_SIZE: {
0202         KIO::filesize_t size = readFilesize_t(stream);
0203         d->start_time = QDateTime::currentMSecsSinceEpoch();
0204         d->last_time = 0;
0205         d->filesize = d->offset;
0206         d->sizes[0] = d->filesize - d->offset;
0207         d->times[0] = 0;
0208         d->nums = 1;
0209         d->speed_timer.start(1000);
0210         d->slave_calcs_speed = false;
0211         Q_EMIT totalSize(size);
0212         break;
0213     }
0214     case INF_PROCESSED_SIZE: {
0215         KIO::filesize_t size = readFilesize_t(stream);
0216         Q_EMIT processedSize(size);
0217         d->filesize = size;
0218         break;
0219     }
0220     case INF_POSITION: {
0221         KIO::filesize_t pos = readFilesize_t(stream);
0222         Q_EMIT position(pos);
0223         break;
0224     }
0225     case INF_TRUNCATED: {
0226         KIO::filesize_t length = readFilesize_t(stream);
0227         Q_EMIT truncated(length);
0228         break;
0229     }
0230     case INF_SPEED:
0231         stream >> ul;
0232         d->slave_calcs_speed = true;
0233         d->speed_timer.stop();
0234         Q_EMIT speed(ul);
0235         break;
0236 #if KIOCORE_BUILD_DEPRECATED_SINCE(3, 0)
0237     case INF_GETTING_FILE:
0238         break;
0239 #endif
0240     case INF_ERROR_PAGE:
0241         Q_EMIT errorPage();
0242         break;
0243     case INF_REDIRECTION: {
0244         QUrl url;
0245         stream >> url;
0246         Q_EMIT redirection(url);
0247         break;
0248     }
0249     case INF_MIME_TYPE:
0250         stream >> str1;
0251         Q_EMIT mimeType(str1);
0252         if (!d->connection->suspended()) {
0253             d->connection->sendnow(CMD_NONE, QByteArray());
0254         }
0255         break;
0256     case INF_WARNING:
0257         stream >> str1;
0258         Q_EMIT warning(str1);
0259         break;
0260     case INF_MESSAGEBOX: {
0261         // qDebug() << "needs a msg box";
0262         QString text;
0263         QString title;
0264         QString primaryActionText;
0265         QString secondaryActionText;
0266         QString dontAskAgainName;
0267         int type;
0268         stream >> type >> text >> title >> primaryActionText >> secondaryActionText;
0269         if (stream.atEnd()) {
0270             messageBox(type, text, title, primaryActionText, secondaryActionText);
0271         } else {
0272             stream >> dontAskAgainName;
0273             messageBox(type, text, title, primaryActionText, secondaryActionText, dontAskAgainName);
0274         }
0275         break;
0276     }
0277     case INF_INFOMESSAGE: {
0278         QString msg;
0279         stream >> msg;
0280         Q_EMIT infoMessage(msg);
0281         break;
0282     }
0283     case INF_META_DATA: {
0284         MetaData m;
0285         stream >> m;
0286         if (m.contains(QLatin1String("ssl_in_use"))) {
0287             const QLatin1String ssl_("ssl_");
0288             const MetaData &constM = m;
0289             for (MetaData::ConstIterator it = constM.lowerBound(ssl_); it != constM.constEnd(); ++it) {
0290                 if (it.key().startsWith(ssl_)) {
0291                     d->sslMetaData.insert(it.key(), it.value());
0292                 } else {
0293                     // we're past the ssl_* entries; remember that QMap is ordered.
0294                     break;
0295                 }
0296             }
0297         } else if (auto it = m.constFind(QStringLiteral("privilege_conf_details")); it != m.cend()) {
0298             // see WORKER_MESSAGEBOX_DETAILS_HACK
0299             d->messageBoxDetails = it.value();
0300         }
0301         Q_EMIT metaData(m);
0302         break;
0303     }
0304     case MSG_NET_REQUEST: {
0305         QString host;
0306         QString slaveid;
0307         stream >> host >> slaveid;
0308         requestNetwork(host, slaveid);
0309         break;
0310     }
0311     case MSG_NET_DROP: {
0312         QString host;
0313         QString slaveid;
0314         stream >> host >> slaveid;
0315         dropNetwork(host, slaveid);
0316         break;
0317     }
0318     case MSG_NEED_SUBURL_DATA: {
0319         Q_EMIT needSubUrlData();
0320         break;
0321     }
0322     case MSG_HOST_INFO_REQ: {
0323         QString hostName;
0324         stream >> hostName;
0325         HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo)));
0326         break;
0327     }
0328     case MSG_PRIVILEGE_EXEC:
0329         Q_EMIT privilegeOperationRequested();
0330         break;
0331     default:
0332         qCWarning(KIO_CORE) << "Worker sends unknown command (" << _cmd << "), dropping worker.";
0333         return false;
0334     }
0335     return true;
0336 }
0337 
0338 void SlaveInterface::setOffset(KIO::filesize_t o)
0339 {
0340     Q_D(SlaveInterface);
0341     d->offset = o;
0342 }
0343 
0344 KIO::filesize_t SlaveInterface::offset() const
0345 {
0346     const Q_D(SlaveInterface);
0347     return d->offset;
0348 }
0349 
0350 void SlaveInterface::requestNetwork(const QString &host, const QString &slaveid)
0351 {
0352     Q_D(SlaveInterface);
0353     Q_UNUSED(host);
0354     Q_UNUSED(slaveid);
0355     // qDebug() << "requestNetwork " << host << slaveid;
0356 
0357     // This is old stuff. We just always return true...
0358 
0359     QByteArray packedArgs;
0360     QDataStream stream(&packedArgs, QIODevice::WriteOnly);
0361     stream << true;
0362     d->connection->sendnow(INF_NETWORK_STATUS, packedArgs);
0363 }
0364 
0365 void SlaveInterface::dropNetwork(const QString &host, const QString &slaveid)
0366 {
0367     Q_UNUSED(host);
0368     Q_UNUSED(slaveid);
0369     // qDebug() << "dropNetwork " << host << slaveid;
0370 }
0371 
0372 void SlaveInterface::sendResumeAnswer(bool resume)
0373 {
0374     Q_D(SlaveInterface);
0375     // qDebug() << "ok for resuming:" << resume;
0376     d->connection->sendnow(resume ? CMD_RESUMEANSWER : CMD_NONE, QByteArray());
0377 }
0378 
0379 void SlaveInterface::sendMessageBoxAnswer(int result)
0380 {
0381     Q_D(SlaveInterface);
0382     if (!d->connection) {
0383         return;
0384     }
0385 
0386     if (d->connection->suspended()) {
0387         d->connection->resume();
0388     }
0389     QByteArray packedArgs;
0390     QDataStream stream(&packedArgs, QIODevice::WriteOnly);
0391     stream << result;
0392     d->connection->sendnow(CMD_MESSAGEBOXANSWER, packedArgs);
0393     // qDebug() << "message box answer" << result;
0394 }
0395 
0396 void SlaveInterface::messageBox(int type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText)
0397 {
0398     messageBox(type, text, title, primaryActionText, secondaryActionText, QString());
0399 }
0400 
0401 void SlaveInterface::messageBox(int type,
0402                                 const QString &text,
0403                                 const QString &title,
0404                                 const QString &primaryActionText,
0405                                 const QString &secondaryActionText,
0406                                 const QString &dontAskAgainName)
0407 {
0408     Q_D(SlaveInterface);
0409     if (d->connection) {
0410         d->connection->suspend();
0411     }
0412 
0413     QHash<UserNotificationHandler::MessageBoxDataType, QVariant> data;
0414     data.insert(UserNotificationHandler::MSG_TEXT, text);
0415     data.insert(UserNotificationHandler::MSG_TITLE, title);
0416     data.insert(UserNotificationHandler::MSG_PRIMARYACTION_TEXT, primaryActionText);
0417     data.insert(UserNotificationHandler::MSG_SECONDARYACTION_TEXT, secondaryActionText);
0418     data.insert(UserNotificationHandler::MSG_DONT_ASK_AGAIN, dontAskAgainName);
0419 
0420     // SMELL: the braindead way to support button icons
0421     // TODO: Fix this in KIO::WorkerBase.
0422     if (primaryActionText == i18n("&Details")) {
0423         data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("help-about"));
0424     } else if (primaryActionText == i18n("&Forever")) {
0425         data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("flag-green"));
0426     } else if (primaryActionText == i18n("C&ontinue Loading")) {
0427         data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("arrow-right"));
0428     }
0429 
0430     if (secondaryActionText == i18n("Co&ntinue")) {
0431         data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("arrow-right"));
0432     } else if (secondaryActionText == i18n("&Current Session only")) {
0433         data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("chronometer"));
0434     }
0435 
0436     if (type == KIO::WorkerBase::SSLMessageBox) {
0437         data.insert(UserNotificationHandler::MSG_META_DATA, d->sslMetaData.toVariant());
0438     } else if (type == KIO::WorkerBase::WarningContinueCancelDetailed) { // see WORKER_MESSAGEBOX_DETAILS_HACK
0439         data.insert(UserNotificationHandler::MSG_DETAILS, d->messageBoxDetails);
0440     }
0441 
0442     globalUserNotificationHandler()->requestMessageBox(this, type, data);
0443 }
0444 
0445 void SlaveInterfacePrivate::slotHostInfo(const QHostInfo &info)
0446 {
0447     QByteArray data;
0448     QDataStream stream(&data, QIODevice::WriteOnly);
0449     stream << info.hostName() << info.addresses() << info.error() << info.errorString();
0450     connection->send(CMD_HOST_INFO, data);
0451 }
0452 
0453 #include "moc_slaveinterface.cpp"