File indexing completed on 2024-04-14 03:53:02

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
0004     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
0005     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
0006     SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-only
0009 */
0010 
0011 #include "slavebase.h"
0012 
0013 #include <config-kiocore.h>
0014 
0015 #include <qplatformdefs.h>
0016 #include <signal.h>
0017 #include <stdlib.h>
0018 #ifdef Q_OS_WIN
0019 #include <process.h>
0020 #endif
0021 
0022 #include <QCoreApplication>
0023 #include <QDataStream>
0024 #include <QDateTime>
0025 #include <QElapsedTimer>
0026 #include <QFile>
0027 #include <QList>
0028 #include <QMap>
0029 #include <QSsl>
0030 #include <QtGlobal>
0031 
0032 #include <KConfig>
0033 #include <KConfigGroup>
0034 #include <KLocalizedString>
0035 #include <QThread>
0036 
0037 #ifndef Q_OS_ANDROID
0038 #include <KCrash>
0039 #endif
0040 
0041 #include "authinfo.h"
0042 #include "kremoteencoding.h"
0043 
0044 #include "commands_p.h"
0045 #include "connection_p.h"
0046 #include "ioworker_defaults.h"
0047 #include "kiocoredebug.h"
0048 #include "kioglobal_p.h"
0049 #include "kpasswdserverclient.h"
0050 #include "workerinterface_p.h"
0051 
0052 #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
0053 #include <KAuth/Action>
0054 #endif
0055 
0056 // TODO: Enable once file KIO worker is ported away and add endif, similar in the header file
0057 // #if KIOCORE_BUILD_DEPRECATED_SINCE(version where file:/ KIO worker was ported)
0058 
0059 #if KIO_ASSERT_WORKER_STATES
0060 #define KIO_STATE_ASSERT(cond, where, what) Q_ASSERT_X(cond, where, what)
0061 #else
0062 /* clang-format off */
0063 #define KIO_STATE_ASSERT(cond, where, what) \
0064     do { \
0065         if (!(cond)) { \
0066             qCWarning(KIO_CORE) << what; \
0067         } \
0068     } while (false)
0069 #endif
0070 /* clang-format on */
0071 
0072 extern "C" {
0073 static void sigpipe_handler(int sig);
0074 }
0075 
0076 using namespace KIO;
0077 
0078 typedef QList<QByteArray> AuthKeysList;
0079 typedef QMap<QString, QByteArray> AuthKeysMap;
0080 
0081 /* clang-format off */
0082 #define KIO_DATA \
0083     QByteArray data; \
0084     QDataStream stream(&data, QIODevice::WriteOnly); \
0085     stream
0086 /* clang-format on */
0087 
0088 static constexpr int KIO_MAX_ENTRIES_PER_BATCH = 200;
0089 static constexpr int KIO_MAX_SEND_BATCH_TIME = 300;
0090 
0091 namespace KIO
0092 {
0093 class SlaveBasePrivate
0094 {
0095 public:
0096     SlaveBase *const q;
0097     explicit SlaveBasePrivate(SlaveBase *owner)
0098         : q(owner)
0099         , nextTimeoutMsecs(0)
0100         , m_confirmationAsked(false)
0101         , m_privilegeOperationStatus(OperationNotAllowed)
0102     {
0103         if (!qEnvironmentVariableIsEmpty("KIOWORKER_ENABLE_TESTMODE")) {
0104             QStandardPaths::setTestModeEnabled(true);
0105         } else if (!qEnvironmentVariableIsEmpty("KIOSLAVE_ENABLE_TESTMODE")) {
0106             QStandardPaths::setTestModeEnabled(true);
0107             qCWarning(KIO_CORE)
0108                 << "KIOSLAVE_ENABLE_TESTMODE is deprecated for KF6, and will be unsupported soon. Please use KIOWORKER_ENABLE_TESTMODE with KF6.";
0109         }
0110         pendingListEntries.reserve(KIO_MAX_ENTRIES_PER_BATCH);
0111         appConnection.setReadMode(Connection::ReadMode::Polled);
0112     }
0113     ~SlaveBasePrivate() = default;
0114 
0115     UDSEntryList pendingListEntries;
0116     QElapsedTimer m_timeSinceLastBatch;
0117     Connection appConnection;
0118     QString poolSocket;
0119     bool isConnectedToApp;
0120 
0121     QString slaveid;
0122     bool resume : 1;
0123     bool needSendCanResume : 1;
0124     bool onHold : 1;
0125     bool inOpenLoop : 1;
0126     std::atomic<bool> wasKilled = false;
0127     std::atomic<bool> exit_loop = false;
0128     std::atomic<bool> runInThread = false;
0129     MetaData configData;
0130     KConfig *config = nullptr;
0131     KConfigGroup *configGroup = nullptr;
0132     QMap<QString, QVariant> mapConfig;
0133     QUrl onHoldUrl;
0134 
0135     QElapsedTimer lastTimeout;
0136     QElapsedTimer nextTimeout;
0137     qint64 nextTimeoutMsecs;
0138     KIO::filesize_t totalSize;
0139     KRemoteEncoding *remotefile = nullptr;
0140     enum { Idle, InsideMethod, InsideTimeoutSpecial, FinishedCalled, ErrorCalled } m_state;
0141     bool m_finalityCommand = true; // whether finished() or error() may/must be called
0142     QByteArray timeoutData;
0143 
0144 #ifndef KIO_ANDROID_STUB
0145     std::unique_ptr<KPasswdServerClient> m_passwdServerClient;
0146 #endif
0147     bool m_rootEntryListed = false;
0148 
0149     bool m_confirmationAsked;
0150     QSet<QString> m_tempAuths;
0151     QString m_warningTitle;
0152     QString m_warningMessage;
0153     int m_privilegeOperationStatus;
0154 
0155     void updateTempAuthStatus()
0156     {
0157 #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
0158         QSet<QString>::iterator it = m_tempAuths.begin();
0159         while (it != m_tempAuths.end()) {
0160             KAuth::Action action(*it);
0161             if (action.status() != KAuth::Action::AuthorizedStatus) {
0162                 it = m_tempAuths.erase(it);
0163             } else {
0164                 ++it;
0165             }
0166         }
0167 #endif
0168     }
0169 
0170     bool hasTempAuth() const
0171     {
0172         return !m_tempAuths.isEmpty();
0173     }
0174 
0175     // Reconstructs configGroup from configData and mIncomingMetaData
0176     void rebuildConfig()
0177     {
0178         mapConfig.clear();
0179 
0180         // mIncomingMetaData cascades over config, so we write config first,
0181         // to let it be overwritten
0182         MetaData::ConstIterator end = configData.constEnd();
0183         for (MetaData::ConstIterator it = configData.constBegin(); it != end; ++it) {
0184             mapConfig.insert(it.key(), it->toUtf8());
0185         }
0186 
0187         end = q->mIncomingMetaData.constEnd();
0188         for (MetaData::ConstIterator it = q->mIncomingMetaData.constBegin(); it != end; ++it) {
0189             mapConfig.insert(it.key(), it->toUtf8());
0190         }
0191 
0192         delete configGroup;
0193         configGroup = nullptr;
0194         delete config;
0195         config = nullptr;
0196     }
0197 
0198     bool finalState() const
0199     {
0200         return ((m_state == FinishedCalled) || (m_state == ErrorCalled));
0201     }
0202 
0203     void verifyState(const char *cmdName)
0204     {
0205         Q_UNUSED(cmdName)
0206         KIO_STATE_ASSERT(finalState(),
0207                          Q_FUNC_INFO,
0208                          qUtf8Printable(QStringLiteral("%1 did not call finished() or error()! Please fix the %2 KIO worker.")
0209                                             .arg(QLatin1String(cmdName))
0210                                             .arg(QCoreApplication::applicationName())));
0211         // Force the command into finished state. We'll not reach this for Debug builds
0212         // that fail the assertion. For Release builds we'll have made sure that the
0213         // command is actually finished after the verification regardless of what
0214         // the slave did.
0215         if (!finalState()) {
0216             q->finished();
0217         }
0218     }
0219 
0220     void verifyErrorFinishedNotCalled(const char *cmdName)
0221     {
0222         Q_UNUSED(cmdName)
0223         KIO_STATE_ASSERT(!finalState(),
0224                          Q_FUNC_INFO,
0225                          qUtf8Printable(QStringLiteral("%1 called finished() or error(), but it's not supposed to! Please fix the %2 KIO worker.")
0226                                             .arg(QLatin1String(cmdName))
0227                                             .arg(QCoreApplication::applicationName())));
0228     }
0229 
0230 #ifndef KIO_ANDROID_STUB
0231     KPasswdServerClient *passwdServerClient()
0232     {
0233         if (!m_passwdServerClient) {
0234             m_passwdServerClient = std::make_unique<KPasswdServerClient>();
0235         }
0236 
0237         return m_passwdServerClient.get();
0238     }
0239 #endif
0240 };
0241 
0242 }
0243 
0244 static volatile bool slaveWriteError = false;
0245 
0246 #ifdef Q_OS_UNIX
0247 static SlaveBase *globalSlave;
0248 
0249 extern "C" {
0250 static void genericsig_handler(int sigNumber)
0251 {
0252     ::signal(sigNumber, SIG_IGN);
0253     // WABA: Don't do anything that requires malloc, we can deadlock on it since
0254     // a SIGTERM signal can come in while we are in malloc/free.
0255     // qDebug()<<"kioslave : exiting due to signal "<<sigNumber;
0256     // set the flag which will be checked in dispatchLoop() and which *should* be checked
0257     // in lengthy operations in the various slaves
0258     if (globalSlave != nullptr) {
0259         globalSlave->setKillFlag();
0260     }
0261     ::signal(SIGALRM, SIG_DFL);
0262     alarm(5); // generate an alarm signal in 5 seconds, in this time the slave has to exit
0263 }
0264 }
0265 #endif
0266 
0267 //////////////
0268 
0269 SlaveBase::SlaveBase(const QByteArray &protocol, const QByteArray &pool_socket, const QByteArray &app_socket)
0270     : mProtocol(protocol)
0271     , d(new SlaveBasePrivate(this))
0272 
0273 {
0274     Q_ASSERT(!app_socket.isEmpty());
0275     d->poolSocket = QFile::decodeName(pool_socket);
0276 
0277     if (QThread::currentThread() == qApp->thread()) {
0278 #ifndef Q_OS_ANDROID
0279         KCrash::initialize();
0280 #endif
0281 
0282 #ifdef Q_OS_UNIX
0283         struct sigaction act;
0284         act.sa_handler = sigpipe_handler;
0285         sigemptyset(&act.sa_mask);
0286         act.sa_flags = 0;
0287         sigaction(SIGPIPE, &act, nullptr);
0288 
0289         ::signal(SIGINT, &genericsig_handler);
0290         ::signal(SIGQUIT, &genericsig_handler);
0291         ::signal(SIGTERM, &genericsig_handler);
0292 
0293         globalSlave = this;
0294 #endif
0295     }
0296 
0297     d->isConnectedToApp = true;
0298 
0299     // by kahl for netmgr (need a way to identify slaves)
0300     d->slaveid = QString::fromUtf8(protocol) + QString::number(getpid());
0301     d->resume = false;
0302     d->needSendCanResume = false;
0303     d->mapConfig = QMap<QString, QVariant>();
0304     d->onHold = false;
0305     //    d->processed_size = 0;
0306     d->totalSize = 0;
0307     connectSlave(QFile::decodeName(app_socket));
0308 
0309     d->remotefile = nullptr;
0310     d->inOpenLoop = false;
0311 }
0312 
0313 SlaveBase::~SlaveBase()
0314 {
0315     delete d->configGroup;
0316     delete d->config;
0317     delete d->remotefile;
0318 }
0319 
0320 void SlaveBase::dispatchLoop()
0321 {
0322     while (!d->exit_loop) {
0323         if (d->nextTimeout.isValid() && (d->nextTimeout.hasExpired(d->nextTimeoutMsecs))) {
0324             QByteArray data = d->timeoutData;
0325             d->nextTimeout.invalidate();
0326             d->timeoutData = QByteArray();
0327             d->m_state = d->InsideTimeoutSpecial;
0328             special(data);
0329             d->m_state = d->Idle;
0330         }
0331 
0332         Q_ASSERT(d->appConnection.inited());
0333 
0334         int ms = -1;
0335         if (d->nextTimeout.isValid()) {
0336             ms = qMax<int>(d->nextTimeoutMsecs - d->nextTimeout.elapsed(), 1);
0337         }
0338 
0339         int ret = -1;
0340         if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(ms)) {
0341             // dispatch application messages
0342             int cmd;
0343             QByteArray data;
0344             ret = d->appConnection.read(&cmd, data);
0345 
0346             if (ret != -1) {
0347                 if (d->inOpenLoop) {
0348                     dispatchOpenCommand(cmd, data);
0349                 } else {
0350                     dispatch(cmd, data);
0351                 }
0352             }
0353         } else {
0354             ret = d->appConnection.isConnected() ? 0 : -1;
0355         }
0356 
0357         if (ret == -1) { // some error occurred, perhaps no more application
0358             // When the app exits, should the slave be put back in the pool ?
0359             if (!d->exit_loop && d->isConnectedToApp && !d->poolSocket.isEmpty()) {
0360                 disconnectSlave();
0361                 d->isConnectedToApp = false;
0362                 closeConnection();
0363                 d->updateTempAuthStatus();
0364                 connectSlave(d->poolSocket);
0365             } else {
0366                 break;
0367             }
0368         }
0369 
0370         // I think we get here when we were killed in dispatch() and not in select()
0371         if (wasKilled()) {
0372             // qDebug() << "worker was killed, returning";
0373             break;
0374         }
0375 
0376         // execute deferred deletes
0377         QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
0378     }
0379 
0380     // execute deferred deletes
0381     QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
0382 }
0383 
0384 void SlaveBase::connectSlave(const QString &address)
0385 {
0386     d->appConnection.connectToRemote(QUrl(address));
0387 
0388     if (!d->appConnection.inited()) {
0389         /*qDebug() << "failed to connect to" << address << endl
0390                       << "Reason:" << d->appConnection.errorString();*/
0391         exit();
0392     }
0393 
0394     d->inOpenLoop = false;
0395 }
0396 
0397 void SlaveBase::disconnectSlave()
0398 {
0399     d->appConnection.close();
0400 }
0401 
0402 void SlaveBase::setMetaData(const QString &key, const QString &value)
0403 {
0404     mOutgoingMetaData.insert(key, value); // replaces existing key if already there
0405 }
0406 
0407 QString SlaveBase::metaData(const QString &key) const
0408 {
0409     auto it = mIncomingMetaData.find(key);
0410     if (it != mIncomingMetaData.end()) {
0411         return *it;
0412     }
0413     return d->configData.value(key);
0414 }
0415 
0416 MetaData SlaveBase::allMetaData() const
0417 {
0418     return mIncomingMetaData;
0419 }
0420 
0421 bool SlaveBase::hasMetaData(const QString &key) const
0422 {
0423     if (mIncomingMetaData.contains(key)) {
0424         return true;
0425     }
0426     if (d->configData.contains(key)) {
0427         return true;
0428     }
0429     return false;
0430 }
0431 
0432 QMap<QString, QVariant> SlaveBase::mapConfig() const
0433 {
0434     return d->mapConfig;
0435 }
0436 
0437 bool SlaveBase::configValue(const QString &key, bool defaultValue) const
0438 {
0439     return d->mapConfig.value(key, defaultValue).toBool();
0440 }
0441 
0442 int SlaveBase::configValue(const QString &key, int defaultValue) const
0443 {
0444     return d->mapConfig.value(key, defaultValue).toInt();
0445 }
0446 
0447 QString SlaveBase::configValue(const QString &key, const QString &defaultValue) const
0448 {
0449     return d->mapConfig.value(key, defaultValue).toString();
0450 }
0451 
0452 KConfigGroup *SlaveBase::config()
0453 {
0454     if (!d->config) {
0455         d->config = new KConfig(QString(), KConfig::SimpleConfig);
0456 
0457         d->configGroup = new KConfigGroup(d->config, QString());
0458 
0459         auto end = d->mapConfig.cend();
0460         for (auto it = d->mapConfig.cbegin(); it != end; ++it) {
0461             d->configGroup->writeEntry(it.key(), it->toString().toUtf8(), KConfigGroup::WriteConfigFlags());
0462         }
0463     }
0464 
0465     return d->configGroup;
0466 }
0467 
0468 void SlaveBase::sendMetaData()
0469 {
0470     sendAndKeepMetaData();
0471     mOutgoingMetaData.clear();
0472 }
0473 
0474 void SlaveBase::sendAndKeepMetaData()
0475 {
0476     if (!mOutgoingMetaData.isEmpty()) {
0477         KIO_DATA << mOutgoingMetaData;
0478 
0479         send(INF_META_DATA, data);
0480     }
0481 }
0482 
0483 KRemoteEncoding *SlaveBase::remoteEncoding()
0484 {
0485     if (d->remotefile) {
0486         return d->remotefile;
0487     }
0488 
0489     const QByteArray charset(metaData(QStringLiteral("Charset")).toLatin1());
0490     return (d->remotefile = new KRemoteEncoding(charset.constData()));
0491 }
0492 
0493 void SlaveBase::data(const QByteArray &data)
0494 {
0495     sendMetaData();
0496     send(MSG_DATA, data);
0497 }
0498 
0499 void SlaveBase::dataReq()
0500 {
0501     // sendMetaData();
0502     if (d->needSendCanResume) {
0503         canResume(0);
0504     }
0505     send(MSG_DATA_REQ);
0506 }
0507 
0508 void SlaveBase::opened()
0509 {
0510     sendMetaData();
0511     send(MSG_OPENED);
0512     d->inOpenLoop = true;
0513 }
0514 
0515 void SlaveBase::error(int _errid, const QString &_text)
0516 {
0517     if (d->m_state == d->InsideTimeoutSpecial) {
0518         qWarning(KIO_CORE) << "TimeoutSpecialCommand failed with" << _errid << _text;
0519         return;
0520     }
0521 
0522     KIO_STATE_ASSERT(
0523         d->m_finalityCommand,
0524         Q_FUNC_INFO,
0525         qUtf8Printable(QStringLiteral("error() was called, but it's not supposed to! Please fix the %1 KIO worker.").arg(QCoreApplication::applicationName())));
0526 
0527     if (d->m_state == d->ErrorCalled) {
0528         KIO_STATE_ASSERT(false,
0529                          Q_FUNC_INFO,
0530                          qUtf8Printable(QStringLiteral("error() called twice! Please fix the %1 KIO worker.").arg(QCoreApplication::applicationName())));
0531         return;
0532     } else if (d->m_state == d->FinishedCalled) {
0533         KIO_STATE_ASSERT(
0534             false,
0535             Q_FUNC_INFO,
0536             qUtf8Printable(QStringLiteral("error() called after finished()! Please fix the %1 KIO worker.").arg(QCoreApplication::applicationName())));
0537         return;
0538     }
0539 
0540     d->m_state = d->ErrorCalled;
0541     mIncomingMetaData.clear(); // Clear meta data
0542     d->rebuildConfig();
0543     mOutgoingMetaData.clear();
0544     KIO_DATA << static_cast<qint32>(_errid) << _text;
0545 
0546     send(MSG_ERROR, data);
0547     // reset
0548     d->totalSize = 0;
0549     d->inOpenLoop = false;
0550     d->m_confirmationAsked = false;
0551     d->m_privilegeOperationStatus = OperationNotAllowed;
0552 }
0553 
0554 void SlaveBase::connected()
0555 {
0556     send(MSG_CONNECTED);
0557 }
0558 
0559 void SlaveBase::finished()
0560 {
0561     if (d->m_state == d->InsideTimeoutSpecial) {
0562         return;
0563     }
0564 
0565     if (!d->pendingListEntries.isEmpty()) {
0566         if (!d->m_rootEntryListed) {
0567             qCWarning(KIO_CORE) << "UDSEntry for '.' not found, creating a default one. Please fix the" << QCoreApplication::applicationName() << "KIO worker.";
0568             KIO::UDSEntry entry;
0569             entry.reserve(4);
0570             entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
0571             entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0572             entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
0573             entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH);
0574             d->pendingListEntries.append(entry);
0575         }
0576 
0577         listEntries(d->pendingListEntries);
0578         d->pendingListEntries.clear();
0579     }
0580 
0581     KIO_STATE_ASSERT(
0582         d->m_finalityCommand,
0583         Q_FUNC_INFO,
0584         qUtf8Printable(
0585             QStringLiteral("finished() was called, but it's not supposed to! Please fix the %2 KIO worker.").arg(QCoreApplication::applicationName())));
0586 
0587     if (d->m_state == d->FinishedCalled) {
0588         KIO_STATE_ASSERT(false,
0589                          Q_FUNC_INFO,
0590                          qUtf8Printable(QStringLiteral("finished() called twice! Please fix the %1 KIO worker.").arg(QCoreApplication::applicationName())));
0591         return;
0592     } else if (d->m_state == d->ErrorCalled) {
0593         KIO_STATE_ASSERT(
0594             false,
0595             Q_FUNC_INFO,
0596             qUtf8Printable(QStringLiteral("finished() called after error()! Please fix the %1 KIO worker.").arg(QCoreApplication::applicationName())));
0597         return;
0598     }
0599 
0600     d->m_state = d->FinishedCalled;
0601     mIncomingMetaData.clear(); // Clear meta data
0602     d->rebuildConfig();
0603     sendMetaData();
0604     send(MSG_FINISHED);
0605 
0606     // reset
0607     d->totalSize = 0;
0608     d->inOpenLoop = false;
0609     d->m_rootEntryListed = false;
0610     d->m_confirmationAsked = false;
0611     d->m_privilegeOperationStatus = OperationNotAllowed;
0612 }
0613 
0614 void SlaveBase::slaveStatus(const QString &host, bool connected)
0615 {
0616     qint64 pid = getpid();
0617     qint8 b = connected ? 1 : 0;
0618     KIO_DATA << pid << mProtocol << host << b << d->onHold << d->onHoldUrl << d->hasTempAuth();
0619     send(MSG_WORKER_STATUS, data);
0620 }
0621 
0622 void SlaveBase::canResume()
0623 {
0624     send(MSG_CANRESUME);
0625 }
0626 
0627 void SlaveBase::totalSize(KIO::filesize_t _bytes)
0628 {
0629     KIO_DATA << static_cast<quint64>(_bytes);
0630     send(INF_TOTAL_SIZE, data);
0631 
0632     // this one is usually called before the first item is listed in listDir()
0633     d->totalSize = _bytes;
0634 }
0635 
0636 void SlaveBase::processedSize(KIO::filesize_t _bytes)
0637 {
0638     bool emitSignal = false;
0639 
0640     if (_bytes == d->totalSize) {
0641         emitSignal = true;
0642     } else {
0643         if (d->lastTimeout.isValid()) {
0644             emitSignal = d->lastTimeout.hasExpired(100); // emit size 10 times a second
0645         } else {
0646             emitSignal = true;
0647         }
0648     }
0649 
0650     if (emitSignal) {
0651         KIO_DATA << static_cast<quint64>(_bytes);
0652         send(INF_PROCESSED_SIZE, data);
0653         d->lastTimeout.start();
0654     }
0655 
0656     //    d->processed_size = _bytes;
0657 }
0658 
0659 void SlaveBase::written(KIO::filesize_t _bytes)
0660 {
0661     KIO_DATA << static_cast<quint64>(_bytes);
0662     send(MSG_WRITTEN, data);
0663 }
0664 
0665 void SlaveBase::position(KIO::filesize_t _pos)
0666 {
0667     KIO_DATA << static_cast<quint64>(_pos);
0668     send(INF_POSITION, data);
0669 }
0670 
0671 void SlaveBase::truncated(KIO::filesize_t _length)
0672 {
0673     KIO_DATA << static_cast<quint64>(_length);
0674     send(INF_TRUNCATED, data);
0675 }
0676 
0677 void SlaveBase::processedPercent(float /* percent */)
0678 {
0679     // qDebug() << "STUB";
0680 }
0681 
0682 void SlaveBase::speed(unsigned long _bytes_per_second)
0683 {
0684     KIO_DATA << static_cast<quint32>(_bytes_per_second);
0685     send(INF_SPEED, data);
0686 }
0687 
0688 void SlaveBase::redirection(const QUrl &_url)
0689 {
0690     KIO_DATA << _url;
0691     send(INF_REDIRECTION, data);
0692 }
0693 
0694 void SlaveBase::errorPage()
0695 {
0696     send(INF_ERROR_PAGE);
0697 }
0698 
0699 static bool isSubCommand(int cmd)
0700 {
0701     /* clang-format off */
0702     return cmd == CMD_REPARSECONFIGURATION
0703         || cmd == CMD_META_DATA
0704         || cmd == CMD_CONFIG
0705         || cmd == CMD_WORKER_STATUS;
0706     /* clang-format on */
0707 }
0708 
0709 void SlaveBase::mimeType(const QString &_type)
0710 {
0711     qCDebug(KIO_CORE) << "detected mimetype" << _type;
0712     int cmd = CMD_NONE;
0713     do {
0714         if (wasKilled()) {
0715             break;
0716         }
0717 
0718         // Send the meta-data each time we send the MIME type.
0719         if (!mOutgoingMetaData.isEmpty()) {
0720             qCDebug(KIO_CORE) << "sending mimetype meta data";
0721             KIO_DATA << mOutgoingMetaData;
0722             send(INF_META_DATA, data);
0723         }
0724         KIO_DATA << _type;
0725         send(INF_MIME_TYPE, data);
0726         while (true) {
0727             cmd = 0;
0728             int ret = -1;
0729             if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
0730                 ret = d->appConnection.read(&cmd, data);
0731             }
0732             if (ret == -1) {
0733                 qCDebug(KIO_CORE) << "read error on app connection while sending mimetype";
0734                 exit();
0735                 break;
0736             }
0737             qCDebug(KIO_CORE) << "got reply after sending mimetype" << cmd;
0738             if (cmd == CMD_HOST) { // Ignore.
0739                 continue;
0740             }
0741             if (!isSubCommand(cmd)) {
0742                 break;
0743             }
0744 
0745             dispatch(cmd, data);
0746         }
0747     } while (cmd != CMD_NONE);
0748     mOutgoingMetaData.clear();
0749 }
0750 
0751 void SlaveBase::exit() // possibly called from another thread, only use atomics in here
0752 {
0753     d->exit_loop = true;
0754     if (d->runInThread) {
0755         d->wasKilled = true;
0756     } else {
0757         // Using ::exit() here is too much (crashes in qdbus's qglobalstatic object),
0758         // so let's cleanly exit dispatchLoop() instead.
0759         // Update: we do need to call exit(), otherwise a long download (get()) would
0760         // keep going until it ends, even though the application exited.
0761         ::exit(255);
0762     }
0763 }
0764 
0765 void SlaveBase::warning(const QString &_msg)
0766 {
0767     KIO_DATA << _msg;
0768     send(INF_WARNING, data);
0769 }
0770 
0771 void SlaveBase::infoMessage(const QString &_msg)
0772 {
0773     KIO_DATA << _msg;
0774     send(INF_INFOMESSAGE, data);
0775 }
0776 
0777 void SlaveBase::statEntry(const UDSEntry &entry)
0778 {
0779     KIO_DATA << entry;
0780     send(MSG_STAT_ENTRY, data);
0781 }
0782 
0783 void SlaveBase::listEntry(const UDSEntry &entry)
0784 {
0785     // #366795: many slaves don't create an entry for ".", so we keep track if they do
0786     // and we provide a fallback in finished() otherwise.
0787     if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1Char('.')) {
0788         d->m_rootEntryListed = true;
0789     }
0790 
0791     // We start measuring the time from the point we start filling the list
0792     if (d->pendingListEntries.isEmpty()) {
0793         d->m_timeSinceLastBatch.restart();
0794     }
0795 
0796     d->pendingListEntries.append(entry);
0797 
0798     // If more then KIO_MAX_SEND_BATCH_TIME time is passed, emit the current batch
0799     // Also emit if we have piled up a large number of entries already, to save memory (and time)
0800     if (d->m_timeSinceLastBatch.elapsed() > KIO_MAX_SEND_BATCH_TIME || d->pendingListEntries.size() > KIO_MAX_ENTRIES_PER_BATCH) {
0801         listEntries(d->pendingListEntries);
0802         d->pendingListEntries.clear();
0803 
0804         // Restart time
0805         d->m_timeSinceLastBatch.restart();
0806     }
0807 }
0808 
0809 void SlaveBase::listEntries(const UDSEntryList &list)
0810 {
0811     QByteArray data;
0812     QDataStream stream(&data, QIODevice::WriteOnly);
0813 
0814     for (const UDSEntry &entry : list) {
0815         stream << entry;
0816     }
0817 
0818     send(MSG_LIST_ENTRIES, data);
0819 }
0820 
0821 static void sigpipe_handler(int)
0822 {
0823     // We ignore a SIGPIPE in slaves.
0824     // A SIGPIPE can happen in two cases:
0825     // 1) Communication error with application.
0826     // 2) Communication error with network.
0827     slaveWriteError = true;
0828 
0829     // Don't add anything else here, especially no debug output
0830 }
0831 
0832 void SlaveBase::setHost(QString const &, quint16, QString const &, QString const &)
0833 {
0834 }
0835 
0836 // TODO: move unsupportedActionErrorString() to workerbase.cpp
0837 // once SlaveBase is dissolved and folded into WorkerBase
0838 // forward declaration is already in workerbase.h
0839 namespace KIO
0840 {
0841 KIOCORE_EXPORT QString unsupportedActionErrorString(const QString &protocol, int cmd)
0842 {
0843     switch (cmd) {
0844     case CMD_CONNECT:
0845         return i18n("Opening connections is not supported with the protocol %1.", protocol);
0846     case CMD_DISCONNECT:
0847         return i18n("Closing connections is not supported with the protocol %1.", protocol);
0848     case CMD_STAT:
0849         return i18n("Accessing files is not supported with the protocol %1.", protocol);
0850     case CMD_PUT:
0851         return i18n("Writing to %1 is not supported.", protocol);
0852     case CMD_SPECIAL:
0853         return i18n("There are no special actions available for protocol %1.", protocol);
0854     case CMD_LISTDIR:
0855         return i18n("Listing folders is not supported for protocol %1.", protocol);
0856     case CMD_GET:
0857         return i18n("Retrieving data from %1 is not supported.", protocol);
0858     case CMD_MIMETYPE:
0859         return i18n("Retrieving mime type information from %1 is not supported.", protocol);
0860     case CMD_RENAME:
0861         return i18n("Renaming or moving files within %1 is not supported.", protocol);
0862     case CMD_SYMLINK:
0863         return i18n("Creating symlinks is not supported with protocol %1.", protocol);
0864     case CMD_COPY:
0865         return i18n("Copying files within %1 is not supported.", protocol);
0866     case CMD_DEL:
0867         return i18n("Deleting files from %1 is not supported.", protocol);
0868     case CMD_MKDIR:
0869         return i18n("Creating folders is not supported with protocol %1.", protocol);
0870     case CMD_CHMOD:
0871         return i18n("Changing the attributes of files is not supported with protocol %1.", protocol);
0872     case CMD_CHOWN:
0873         return i18n("Changing the ownership of files is not supported with protocol %1.", protocol);
0874     case CMD_OPEN:
0875         return i18n("Opening files is not supported with protocol %1.", protocol);
0876     default:
0877         return i18n("Protocol %1 does not support action %2.", protocol, cmd);
0878     } /*end switch*/
0879 }
0880 }
0881 
0882 void SlaveBase::openConnection()
0883 {
0884     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CONNECT));
0885 }
0886 void SlaveBase::closeConnection()
0887 {
0888 } // No response!
0889 void SlaveBase::stat(QUrl const &)
0890 {
0891     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_STAT));
0892 }
0893 void SlaveBase::put(QUrl const &, int, JobFlags)
0894 {
0895     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_PUT));
0896 }
0897 void SlaveBase::special(const QByteArray &)
0898 {
0899     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SPECIAL));
0900 }
0901 void SlaveBase::listDir(QUrl const &)
0902 {
0903     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_LISTDIR));
0904 }
0905 void SlaveBase::get(QUrl const &)
0906 {
0907     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_GET));
0908 }
0909 void SlaveBase::open(QUrl const &, QIODevice::OpenMode)
0910 {
0911     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_OPEN));
0912 }
0913 void SlaveBase::read(KIO::filesize_t)
0914 {
0915     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_READ));
0916 }
0917 void SlaveBase::write(const QByteArray &)
0918 {
0919     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_WRITE));
0920 }
0921 void SlaveBase::seek(KIO::filesize_t)
0922 {
0923     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SEEK));
0924 }
0925 void SlaveBase::close()
0926 {
0927     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CLOSE));
0928 }
0929 void SlaveBase::mimetype(QUrl const &url)
0930 {
0931     get(url);
0932 }
0933 void SlaveBase::rename(QUrl const &, QUrl const &, JobFlags)
0934 {
0935     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_RENAME));
0936 }
0937 void SlaveBase::symlink(QString const &, QUrl const &, JobFlags)
0938 {
0939     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SYMLINK));
0940 }
0941 void SlaveBase::copy(QUrl const &, QUrl const &, int, JobFlags)
0942 {
0943     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_COPY));
0944 }
0945 void SlaveBase::del(QUrl const &, bool)
0946 {
0947     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_DEL));
0948 }
0949 void SlaveBase::setLinkDest(const QUrl &, const QString &)
0950 {
0951     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SETLINKDEST));
0952 }
0953 void SlaveBase::mkdir(QUrl const &, int)
0954 {
0955     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_MKDIR));
0956 }
0957 void SlaveBase::chmod(QUrl const &, int)
0958 {
0959     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CHMOD));
0960 }
0961 void SlaveBase::setModificationTime(QUrl const &, const QDateTime &)
0962 {
0963     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SETMODIFICATIONTIME));
0964 }
0965 void SlaveBase::chown(QUrl const &, const QString &, const QString &)
0966 {
0967     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CHOWN));
0968 }
0969 
0970 void SlaveBase::slave_status()
0971 {
0972     slaveStatus(QString(), false);
0973 }
0974 
0975 void SlaveBase::reparseConfiguration()
0976 {
0977     delete d->remotefile;
0978     d->remotefile = nullptr;
0979 }
0980 
0981 int SlaveBase::openPasswordDialogV2(AuthInfo &info, const QString &errorMsg)
0982 {
0983     const long windowId = metaData(QStringLiteral("window-id")).toLong();
0984     const unsigned long userTimestamp = metaData(QStringLiteral("user-timestamp")).toULong();
0985     QString errorMessage;
0986     if (metaData(QStringLiteral("no-auth-prompt")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) {
0987         errorMessage = QStringLiteral("<NoAuthPrompt>");
0988     } else {
0989         errorMessage = errorMsg;
0990     }
0991 
0992     AuthInfo dlgInfo(info);
0993     // Make sure the modified flag is not set.
0994     dlgInfo.setModified(false);
0995     // Prevent queryAuthInfo from caching the user supplied password since
0996     // we need the ioslaves to first authenticate against the server with
0997     // it to ensure it is valid.
0998     dlgInfo.setExtraField(QStringLiteral("skip-caching-on-query"), true);
0999 
1000 #ifndef KIO_ANDROID_STUB
1001     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1002     const int errCode = passwdServerClient->queryAuthInfo(&dlgInfo, errorMessage, windowId, userTimestamp);
1003     if (errCode == KJob::NoError) {
1004         info = dlgInfo;
1005     }
1006     return errCode;
1007 #else
1008     return KJob::NoError;
1009 #endif
1010 }
1011 
1012 int SlaveBase::messageBox(MessageBoxType type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText)
1013 {
1014     return messageBox(text, type, title, primaryActionText, secondaryActionText, QString());
1015 }
1016 
1017 int SlaveBase::messageBox(const QString &text,
1018                           MessageBoxType type,
1019                           const QString &title,
1020                           const QString &primaryActionText,
1021                           const QString &secondaryActionText,
1022                           const QString &dontAskAgainName)
1023 {
1024     KIO_DATA << static_cast<qint32>(type) << text << title << primaryActionText << secondaryActionText << dontAskAgainName;
1025     send(INF_MESSAGEBOX, data);
1026     if (waitForAnswer(CMD_MESSAGEBOXANSWER, 0, data) != -1) {
1027         QDataStream stream(data);
1028         int answer;
1029         stream >> answer;
1030         return answer;
1031     } else {
1032         return 0; // communication failure
1033     }
1034 }
1035 
1036 int SlaveBase::sslError(const QVariantMap &sslData)
1037 {
1038     KIO_DATA << sslData;
1039     send(INF_SSLERROR, data);
1040     if (waitForAnswer(CMD_SSLERRORANSWER, 0, data) != -1) {
1041         QDataStream stream(data);
1042         int answer;
1043         stream >> answer;
1044         return answer;
1045     } else {
1046         return 0; // communication failure
1047     }
1048 }
1049 
1050 bool SlaveBase::canResume(KIO::filesize_t offset)
1051 {
1052     // qDebug() << "offset=" << KIO::number(offset);
1053     d->needSendCanResume = false;
1054     KIO_DATA << static_cast<quint64>(offset);
1055     send(MSG_RESUME, data);
1056     if (offset) {
1057         int cmd;
1058         if (waitForAnswer(CMD_RESUMEANSWER, CMD_NONE, data, &cmd) != -1) {
1059             // qDebug() << "returning" << (cmd == CMD_RESUMEANSWER);
1060             return cmd == CMD_RESUMEANSWER;
1061         } else {
1062             return false;
1063         }
1064     } else { // No resuming possible -> no answer to wait for
1065         return true;
1066     }
1067 }
1068 
1069 int SlaveBase::waitForAnswer(int expected1, int expected2, QByteArray &data, int *pCmd)
1070 {
1071     int cmd = 0;
1072     int result = -1;
1073     for (;;) {
1074         if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
1075             result = d->appConnection.read(&cmd, data);
1076         }
1077         if (result == -1) {
1078             // qDebug() << "read error.";
1079             return -1;
1080         }
1081 
1082         if (cmd == expected1 || cmd == expected2) {
1083             if (pCmd) {
1084                 *pCmd = cmd;
1085             }
1086             return result;
1087         }
1088         if (isSubCommand(cmd)) {
1089             dispatch(cmd, data);
1090         } else {
1091             qFatal("Fatal Error: Got cmd %d, while waiting for an answer!", cmd);
1092         }
1093     }
1094 }
1095 
1096 int SlaveBase::readData(QByteArray &buffer)
1097 {
1098     int result = waitForAnswer(MSG_DATA, 0, buffer);
1099     // qDebug() << "readData: length = " << result << " ";
1100     return result;
1101 }
1102 
1103 void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data)
1104 {
1105     if (timeout > 0) {
1106         d->nextTimeoutMsecs = timeout * 1000; // from seconds to milliseconds
1107         d->nextTimeout.start();
1108     } else if (timeout == 0) {
1109         d->nextTimeoutMsecs = 1000; // Immediate timeout
1110         d->nextTimeout.start();
1111     } else {
1112         d->nextTimeout.invalidate(); // Canceled
1113     }
1114 
1115     d->timeoutData = data;
1116 }
1117 
1118 void SlaveBase::dispatch(int command, const QByteArray &data)
1119 {
1120     QDataStream stream(data);
1121 
1122     QUrl url;
1123     int i;
1124 
1125     d->m_finalityCommand = true; // default
1126 
1127     switch (command) {
1128     case CMD_HOST: {
1129         QString passwd;
1130         QString host;
1131         QString user;
1132         quint16 port;
1133         stream >> host >> port >> user >> passwd;
1134         d->m_state = d->InsideMethod;
1135         d->m_finalityCommand = false;
1136         setHost(host, port, user, passwd);
1137         d->m_state = d->Idle;
1138         break;
1139     }
1140     case CMD_CONNECT: {
1141         openConnection();
1142         break;
1143     }
1144     case CMD_DISCONNECT: {
1145         closeConnection();
1146         break;
1147     }
1148     case CMD_WORKER_STATUS: {
1149         d->m_state = d->InsideMethod;
1150         d->m_finalityCommand = false;
1151         slave_status();
1152         // TODO verify that the slave has called slaveStatus()?
1153         d->m_state = d->Idle;
1154         break;
1155     }
1156     case CMD_REPARSECONFIGURATION: {
1157         d->m_state = d->InsideMethod;
1158         d->m_finalityCommand = false;
1159         reparseConfiguration();
1160         d->m_state = d->Idle;
1161         break;
1162     }
1163     case CMD_CONFIG: {
1164         stream >> d->configData;
1165         d->rebuildConfig();
1166         delete d->remotefile;
1167         d->remotefile = nullptr;
1168         break;
1169     }
1170     case CMD_GET: {
1171         stream >> url;
1172         d->m_state = d->InsideMethod;
1173         get(url);
1174         d->verifyState("get()");
1175         d->m_state = d->Idle;
1176         break;
1177     }
1178     case CMD_OPEN: {
1179         stream >> url >> i;
1180         QIODevice::OpenMode mode = QFlag(i);
1181         d->m_state = d->InsideMethod;
1182         open(url, mode); // krazy:exclude=syscalls
1183         d->m_state = d->Idle;
1184         break;
1185     }
1186     case CMD_PUT: {
1187         int permissions;
1188         qint8 iOverwrite;
1189         qint8 iResume;
1190         stream >> url >> iOverwrite >> iResume >> permissions;
1191         JobFlags flags;
1192         if (iOverwrite != 0) {
1193             flags |= Overwrite;
1194         }
1195         if (iResume != 0) {
1196             flags |= Resume;
1197         }
1198 
1199         // Remember that we need to send canResume(), TransferJob is expecting
1200         // it. Well, in theory this shouldn't be done if resume is true.
1201         //   (the resume bool is currently unused)
1202         d->needSendCanResume = true /* !resume */;
1203 
1204         d->m_state = d->InsideMethod;
1205         put(url, permissions, flags);
1206         d->verifyState("put()");
1207         d->m_state = d->Idle;
1208         break;
1209     }
1210     case CMD_STAT: {
1211         stream >> url;
1212         d->m_state = d->InsideMethod;
1213         stat(url); // krazy:exclude=syscalls
1214         d->verifyState("stat()");
1215         d->m_state = d->Idle;
1216         break;
1217     }
1218     case CMD_MIMETYPE: {
1219         stream >> url;
1220         d->m_state = d->InsideMethod;
1221         mimetype(url);
1222         d->verifyState("mimetype()");
1223         d->m_state = d->Idle;
1224         break;
1225     }
1226     case CMD_LISTDIR: {
1227         stream >> url;
1228         d->m_state = d->InsideMethod;
1229         listDir(url);
1230         d->verifyState("listDir()");
1231         d->m_state = d->Idle;
1232         break;
1233     }
1234     case CMD_MKDIR: {
1235         stream >> url >> i;
1236         d->m_state = d->InsideMethod;
1237         mkdir(url, i); // krazy:exclude=syscalls
1238         d->verifyState("mkdir()");
1239         d->m_state = d->Idle;
1240         break;
1241     }
1242     case CMD_RENAME: {
1243         qint8 iOverwrite;
1244         QUrl url2;
1245         stream >> url >> url2 >> iOverwrite;
1246         JobFlags flags;
1247         if (iOverwrite != 0) {
1248             flags |= Overwrite;
1249         }
1250         d->m_state = d->InsideMethod;
1251         rename(url, url2, flags); // krazy:exclude=syscalls
1252         d->verifyState("rename()");
1253         d->m_state = d->Idle;
1254         break;
1255     }
1256     case CMD_SYMLINK: {
1257         qint8 iOverwrite;
1258         QString target;
1259         stream >> target >> url >> iOverwrite;
1260         JobFlags flags;
1261         if (iOverwrite != 0) {
1262             flags |= Overwrite;
1263         }
1264         d->m_state = d->InsideMethod;
1265         symlink(target, url, flags);
1266         d->verifyState("symlink()");
1267         d->m_state = d->Idle;
1268         break;
1269     }
1270     case CMD_COPY: {
1271         int permissions;
1272         qint8 iOverwrite;
1273         QUrl url2;
1274         stream >> url >> url2 >> permissions >> iOverwrite;
1275         JobFlags flags;
1276         if (iOverwrite != 0) {
1277             flags |= Overwrite;
1278         }
1279         d->m_state = d->InsideMethod;
1280         copy(url, url2, permissions, flags);
1281         d->verifyState("copy()");
1282         d->m_state = d->Idle;
1283         break;
1284     }
1285     case CMD_DEL: {
1286         qint8 isFile;
1287         stream >> url >> isFile;
1288         d->m_state = d->InsideMethod;
1289         del(url, isFile != 0);
1290         d->verifyState("del()");
1291         d->m_state = d->Idle;
1292         break;
1293     }
1294     case CMD_CHMOD: {
1295         stream >> url >> i;
1296         d->m_state = d->InsideMethod;
1297         chmod(url, i);
1298         d->verifyState("chmod()");
1299         d->m_state = d->Idle;
1300         break;
1301     }
1302     case CMD_CHOWN: {
1303         QString owner;
1304         QString group;
1305         stream >> url >> owner >> group;
1306         d->m_state = d->InsideMethod;
1307         chown(url, owner, group);
1308         d->verifyState("chown()");
1309         d->m_state = d->Idle;
1310         break;
1311     }
1312     case CMD_SETMODIFICATIONTIME: {
1313         QDateTime dt;
1314         stream >> url >> dt;
1315         d->m_state = d->InsideMethod;
1316         setModificationTime(url, dt);
1317         d->verifyState("setModificationTime()");
1318         d->m_state = d->Idle;
1319         break;
1320     }
1321     case CMD_SPECIAL: {
1322         d->m_state = d->InsideMethod;
1323         special(data);
1324         d->verifyState("special()");
1325         d->m_state = d->Idle;
1326         break;
1327     }
1328     case CMD_META_DATA: {
1329         // qDebug() << "(" << getpid() << ") Incoming meta-data...";
1330         stream >> mIncomingMetaData;
1331         d->rebuildConfig();
1332         break;
1333     }
1334     case CMD_NONE: {
1335         qCWarning(KIO_CORE) << "Got unexpected CMD_NONE!";
1336         break;
1337     }
1338     case CMD_FILESYSTEMFREESPACE: {
1339         stream >> url;
1340 
1341         void *data = static_cast<void *>(&url);
1342 
1343         d->m_state = d->InsideMethod;
1344         virtual_hook(GetFileSystemFreeSpace, data);
1345         d->verifyState("fileSystemFreeSpace()");
1346         d->m_state = d->Idle;
1347         break;
1348     }
1349     default: {
1350         // Some command we don't understand.
1351         // Just ignore it, it may come from some future version of KIO.
1352         break;
1353     }
1354     }
1355 }
1356 
1357 bool SlaveBase::checkCachedAuthentication(AuthInfo &info)
1358 {
1359 #ifndef KIO_ANDROID_STUB
1360     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1361     return (passwdServerClient->checkAuthInfo(&info, metaData(QStringLiteral("window-id")).toLong(), metaData(QStringLiteral("user-timestamp")).toULong()));
1362 #else
1363     return false;
1364 #endif
1365 }
1366 
1367 void SlaveBase::dispatchOpenCommand(int command, const QByteArray &data)
1368 {
1369     QDataStream stream(data);
1370 
1371     switch (command) {
1372     case CMD_READ: {
1373         KIO::filesize_t bytes;
1374         stream >> bytes;
1375         read(bytes);
1376         break;
1377     }
1378     case CMD_WRITE: {
1379         write(data);
1380         break;
1381     }
1382     case CMD_SEEK: {
1383         KIO::filesize_t offset;
1384         stream >> offset;
1385         seek(offset);
1386         break;
1387     }
1388     case CMD_TRUNCATE: {
1389         KIO::filesize_t length;
1390         stream >> length;
1391         void *data = static_cast<void *>(&length);
1392         virtual_hook(Truncate, data);
1393         break;
1394     }
1395     case CMD_NONE:
1396         break;
1397     case CMD_CLOSE:
1398         close(); // must call finish(), which will set d->inOpenLoop=false
1399         break;
1400     default:
1401         // Some command we don't understand.
1402         // Just ignore it, it may come from some future version of KIO.
1403         break;
1404     }
1405 }
1406 
1407 bool SlaveBase::cacheAuthentication(const AuthInfo &info)
1408 {
1409 #ifndef KIO_ANDROID_STUB
1410     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1411     passwdServerClient->addAuthInfo(info, metaData(QStringLiteral("window-id")).toLongLong());
1412 #endif
1413     return true;
1414 }
1415 
1416 int SlaveBase::connectTimeout()
1417 {
1418     bool ok;
1419     QString tmp = metaData(QStringLiteral("ConnectTimeout"));
1420     int result = tmp.toInt(&ok);
1421     if (ok) {
1422         return result;
1423     }
1424     return DEFAULT_CONNECT_TIMEOUT;
1425 }
1426 
1427 int SlaveBase::proxyConnectTimeout()
1428 {
1429     bool ok;
1430     QString tmp = metaData(QStringLiteral("ProxyConnectTimeout"));
1431     int result = tmp.toInt(&ok);
1432     if (ok) {
1433         return result;
1434     }
1435     return DEFAULT_PROXY_CONNECT_TIMEOUT;
1436 }
1437 
1438 int SlaveBase::responseTimeout()
1439 {
1440     bool ok;
1441     QString tmp = metaData(QStringLiteral("ResponseTimeout"));
1442     int result = tmp.toInt(&ok);
1443     if (ok) {
1444         return result;
1445     }
1446     return DEFAULT_RESPONSE_TIMEOUT;
1447 }
1448 
1449 int SlaveBase::readTimeout()
1450 {
1451     bool ok;
1452     QString tmp = metaData(QStringLiteral("ReadTimeout"));
1453     int result = tmp.toInt(&ok);
1454     if (ok) {
1455         return result;
1456     }
1457     return DEFAULT_READ_TIMEOUT;
1458 }
1459 
1460 bool SlaveBase::wasKilled() const
1461 {
1462     return d->wasKilled;
1463 }
1464 
1465 void SlaveBase::setKillFlag()
1466 {
1467     d->wasKilled = true;
1468 }
1469 
1470 void SlaveBase::send(int cmd, const QByteArray &arr)
1471 {
1472     if (d->runInThread) {
1473         if (!d->appConnection.send(cmd, arr)) {
1474             exit();
1475         }
1476     } else {
1477         slaveWriteError = false;
1478         if (!d->appConnection.send(cmd, arr))
1479         // Note that slaveWriteError can also be set by sigpipe_handler
1480         {
1481             slaveWriteError = true;
1482         }
1483         if (slaveWriteError) {
1484             qCWarning(KIO_CORE) << "An error occurred during write. The worker terminates now.";
1485             exit();
1486         }
1487     }
1488 }
1489 
1490 void SlaveBase::virtual_hook(int id, void *data)
1491 {
1492     Q_UNUSED(data);
1493 
1494     switch (id) {
1495     case GetFileSystemFreeSpace: {
1496         error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_FILESYSTEMFREESPACE));
1497         break;
1498     }
1499     case Truncate: {
1500         error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_TRUNCATE));
1501         break;
1502     }
1503     }
1504 }
1505 
1506 void SlaveBase::setRunInThread(bool b)
1507 {
1508     d->runInThread = b;
1509 }
1510 
1511 void SlaveBase::lookupHost(const QString &host)
1512 {
1513     KIO_DATA << host;
1514     send(MSG_HOST_INFO_REQ, data);
1515 }
1516 
1517 int SlaveBase::waitForHostInfo(QHostInfo &info)
1518 {
1519     QByteArray data;
1520     int result = waitForAnswer(CMD_HOST_INFO, 0, data);
1521 
1522     if (result == -1) {
1523         info.setError(QHostInfo::UnknownError);
1524         info.setErrorString(i18n("Unknown Error"));
1525         return result;
1526     }
1527 
1528     QDataStream stream(data);
1529     QString hostName;
1530     QList<QHostAddress> addresses;
1531     int error;
1532     QString errorString;
1533 
1534     stream >> hostName >> addresses >> error >> errorString;
1535 
1536     info.setHostName(hostName);
1537     info.setAddresses(addresses);
1538     info.setError(QHostInfo::HostInfoError(error));
1539     info.setErrorString(errorString);
1540 
1541     return result;
1542 }
1543 
1544 PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation(const QString &operationDetails)
1545 {
1546     if (d->m_privilegeOperationStatus == OperationNotAllowed) {
1547         QByteArray buffer;
1548         send(MSG_PRIVILEGE_EXEC);
1549         waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer);
1550         QDataStream ds(buffer);
1551         ds >> d->m_privilegeOperationStatus >> d->m_warningTitle >> d->m_warningMessage;
1552     }
1553 
1554     if (metaData(QStringLiteral("UnitTesting")) != QLatin1String("true") && d->m_privilegeOperationStatus == OperationAllowed && !d->m_confirmationAsked) {
1555         // WORKER_MESSAGEBOX_DETAILS_HACK
1556         // SlaveBase::messageBox() overloads miss a parameter to pass an details argument.
1557         // As workaround details are passed instead via metadata before and then cached by the WorkerInterface,
1558         // to be used in the upcoming messageBox call (needs WarningContinueCancelDetailed type)
1559         // TODO: add a messageBox() overload taking details and use here,
1560         // then remove or adapt all code marked with WORKER_MESSAGEBOX_DETAILS
1561         setMetaData(QStringLiteral("privilege_conf_details"), operationDetails);
1562         sendMetaData();
1563 
1564         int result = messageBox(d->m_warningMessage, WarningContinueCancelDetailed, d->m_warningTitle, QString(), QString(), QString());
1565         d->m_privilegeOperationStatus = result == Continue ? OperationAllowed : OperationCanceled;
1566         d->m_confirmationAsked = true;
1567     }
1568 
1569     return KIO::PrivilegeOperationStatus(d->m_privilegeOperationStatus);
1570 }
1571 
1572 void SlaveBase::addTemporaryAuthorization(const QString &action)
1573 {
1574     d->m_tempAuths.insert(action);
1575 }