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