File indexing completed on 2023-09-24 04:08:43

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