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

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         if (wasKilled()) {
0716             break;
0717         }
0718 
0719         // Send the meta-data each time we send the MIME type.
0720         if (!mOutgoingMetaData.isEmpty()) {
0721             // qDebug() << "emitting meta data";
0722             KIO_DATA << mOutgoingMetaData;
0723             send(INF_META_DATA, data);
0724         }
0725         KIO_DATA << _type;
0726         send(INF_MIME_TYPE, data);
0727         while (true) {
0728             cmd = 0;
0729             int ret = -1;
0730             if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
0731                 ret = d->appConnection.read(&cmd, data);
0732             }
0733             if (ret == -1) {
0734                 // qDebug() << "read error";
0735                 exit();
0736             }
0737             // qDebug() << "got" << 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 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0778 bool SlaveBase::requestNetwork(const QString &host)
0779 {
0780     KIO_DATA << host << d->slaveid;
0781     send(MSG_NET_REQUEST, data);
0782 
0783     if (waitForAnswer(INF_NETWORK_STATUS, 0, data) != -1) {
0784         bool status;
0785         QDataStream stream(data);
0786         stream >> status;
0787         return status;
0788     } else {
0789         return false;
0790     }
0791 }
0792 #endif
0793 
0794 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0795 void SlaveBase::dropNetwork(const QString &host)
0796 {
0797     KIO_DATA << host << d->slaveid;
0798     send(MSG_NET_DROP, data);
0799 }
0800 #endif
0801 
0802 void SlaveBase::statEntry(const UDSEntry &entry)
0803 {
0804     KIO_DATA << entry;
0805     send(MSG_STAT_ENTRY, data);
0806 }
0807 
0808 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0809 void SlaveBase::listEntry(const UDSEntry &entry, bool _ready)
0810 {
0811     if (_ready) {
0812         // #366795: many slaves don't create an entry for ".", so we keep track if they do
0813         // and we provide a fallback in finished() otherwise.
0814         if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1Char('.')) {
0815             d->m_rootEntryListed = true;
0816         }
0817         listEntries(d->pendingListEntries);
0818         d->pendingListEntries.clear();
0819     } else {
0820         listEntry(entry);
0821     }
0822 }
0823 #endif
0824 
0825 void SlaveBase::listEntry(const UDSEntry &entry)
0826 {
0827     // #366795: many slaves don't create an entry for ".", so we keep track if they do
0828     // and we provide a fallback in finished() otherwise.
0829     if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1Char('.')) {
0830         d->m_rootEntryListed = true;
0831     }
0832 
0833     // We start measuring the time from the point we start filling the list
0834     if (d->pendingListEntries.isEmpty()) {
0835         d->m_timeSinceLastBatch.restart();
0836     }
0837 
0838     d->pendingListEntries.append(entry);
0839 
0840     // If more then KIO_MAX_SEND_BATCH_TIME time is passed, emit the current batch
0841     // Also emit if we have piled up a large number of entries already, to save memory (and time)
0842     if (d->m_timeSinceLastBatch.elapsed() > KIO_MAX_SEND_BATCH_TIME || d->pendingListEntries.size() > KIO_MAX_ENTRIES_PER_BATCH) {
0843         listEntries(d->pendingListEntries);
0844         d->pendingListEntries.clear();
0845 
0846         // Restart time
0847         d->m_timeSinceLastBatch.restart();
0848     }
0849 }
0850 
0851 void SlaveBase::listEntries(const UDSEntryList &list)
0852 {
0853     QByteArray data;
0854     QDataStream stream(&data, QIODevice::WriteOnly);
0855 
0856     for (const UDSEntry &entry : list) {
0857         stream << entry;
0858     }
0859 
0860     send(MSG_LIST_ENTRIES, data);
0861 }
0862 
0863 static void sigpipe_handler(int)
0864 {
0865     // We ignore a SIGPIPE in slaves.
0866     // A SIGPIPE can happen in two cases:
0867     // 1) Communication error with application.
0868     // 2) Communication error with network.
0869     slaveWriteError = true;
0870 
0871     // Don't add anything else here, especially no debug output
0872 }
0873 
0874 void SlaveBase::setHost(QString const &, quint16, QString const &, QString const &)
0875 {
0876 }
0877 
0878 KIOCORE_EXPORT QString KIO::unsupportedActionErrorString(const QString &protocol, int cmd)
0879 {
0880     switch (cmd) {
0881     case CMD_CONNECT:
0882         return i18n("Opening connections is not supported with the protocol %1.", protocol);
0883     case CMD_DISCONNECT:
0884         return i18n("Closing connections is not supported with the protocol %1.", protocol);
0885     case CMD_STAT:
0886         return i18n("Accessing files is not supported with the protocol %1.", protocol);
0887     case CMD_PUT:
0888         return i18n("Writing to %1 is not supported.", protocol);
0889     case CMD_SPECIAL:
0890         return i18n("There are no special actions available for protocol %1.", protocol);
0891     case CMD_LISTDIR:
0892         return i18n("Listing folders is not supported for protocol %1.", protocol);
0893     case CMD_GET:
0894         return i18n("Retrieving data from %1 is not supported.", protocol);
0895     case CMD_MIMETYPE:
0896         return i18n("Retrieving mime type information from %1 is not supported.", protocol);
0897     case CMD_RENAME:
0898         return i18n("Renaming or moving files within %1 is not supported.", protocol);
0899     case CMD_SYMLINK:
0900         return i18n("Creating symlinks is not supported with protocol %1.", protocol);
0901     case CMD_COPY:
0902         return i18n("Copying files within %1 is not supported.", protocol);
0903     case CMD_DEL:
0904         return i18n("Deleting files from %1 is not supported.", protocol);
0905     case CMD_MKDIR:
0906         return i18n("Creating folders is not supported with protocol %1.", protocol);
0907     case CMD_CHMOD:
0908         return i18n("Changing the attributes of files is not supported with protocol %1.", protocol);
0909     case CMD_CHOWN:
0910         return i18n("Changing the ownership of files is not supported with protocol %1.", protocol);
0911     case CMD_SUBURL:
0912         return i18n("Using sub-URLs with %1 is not supported.", protocol);
0913     case CMD_MULTI_GET:
0914         return i18n("Multiple get is not supported with protocol %1.", protocol);
0915     case CMD_OPEN:
0916         return i18n("Opening files is not supported with protocol %1.", protocol);
0917     default:
0918         return i18n("Protocol %1 does not support action %2.", protocol, cmd);
0919     } /*end switch*/
0920 }
0921 
0922 void SlaveBase::openConnection()
0923 {
0924     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CONNECT));
0925 }
0926 void SlaveBase::closeConnection()
0927 {
0928 } // No response!
0929 void SlaveBase::stat(QUrl const &)
0930 {
0931     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_STAT));
0932 }
0933 void SlaveBase::put(QUrl const &, int, JobFlags)
0934 {
0935     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_PUT));
0936 }
0937 void SlaveBase::special(const QByteArray &)
0938 {
0939     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SPECIAL));
0940 }
0941 void SlaveBase::listDir(QUrl const &)
0942 {
0943     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_LISTDIR));
0944 }
0945 void SlaveBase::get(QUrl const &)
0946 {
0947     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_GET));
0948 }
0949 void SlaveBase::open(QUrl const &, QIODevice::OpenMode)
0950 {
0951     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_OPEN));
0952 }
0953 void SlaveBase::read(KIO::filesize_t)
0954 {
0955     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_READ));
0956 }
0957 void SlaveBase::write(const QByteArray &)
0958 {
0959     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_WRITE));
0960 }
0961 void SlaveBase::seek(KIO::filesize_t)
0962 {
0963     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SEEK));
0964 }
0965 void SlaveBase::close()
0966 {
0967     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CLOSE));
0968 }
0969 void SlaveBase::mimetype(QUrl const &url)
0970 {
0971     get(url);
0972 }
0973 void SlaveBase::rename(QUrl const &, QUrl const &, JobFlags)
0974 {
0975     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_RENAME));
0976 }
0977 void SlaveBase::symlink(QString const &, QUrl const &, JobFlags)
0978 {
0979     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SYMLINK));
0980 }
0981 void SlaveBase::copy(QUrl const &, QUrl const &, int, JobFlags)
0982 {
0983     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_COPY));
0984 }
0985 void SlaveBase::del(QUrl const &, bool)
0986 {
0987     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_DEL));
0988 }
0989 void SlaveBase::setLinkDest(const QUrl &, const QString &)
0990 {
0991     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SETLINKDEST));
0992 }
0993 void SlaveBase::mkdir(QUrl const &, int)
0994 {
0995     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_MKDIR));
0996 }
0997 void SlaveBase::chmod(QUrl const &, int)
0998 {
0999     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CHMOD));
1000 }
1001 void SlaveBase::setModificationTime(QUrl const &, const QDateTime &)
1002 {
1003     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SETMODIFICATIONTIME));
1004 }
1005 void SlaveBase::chown(QUrl const &, const QString &, const QString &)
1006 {
1007     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_CHOWN));
1008 }
1009 void SlaveBase::setSubUrl(QUrl const &)
1010 {
1011     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_SUBURL));
1012 }
1013 void SlaveBase::multiGet(const QByteArray &)
1014 {
1015     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_MULTI_GET));
1016 }
1017 
1018 void SlaveBase::slave_status()
1019 {
1020     slaveStatus(QString(), false);
1021 }
1022 
1023 void SlaveBase::reparseConfiguration()
1024 {
1025     delete d->remotefile;
1026     d->remotefile = nullptr;
1027 }
1028 
1029 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 24)
1030 bool SlaveBase::openPasswordDialog(AuthInfo &info, const QString &errorMsg)
1031 {
1032     const int errorCode = openPasswordDialogV2(info, errorMsg);
1033     return errorCode == KJob::NoError;
1034 }
1035 #endif
1036 
1037 int SlaveBase::openPasswordDialogV2(AuthInfo &info, const QString &errorMsg)
1038 {
1039     const long windowId = metaData(QStringLiteral("window-id")).toLong();
1040     const unsigned long userTimestamp = metaData(QStringLiteral("user-timestamp")).toULong();
1041     QString errorMessage;
1042     if (metaData(QStringLiteral("no-auth-prompt")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) {
1043         errorMessage = QStringLiteral("<NoAuthPrompt>");
1044     } else {
1045         errorMessage = errorMsg;
1046     }
1047 
1048     AuthInfo dlgInfo(info);
1049     // Make sure the modified flag is not set.
1050     dlgInfo.setModified(false);
1051     // Prevent queryAuthInfo from caching the user supplied password since
1052     // we need the ioslaves to first authenticate against the server with
1053     // it to ensure it is valid.
1054     dlgInfo.setExtraField(QStringLiteral("skip-caching-on-query"), true);
1055 
1056 #ifndef KIO_ANDROID_STUB
1057     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1058     const int errCode = passwdServerClient->queryAuthInfo(&dlgInfo, errorMessage, windowId, userTimestamp);
1059     if (errCode == KJob::NoError) {
1060         info = dlgInfo;
1061     }
1062     return errCode;
1063 #else
1064     return KJob::NoError;
1065 #endif
1066 }
1067 
1068 int SlaveBase::messageBox(MessageBoxType type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText)
1069 {
1070     return messageBox(text, type, title, primaryActionText, secondaryActionText, QString());
1071 }
1072 
1073 int SlaveBase::messageBox(const QString &text,
1074                           MessageBoxType type,
1075                           const QString &title,
1076                           const QString &primaryActionText,
1077                           const QString &secondaryActionText,
1078                           const QString &dontAskAgainName)
1079 {
1080 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 100)
1081     if ((type != Information) || (type != SSLMessageBox)) {
1082         if (primaryActionText.isNull()) {
1083             qCWarning(KIO_CORE) << "Deprecated: messageBox() called with null primaryActionText arg."
1084                                 << "type:" << type << "text:" << text;
1085         }
1086         if ((type != WarningContinueCancel) || (type != WarningContinueCancelDetailed)) {
1087             if (secondaryActionText.isNull()) {
1088                 qCWarning(KIO_CORE) << "Deprecated: messageBox() called with null secondaryActionText arg."
1089                                     << "type:" << type << "text:" << text;
1090             }
1091         }
1092     }
1093     const QString _primaryActionText = primaryActionText.isNull() ? i18n("&Yes") : primaryActionText;
1094     const QString _secondaryActionText = secondaryActionText.isNull() ? i18n("&No") : secondaryActionText;
1095     // qDebug() << "messageBox " << type << " " << text << " - " << title << _primaryActionText << _secondaryActionText;
1096     KIO_DATA << static_cast<qint32>(type) << text << title << _primaryActionText << _secondaryActionText << dontAskAgainName;
1097 #else
1098     KIO_DATA << static_cast<qint32>(type) << text << title << primaryActionText << secondaryActionText << dontAskAgainName;
1099 #endif
1100     send(INF_MESSAGEBOX, data);
1101     if (waitForAnswer(CMD_MESSAGEBOXANSWER, 0, data) != -1) {
1102         QDataStream stream(data);
1103         int answer;
1104         stream >> answer;
1105         // qDebug() << "got messagebox answer" << answer;
1106         return answer;
1107     } else {
1108         return 0; // communication failure
1109     }
1110 }
1111 
1112 bool SlaveBase::canResume(KIO::filesize_t offset)
1113 {
1114     // qDebug() << "offset=" << KIO::number(offset);
1115     d->needSendCanResume = false;
1116     KIO_DATA << static_cast<quint64>(offset);
1117     send(MSG_RESUME, data);
1118     if (offset) {
1119         int cmd;
1120         if (waitForAnswer(CMD_RESUMEANSWER, CMD_NONE, data, &cmd) != -1) {
1121             // qDebug() << "returning" << (cmd == CMD_RESUMEANSWER);
1122             return cmd == CMD_RESUMEANSWER;
1123         } else {
1124             return false;
1125         }
1126     } else { // No resuming possible -> no answer to wait for
1127         return true;
1128     }
1129 }
1130 
1131 int SlaveBase::waitForAnswer(int expected1, int expected2, QByteArray &data, int *pCmd)
1132 {
1133     int cmd = 0;
1134     int result = -1;
1135     for (;;) {
1136         if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
1137             result = d->appConnection.read(&cmd, data);
1138         }
1139         if (result == -1) {
1140             // qDebug() << "read error.";
1141             return -1;
1142         }
1143 
1144         if (cmd == expected1 || cmd == expected2) {
1145             if (pCmd) {
1146                 *pCmd = cmd;
1147             }
1148             return result;
1149         }
1150         if (isSubCommand(cmd)) {
1151             dispatch(cmd, data);
1152         } else {
1153             qFatal("Fatal Error: Got cmd %d, while waiting for an answer!", cmd);
1154         }
1155     }
1156 }
1157 
1158 int SlaveBase::readData(QByteArray &buffer)
1159 {
1160     int result = waitForAnswer(MSG_DATA, 0, buffer);
1161     // qDebug() << "readData: length = " << result << " ";
1162     return result;
1163 }
1164 
1165 void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data)
1166 {
1167     if (timeout > 0) {
1168         d->nextTimeoutMsecs = timeout * 1000; // from seconds to milliseconds
1169         d->nextTimeout.start();
1170     } else if (timeout == 0) {
1171         d->nextTimeoutMsecs = 1000; // Immediate timeout
1172         d->nextTimeout.start();
1173     } else {
1174         d->nextTimeout.invalidate(); // Canceled
1175     }
1176 
1177     d->timeoutData = data;
1178 }
1179 
1180 void SlaveBase::dispatch(int command, const QByteArray &data)
1181 {
1182     QDataStream stream(data);
1183 
1184     QUrl url;
1185     int i;
1186 
1187     d->m_finalityCommand = true; // default
1188 
1189     switch (command) {
1190     case CMD_HOST: {
1191         QString passwd;
1192         QString host;
1193         QString user;
1194         quint16 port;
1195         stream >> host >> port >> user >> passwd;
1196         d->m_state = d->InsideMethod;
1197         d->m_finalityCommand = false;
1198         setHost(host, port, user, passwd);
1199         d->m_state = d->Idle;
1200         break;
1201     }
1202     case CMD_CONNECT: {
1203         openConnection();
1204         break;
1205     }
1206     case CMD_DISCONNECT: {
1207         closeConnection();
1208         break;
1209     }
1210     case CMD_SLAVE_STATUS: {
1211         d->m_state = d->InsideMethod;
1212         d->m_finalityCommand = false;
1213         slave_status();
1214         // TODO verify that the slave has called slaveStatus()?
1215         d->m_state = d->Idle;
1216         break;
1217     }
1218     case CMD_SLAVE_CONNECT: {
1219         d->onHold = false;
1220         QString app_socket;
1221         QDataStream stream(data);
1222         stream >> app_socket;
1223         d->appConnection.send(MSG_SLAVE_ACK);
1224         disconnectSlave();
1225         d->isConnectedToApp = true;
1226         connectSlave(app_socket);
1227         virtual_hook(AppConnectionMade, nullptr);
1228         break;
1229     }
1230     case CMD_SLAVE_HOLD: { // TODO KF6: remove, unused
1231         QUrl url;
1232         QDataStream stream(data);
1233         stream >> url;
1234         d->onHoldUrl = url;
1235         d->onHold = true;
1236         disconnectSlave();
1237         d->isConnectedToApp = false;
1238         if (!d->poolSocket.isEmpty()) {
1239             // Do not close connection!
1240             connectSlave(d->poolSocket);
1241         }
1242         break;
1243     }
1244     case CMD_REPARSECONFIGURATION: {
1245         d->m_state = d->InsideMethod;
1246         d->m_finalityCommand = false;
1247         reparseConfiguration();
1248         d->m_state = d->Idle;
1249         break;
1250     }
1251     case CMD_CONFIG: {
1252         stream >> d->configData;
1253         d->rebuildConfig();
1254         delete d->remotefile;
1255         d->remotefile = nullptr;
1256         break;
1257     }
1258     case CMD_GET: {
1259         stream >> url;
1260         d->m_state = d->InsideMethod;
1261         get(url);
1262         d->verifyState("get()");
1263         d->m_state = d->Idle;
1264         break;
1265     }
1266     case CMD_OPEN: {
1267         stream >> url >> i;
1268         QIODevice::OpenMode mode = QFlag(i);
1269         d->m_state = d->InsideMethod;
1270         open(url, mode); // krazy:exclude=syscalls
1271         d->m_state = d->Idle;
1272         break;
1273     }
1274     case CMD_PUT: {
1275         int permissions;
1276         qint8 iOverwrite;
1277         qint8 iResume;
1278         stream >> url >> iOverwrite >> iResume >> permissions;
1279         JobFlags flags;
1280         if (iOverwrite != 0) {
1281             flags |= Overwrite;
1282         }
1283         if (iResume != 0) {
1284             flags |= Resume;
1285         }
1286 
1287         // Remember that we need to send canResume(), TransferJob is expecting
1288         // it. Well, in theory this shouldn't be done if resume is true.
1289         //   (the resume bool is currently unused)
1290         d->needSendCanResume = true /* !resume */;
1291 
1292         d->m_state = d->InsideMethod;
1293         put(url, permissions, flags);
1294         d->verifyState("put()");
1295         d->m_state = d->Idle;
1296         break;
1297     }
1298     case CMD_STAT: {
1299         stream >> url;
1300         d->m_state = d->InsideMethod;
1301         stat(url); // krazy:exclude=syscalls
1302         d->verifyState("stat()");
1303         d->m_state = d->Idle;
1304         break;
1305     }
1306     case CMD_MIMETYPE: {
1307         stream >> url;
1308         d->m_state = d->InsideMethod;
1309         mimetype(url);
1310         d->verifyState("mimetype()");
1311         d->m_state = d->Idle;
1312         break;
1313     }
1314     case CMD_LISTDIR: {
1315         stream >> url;
1316         d->m_state = d->InsideMethod;
1317         listDir(url);
1318         d->verifyState("listDir()");
1319         d->m_state = d->Idle;
1320         break;
1321     }
1322     case CMD_MKDIR: {
1323         stream >> url >> i;
1324         d->m_state = d->InsideMethod;
1325         mkdir(url, i); // krazy:exclude=syscalls
1326         d->verifyState("mkdir()");
1327         d->m_state = d->Idle;
1328         break;
1329     }
1330     case CMD_RENAME: {
1331         qint8 iOverwrite;
1332         QUrl url2;
1333         stream >> url >> url2 >> iOverwrite;
1334         JobFlags flags;
1335         if (iOverwrite != 0) {
1336             flags |= Overwrite;
1337         }
1338         d->m_state = d->InsideMethod;
1339         rename(url, url2, flags); // krazy:exclude=syscalls
1340         d->verifyState("rename()");
1341         d->m_state = d->Idle;
1342         break;
1343     }
1344     case CMD_SYMLINK: {
1345         qint8 iOverwrite;
1346         QString target;
1347         stream >> target >> url >> iOverwrite;
1348         JobFlags flags;
1349         if (iOverwrite != 0) {
1350             flags |= Overwrite;
1351         }
1352         d->m_state = d->InsideMethod;
1353         symlink(target, url, flags);
1354         d->verifyState("symlink()");
1355         d->m_state = d->Idle;
1356         break;
1357     }
1358     case CMD_COPY: {
1359         int permissions;
1360         qint8 iOverwrite;
1361         QUrl url2;
1362         stream >> url >> url2 >> permissions >> iOverwrite;
1363         JobFlags flags;
1364         if (iOverwrite != 0) {
1365             flags |= Overwrite;
1366         }
1367         d->m_state = d->InsideMethod;
1368         copy(url, url2, permissions, flags);
1369         d->verifyState("copy()");
1370         d->m_state = d->Idle;
1371         break;
1372     }
1373     case CMD_DEL: {
1374         qint8 isFile;
1375         stream >> url >> isFile;
1376         d->m_state = d->InsideMethod;
1377         del(url, isFile != 0);
1378         d->verifyState("del()");
1379         d->m_state = d->Idle;
1380         break;
1381     }
1382     case CMD_CHMOD: {
1383         stream >> url >> i;
1384         d->m_state = d->InsideMethod;
1385         chmod(url, i);
1386         d->verifyState("chmod()");
1387         d->m_state = d->Idle;
1388         break;
1389     }
1390     case CMD_CHOWN: {
1391         QString owner;
1392         QString group;
1393         stream >> url >> owner >> group;
1394         d->m_state = d->InsideMethod;
1395         chown(url, owner, group);
1396         d->verifyState("chown()");
1397         d->m_state = d->Idle;
1398         break;
1399     }
1400     case CMD_SETMODIFICATIONTIME: {
1401         QDateTime dt;
1402         stream >> url >> dt;
1403         d->m_state = d->InsideMethod;
1404         setModificationTime(url, dt);
1405         d->verifyState("setModificationTime()");
1406         d->m_state = d->Idle;
1407         break;
1408     }
1409     case CMD_SPECIAL: {
1410         d->m_state = d->InsideMethod;
1411         special(data);
1412         d->verifyState("special()");
1413         d->m_state = d->Idle;
1414         break;
1415     }
1416     case CMD_META_DATA: {
1417         // qDebug() << "(" << getpid() << ") Incoming meta-data...";
1418         stream >> mIncomingMetaData;
1419         d->rebuildConfig();
1420         break;
1421     }
1422     case CMD_SUBURL: {
1423         stream >> url;
1424         d->m_state = d->InsideMethod;
1425         setSubUrl(url);
1426         d->verifyErrorFinishedNotCalled("setSubUrl()");
1427         d->m_state = d->Idle;
1428         break;
1429     }
1430     case CMD_NONE: {
1431         qCWarning(KIO_CORE) << "Got unexpected CMD_NONE!";
1432         break;
1433     }
1434     case CMD_MULTI_GET: {
1435         d->m_state = d->InsideMethod;
1436         multiGet(data);
1437         d->verifyState("multiGet()");
1438         d->m_state = d->Idle;
1439         break;
1440     }
1441     case CMD_FILESYSTEMFREESPACE: {
1442         stream >> url;
1443 
1444         void *data = static_cast<void *>(&url);
1445 
1446         d->m_state = d->InsideMethod;
1447         virtual_hook(GetFileSystemFreeSpace, data);
1448         d->verifyState("fileSystemFreeSpace()");
1449         d->m_state = d->Idle;
1450         break;
1451     }
1452     default: {
1453         // Some command we don't understand.
1454         // Just ignore it, it may come from some future version of KIO.
1455         break;
1456     }
1457     }
1458 }
1459 
1460 bool SlaveBase::checkCachedAuthentication(AuthInfo &info)
1461 {
1462 #ifndef KIO_ANDROID_STUB
1463     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1464     return (passwdServerClient->checkAuthInfo(&info, metaData(QStringLiteral("window-id")).toLong(), metaData(QStringLiteral("user-timestamp")).toULong()));
1465 #else
1466     return false;
1467 #endif
1468 }
1469 
1470 void SlaveBase::dispatchOpenCommand(int command, const QByteArray &data)
1471 {
1472     QDataStream stream(data);
1473 
1474     switch (command) {
1475     case CMD_READ: {
1476         KIO::filesize_t bytes;
1477         stream >> bytes;
1478         read(bytes);
1479         break;
1480     }
1481     case CMD_WRITE: {
1482         write(data);
1483         break;
1484     }
1485     case CMD_SEEK: {
1486         KIO::filesize_t offset;
1487         stream >> offset;
1488         seek(offset);
1489         break;
1490     }
1491     case CMD_TRUNCATE: {
1492         KIO::filesize_t length;
1493         stream >> length;
1494         void *data = static_cast<void *>(&length);
1495         virtual_hook(Truncate, data);
1496         break;
1497     }
1498     case CMD_NONE:
1499         break;
1500     case CMD_CLOSE:
1501         close(); // must call finish(), which will set d->inOpenLoop=false
1502         break;
1503     default:
1504         // Some command we don't understand.
1505         // Just ignore it, it may come from some future version of KIO.
1506         break;
1507     }
1508 }
1509 
1510 bool SlaveBase::cacheAuthentication(const AuthInfo &info)
1511 {
1512 #ifndef KIO_ANDROID_STUB
1513     KPasswdServerClient *passwdServerClient = d->passwdServerClient();
1514     passwdServerClient->addAuthInfo(info, metaData(QStringLiteral("window-id")).toLongLong());
1515 #endif
1516     return true;
1517 }
1518 
1519 int SlaveBase::connectTimeout()
1520 {
1521     bool ok;
1522     QString tmp = metaData(QStringLiteral("ConnectTimeout"));
1523     int result = tmp.toInt(&ok);
1524     if (ok) {
1525         return result;
1526     }
1527     return DEFAULT_CONNECT_TIMEOUT;
1528 }
1529 
1530 int SlaveBase::proxyConnectTimeout()
1531 {
1532     bool ok;
1533     QString tmp = metaData(QStringLiteral("ProxyConnectTimeout"));
1534     int result = tmp.toInt(&ok);
1535     if (ok) {
1536         return result;
1537     }
1538     return DEFAULT_PROXY_CONNECT_TIMEOUT;
1539 }
1540 
1541 int SlaveBase::responseTimeout()
1542 {
1543     bool ok;
1544     QString tmp = metaData(QStringLiteral("ResponseTimeout"));
1545     int result = tmp.toInt(&ok);
1546     if (ok) {
1547         return result;
1548     }
1549     return DEFAULT_RESPONSE_TIMEOUT;
1550 }
1551 
1552 int SlaveBase::readTimeout()
1553 {
1554     bool ok;
1555     QString tmp = metaData(QStringLiteral("ReadTimeout"));
1556     int result = tmp.toInt(&ok);
1557     if (ok) {
1558         return result;
1559     }
1560     return DEFAULT_READ_TIMEOUT;
1561 }
1562 
1563 bool SlaveBase::wasKilled() const
1564 {
1565     return d->wasKilled;
1566 }
1567 
1568 void SlaveBase::setKillFlag()
1569 {
1570     d->wasKilled = true;
1571 }
1572 
1573 void SlaveBase::send(int cmd, const QByteArray &arr)
1574 {
1575     if (d->runInThread) {
1576         if (!d->appConnection.send(cmd, arr)) {
1577             exit();
1578         }
1579     } else {
1580         slaveWriteError = false;
1581         if (!d->appConnection.send(cmd, arr))
1582         // Note that slaveWriteError can also be set by sigpipe_handler
1583         {
1584             slaveWriteError = true;
1585         }
1586         if (slaveWriteError) {
1587             exit();
1588         }
1589     }
1590 }
1591 
1592 void SlaveBase::virtual_hook(int id, void *data)
1593 {
1594     Q_UNUSED(data);
1595 
1596     switch (id) {
1597     case GetFileSystemFreeSpace: {
1598         error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_FILESYSTEMFREESPACE));
1599         break;
1600     }
1601     case Truncate: {
1602         error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(protocolName(), CMD_TRUNCATE));
1603         break;
1604     }
1605     }
1606 }
1607 
1608 void SlaveBase::setRunInThread(bool b)
1609 {
1610     d->runInThread = b;
1611 }
1612 
1613 void SlaveBase::lookupHost(const QString &host)
1614 {
1615     KIO_DATA << host;
1616     send(MSG_HOST_INFO_REQ, data);
1617 }
1618 
1619 int SlaveBase::waitForHostInfo(QHostInfo &info)
1620 {
1621     QByteArray data;
1622     int result = waitForAnswer(CMD_HOST_INFO, 0, data);
1623 
1624     if (result == -1) {
1625         info.setError(QHostInfo::UnknownError);
1626         info.setErrorString(i18n("Unknown Error"));
1627         return result;
1628     }
1629 
1630     QDataStream stream(data);
1631     QString hostName;
1632     QList<QHostAddress> addresses;
1633     int error;
1634     QString errorString;
1635 
1636     stream >> hostName >> addresses >> error >> errorString;
1637 
1638     info.setHostName(hostName);
1639     info.setAddresses(addresses);
1640     info.setError(QHostInfo::HostInfoError(error));
1641     info.setErrorString(errorString);
1642 
1643     return result;
1644 }
1645 
1646 PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation(const QString &operationDetails)
1647 {
1648     if (d->m_privilegeOperationStatus == OperationNotAllowed) {
1649         QByteArray buffer;
1650         send(MSG_PRIVILEGE_EXEC);
1651         waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer);
1652         QDataStream ds(buffer);
1653         ds >> d->m_privilegeOperationStatus >> d->m_warningTitle >> d->m_warningMessage;
1654     }
1655 
1656     if (metaData(QStringLiteral("UnitTesting")) != QLatin1String("true") && d->m_privilegeOperationStatus == OperationAllowed && !d->m_confirmationAsked) {
1657         // WORKER_MESSAGEBOX_DETAILS_HACK
1658         // SlaveBase::messageBox() overloads miss a parameter to pass an details argument.
1659         // As workaround details are passed instead via metadata before and then cached by the SlaveInterface,
1660         // to be used in the upcoming messageBox call (needs WarningContinueCancelDetailed type)
1661         // TODO: add a messageBox() overload taking details and use here,
1662         // then remove or adapt all code marked with WORKER_MESSAGEBOX_DETAILS
1663         setMetaData(QStringLiteral("privilege_conf_details"), operationDetails);
1664         sendMetaData();
1665 
1666         int result = messageBox(d->m_warningMessage, WarningContinueCancelDetailed, d->m_warningTitle, QString(), QString(), QString());
1667         d->m_privilegeOperationStatus = result == Continue ? OperationAllowed : OperationCanceled;
1668         d->m_confirmationAsked = true;
1669     }
1670 
1671     return KIO::PrivilegeOperationStatus(d->m_privilegeOperationStatus);
1672 }
1673 
1674 void SlaveBase::addTemporaryAuthorization(const QString &action)
1675 {
1676     d->m_tempAuths.insert(action);
1677 }
1678 
1679 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 66)
1680 PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation()
1681 {
1682     return KIO::OperationNotAllowed;
1683 }
1684 #endif