File indexing completed on 2024-04-28 11:41:09
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