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