diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp index f399695e..836a684a 100644 --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -1,1507 +1,1504 @@ /* * This file is part of the KDE libraries * Copyright (c) 2000 Waldo Bastian * Copyright (c) 2000 David Faure * Copyright (c) 2000 Stephan Kulow * Copyright (c) 2007 Thiago Macieira * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * **/ #include "slavebase.h" #include #include #include #include #ifdef Q_OS_WIN #include #endif #include #include #include #include #include #include #include #include #include #include "kremoteencoding.h" #include "kioglobal_p.h" #include "connection_p.h" #include "commands_p.h" #include "ioslave_defaults.h" #include "slaveinterface.h" #include "kpasswdserverclient.h" #include "kiocoredebug.h" #ifndef NDEBUG #if HAVE_BACKTRACE #include #endif #endif extern "C" { Q_NORETURN static void sigsegv_handler(int sig); static void sigpipe_handler(int sig); } using namespace KIO; typedef QList AuthKeysList; typedef QMap AuthKeysMap; #define KIO_DATA QByteArray data; QDataStream stream( &data, QIODevice::WriteOnly ); stream #define KIO_FILESIZE_T(x) quint64(x) static const int KIO_MAX_ENTRIES_PER_BATCH = 200; static const int KIO_MAX_SEND_BATCH_TIME = 300; namespace KIO { class SlaveBasePrivate { public: SlaveBase *q; SlaveBasePrivate(SlaveBase *owner): q(owner), nextTimeoutMsecs(0), m_passwdServerClient(nullptr), m_confirmationAsked(false), m_privilegeOperationStatus(OperationNotAllowed) { if (!qEnvironmentVariableIsEmpty("KIOSLAVE_ENABLE_TESTMODE")) { QStandardPaths::enableTestMode(true); } pendingListEntries.reserve(KIO_MAX_ENTRIES_PER_BATCH); } ~SlaveBasePrivate() { delete m_passwdServerClient; } UDSEntryList pendingListEntries; QElapsedTimer m_timeSinceLastBatch; Connection appConnection; QString poolSocket; bool isConnectedToApp; QString slaveid; bool resume: 1; bool needSendCanResume: 1; bool onHold: 1; bool wasKilled: 1; bool inOpenLoop: 1; bool exit_loop: 1; MetaData configData; KConfig *config; KConfigGroup *configGroup; QUrl onHoldUrl; QElapsedTimer lastTimeout; QElapsedTimer nextTimeout; qint64 nextTimeoutMsecs; KIO::filesize_t totalSize; KRemoteEncoding *remotefile; enum { Idle, InsideMethod, FinishedCalled, ErrorCalled } m_state; QByteArray timeoutData; KPasswdServerClient *m_passwdServerClient; bool m_rootEntryListed = false; bool m_confirmationAsked; QSet m_tempAuths; QString m_warningCaption; QString m_warningMessage; int m_privilegeOperationStatus; PrivilegeOperationStatus askConfirmation() { int status = q->messageBox(SlaveBase::WarningContinueCancel, m_warningMessage, m_warningCaption, QStringLiteral("Continue"), QStringLiteral("Cancel")); switch (status) { case SlaveBase::Continue: return OperationAllowed; case SlaveBase::Cancel: return OperationCanceled; default: return OperationNotAllowed; } } // Reconstructs configGroup from configData and mIncomingMetaData void rebuildConfig() { configGroup->deleteGroup(KConfigGroup::WriteConfigFlags()); // mIncomingMetaData cascades over config, so we write config first, // to let it be overwritten MetaData::ConstIterator end = configData.constEnd(); for (MetaData::ConstIterator it = configData.constBegin(); it != end; ++it) { configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags()); } end = q->mIncomingMetaData.constEnd(); for (MetaData::ConstIterator it = q->mIncomingMetaData.constBegin(); it != end; ++it) { configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags()); } } void verifyState(const char *cmdName) { if ((m_state != FinishedCalled) && (m_state != ErrorCalled)) { qCWarning(KIO_CORE) << cmdName << "did not call finished() or error()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; } } void verifyErrorFinishedNotCalled(const char *cmdName) { if (m_state == FinishedCalled || m_state == ErrorCalled) { qCWarning(KIO_CORE) << cmdName << "called finished() or error(), but it's not supposed to! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; } } KPasswdServerClient *passwdServerClient() { if (!m_passwdServerClient) { m_passwdServerClient = new KPasswdServerClient; } return m_passwdServerClient; } }; } static SlaveBase *globalSlave; static volatile bool slaveWriteError = false; static const char *s_protocol; #ifdef Q_OS_UNIX extern "C" { static void genericsig_handler(int sigNumber) { ::signal(sigNumber, SIG_IGN); //WABA: Don't do anything that requires malloc, we can deadlock on it since //a SIGTERM signal can come in while we are in malloc/free. //qDebug()<<"kioslave : exiting due to signal "<setKillFlag(); } ::signal(SIGALRM, SIG_DFL); alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit } } #endif ////////////// SlaveBase::SlaveBase(const QByteArray &protocol, const QByteArray &pool_socket, const QByteArray &app_socket) : mProtocol(protocol), d(new SlaveBasePrivate(this)) { Q_ASSERT(!app_socket.isEmpty()); d->poolSocket = QFile::decodeName(pool_socket); s_protocol = protocol.data(); #ifdef Q_OS_UNIX if (qEnvironmentVariableIsEmpty("KDE_DEBUG")) { ::signal(SIGSEGV, &sigsegv_handler); ::signal(SIGILL, &sigsegv_handler); ::signal(SIGTRAP, &sigsegv_handler); ::signal(SIGABRT, &sigsegv_handler); ::signal(SIGBUS, &sigsegv_handler); ::signal(SIGALRM, &sigsegv_handler); ::signal(SIGFPE, &sigsegv_handler); #ifdef SIGPOLL ::signal(SIGPOLL, &sigsegv_handler); #endif #ifdef SIGSYS ::signal(SIGSYS, &sigsegv_handler); #endif #ifdef SIGVTALRM ::signal(SIGVTALRM, &sigsegv_handler); #endif #ifdef SIGXCPU ::signal(SIGXCPU, &sigsegv_handler); #endif #ifdef SIGXFSZ ::signal(SIGXFSZ, &sigsegv_handler); #endif } struct sigaction act; act.sa_handler = sigpipe_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGPIPE, &act, nullptr); ::signal(SIGINT, &genericsig_handler); ::signal(SIGQUIT, &genericsig_handler); ::signal(SIGTERM, &genericsig_handler); #endif globalSlave = this; d->isConnectedToApp = true; // by kahl for netmgr (need a way to identify slaves) d->slaveid = protocol; d->slaveid += QString::number(getpid()); d->resume = false; d->needSendCanResume = false; d->config = new KConfig(QString(), KConfig::SimpleConfig); // The KConfigGroup needs the KConfig to exist during its whole lifetime. d->configGroup = new KConfigGroup(d->config, QString()); d->onHold = false; d->wasKilled = false; // d->processed_size = 0; d->totalSize = 0; connectSlave(QFile::decodeName(app_socket)); d->remotefile = nullptr; d->inOpenLoop = false; d->exit_loop = false; } SlaveBase::~SlaveBase() { delete d->configGroup; delete d->config; delete d->remotefile; delete d; s_protocol = ""; } void SlaveBase::dispatchLoop() { while (!d->exit_loop) { if (d->nextTimeout.isValid() && (d->nextTimeout.hasExpired(d->nextTimeoutMsecs))) { QByteArray data = d->timeoutData; d->nextTimeout.invalidate(); d->timeoutData = QByteArray(); special(data); } Q_ASSERT(d->appConnection.inited()); int ms = -1; if (d->nextTimeout.isValid()) { ms = qMax(d->nextTimeout.elapsed() - d->nextTimeoutMsecs, 1); } int ret = -1; if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(ms)) { // dispatch application messages int cmd; QByteArray data; ret = d->appConnection.read(&cmd, data); if (ret != -1) { if (d->inOpenLoop) { dispatchOpenCommand(cmd, data); } else { dispatch(cmd, data); } } } else { ret = d->appConnection.isConnected() ? 0 : -1; } if (ret == -1) { // some error occurred, perhaps no more application // When the app exits, should the slave be put back in the pool ? if (!d->exit_loop && d->isConnectedToApp && !d->poolSocket.isEmpty()) { disconnectSlave(); d->isConnectedToApp = false; closeConnection(); connectSlave(d->poolSocket); } else { break; } } //I think we get here when we were killed in dispatch() and not in select() if (wasKilled()) { //qDebug() << "slave was killed, returning"; break; } // execute deferred deletes QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } // execute deferred deletes QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } void SlaveBase::connectSlave(const QString &address) { d->appConnection.connectToRemote(QUrl(address)); if (!d->appConnection.inited()) { /*qDebug() << "failed to connect to" << address << endl << "Reason:" << d->appConnection.errorString();*/ exit(); } d->inOpenLoop = false; } void SlaveBase::disconnectSlave() { d->appConnection.close(); } void SlaveBase::setMetaData(const QString &key, const QString &value) { mOutgoingMetaData.insert(key, value); // replaces existing key if already there } QString SlaveBase::metaData(const QString &key) const { auto it = mIncomingMetaData.find(key); if (it != mIncomingMetaData.end()) { return *it; } return d->configData.value(key); } MetaData SlaveBase::allMetaData() const { return mIncomingMetaData; } bool SlaveBase::hasMetaData(const QString &key) const { if (mIncomingMetaData.contains(key)) { return true; } if (d->configData.contains(key)) { return true; } return false; } KConfigGroup *SlaveBase::config() { return d->configGroup; } void SlaveBase::sendMetaData() { sendAndKeepMetaData(); mOutgoingMetaData.clear(); } void SlaveBase::sendAndKeepMetaData() { if (!mOutgoingMetaData.isEmpty()) { KIO_DATA << mOutgoingMetaData; send(INF_META_DATA, data); } } KRemoteEncoding *SlaveBase::remoteEncoding() { if (d->remotefile) { return d->remotefile; } const QByteArray charset(metaData(QStringLiteral("Charset")).toLatin1()); return (d->remotefile = new KRemoteEncoding(charset)); } void SlaveBase::data(const QByteArray &data) { sendMetaData(); send(MSG_DATA, data); } void SlaveBase::dataReq() { //sendMetaData(); if (d->needSendCanResume) { canResume(0); } send(MSG_DATA_REQ); } void SlaveBase::opened() { sendMetaData(); send(MSG_OPENED); d->inOpenLoop = true; } void SlaveBase::error(int _errid, const QString &_text) { if (d->m_state == d->ErrorCalled) { qCWarning(KIO_CORE) << "error() called twice! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } else if (d->m_state == d->FinishedCalled) { qCWarning(KIO_CORE) << "error() called after finished()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } d->m_state = d->ErrorCalled; mIncomingMetaData.clear(); // Clear meta data d->rebuildConfig(); mOutgoingMetaData.clear(); KIO_DATA << static_cast(_errid) << _text; send(MSG_ERROR, data); //reset d->totalSize = 0; d->inOpenLoop = false; d->m_confirmationAsked = false; d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::connected() { send(MSG_CONNECTED); } void SlaveBase::finished() { if (!d->pendingListEntries.isEmpty()) { if (!d->m_rootEntryListed) { qCWarning(KIO_CORE) << "UDSEntry for '.' not found, creating a default one. Please fix the" << QCoreApplication::applicationName() << "KIO slave"; KIO::UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_SIZE, 0); entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); d->pendingListEntries.append(entry); } listEntries(d->pendingListEntries); d->pendingListEntries.clear(); } if (d->m_state == d->FinishedCalled) { qCWarning(KIO_CORE) << "finished() called twice! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } else if (d->m_state == d->ErrorCalled) { qCWarning(KIO_CORE) << "finished() called after error()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } d->m_state = d->FinishedCalled; mIncomingMetaData.clear(); // Clear meta data d->rebuildConfig(); sendMetaData(); send(MSG_FINISHED); // reset d->totalSize = 0; d->inOpenLoop = false; d->m_rootEntryListed = false; d->m_confirmationAsked = false; d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::needSubUrlData() { send(MSG_NEED_SUBURL_DATA); } void SlaveBase::slaveStatus(const QString &host, bool connected) { qint64 pid = getpid(); qint8 b = connected ? 1 : 0; - KIO_DATA << pid << mProtocol << host << b; - if (d->onHold) { - stream << d->onHoldUrl; - } - send(MSG_SLAVE_STATUS, data); + KIO_DATA << pid << mProtocol << host << b << d->onHold << d->onHoldUrl; + send(MSG_SLAVE_STATUS_V2, data); } void SlaveBase::canResume() { send(MSG_CANRESUME); } void SlaveBase::totalSize(KIO::filesize_t _bytes) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(INF_TOTAL_SIZE, data); //this one is usually called before the first item is listed in listDir() d->totalSize = _bytes; } void SlaveBase::processedSize(KIO::filesize_t _bytes) { bool emitSignal = false; if (_bytes == d->totalSize) { emitSignal = true; } else { if (d->lastTimeout.isValid()) { emitSignal = d->lastTimeout.hasExpired(100); // emit size 10 times a second } else { emitSignal = true; } } if (emitSignal) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(INF_PROCESSED_SIZE, data); d->lastTimeout.start(); } // d->processed_size = _bytes; } void SlaveBase::written(KIO::filesize_t _bytes) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(MSG_WRITTEN, data); } void SlaveBase::position(KIO::filesize_t _pos) { KIO_DATA << KIO_FILESIZE_T(_pos); send(INF_POSITION, data); } void SlaveBase::processedPercent(float /* percent */) { //qDebug() << "STUB"; } void SlaveBase::speed(unsigned long _bytes_per_second) { KIO_DATA << static_cast(_bytes_per_second); send(INF_SPEED, data); } void SlaveBase::redirection(const QUrl &_url) { KIO_DATA << _url; send(INF_REDIRECTION, data); } void SlaveBase::errorPage() { send(INF_ERROR_PAGE); } static bool isSubCommand(int cmd) { return ((cmd == CMD_REPARSECONFIGURATION) || (cmd == CMD_META_DATA) || (cmd == CMD_CONFIG) || (cmd == CMD_SUBURL) || (cmd == CMD_SLAVE_STATUS) || (cmd == CMD_SLAVE_CONNECT) || (cmd == CMD_SLAVE_HOLD) || (cmd == CMD_MULTI_GET)); } void SlaveBase::mimeType(const QString &_type) { //qDebug() << _type; int cmd; do { // Send the meta-data each time we send the mime-type. if (!mOutgoingMetaData.isEmpty()) { //qDebug() << "emitting meta data"; KIO_DATA << mOutgoingMetaData; send(INF_META_DATA, data); } KIO_DATA << _type; send(INF_MIME_TYPE, data); while (true) { cmd = 0; int ret = -1; if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) { ret = d->appConnection.read(&cmd, data); } if (ret == -1) { //qDebug() << "read error"; exit(); } //qDebug() << "got" << cmd; if (cmd == CMD_HOST) { // Ignore. continue; } if (!isSubCommand(cmd)) { break; } dispatch(cmd, data); } } while (cmd != CMD_NONE); mOutgoingMetaData.clear(); } void SlaveBase::exit() { d->exit_loop = true; // Using ::exit() here is too much (crashes in qdbus's qglobalstatic object), // so let's cleanly exit dispatchLoop() instead. // Update: we do need to call exit(), otherwise a long download (get()) would // keep going until it ends, even though the application exited. ::exit(255); } void SlaveBase::warning(const QString &_msg) { KIO_DATA << _msg; send(INF_WARNING, data); } void SlaveBase::infoMessage(const QString &_msg) { KIO_DATA << _msg; send(INF_INFOMESSAGE, data); } #ifndef KIOCORE_NO_DEPRECATED bool SlaveBase::requestNetwork(const QString &host) { KIO_DATA << host << d->slaveid; send(MSG_NET_REQUEST, data); if (waitForAnswer(INF_NETWORK_STATUS, 0, data) != -1) { bool status; QDataStream stream(data); stream >> status; return status; } else { return false; } } void SlaveBase::dropNetwork(const QString &host) { KIO_DATA << host << d->slaveid; send(MSG_NET_DROP, data); } #endif void SlaveBase::statEntry(const UDSEntry &entry) { KIO_DATA << entry; send(MSG_STAT_ENTRY, data); } #ifndef KIOCORE_NO_DEPRECATED void SlaveBase::listEntry(const UDSEntry &entry, bool _ready) { if (_ready) { // #366795: many slaves don't create an entry for ".", so we keep track if they do // and we provide a fallback in finished() otherwise. if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1String(".")) { d->m_rootEntryListed = true; } listEntries(d->pendingListEntries); d->pendingListEntries.clear(); } else { listEntry(entry); } } #endif void SlaveBase::listEntry(const UDSEntry &entry) { // #366795: many slaves don't create an entry for ".", so we keep track if they do // and we provide a fallback in finished() otherwise. if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1String(".")) { d->m_rootEntryListed = true; } // We start measuring the time from the point we start filling the list if (d->pendingListEntries.isEmpty()) { d->m_timeSinceLastBatch.restart(); } d->pendingListEntries.append(entry); // If more then KIO_MAX_SEND_BATCH_TIME time is passed, emit the current batch // Also emit if we have piled up a large number of entries already, to save memory (and time) if (d->m_timeSinceLastBatch.elapsed() > KIO_MAX_SEND_BATCH_TIME || d->pendingListEntries.size() > KIO_MAX_ENTRIES_PER_BATCH) { listEntries(d->pendingListEntries); d->pendingListEntries.clear(); // Restart time d->m_timeSinceLastBatch.restart(); } } void SlaveBase::listEntries(const UDSEntryList &list) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); foreach (const UDSEntry &entry, list) { stream << entry; } send(MSG_LIST_ENTRIES, data); } Q_NORETURN static void sigsegv_handler(int sig) { #ifdef Q_OS_UNIX ::signal(sig, SIG_DFL); // Next one kills //Kill us if we deadlock ::signal(SIGALRM, SIG_DFL); alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit // Debug and printf should be avoided because they might // call malloc.. and get in a nice recursive malloc loop char buffer[120]; qsnprintf(buffer, sizeof(buffer), "kioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig); write(2, buffer, strlen(buffer)); #ifndef NDEBUG #if HAVE_BACKTRACE void *trace[256]; int n = backtrace(trace, 256); if (n) { backtrace_symbols_fd(trace, n, 2); } #endif #endif ::exit(1); #endif } static void sigpipe_handler(int) { // We ignore a SIGPIPE in slaves. // A SIGPIPE can happen in two cases: // 1) Communication error with application. // 2) Communication error with network. slaveWriteError = true; // Don't add anything else here, especially no debug output } void SlaveBase::setHost(QString const &, quint16, QString const &, QString const &) { } KIOCORE_EXPORT QString KIO::unsupportedActionErrorString(const QString &protocol, int cmd) { switch (cmd) { case CMD_CONNECT: return i18n("Opening connections is not supported with the protocol %1.", protocol); case CMD_DISCONNECT: return i18n("Closing connections is not supported with the protocol %1.", protocol); case CMD_STAT: return i18n("Accessing files is not supported with the protocol %1.", protocol); case CMD_PUT: return i18n("Writing to %1 is not supported.", protocol); case CMD_SPECIAL: return i18n("There are no special actions available for protocol %1.", protocol); case CMD_LISTDIR: return i18n("Listing folders is not supported for protocol %1.", protocol); case CMD_GET: return i18n("Retrieving data from %1 is not supported.", protocol); case CMD_MIMETYPE: return i18n("Retrieving mime type information from %1 is not supported.", protocol); case CMD_RENAME: return i18n("Renaming or moving files within %1 is not supported.", protocol); case CMD_SYMLINK: return i18n("Creating symlinks is not supported with protocol %1.", protocol); case CMD_COPY: return i18n("Copying files within %1 is not supported.", protocol); case CMD_DEL: return i18n("Deleting files from %1 is not supported.", protocol); case CMD_MKDIR: return i18n("Creating folders is not supported with protocol %1.", protocol); case CMD_CHMOD: return i18n("Changing the attributes of files is not supported with protocol %1.", protocol); case CMD_CHOWN: return i18n("Changing the ownership of files is not supported with protocol %1.", protocol); case CMD_SUBURL: return i18n("Using sub-URLs with %1 is not supported.", protocol); case CMD_MULTI_GET: return i18n("Multiple get is not supported with protocol %1.", protocol); case CMD_OPEN: return i18n("Opening files is not supported with protocol %1.", protocol); default: return i18n("Protocol %1 does not support action %2.", protocol, cmd); }/*end switch*/ } void SlaveBase::openConnection(void) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CONNECT)); } void SlaveBase::closeConnection(void) { } // No response! void SlaveBase::stat(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_STAT)); } void SlaveBase::put(QUrl const &, int, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_PUT)); } void SlaveBase::special(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SPECIAL)); } void SlaveBase::listDir(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTDIR)); } void SlaveBase::get(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_GET)); } void SlaveBase::open(QUrl const &, QIODevice::OpenMode) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_OPEN)); } void SlaveBase::read(KIO::filesize_t) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_READ)); } void SlaveBase::write(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_WRITE)); } void SlaveBase::seek(KIO::filesize_t) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SEEK)); } void SlaveBase::close() { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CLOSE)); } void SlaveBase::mimetype(QUrl const &url) { get(url); } void SlaveBase::rename(QUrl const &, QUrl const &, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_RENAME)); } void SlaveBase::symlink(QString const &, QUrl const &, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SYMLINK)); } void SlaveBase::copy(QUrl const &, QUrl const &, int, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); } void SlaveBase::del(QUrl const &, bool) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_DEL)); } void SlaveBase::setLinkDest(const QUrl &, const QString &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETLINKDEST)); } void SlaveBase::mkdir(QUrl const &, int) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); } void SlaveBase::chmod(QUrl const &, int) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); } void SlaveBase::setModificationTime(QUrl const &, const QDateTime &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETMODIFICATIONTIME)); } void SlaveBase::chown(QUrl const &, const QString &, const QString &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHOWN)); } void SlaveBase::setSubUrl(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); } void SlaveBase::multiGet(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MULTI_GET)); } void SlaveBase::slave_status() { slaveStatus(QString(), false); } void SlaveBase::reparseConfiguration() { delete d->remotefile; d->remotefile = nullptr; } bool SlaveBase::openPasswordDialog(AuthInfo &info, const QString &errorMsg) { const int errorCode = openPasswordDialogV2(info, errorMsg); return errorCode == KJob::NoError; } int SlaveBase::openPasswordDialogV2(AuthInfo &info, const QString &errorMsg) { const long windowId = metaData(QStringLiteral("window-id")).toLong(); const unsigned long userTimestamp = metaData(QStringLiteral("user-timestamp")).toULong(); QString errorMessage; if (metaData(QStringLiteral("no-auth-prompt")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) { errorMessage = QStringLiteral(""); } else { errorMessage = errorMsg; } AuthInfo dlgInfo(info); // Make sure the modified flag is not set. dlgInfo.setModified(false); // Prevent queryAuthInfo from caching the user supplied password since // we need the ioslaves to first authenticate against the server with // it to ensure it is valid. dlgInfo.setExtraField(QStringLiteral("skip-caching-on-query"), true); KPasswdServerClient *passwdServerClient = d->passwdServerClient(); const int errCode = passwdServerClient->queryAuthInfo(&dlgInfo, errorMessage, windowId, userTimestamp); if (errCode == KJob::NoError) { info = dlgInfo; } return errCode; } int SlaveBase::messageBox(MessageBoxType type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo) { return messageBox(text, type, caption, buttonYes, buttonNo, QString()); } int SlaveBase::messageBox(const QString &text, MessageBoxType type, const QString &caption, const QString &_buttonYes, const QString &_buttonNo, const QString &dontAskAgainName) { QString buttonYes = _buttonYes.isNull() ? i18n("&Yes") : _buttonYes; QString buttonNo = _buttonNo.isNull() ? i18n("&No") : _buttonNo; //qDebug() << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo; KIO_DATA << static_cast(type) << text << caption << buttonYes << buttonNo << dontAskAgainName; send(INF_MESSAGEBOX, data); if (waitForAnswer(CMD_MESSAGEBOXANSWER, 0, data) != -1) { QDataStream stream(data); int answer; stream >> answer; //qDebug() << "got messagebox answer" << answer; return answer; } else { return 0; // communication failure } } bool SlaveBase::canResume(KIO::filesize_t offset) { //qDebug() << "offset=" << KIO::number(offset); d->needSendCanResume = false; KIO_DATA << KIO_FILESIZE_T(offset); send(MSG_RESUME, data); if (offset) { int cmd; if (waitForAnswer(CMD_RESUMEANSWER, CMD_NONE, data, &cmd) != -1) { //qDebug() << "returning" << (cmd == CMD_RESUMEANSWER); return cmd == CMD_RESUMEANSWER; } else { return false; } } else { // No resuming possible -> no answer to wait for return true; } } int SlaveBase::waitForAnswer(int expected1, int expected2, QByteArray &data, int *pCmd) { int cmd = 0; int result = -1; for (;;) { if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) { result = d->appConnection.read(&cmd, data); } if (result == -1) { //qDebug() << "read error."; return -1; } if (cmd == expected1 || cmd == expected2) { if (pCmd) { *pCmd = cmd; } return result; } if (isSubCommand(cmd)) { dispatch(cmd, data); } else { qFatal("Fatal Error: Got cmd %d, while waiting for an answer!", cmd); } } } int SlaveBase::readData(QByteArray &buffer) { int result = waitForAnswer(MSG_DATA, 0, buffer); //qDebug() << "readData: length = " << result << " "; return result; } void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data) { if (timeout > 0) { d->nextTimeoutMsecs = timeout*1000; // from seconds to miliseconds d->nextTimeout.start(); } else if (timeout == 0) { d->nextTimeoutMsecs = 1000; // Immediate timeout d->nextTimeout.start(); } else { d->nextTimeout.invalidate(); // Canceled } d->timeoutData = data; } void SlaveBase::dispatch(int command, const QByteArray &data) { QDataStream stream(data); QUrl url; int i; switch (command) { case CMD_HOST: { QString passwd; QString host, user; quint16 port; stream >> host >> port >> user >> passwd; d->m_state = d->InsideMethod; setHost(host, port, user, passwd); d->verifyErrorFinishedNotCalled("setHost()"); d->m_state = d->Idle; } break; case CMD_CONNECT: { openConnection(); } break; case CMD_DISCONNECT: { closeConnection(); } break; case CMD_SLAVE_STATUS: { d->m_state = d->InsideMethod; slave_status(); // TODO verify that the slave has called slaveStatus()? d->verifyErrorFinishedNotCalled("slave_status()"); d->m_state = d->Idle; } break; case CMD_SLAVE_CONNECT: { d->onHold = false; QString app_socket; QDataStream stream(data); stream >> app_socket; d->appConnection.send(MSG_SLAVE_ACK); disconnectSlave(); d->isConnectedToApp = true; connectSlave(app_socket); virtual_hook(AppConnectionMade, nullptr); } break; case CMD_SLAVE_HOLD: { QUrl url; QDataStream stream(data); stream >> url; d->onHoldUrl = url; d->onHold = true; disconnectSlave(); d->isConnectedToApp = false; // Do not close connection! connectSlave(d->poolSocket); } break; case CMD_REPARSECONFIGURATION: { d->m_state = d->InsideMethod; reparseConfiguration(); d->verifyErrorFinishedNotCalled("reparseConfiguration()"); d->m_state = d->Idle; } break; case CMD_CONFIG: { stream >> d->configData; d->rebuildConfig(); delete d->remotefile; d->remotefile = nullptr; } break; case CMD_GET: { stream >> url; d->m_state = d->InsideMethod; get(url); d->verifyState("get()"); d->m_state = d->Idle; } break; case CMD_OPEN: { stream >> url >> i; QIODevice::OpenMode mode = QFlag(i); d->m_state = d->InsideMethod; open(url, mode); //krazy:exclude=syscalls d->m_state = d->Idle; } break; case CMD_PUT: { int permissions; qint8 iOverwrite, iResume; stream >> url >> iOverwrite >> iResume >> permissions; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } if (iResume != 0) { flags |= Resume; } // Remember that we need to send canResume(), TransferJob is expecting // it. Well, in theory this shouldn't be done if resume is true. // (the resume bool is currently unused) d->needSendCanResume = true /* !resume */; d->m_state = d->InsideMethod; put(url, permissions, flags); d->verifyState("put()"); d->m_state = d->Idle; } break; case CMD_STAT: { stream >> url; d->m_state = d->InsideMethod; stat(url); //krazy:exclude=syscalls d->verifyState("stat()"); d->m_state = d->Idle; } break; case CMD_MIMETYPE: { stream >> url; d->m_state = d->InsideMethod; mimetype(url); d->verifyState("mimetype()"); d->m_state = d->Idle; } break; case CMD_LISTDIR: { stream >> url; d->m_state = d->InsideMethod; listDir(url); d->verifyState("listDir()"); d->m_state = d->Idle; } break; case CMD_MKDIR: { stream >> url >> i; d->m_state = d->InsideMethod; mkdir(url, i); //krazy:exclude=syscalls d->verifyState("mkdir()"); d->m_state = d->Idle; } break; case CMD_RENAME: { qint8 iOverwrite; QUrl url2; stream >> url >> url2 >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; rename(url, url2, flags); //krazy:exclude=syscalls d->verifyState("rename()"); d->m_state = d->Idle; } break; case CMD_SYMLINK: { qint8 iOverwrite; QString target; stream >> target >> url >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; symlink(target, url, flags); d->verifyState("symlink()"); d->m_state = d->Idle; } break; case CMD_COPY: { int permissions; qint8 iOverwrite; QUrl url2; stream >> url >> url2 >> permissions >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; copy(url, url2, permissions, flags); d->verifyState("copy()"); d->m_state = d->Idle; } break; case CMD_DEL: { qint8 isFile; stream >> url >> isFile; d->m_state = d->InsideMethod; del(url, isFile != 0); d->verifyState("del()"); d->m_state = d->Idle; } break; case CMD_CHMOD: { stream >> url >> i; d->m_state = d->InsideMethod; chmod(url, i); d->verifyState("chmod()"); d->m_state = d->Idle; } break; case CMD_CHOWN: { QString owner, group; stream >> url >> owner >> group; d->m_state = d->InsideMethod; chown(url, owner, group); d->verifyState("chown()"); d->m_state = d->Idle; } break; case CMD_SETMODIFICATIONTIME: { QDateTime dt; stream >> url >> dt; d->m_state = d->InsideMethod; setModificationTime(url, dt); d->verifyState("setModificationTime()"); d->m_state = d->Idle; } break; case CMD_SPECIAL: { d->m_state = d->InsideMethod; special(data); d->verifyState("special()"); d->m_state = d->Idle; } break; case CMD_META_DATA: { //qDebug() << "(" << getpid() << ") Incoming meta-data..."; stream >> mIncomingMetaData; d->rebuildConfig(); } break; case CMD_SUBURL: { stream >> url; d->m_state = d->InsideMethod; setSubUrl(url); d->verifyErrorFinishedNotCalled("setSubUrl()"); d->m_state = d->Idle; } break; case CMD_NONE: { qCWarning(KIO_CORE) << "Got unexpected CMD_NONE!"; } break; case CMD_MULTI_GET: { d->m_state = d->InsideMethod; multiGet(data); d->verifyState("multiGet()"); d->m_state = d->Idle; } break; case CMD_FILESYSTEMFREESPACE: { stream >> url; void *data = static_cast(&url); d->m_state = d->InsideMethod; virtual_hook(GetFileSystemFreeSpace, data); d->verifyState("fileSystemFreeSpace()"); d->m_state = d->Idle; } break; default: { // Some command we don't understand. // Just ignore it, it may come from some future version of KDE. } break; } } bool SlaveBase::checkCachedAuthentication(AuthInfo &info) { KPasswdServerClient *passwdServerClient = d->passwdServerClient(); return (passwdServerClient->checkAuthInfo(&info, metaData(QStringLiteral("window-id")).toLong(), metaData(QStringLiteral("user-timestamp")).toULong())); } void SlaveBase::dispatchOpenCommand(int command, const QByteArray &data) { QDataStream stream(data); switch (command) { case CMD_READ: { KIO::filesize_t bytes; stream >> bytes; read(bytes); break; } case CMD_WRITE: { write(data); break; } case CMD_SEEK: { KIO::filesize_t offset; stream >> offset; seek(offset); break; } case CMD_NONE: break; case CMD_CLOSE: close(); // must call finish(), which will set d->inOpenLoop=false break; default: // Some command we don't understand. // Just ignore it, it may come from some future version of KDE. break; } } bool SlaveBase::cacheAuthentication(const AuthInfo &info) { KPasswdServerClient *passwdServerClient = d->passwdServerClient(); passwdServerClient->addAuthInfo(info, metaData(QStringLiteral("window-id")).toLongLong()); return true; } int SlaveBase::connectTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ConnectTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_CONNECT_TIMEOUT; } int SlaveBase::proxyConnectTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ProxyConnectTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_PROXY_CONNECT_TIMEOUT; } int SlaveBase::responseTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ResponseTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_RESPONSE_TIMEOUT; } int SlaveBase::readTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ReadTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_READ_TIMEOUT; } bool SlaveBase::wasKilled() const { return d->wasKilled; } void SlaveBase::setKillFlag() { d->wasKilled = true; } void SlaveBase::send(int cmd, const QByteArray &arr) { slaveWriteError = false; if (!d->appConnection.send(cmd, arr)) // Note that slaveWriteError can also be set by sigpipe_handler { slaveWriteError = true; } if (slaveWriteError) { exit(); } } void SlaveBase::virtual_hook(int id, void *data) { Q_UNUSED(data); switch(id) { case GetFileSystemFreeSpace: { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_FILESYSTEMFREESPACE)); } break; } } void SlaveBase::lookupHost(const QString &host) { KIO_DATA << host; send(MSG_HOST_INFO_REQ, data); } int SlaveBase::waitForHostInfo(QHostInfo &info) { QByteArray data; int result = waitForAnswer(CMD_HOST_INFO, 0, data); if (result == -1) { info.setError(QHostInfo::UnknownError); info.setErrorString(i18n("Unknown Error")); return result; } QDataStream stream(data); QString hostName; QList addresses; int error; QString errorString; stream >> hostName >> addresses >> error >> errorString; info.setHostName(hostName); info.setAddresses(addresses); info.setError(QHostInfo::HostInfoError(error)); info.setErrorString(errorString); return result; } PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation() { if (d->m_privilegeOperationStatus == OperationNotAllowed) { QByteArray buffer; send(MSG_PRIVILEGE_EXEC); waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer); QDataStream ds(buffer); ds >> d->m_privilegeOperationStatus >> d->m_warningCaption >> d-> m_warningMessage; } if (d->m_privilegeOperationStatus == OperationAllowed && !d->m_confirmationAsked) { d->m_privilegeOperationStatus = d->askConfirmation(); d->m_confirmationAsked = true; } return KIO::PrivilegeOperationStatus(d->m_privilegeOperationStatus); } void SlaveBase::addTemporaryAuthorization(const QString &action) { d->m_tempAuths.insert(action); } diff --git a/src/core/slaveinterface.cpp b/src/core/slaveinterface.cpp index dcb68917..0a9e6133 100644 --- a/src/core/slaveinterface.cpp +++ b/src/core/slaveinterface.cpp @@ -1,440 +1,441 @@ /* This file is part of the KDE libraries Copyright (C) 2000 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "slaveinterface.h" #include "slaveinterface_p.h" #include "usernotificationhandler_p.h" #include "slavebase.h" #include "connection_p.h" #include "commands_p.h" #include "hostinfo.h" #include #include #include #include #include #include #include #include #include #include using namespace KIO; Q_GLOBAL_STATIC(UserNotificationHandler, globalUserNotificationHandler) SlaveInterface::SlaveInterface(SlaveInterfacePrivate &dd, QObject *parent) : QObject(parent), d_ptr(&dd) { connect(&d_ptr->speed_timer, SIGNAL(timeout()), SLOT(calcSpeed())); } SlaveInterface::~SlaveInterface() { // Note: no Debug() here (scheduler is deleted very late) delete d_ptr; } void SlaveInterface::setConnection(Connection *connection) { Q_D(SlaveInterface); d->connection = connection; } Connection *SlaveInterface::connection() const { const Q_D(SlaveInterface); return d->connection; } static KIO::filesize_t readFilesize_t(QDataStream &stream) { KIO::filesize_t result; stream >> result; return result; } bool SlaveInterface::dispatch() { Q_D(SlaveInterface); Q_ASSERT(d->connection); int cmd; QByteArray data; int ret = d->connection->read(&cmd, data); if (ret == -1) { return false; } return dispatch(cmd, data); } void SlaveInterface::calcSpeed() { Q_D(SlaveInterface); if (d->slave_calcs_speed) { d->speed_timer.stop(); return; } const qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); const qint64 diff = currentTime - d->start_time; if (diff - d->last_time >= 900) { d->last_time = diff; if (d->nums == max_nums) { // let's hope gcc can optimize that well enough // otherwise I'd try memcpy :) for (unsigned int i = 1; i < max_nums; ++i) { d->times[i - 1] = d->times[i]; d->sizes[i - 1] = d->sizes[i]; } d->nums--; } d->times[d->nums] = diff; d->sizes[d->nums++] = d->filesize - d->offset; KIO::filesize_t lspeed = 1000 * (d->sizes[d->nums - 1] - d->sizes[0]) / (d->times[d->nums - 1] - d->times[0]); //qDebug() << (long)d->filesize << diff // << long(d->sizes[d->nums-1] - d->sizes[0]) // << d->times[d->nums-1] - d->times[0] // << long(lspeed) << double(d->filesize) / diff // << convertSize(lspeed) // << convertSize(long(double(d->filesize) / diff) * 1000); if (!lspeed) { d->nums = 1; d->times[0] = diff; d->sizes[0] = d->filesize - d->offset; } emit speed(lspeed); } } bool SlaveInterface::dispatch(int _cmd, const QByteArray &rawdata) { Q_D(SlaveInterface); //qDebug() << "dispatch " << _cmd; QDataStream stream(rawdata); QString str1; qint32 i; qint8 b; quint32 ul; switch (_cmd) { case MSG_DATA: emit data(rawdata); break; case MSG_DATA_REQ: emit dataReq(); break; case MSG_OPENED: emit open(); break; case MSG_FINISHED: //qDebug() << "Finished [this = " << this << "]"; d->offset = 0; d->speed_timer.stop(); emit finished(); break; case MSG_STAT_ENTRY: { UDSEntry entry; stream >> entry; emit statEntry(entry); break; } case MSG_LIST_ENTRIES: { UDSEntryList list; UDSEntry entry; while (!stream.atEnd()) { stream >> entry; list.append(entry); } emit listEntries(list); break; } case MSG_RESUME: { // From the put job d->offset = readFilesize_t(stream); emit canResume(d->offset); break; } case MSG_CANRESUME: // From the get job d->filesize = d->offset; emit canResume(0); // the arg doesn't matter break; case MSG_ERROR: stream >> i >> str1; //qDebug() << "error " << i << " " << str1; emit error(i, str1); break; - case MSG_SLAVE_STATUS: { + case MSG_SLAVE_STATUS: + case MSG_SLAVE_STATUS_V2: { qint64 pid; QByteArray protocol; stream >> pid >> protocol >> str1 >> b; emit slaveStatus(pid, protocol, str1, (b != 0)); break; } case MSG_CONNECTED: emit connected(); break; case MSG_WRITTEN: { KIO::filesize_t size = readFilesize_t(stream); emit written(size); break; } case INF_TOTAL_SIZE: { KIO::filesize_t size = readFilesize_t(stream); d->start_time = QDateTime::currentMSecsSinceEpoch(); d->last_time = 0; d->filesize = d->offset; d->sizes[0] = d->filesize - d->offset; d->times[0] = 0; d->nums = 1; d->speed_timer.start(1000); d->slave_calcs_speed = false; emit totalSize(size); break; } case INF_PROCESSED_SIZE: { KIO::filesize_t size = readFilesize_t(stream); emit processedSize(size); d->filesize = size; break; } case INF_POSITION: { KIO::filesize_t pos = readFilesize_t(stream); emit position(pos); break; } case INF_SPEED: stream >> ul; d->slave_calcs_speed = true; d->speed_timer.stop(); emit speed(ul); break; case INF_GETTING_FILE: break; case INF_ERROR_PAGE: emit errorPage(); break; case INF_REDIRECTION: { QUrl url; stream >> url; emit redirection(url); break; } case INF_MIME_TYPE: stream >> str1; emit mimeType(str1); if (!d->connection->suspended()) { d->connection->sendnow(CMD_NONE, QByteArray()); } break; case INF_WARNING: stream >> str1; emit warning(str1); break; case INF_MESSAGEBOX: { //qDebug() << "needs a msg box"; QString text, caption, buttonYes, buttonNo, dontAskAgainName; int type; stream >> type >> text >> caption >> buttonYes >> buttonNo; if (stream.atEnd()) { messageBox(type, text, caption, buttonYes, buttonNo); } else { stream >> dontAskAgainName; messageBox(type, text, caption, buttonYes, buttonNo, dontAskAgainName); } break; } case INF_INFOMESSAGE: { QString msg; stream >> msg; emit infoMessage(msg); break; } case INF_META_DATA: { MetaData m; stream >> m; if (m.contains(QStringLiteral("ssl_in_use"))) { const QLatin1String ssl_("ssl_"); const MetaData constM = m; for (MetaData::ConstIterator it = constM.lowerBound(ssl_); it != constM.constEnd(); ++it) { if (it.key().startsWith(ssl_)) { d->sslMetaData.insert(it.key(), it.value()); } else { // we're past the ssl_* entries; remember that QMap is ordered. break; } } } emit metaData(m); break; } case MSG_NET_REQUEST: { QString host; QString slaveid; stream >> host >> slaveid; requestNetwork(host, slaveid); break; } case MSG_NET_DROP: { QString host; QString slaveid; stream >> host >> slaveid; dropNetwork(host, slaveid); break; } case MSG_NEED_SUBURL_DATA: { emit needSubUrlData(); break; } case MSG_HOST_INFO_REQ: { QString hostName; stream >> hostName; HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo))); break; } case MSG_PRIVILEGE_EXEC: emit privilegeOperationRequested(); break; default: qCWarning(KIO_CORE) << "Slave sends unknown command (" << _cmd << "), dropping slave"; return false; } return true; } void SlaveInterface::setOffset(KIO::filesize_t o) { Q_D(SlaveInterface); d->offset = o; } KIO::filesize_t SlaveInterface::offset() const { const Q_D(SlaveInterface); return d->offset; } void SlaveInterface::requestNetwork(const QString &host, const QString &slaveid) { Q_D(SlaveInterface); Q_UNUSED(host); Q_UNUSED(slaveid); //qDebug() << "requestNetwork " << host << slaveid; // This is old stuff. We just always return true... QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << true; d->connection->sendnow(INF_NETWORK_STATUS, packedArgs); } void SlaveInterface::dropNetwork(const QString &host, const QString &slaveid) { Q_UNUSED(host); Q_UNUSED(slaveid); //qDebug() << "dropNetwork " << host << slaveid; } void SlaveInterface::sendResumeAnswer(bool resume) { Q_D(SlaveInterface); //qDebug() << "ok for resuming:" << resume; d->connection->sendnow(resume ? CMD_RESUMEANSWER : CMD_NONE, QByteArray()); } void SlaveInterface::sendMessageBoxAnswer(int result) { Q_D(SlaveInterface); if (!d->connection) { return; } if (d->connection->suspended()) { d->connection->resume(); } QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << result; d->connection->sendnow(CMD_MESSAGEBOXANSWER, packedArgs); // qDebug() << "message box answer" << result; } void SlaveInterface::messageBox(int type, const QString &text, const QString &_caption, const QString &buttonYes, const QString &buttonNo) { messageBox(type, text, _caption, buttonYes, buttonNo, QString()); } void SlaveInterface::messageBox(int type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &dontAskAgainName) { Q_D(SlaveInterface); if (d->connection) { d->connection->suspend(); } QHash data; data.insert(UserNotificationHandler::MSG_TEXT, text); data.insert(UserNotificationHandler::MSG_CAPTION, caption); data.insert(UserNotificationHandler::MSG_YES_BUTTON_TEXT, buttonYes); data.insert(UserNotificationHandler::MSG_NO_BUTTON_TEXT, buttonNo); data.insert(UserNotificationHandler::MSG_DONT_ASK_AGAIN, dontAskAgainName); // SMELL: the braindead way to support button icons // TODO: Fix this in KIO::SlaveBase. if (buttonYes == i18n("&Details")) { data.insert(UserNotificationHandler::MSG_YES_BUTTON_ICON, QLatin1String("help-about")); } else if (buttonYes == i18n("&Forever")) { data.insert(UserNotificationHandler::MSG_YES_BUTTON_ICON, QLatin1String("flag-green")); } if (buttonNo == i18n("Co&ntinue")) { data.insert(UserNotificationHandler::MSG_NO_BUTTON_ICON, QLatin1String("arrow-right")); } else if (buttonNo == i18n("&Current Session only")) { data.insert(UserNotificationHandler::MSG_NO_BUTTON_ICON, QLatin1String("chronometer")); } if (type == KIO::SlaveBase::SSLMessageBox) { data.insert(UserNotificationHandler::MSG_META_DATA, d->sslMetaData.toVariant()); } globalUserNotificationHandler()->requestMessageBox(this, type, data); } void SlaveInterfacePrivate::slotHostInfo(const QHostInfo &info) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << info.hostName() << info.addresses() << info.error() << info.errorString(); connection->send(CMD_HOST_INFO, data); } #include "moc_slaveinterface.cpp" diff --git a/src/core/slaveinterface.h b/src/core/slaveinterface.h index e80dde96..dbb3ff33 100644 --- a/src/core/slaveinterface.h +++ b/src/core/slaveinterface.h @@ -1,196 +1,197 @@ /* This file is part of the KDE project Copyright (C) 2000 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __kio_slaveinterface_h #define __kio_slaveinterface_h #include #include #include #include #include #include class QUrl; namespace KIO { class Connection; // better there is one ... class SlaveInterfacePrivate; // Definition of enum Command has been moved to global.h /** * Identifiers for KIO informational messages. */ enum Info { INF_TOTAL_SIZE = 10, INF_PROCESSED_SIZE = 11, INF_SPEED, INF_REDIRECTION = 20, INF_MIME_TYPE = 21, INF_ERROR_PAGE = 22, INF_WARNING = 23, INF_GETTING_FILE, ///< @deprecated INF_UNUSED = 25, ///< now unused INF_INFOMESSAGE, INF_META_DATA, INF_NETWORK_STATUS, INF_MESSAGEBOX, INF_POSITION // add new ones here once a release is done, to avoid breaking binary compatibility }; /** * Identifiers for KIO data messages. */ enum Message { MSG_DATA = 100, MSG_DATA_REQ, MSG_ERROR, MSG_CONNECTED, MSG_FINISHED, MSG_STAT_ENTRY, // 105 MSG_LIST_ENTRIES, MSG_RENAMED, ///< unused MSG_RESUME, MSG_SLAVE_STATUS, MSG_SLAVE_ACK, // 110 MSG_NET_REQUEST, MSG_NET_DROP, MSG_NEED_SUBURL_DATA, MSG_CANRESUME, MSG_AUTH_KEY, ///< @deprecated MSG_DEL_AUTH_KEY, ///< @deprecated MSG_OPENED, MSG_WRITTEN, MSG_HOST_INFO_REQ, - MSG_PRIVILEGE_EXEC + MSG_PRIVILEGE_EXEC, + MSG_SLAVE_STATUS_V2 // add new ones here once a release is done, to avoid breaking binary compatibility }; /** * @class KIO::SlaveInterface slaveinterface.h * * There are two classes that specifies the protocol between application * ( KIO::Job) and kioslave. SlaveInterface is the class to use on the application * end, SlaveBase is the one to use on the slave end. * * A call to foo() results in a call to slotFoo() on the other end. */ class KIOCORE_EXPORT SlaveInterface : public QObject { Q_OBJECT protected: SlaveInterface(SlaveInterfacePrivate &dd, QObject *parent = nullptr); public: virtual ~SlaveInterface(); void setConnection(Connection *connection); Connection *connection() const; // Send our answer to the MSG_RESUME (canResume) request // (to tell the "put" job whether to resume or not) void sendResumeAnswer(bool resume); /** * Sends our answer for the INF_MESSAGEBOX request. * * @since 4.11 */ void sendMessageBoxAnswer(int result); void setOffset(KIO::filesize_t offset); KIO::filesize_t offset() const; Q_SIGNALS: /////////// // Messages sent by the slave /////////// void data(const QByteArray &); void dataReq(); void error(int, const QString &); void connected(); void finished(); void slaveStatus(qint64, const QByteArray &, const QString &, bool); void listEntries(const KIO::UDSEntryList &); void statEntry(const KIO::UDSEntry &); void needSubUrlData(); void canResume(KIO::filesize_t); void open(); void written(KIO::filesize_t); void privilegeOperationRequested(); /////////// // Info sent by the slave ////////// void metaData(const KIO::MetaData &); void totalSize(KIO::filesize_t); void processedSize(KIO::filesize_t); void redirection(const QUrl &); void position(KIO::filesize_t); void speed(unsigned long); void errorPage(); void mimeType(const QString &); void warning(const QString &); void infoMessage(const QString &); //void connectFinished(); //it does not get emitted anywhere protected: ///////////////// // Dispatching //////////////// virtual bool dispatch(); virtual bool dispatch(int _cmd, const QByteArray &data); void messageBox(int type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo); void messageBox(int type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &dontAskAgainName); // I need to identify the slaves void requestNetwork(const QString &, const QString &); void dropNetwork(const QString &, const QString &); protected Q_SLOTS: void calcSpeed(); protected: SlaveInterfacePrivate *const d_ptr; Q_DECLARE_PRIVATE(SlaveInterface) private: Q_PRIVATE_SLOT(d_func(), void slotHostInfo(QHostInfo)) }; } #endif