diff --git a/src/core/connectionbackend.cpp b/src/core/connectionbackend.cpp index d3f7d877..b3e34bc9 100644 --- a/src/core/connectionbackend.cpp +++ b/src/core/connectionbackend.cpp @@ -1,340 +1,340 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow David Faure 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 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. */ #include "connectionbackend_p.h" #include #include #include #include "klocalsocket.h" #include #include #include #include #include #include #include #include "kiocoredebug.h" using namespace KIO; ConnectionBackend::ConnectionBackend(Mode m, QObject *parent) : QObject(parent), state(Idle), socket(nullptr), len(-1), cmd(0), signalEmitted(false), mode(m) { localServer = nullptr; } ConnectionBackend::~ConnectionBackend() { if (mode == LocalSocketMode && localServer && localServer->localSocketType() == KLocalSocket::UnixSocket) { QFile::remove(localServer->localPath()); } } void ConnectionBackend::setSuspended(bool enable) { if (state != Connected) { return; } Q_ASSERT(socket); Q_ASSERT(!localServer); // !tcpServer as well if (enable) { //qCDebug(KIO_CORE) << socket << "suspending"; socket->setReadBufferSize(1); } else { //qCDebug(KIO_CORE) << socket << "resuming"; // Calling setReadBufferSize from a readyRead slot leads to a bug in Qt, fixed in 13c246ee119 socket->setReadBufferSize(StandardBufferSize); if (socket->bytesAvailable() >= HeaderSize) { // there are bytes available QMetaObject::invokeMethod(this, "socketReadyRead", Qt::QueuedConnection); } // We read all bytes here, but we don't use readAll() because we need // to read at least one byte (even if there isn't any) so that the // socket notifier is reenabled QByteArray data = socket->read(socket->bytesAvailable() + 1); for (int i = data.size(); --i >= 0;) { socket->ungetChar(data[i]); } // Workaround Qt5 bug, readyRead isn't always emitted here... QMetaObject::invokeMethod(this, "socketReadyRead", Qt::QueuedConnection); } } bool ConnectionBackend::connectToRemote(const QUrl &url) { Q_ASSERT(state == Idle); Q_ASSERT(!socket); Q_ASSERT(!localServer); // !tcpServer as well if (mode == LocalSocketMode) { KLocalSocket *sock = new KLocalSocket(this); QString path = url.path(); #if 0 // TODO: Activate once abstract socket support is implemented in Qt. KLocalSocket::LocalSocketType type = KLocalSocket::UnixSocket; if (url.queryItem(QLatin1String("abstract")) == QLatin1String("1")) { type = KLocalSocket::AbstractUnixSocket; } #endif sock->connectToPath(path); socket = sock; } else { socket = new QTcpSocket(this); socket->connectToHost(url.host(), url.port()); if (!socket->waitForConnected(1000)) { state = Idle; qCDebug(KIO_CORE) << "could not connect to" << url; return false; } } - connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); - connect(socket, SIGNAL(disconnected()), SLOT(socketDisconnected())); + connect(socket, &QIODevice::readyRead, this, &ConnectionBackend::socketReadyRead); + connect(socket, &QAbstractSocket::disconnected, this, &ConnectionBackend::socketDisconnected); state = Connected; return true; } void ConnectionBackend::socketDisconnected() { state = Idle; emit disconnected(); } bool ConnectionBackend::listenForRemote() { Q_ASSERT(state == Idle); Q_ASSERT(!socket); Q_ASSERT(!localServer); // !tcpServer as well if (mode == LocalSocketMode) { const QString prefix = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); static QBasicAtomicInt s_socketCounter = Q_BASIC_ATOMIC_INITIALIZER(1); QString appName = QCoreApplication::instance()->applicationName(); appName.replace(QLatin1Char('/'), QLatin1Char('_')); // #357499 QTemporaryFile socketfile(prefix + QLatin1Char('/') + appName + QStringLiteral("XXXXXX.%1.slave-socket").arg(s_socketCounter.fetchAndAddAcquire(1))); if (!socketfile.open()) { errorString = i18n("Unable to create io-slave: %1", QString::fromUtf8(strerror(errno))); return false; } QString sockname = socketfile.fileName(); address.clear(); address.setScheme(QStringLiteral("local")); address.setPath(sockname); socketfile.setAutoRemove(false); socketfile.remove(); // can't bind if there is such a file localServer = new KLocalSocketServer(this); if (!localServer->listen(sockname, KLocalSocket::UnixSocket)) { errorString = localServer->errorString(); delete localServer; localServer = nullptr; return false; } - connect(localServer, SIGNAL(newConnection()), SIGNAL(newConnection())); + connect(localServer, &KLocalSocketServer::newConnection, this, &ConnectionBackend::newConnection); } else { tcpServer = new QTcpServer(this); tcpServer->listen(QHostAddress::LocalHost); if (!tcpServer->isListening()) { errorString = tcpServer->errorString(); delete tcpServer; tcpServer = nullptr; return false; } address = QUrl(QLatin1String("tcp://127.0.0.1:") + QString::number(tcpServer->serverPort())); - connect(tcpServer, SIGNAL(newConnection()), SIGNAL(newConnection())); + connect(tcpServer, &QTcpServer::newConnection, this, &ConnectionBackend::newConnection); } state = Listening; return true; } bool ConnectionBackend::waitForIncomingTask(int ms) { Q_ASSERT(state == Connected); Q_ASSERT(socket); if (socket->state() != QAbstractSocket::ConnectedState) { state = Idle; return false; // socket has probably closed, what do we do? } signalEmitted = false; if (socket->bytesAvailable()) { socketReadyRead(); } if (signalEmitted) { return true; // there was enough data in the socket } // not enough data in the socket, so wait for more QElapsedTimer timer; timer.start(); while (socket->state() == QAbstractSocket::ConnectedState && !signalEmitted && (ms == -1 || timer.elapsed() < ms)) if (!socket->waitForReadyRead(ms == -1 ? -1 : ms - timer.elapsed())) { break; } if (signalEmitted) { return true; } if (socket->state() != QAbstractSocket::ConnectedState) { state = Idle; } return false; } bool ConnectionBackend::sendCommand(int cmd, const QByteArray &data) const { Q_ASSERT(state == Connected); Q_ASSERT(socket); char buffer[HeaderSize + 2]; sprintf(buffer, "%6x_%2x_", data.size(), cmd); socket->write(buffer, HeaderSize); socket->write(data); //qCDebug(KIO_CORE) << this << "Sending command" << hex << cmd << "of" // << data.size() << "bytes (" << socket->bytesToWrite() // << "bytes left to write )"; // blocking mode: while (socket->bytesToWrite() > 0 && socket->state() == QAbstractSocket::ConnectedState) { socket->waitForBytesWritten(-1); } return socket->state() == QAbstractSocket::ConnectedState; } ConnectionBackend *ConnectionBackend::nextPendingConnection() { Q_ASSERT(state == Listening); Q_ASSERT(localServer || tcpServer); Q_ASSERT(!socket); //qCDebug(KIO_CORE) << "Got a new connection"; QTcpSocket *newSocket; if (mode == LocalSocketMode) { newSocket = localServer->nextPendingConnection(); } else { newSocket = tcpServer->nextPendingConnection(); } if (!newSocket) { return nullptr; // there was no connection... } ConnectionBackend *result = new ConnectionBackend(Mode(mode)); result->state = Connected; result->socket = newSocket; newSocket->setParent(result); - connect(newSocket, SIGNAL(readyRead()), result, SLOT(socketReadyRead())); - connect(newSocket, SIGNAL(disconnected()), result, SLOT(socketDisconnected())); + connect(newSocket, &QIODevice::readyRead, result, &ConnectionBackend::socketReadyRead); + connect(newSocket, &QAbstractSocket::disconnected, result, &ConnectionBackend::socketDisconnected); return result; } void ConnectionBackend::socketReadyRead() { bool shouldReadAnother; do { if (!socket) // might happen if the invokeMethods were delivered after we disconnected { return; } //qCDebug(KIO_CORE) << this << "Got" << socket->bytesAvailable() << "bytes"; if (len == -1) { // We have to read the header char buffer[HeaderSize]; if (socket->bytesAvailable() < HeaderSize) { return; // wait for more data } socket->read(buffer, sizeof buffer); buffer[6] = 0; buffer[9] = 0; char *p = buffer; while (*p == ' ') { p++; } len = strtol(p, nullptr, 16); p = buffer + 7; while (*p == ' ') { p++; } cmd = strtol(p, nullptr, 16); //qCDebug(KIO_CORE) << this << "Beginning of command" << hex << cmd << "of size" << len; } QPointer that = this; //qCDebug(KIO_CORE) << socket << "Want to read" << len << "bytes"; if (socket->bytesAvailable() >= len) { Task task; task.cmd = cmd; if (len) { task.data = socket->read(len); } len = -1; signalEmitted = true; emit commandReceived(task); } else if (len > StandardBufferSize) { qCDebug(KIO_CORE) << socket << "Jumbo packet of" << len << "bytes"; // Calling setReadBufferSize from a readyRead slot leads to a bug in Qt, fixed in 13c246ee119 socket->setReadBufferSize(len + 1); } // If we're dead, better don't try anything. if (that.isNull()) { return; } // Do we have enough for an another read? if (len == -1) { shouldReadAnother = socket->bytesAvailable() >= HeaderSize; } else { shouldReadAnother = socket->bytesAvailable() >= len; } } while (shouldReadAnother); } diff --git a/src/core/connectionserver.cpp b/src/core/connectionserver.cpp index c40bd5f7..a7fe176f 100644 --- a/src/core/connectionserver.cpp +++ b/src/core/connectionserver.cpp @@ -1,112 +1,112 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 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. */ #include "connectionserver.h" #include "connection_p.h" #include "connectionbackend_p.h" using namespace KIO; class KIO::ConnectionServerPrivate { public: inline ConnectionServerPrivate() : backend(nullptr) { } ConnectionServer *q; ConnectionBackend *backend; }; ConnectionServer::ConnectionServer(QObject *parent) : QObject(parent), d(new ConnectionServerPrivate) { d->q = this; } ConnectionServer::~ConnectionServer() { delete d; } void ConnectionServer::listenForRemote() { #ifdef Q_OS_WIN d->backend = new ConnectionBackend(ConnectionBackend::TcpSocketMode, this); #else d->backend = new ConnectionBackend(ConnectionBackend::LocalSocketMode, this); #endif if (!d->backend->listenForRemote()) { delete d->backend; d->backend = nullptr; return; } - connect(d->backend, SIGNAL(newConnection()), SIGNAL(newConnection())); + connect(d->backend, &ConnectionBackend::newConnection, this, &ConnectionServer::newConnection); //qDebug() << "Listening on" << d->backend->address; } QUrl ConnectionServer::address() const { if (d->backend) { return d->backend->address; } return QUrl(); } bool ConnectionServer::isListening() const { return d->backend && d->backend->state == ConnectionBackend::Listening; } void ConnectionServer::close() { delete d->backend; d->backend = nullptr; } Connection *ConnectionServer::nextPendingConnection() { if (!isListening()) { return nullptr; } ConnectionBackend *newBackend = d->backend->nextPendingConnection(); if (!newBackend) { return nullptr; // no new backend... } Connection *result = new Connection; result->d->setBackend(newBackend); newBackend->setParent(result); return result; } void ConnectionServer::setNextPendingConnection(Connection *conn) { ConnectionBackend *newBackend = d->backend->nextPendingConnection(); Q_ASSERT(newBackend); conn->d->setBackend(newBackend); newBackend->setParent(conn); conn->d->dequeue(); } diff --git a/src/core/dataslave.cpp b/src/core/dataslave.cpp index 80c63d24..882ca13e 100644 --- a/src/core/dataslave.cpp +++ b/src/core/dataslave.cpp @@ -1,194 +1,194 @@ /* * This file is part of the KDE libraries * Copyright (c) 2003 Leo Savernik * Derived from slave.cpp * * 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 "dataslave_p.h" #include "dataprotocol_p.h" #include "commands_p.h" #include "slavebase.h" #include #include #include using namespace KIO; #define KIO_DATA_POLL_INTERVAL 0 // don't forget to sync DISPATCH_DECL in dataslave_p.h #define DISPATCH_IMPL(type) \ void DataSlave::dispatch_##type() { \ if (_suspended) { \ QueueStruct q(Queue_##type); \ q.size = -1; \ dispatchQueue.push_back(q); \ if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \ } else \ emit type(); \ } // don't forget to sync DISPATCH_DECL1 in dataslave_p.h #define DISPATCH_IMPL1(type, paramtype, paramname) \ void DataSlave::dispatch_##type(paramtype paramname) { \ if (_suspended) { \ QueueStruct q(Queue_##type); \ q.paramname = paramname; \ dispatchQueue.push_back(q); \ if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \ } else \ emit type(paramname); \ } DataSlave::DataSlave() : Slave(QStringLiteral("data")) { //qDebug() << this; _suspended = false; timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(dispatchNext())); + connect(timer, &QTimer::timeout, this, &DataSlave::dispatchNext); } DataSlave::~DataSlave() { //qDebug() << this; } void DataSlave::hold(const QUrl &/*url*/) { // ignored } void DataSlave::suspend() { _suspended = true; //qDebug() << this; timer->stop(); } void DataSlave::resume() { _suspended = false; //qDebug() << this; // aarrrgh! This makes the once hyper fast and efficient data protocol // implementation slow as molasses. But it wouldn't work otherwise, // and I don't want to start messing around with threads timer->start(KIO_DATA_POLL_INTERVAL); } // finished is a special case. If we emit it right away, then // TransferJob::start can delete the job even before the end of the method void DataSlave::dispatch_finished() { QueueStruct q(Queue_finished); q.size = -1; dispatchQueue.push_back(q); if (!timer->isActive()) { timer->start(KIO_DATA_POLL_INTERVAL); } } void DataSlave::dispatchNext() { if (dispatchQueue.empty()) { timer->stop(); return; } const QueueStruct &q = dispatchQueue.front(); //qDebug() << this << "dispatching" << q.type << dispatchQueue.size() << "left"; switch (q.type) { case Queue_mimeType: emit mimeType(q.s); break; case Queue_totalSize: emit totalSize(q.size); break; case Queue_sendMetaData: sendMetaData(); break; case Queue_data: emit data(q.ba); break; case Queue_finished: emit finished(); break; }/*end switch*/ dispatchQueue.pop_front(); } void DataSlave::send(int cmd, const QByteArray &arr) { QDataStream stream(arr); QUrl url; switch (cmd) { case CMD_GET: { stream >> url; get(url); break; } case CMD_MIMETYPE: { stream >> url; mimetype(url); break; } // ignore these (must not emit error, otherwise SIGSEGV occurs) case CMD_REPARSECONFIGURATION: case CMD_META_DATA: case CMD_SUBURL: break; default: emit error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(QStringLiteral("data"), cmd)); }/*end switch*/ } bool DataSlave::suspended() { return _suspended; } void DataSlave::setHost(const QString &/*host*/, quint16 /*port*/, const QString &/*user*/, const QString &/*passwd*/) { // irrelevant -> will be ignored } void DataSlave::setConfig(const MetaData &/*config*/) { // FIXME: decide to handle this directly or not at all #if 0 QByteArray data; QDataStream stream(data, QIODevice::WriteOnly); stream << config; slaveconn.send(CMD_CONFIG, data); #endif } void DataSlave::setAllMetaData(const MetaData &md) { meta_data = md; } void DataSlave::sendMetaData() { emit metaData(meta_data); } DISPATCH_IMPL1(mimeType, const QString &, s) DISPATCH_IMPL1(totalSize, KIO::filesize_t, size) DISPATCH_IMPL(sendMetaData) DISPATCH_IMPL1(data, const QByteArray &, ba) #undef DISPATCH_IMPL #undef DISPATCH_IMPL1 diff --git a/src/core/hostinfo.cpp b/src/core/hostinfo.cpp index 8f641591..c54d60cb 100644 --- a/src/core/hostinfo.cpp +++ b/src/core/hostinfo.cpp @@ -1,399 +1,399 @@ /* Copyright 2008 Roland Harnau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "hostinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX # include # include # include # include // for _PATH_RESCONF # ifndef _PATH_RESCONF # define _PATH_RESCONF "/etc/resolv.conf" # endif #endif #define TTL 300 namespace KIO { class HostInfoAgentPrivate : public QObject { Q_OBJECT public: explicit HostInfoAgentPrivate(int cacheSize = 100); virtual ~HostInfoAgentPrivate() {} void lookupHost(const QString &hostName, QObject *receiver, const char *member); QHostInfo lookupCachedHostInfoFor(const QString &hostName); void cacheLookup(const QHostInfo &); void setCacheSize(int s) { dnsCache.setMaxCost(s); } void setTTL(int _ttl) { ttl = _ttl; } private Q_SLOTS: void queryFinished(const QHostInfo &); private: class Result; class Query; QHash openQueries; QCache > dnsCache; QDateTime resolvConfMTime; int ttl; }; class HostInfoAgentPrivate::Result : public QObject { Q_OBJECT Q_SIGNALS: void result(const QHostInfo &); private: friend class HostInfoAgentPrivate; }; class HostInfoAgentPrivate::Query : public QObject { Q_OBJECT public: Query(): m_watcher(), m_hostName() { - connect(&m_watcher, SIGNAL(finished()), this, SLOT(relayFinished())); + connect(&m_watcher, &QFutureWatcher::finished, this, &Query::relayFinished); } void start(const QString &hostName) { m_hostName = hostName; QFuture future = QtConcurrent::run(&QHostInfo::fromName, hostName); m_watcher.setFuture(future); } QString hostName() const { return m_hostName; } Q_SIGNALS: void result(const QHostInfo &); private Q_SLOTS: void relayFinished() { emit result(m_watcher.result()); } private: QFutureWatcher m_watcher; QString m_hostName; }; class NameLookupThreadRequest { public: NameLookupThreadRequest(const QString &hostName) : m_hostName(hostName) { } QSemaphore *semaphore() { return &m_semaphore; } QHostInfo result() const { return m_hostInfo; } void setResult(const QHostInfo &hostInfo) { m_hostInfo = hostInfo; } QString hostName() const { return m_hostName; } int lookupId() const { return m_lookupId; } void setLookupId(int id) { m_lookupId = id; } private: Q_DISABLE_COPY(NameLookupThreadRequest) QString m_hostName; QSemaphore m_semaphore; QHostInfo m_hostInfo; int m_lookupId; }; } Q_DECLARE_METATYPE(QSharedPointer) namespace KIO { class NameLookUpThreadWorker : public QObject { Q_OBJECT public Q_SLOTS: void lookupHost(const QSharedPointer &request) { const QString hostName = request->hostName(); const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo))); request->setLookupId(lookupId); m_lookups.insert(lookupId, request); } void abortLookup(const QSharedPointer &request) { QHostInfo::abortHostLookup(request->lookupId()); m_lookups.remove(request->lookupId()); } void lookupFinished(const QHostInfo &hostInfo) { QMap >::iterator it = m_lookups.find(hostInfo.lookupId()); if (it != m_lookups.end()) { (*it)->setResult(hostInfo); (*it)->semaphore()->release(); m_lookups.erase(it); } } private: QMap > m_lookups; }; class NameLookUpThread : public QThread { Q_OBJECT public: NameLookUpThread() : m_worker(nullptr) { qRegisterMetaType< QSharedPointer > (); start(); } ~NameLookUpThread() override { quit(); wait(); } NameLookUpThreadWorker *worker() { return m_worker; } QSemaphore *semaphore() { return &m_semaphore; } void run() override { NameLookUpThreadWorker worker; m_worker = &worker; m_semaphore.release(); exec(); } private: NameLookUpThreadWorker *m_worker; QSemaphore m_semaphore; }; } using namespace KIO; Q_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate) Q_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread) void HostInfo::lookupHost(const QString &hostName, QObject *receiver, const char *member) { hostInfoAgentPrivate()->lookupHost(hostName, receiver, member); } QHostInfo HostInfo::lookupHost(const QString &hostName, unsigned long timeout) { // Do not perform a reverse lookup here... QHostAddress address(hostName); QHostInfo hostInfo; if (!address.isNull()) { QList addressList; addressList << address; hostInfo.setAddresses(addressList); return hostInfo; } // Look up the name in the KIO/KHTML DNS cache... hostInfo = HostInfo::lookupCachedHostInfoFor(hostName); if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) { return hostInfo; } // Failing all of the above, do the lookup... QSharedPointer request = QSharedPointer(new NameLookupThreadRequest(hostName)); nameLookUpThread()->semaphore()->acquire(); nameLookUpThread()->semaphore()->release(); QMetaObject::invokeMethod(nameLookUpThread()->worker(), "lookupHost", Qt::QueuedConnection, Q_ARG(QSharedPointer, request)); if (request->semaphore()->tryAcquire(1, timeout)) { hostInfo = request->result(); if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) { HostInfo::cacheLookup(hostInfo); // cache the look up... } } else { QMetaObject::invokeMethod(nameLookUpThread()->worker(), "abortLookup", Qt::QueuedConnection, Q_ARG(QSharedPointer, request)); } //qDebug() << "Name look up succeeded for" << hostName; return hostInfo; } QHostInfo HostInfo::lookupCachedHostInfoFor(const QString &hostName) { return hostInfoAgentPrivate()->lookupCachedHostInfoFor(hostName); } void HostInfo::cacheLookup(const QHostInfo &info) { hostInfoAgentPrivate()->cacheLookup(info); } void HostInfo::prefetchHost(const QString &hostName) { hostInfoAgentPrivate()->lookupHost(hostName, nullptr, nullptr); } void HostInfo::setCacheSize(int s) { hostInfoAgentPrivate()->setCacheSize(s); } void HostInfo::setTTL(int ttl) { hostInfoAgentPrivate()->setTTL(ttl); } HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize) : openQueries(), dnsCache(cacheSize), ttl(TTL) { qRegisterMetaType(); } void HostInfoAgentPrivate::lookupHost(const QString &hostName, QObject *receiver, const char *member) { #ifdef _PATH_RESCONF QFileInfo resolvConf(QFile::decodeName(_PATH_RESCONF)); QDateTime currentMTime = resolvConf.lastModified(); if (resolvConf.exists() && currentMTime != resolvConfMTime) { // /etc/resolv.conf has been modified // clear our cache resolvConfMTime = currentMTime; dnsCache.clear(); } #endif if (QPair *info = dnsCache.object(hostName)) { if (QTime::currentTime() <= info->second.addSecs(ttl)) { Result result; if (receiver) { QObject::connect(&result, SIGNAL(result(QHostInfo)), receiver, member); emit result.result(info->first); } return; } dnsCache.remove(hostName); } if (Query *query = openQueries.value(hostName)) { if (receiver) { connect(query, SIGNAL(result(QHostInfo)), receiver, member); } return; } Query *query = new Query(); openQueries.insert(hostName, query); - connect(query, SIGNAL(result(QHostInfo)), this, SLOT(queryFinished(QHostInfo))); + connect(query, &Query::result, this, &HostInfoAgentPrivate::queryFinished); if (receiver) { connect(query, SIGNAL(result(QHostInfo)), receiver, member); } query->start(hostName); } QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString &hostName) { QPair *info = dnsCache.object(hostName); if (info && info->second.addSecs(ttl) >= QTime::currentTime()) { return info->first; } return QHostInfo(); } void HostInfoAgentPrivate::cacheLookup(const QHostInfo &info) { if (info.hostName().isEmpty()) { return; } if (info.error() != QHostInfo::NoError) { return; } dnsCache.insert(info.hostName(), new QPair(info, QTime::currentTime())); } void HostInfoAgentPrivate::queryFinished(const QHostInfo &info) { Query *query = static_cast(sender()); openQueries.remove(query->hostName()); if (info.error() == QHostInfo::NoError) { dnsCache.insert(query->hostName(), new QPair(info, QTime::currentTime())); } query->deleteLater(); } #include "hostinfo.moc" diff --git a/src/core/idleslave.cpp b/src/core/idleslave.cpp index fe422eeb..8f076016 100644 --- a/src/core/idleslave.cpp +++ b/src/core/idleslave.cpp @@ -1,163 +1,163 @@ /* This file is part of the KDE libraries Copyright (c) 1999 Waldo Bastian Copyright (c) 2013 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 "idleslave.h" #include "connection_p.h" #include "commands_p.h" // CMD_* #include "slaveinterface.h" // MSG_* #include using namespace KIO; class KIO::IdleSlavePrivate { public: KIO::Connection mConn; QString mProtocol; QString mHost; bool mConnected; qint64 mPid; QDateTime mBirthDate; bool mOnHold; QUrl mUrl; bool mHasTempAuth; }; IdleSlave::IdleSlave(QObject *parent) : QObject(parent), d(new IdleSlavePrivate) { - QObject::connect(&d->mConn, SIGNAL(readyRead()), this, SLOT(gotInput())); + QObject::connect(&d->mConn, &Connection::readyRead, this, &IdleSlave::gotInput); // Send it a SLAVE_STATUS command. d->mConn.send(CMD_SLAVE_STATUS); d->mPid = 0; d->mBirthDate = QDateTime::currentDateTime(); d->mOnHold = false; d->mHasTempAuth = false; } IdleSlave::~IdleSlave() { } void IdleSlave::gotInput() { int cmd; QByteArray data; if (d->mConn.read(&cmd, data) == -1) { // Communication problem with slave. //qCritical() << "No communication with KIO slave."; deleteLater(); } else if (cmd == MSG_SLAVE_ACK) { deleteLater(); } else if (cmd != MSG_SLAVE_STATUS_V2 && cmd != MSG_SLAVE_STATUS) { qCritical() << "Unexpected data from KIO slave."; deleteLater(); } else { QDataStream stream(data); qint64 pid; QByteArray protocol; QString host; qint8 b; // Overload with (bool) onHold, (QUrl) url. stream >> pid >> protocol >> host >> b; if (cmd == MSG_SLAVE_STATUS_V2) { QUrl url; bool onHold; bool tempAuth; stream >> onHold >> url >> tempAuth; d->mHasTempAuth = tempAuth; if (onHold) { d->mOnHold = onHold; d->mUrl = url; } } else { // compat code for KF < 5.45. TODO KF6: remove if (!stream.atEnd()) { QUrl url; stream >> url; d->mOnHold = true; d->mUrl = url; } } d->mPid = pid; d->mConnected = (b != 0); d->mProtocol = QString::fromLatin1(protocol); d->mHost = host; emit statusUpdate(this); } } void IdleSlave::connect(const QString &app_socket) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << app_socket; d->mConn.send(CMD_SLAVE_CONNECT, data); // Timeout! } qint64 IdleSlave::pid() const { return d->mPid; } void IdleSlave::reparseConfiguration() { d->mConn.send(CMD_REPARSECONFIGURATION); } bool IdleSlave::match(const QString &protocol, const QString &host, bool needConnected) const { if (d->mOnHold || protocol != d->mProtocol) { return false; } if (host.isEmpty()) { return true; } return (host == d->mHost) && (!needConnected || d->mConnected); } bool IdleSlave::onHold(const QUrl &url) const { if (!d->mOnHold) { return false; } return (url == d->mUrl); } int IdleSlave::age(const QDateTime &now) const { return d->mBirthDate.secsTo(now); } QString IdleSlave::protocol() const { return d->mProtocol; } Connection *IdleSlave::connection() const { return &d->mConn; } bool IdleSlave::hasTempAuthorization() const { return d->mHasTempAuth; } diff --git a/src/core/job.cpp b/src/core/job.cpp index d1c0df5a..5195d05c 100644 --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -1,394 +1,394 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 David Faure Waldo Bastian 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. */ #include "job.h" #include "job_p.h" #include #include #include #include #include #include #include "slave.h" #include "scheduler.h" using namespace KIO; Job::Job() : KCompositeJob(nullptr) , d_ptr(new JobPrivate) { d_ptr->q_ptr = this; setCapabilities(KJob::Killable | KJob::Suspendable); } Job::Job(JobPrivate &dd) : KCompositeJob(nullptr) , d_ptr(&dd) { d_ptr->q_ptr = this; setCapabilities(KJob::Killable | KJob::Suspendable); } Job::~Job() { delete d_ptr; } // Exists for historical reasons only KJobUiDelegate *Job::ui() const { return uiDelegate(); } JobUiDelegateExtension *Job::uiDelegateExtension() const { Q_D(const Job); return d->m_uiDelegateExtension; } void Job::setUiDelegateExtension(JobUiDelegateExtension *extension) { Q_D(Job); d->m_uiDelegateExtension = extension; } bool Job::addSubjob(KJob *jobBase) { //qDebug() << "addSubjob(" << jobBase << ") this=" << this; bool ok = KCompositeJob::addSubjob(jobBase); KIO::Job *job = dynamic_cast(jobBase); if (ok && job) { // Copy metadata into subjob (e.g. window-id, user-timestamp etc.) Q_D(Job); job->mergeMetaData(d->m_outgoingMetaData); // Forward information from that subjob. connect(job, SIGNAL(speed(KJob*,ulong)), SLOT(slotSpeed(KJob*,ulong))); job->setProperty("window", property("window")); // see KJobWidgets job->setProperty("userTimestamp", property("userTimestamp")); // see KJobWidgets job->setUiDelegateExtension(d->m_uiDelegateExtension); } return ok; } bool Job::removeSubjob(KJob *jobBase) { //qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count(); return KCompositeJob::removeSubjob(jobBase); } static QString url_description_string(const QUrl& url) { return url.scheme() == QLatin1String("data") ? QStringLiteral("data:[...]") : KStringHandler::csqueeze(url.toDisplayString(QUrl::PreferLocalFile), 100); } KIO::JobPrivate::~JobPrivate() { } void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest) { emit job->description(job, i18nc("@title job", "Moving"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(src)), qMakePair(i18nc("The destination of a file operation", "Destination"), url_description_string(dest))); } void JobPrivate::emitCopying(KIO::Job *job, const QUrl &src, const QUrl &dest) { emit job->description(job, i18nc("@title job", "Copying"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(src)), qMakePair(i18nc("The destination of a file operation", "Destination"), url_description_string(dest))); } void JobPrivate::emitCreatingDir(KIO::Job *job, const QUrl &dir) { emit job->description(job, i18nc("@title job", "Creating directory"), qMakePair(i18n("Directory"), url_description_string(dir))); } void JobPrivate::emitDeleting(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Deleting"), qMakePair(i18n("File"), url_description_string(url))); } void JobPrivate::emitStating(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Examining"), qMakePair(i18n("File"), url_description_string(url))); } void JobPrivate::emitTransferring(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Transferring"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(url))); } void JobPrivate::emitMounting(KIO::Job *job, const QString &dev, const QString &point) { emit job->description(job, i18nc("@title job", "Mounting"), qMakePair(i18n("Device"), dev), qMakePair(i18n("Mountpoint"), point)); } void JobPrivate::emitUnmounting(KIO::Job *job, const QString &point) { emit job->description(job, i18nc("@title job", "Unmounting"), qMakePair(i18n("Mountpoint"), point)); } bool Job::doKill() { // kill all subjobs, without triggering their result slot Q_FOREACH (KJob *it, subjobs()) { it->kill(KJob::Quietly); } clearSubjobs(); return true; } bool Job::doSuspend() { Q_FOREACH (KJob *it, subjobs()) { if (!it->suspend()) { return false; } } return true; } bool Job::doResume() { Q_FOREACH (KJob *it, subjobs()) { if (!it->resume()) { return false; } } return true; } void JobPrivate::slotSpeed(KJob *, unsigned long speed) { //qDebug() << speed; q_func()->emitSpeed(speed); } //Job::errorString is implemented in job_error.cpp void Job::setParentJob(Job *job) { Q_D(Job); Q_ASSERT(d->m_parentJob == nullptr); Q_ASSERT(job); d->m_parentJob = job; } Job *Job::parentJob() const { return d_func()->m_parentJob; } MetaData Job::metaData() const { return d_func()->m_incomingMetaData; } QString Job::queryMetaData(const QString &key) { return d_func()->m_incomingMetaData.value(key, QString()); } void Job::setMetaData(const KIO::MetaData &_metaData) { Q_D(Job); d->m_outgoingMetaData = _metaData; } void Job::addMetaData(const QString &key, const QString &value) { d_func()->m_outgoingMetaData.insert(key, value); } void Job::addMetaData(const QMap &values) { Q_D(Job); QMap::const_iterator it = values.begin(); for (; it != values.end(); ++it) { d->m_outgoingMetaData.insert(it.key(), it.value()); } } void Job::mergeMetaData(const QMap &values) { Q_D(Job); QMap::const_iterator it = values.begin(); for (; it != values.end(); ++it) // there's probably a faster way if (!d->m_outgoingMetaData.contains(it.key())) { d->m_outgoingMetaData.insert(it.key(), it.value()); } } MetaData Job::outgoingMetaData() const { return d_func()->m_outgoingMetaData; } QByteArray JobPrivate::privilegeOperationData() { PrivilegeOperationStatus status = OperationNotAllowed; if (m_parentJob) { QByteArray jobData = m_parentJob->d_func()->privilegeOperationData(); // Copy meta-data from parent job m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData"))); return jobData; } else { if (m_privilegeExecutionEnabled) { status = OperationAllowed; switch (m_operationType) { case ChangeAttr: m_caption = i18n("Change Attribute"); m_message = i18n("Root privileges are required to change file attributes. " "Do you want to continue?"); break; case Copy: m_caption = i18n("Copy Files"); m_message = i18n("Root privileges are required to complete the copy operation. " "Do you want to continue?"); break; case Delete: m_caption = i18n("Delete Files"); m_message = i18n("Root privileges are required to complete the delete operation. " "However, doing so may damage your system. Do you want to continue?"); break; case MkDir: m_caption = i18n("Create Folder"); m_message = i18n("Root privileges are required to create this folder. " "Do you want to continue?"); break; case Move: m_caption = i18n("Move Items"); m_message = i18n("Root privileges are required to complete the move operation. " "Do you want to continue?"); break; case Rename: m_caption = i18n("Rename"); m_message = i18n("Root privileges are required to complete renaming. " "Do you want to continue?"); break; case Symlink: m_caption = i18n("Create Symlink"); m_message = i18n("Root privileges are required to create a symlink. " "Do you want to continue?"); break; case Transfer: m_caption = i18n("Transfer data"); m_message = i18n("Root privileges are required to complete transferring data. " "Do you want to continue?"); Q_FALLTHROUGH(); default: break; } if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) { // Set meta-data for the top-level job m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed")); } } } QByteArray parentJobData; QDataStream ds(&parentJobData, QIODevice::WriteOnly); ds << status << m_caption << m_message; return parentJobData; } ////////////////////////// class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate { public: DirectCopyJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs) : SimpleJobPrivate(url, command, packedArgs) {} /** * @internal * Called by the scheduler when a @p slave gets to * work on this job. * @param slave the slave that starts working on this job */ void start(Slave *slave) override; Q_DECLARE_PUBLIC(DirectCopyJob) }; DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs) : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs)) { setUiDelegate(KIO::createDefaultJobUiDelegate()); } DirectCopyJob::~DirectCopyJob() { } void DirectCopyJobPrivate::start(Slave *slave) { Q_Q(DirectCopyJob); - q->connect(slave, SIGNAL(canResume(KIO::filesize_t)), - SLOT(slotCanResume(KIO::filesize_t))); + q->connect(slave, &SlaveInterface::canResume, + q, &DirectCopyJob::slotCanResume); SimpleJobPrivate::start(slave); } void DirectCopyJob::slotCanResume(KIO::filesize_t offset) { emit canResume(this, offset); } ////////////////////////// SimpleJob *KIO::file_delete(const QUrl &src, JobFlags flags) { KIO_ARGS << src << qint8(true); // isFile SimpleJob *job = SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags); if (job->uiDelegateExtension()) { job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent); } return job; } ////////// //// #include "moc_job_base.cpp" #include "moc_job_p.cpp" diff --git a/src/core/kpasswdserverclient.cpp b/src/core/kpasswdserverclient.cpp index 7c108f95..4dbc4c9a 100644 --- a/src/core/kpasswdserverclient.cpp +++ b/src/core/kpasswdserverclient.cpp @@ -1,154 +1,154 @@ /* * This file is part of the KDE libraries * Copyright (c) 2009 Michael Leupold * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "kpasswdserverclient.h" #include "kiocoredebug.h" #include #include #include #include #include "kpasswdserverloop_p.h" #include "kpasswdserver_interface.h" class KPasswdServerClientPrivate { public: KPasswdServerClientPrivate() : seqNr(0) {} qlonglong seqNr; QString lastHost; }; KPasswdServerClient::KPasswdServerClient() : m_interface(new OrgKdeKPasswdServerInterface(QStringLiteral("org.kde.kpasswdserver"), QStringLiteral("/modules/kpasswdserver"), QDBusConnection::sessionBus())), d(new KPasswdServerClientPrivate) { } KPasswdServerClient::~KPasswdServerClient() { delete m_interface; delete d; } bool KPasswdServerClient::checkAuthInfo(KIO::AuthInfo *info, qlonglong windowId, qlonglong usertime) { //qDebug() << "window-id=" << windowId << "url=" << info.url; if (!QCoreApplication::instance()) { qCWarning(KIO_CORE) << "kioslave is not a QCoreApplication! This is required for checkAuthInfo."; return false; } // create the loop for waiting for a result before sending the request KPasswdServerLoop loop; - QObject::connect(m_interface, SIGNAL(checkAuthInfoAsyncResult(qlonglong,qlonglong,KIO::AuthInfo)), - &loop, SLOT(slotQueryResult(qlonglong,qlonglong,KIO::AuthInfo))); + QObject::connect(m_interface, &OrgKdeKPasswdServerInterface::checkAuthInfoAsyncResult, + &loop, &KPasswdServerLoop::slotQueryResult); QDBusReply reply = m_interface->checkAuthInfoAsync(*info, windowId, usertime); if (!reply.isValid()) { qCWarning(KIO_CORE) << "Can't communicate with kiod_kpasswdserver (for checkAuthInfo)!"; //qDebug() << reply.error().name() << reply.error().message(); return false; } if (!loop.waitForResult(reply.value())) { qCWarning(KIO_CORE) << "kiod_kpasswdserver died while waiting for reply!"; return false; } if (loop.authInfo().isModified()) { //qDebug() << "username=" << info.username << "password=[hidden]"; *info = loop.authInfo(); return true; } return false; } int KPasswdServerClient::queryAuthInfo(KIO::AuthInfo *info, const QString &errorMsg, qlonglong windowId, qlonglong usertime) { if (info->url.host() != d->lastHost) { // see kpasswdserver/DESIGN d->lastHost = info->url.host(); d->seqNr = 0; } //qDebug() << "window-id=" << windowId; if (!QCoreApplication::instance()) { qCWarning(KIO_CORE) << "kioslave is not a QCoreApplication! This is required for queryAuthInfo."; return KIO::ERR_PASSWD_SERVER; } // create the loop for waiting for a result before sending the request KPasswdServerLoop loop; - QObject::connect(m_interface, SIGNAL(queryAuthInfoAsyncResult(qlonglong,qlonglong,KIO::AuthInfo)), - &loop, SLOT(slotQueryResult(qlonglong,qlonglong,KIO::AuthInfo))); + QObject::connect(m_interface, &OrgKdeKPasswdServerInterface::queryAuthInfoAsyncResult, + &loop, &KPasswdServerLoop::slotQueryResult); QDBusReply reply = m_interface->queryAuthInfoAsync(*info, errorMsg, windowId, d->seqNr, usertime); if (!reply.isValid()) { qCWarning(KIO_CORE) << "Can't communicate with kiod_kpasswdserver (for queryAuthInfo)!"; //qDebug() << reply.error().name() << reply.error().message(); return KIO::ERR_PASSWD_SERVER; } if (!loop.waitForResult(reply.value())) { qCWarning(KIO_CORE) << "kiod_kpasswdserver died while waiting for reply!"; return KIO::ERR_PASSWD_SERVER; } *info = loop.authInfo(); //qDebug() << "username=" << info->username << "password=[hidden]"; const qlonglong newSeqNr = loop.seqNr(); if (newSeqNr > 0) { d->seqNr = newSeqNr; if (info->isModified()) { return KJob::NoError; } } return KIO::ERR_USER_CANCELED; } void KPasswdServerClient::addAuthInfo(const KIO::AuthInfo &info, qlonglong windowId) { m_interface->addAuthInfo(info, windowId); } void KPasswdServerClient::removeAuthInfo(const QString &host, const QString &protocol, const QString &user) { m_interface->removeAuthInfo(host, protocol, user); } diff --git a/src/core/kpasswdserverloop.cpp b/src/core/kpasswdserverloop.cpp index 642c7fda..13968e17 100644 --- a/src/core/kpasswdserverloop.cpp +++ b/src/core/kpasswdserverloop.cpp @@ -1,72 +1,72 @@ /* * This file is part of the KDE libraries * Copyright (c) 2009 Michael Leupold * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "kpasswdserverloop_p.h" #include #include KPasswdServerLoop::KPasswdServerLoop() : m_seqNr(-1) { QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kpasswdserver"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this); - connect(watcher, SIGNAL(serviceUnregistered(QString)), - this, SLOT(kdedServiceUnregistered())); + connect(watcher, &QDBusServiceWatcher::serviceUnregistered, + this, &KPasswdServerLoop::kdedServiceUnregistered); } KPasswdServerLoop::~KPasswdServerLoop() { } bool KPasswdServerLoop::waitForResult(qlonglong requestId) { m_requestId = requestId; m_seqNr = -1; m_authInfo = KIO::AuthInfo(); return (exec() == 0); } qlonglong KPasswdServerLoop::seqNr() const { return m_seqNr; } const KIO::AuthInfo &KPasswdServerLoop::authInfo() const { return m_authInfo; } void KPasswdServerLoop::slotQueryResult(qlonglong requestId, qlonglong seqNr, const KIO::AuthInfo &authInfo) { if (m_requestId == requestId) { m_seqNr = seqNr; m_authInfo = authInfo; exit(0); } } void KPasswdServerLoop::kdedServiceUnregistered() { exit(-1); } #include "moc_kpasswdserverloop_p.cpp" diff --git a/src/core/ktcpsocket.cpp b/src/core/ktcpsocket.cpp index b4c8664e..adb930d0 100644 --- a/src/core/ktcpsocket.cpp +++ b/src/core/ktcpsocket.cpp @@ -1,1091 +1,1092 @@ /* This file is part of the KDE libraries Copyright (C) 2007, 2008 Andreas Hartmetz 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. */ #include "ktcpsocket.h" #include "ktcpsocket_p.h" #include "kiocoredebug.h" #include #include #include #include #include #include #include #include #include +#include static KTcpSocket::SslVersion kSslVersionFromQ(QSsl::SslProtocol protocol) { switch (protocol) { case QSsl::SslV2: return KTcpSocket::SslV2; case QSsl::SslV3: return KTcpSocket::SslV3; case QSsl::TlsV1_0: return KTcpSocket::TlsV1; case QSsl::TlsV1_1: return KTcpSocket::TlsV1_1; case QSsl::TlsV1_2: return KTcpSocket::TlsV1_2; case QSsl::AnyProtocol: return KTcpSocket::AnySslVersion; case QSsl::TlsV1SslV3: return KTcpSocket::TlsV1SslV3; case QSsl::SecureProtocols: return KTcpSocket::SecureProtocols; default: return KTcpSocket::UnknownSslVersion; } } static QSsl::SslProtocol qSslProtocolFromK(KTcpSocket::SslVersion sslVersion) { //### this lowlevel bit-banging is a little dangerous and a likely source of bugs if (sslVersion == KTcpSocket::AnySslVersion) { return QSsl::AnyProtocol; } //does it contain any valid protocol? KTcpSocket::SslVersions validVersions(KTcpSocket::SslV2 | KTcpSocket::SslV3 | KTcpSocket::TlsV1); validVersions |= KTcpSocket::TlsV1_1; validVersions |= KTcpSocket::TlsV1_2; validVersions |= KTcpSocket::TlsV1SslV3; validVersions |= KTcpSocket::SecureProtocols; if (!(sslVersion & validVersions)) { return QSsl::UnknownProtocol; } switch (sslVersion) { case KTcpSocket::SslV2: return QSsl::SslV2; case KTcpSocket::SslV3: return QSsl::SslV3; case KTcpSocket::TlsV1_0: return QSsl::TlsV1_0; case KTcpSocket::TlsV1_1: return QSsl::TlsV1_1; case KTcpSocket::TlsV1_2: return QSsl::TlsV1_2; case KTcpSocket::TlsV1SslV3: return QSsl::TlsV1SslV3; case KTcpSocket::SecureProtocols: return QSsl::SecureProtocols; default: //QSslSocket doesn't really take arbitrary combinations. It's one or all. return QSsl::AnyProtocol; } } static QString protocolString(QSsl::SslProtocol protocol) { switch (protocol) { case QSsl::SslV2: return QStringLiteral("SSLv2"); case QSsl::SslV3: return QStringLiteral("SSLv3"); case QSsl::TlsV1_0: return QStringLiteral("TLSv1.0"); case QSsl::TlsV1_1: return QStringLiteral("TLSv1.1"); case QSsl::TlsV1_2: return QStringLiteral("TLSv1.2"); default: return QStringLiteral("Unknown");; } } //cipher class converter KSslCipher -> QSslCipher class CipherCc { public: CipherCc() { foreach (const QSslCipher &c, QSslSocket::supportedCiphers()) { allCiphers.insert(c.name(), c); } } QSslCipher converted(const KSslCipher &ksc) { return allCiphers.value(ksc.name()); } private: QHash allCiphers; }; class KSslErrorPrivate { public: static KSslError::Error errorFromQSslError(QSslError::SslError e) { switch (e) { case QSslError::NoError: return KSslError::NoError; case QSslError::UnableToGetLocalIssuerCertificate: case QSslError::InvalidCaCertificate: return KSslError::InvalidCertificateAuthorityCertificate; case QSslError::InvalidNotBeforeField: case QSslError::InvalidNotAfterField: case QSslError::CertificateNotYetValid: case QSslError::CertificateExpired: return KSslError::ExpiredCertificate; case QSslError::UnableToDecodeIssuerPublicKey: case QSslError::SubjectIssuerMismatch: case QSslError::AuthorityIssuerSerialNumberMismatch: return KSslError::InvalidCertificate; case QSslError::SelfSignedCertificate: case QSslError::SelfSignedCertificateInChain: return KSslError::SelfSignedCertificate; case QSslError::CertificateRevoked: return KSslError::RevokedCertificate; case QSslError::InvalidPurpose: return KSslError::InvalidCertificatePurpose; case QSslError::CertificateUntrusted: return KSslError::UntrustedCertificate; case QSslError::CertificateRejected: return KSslError::RejectedCertificate; case QSslError::NoPeerCertificate: return KSslError::NoPeerCertificate; case QSslError::HostNameMismatch: return KSslError::HostNameMismatch; case QSslError::UnableToVerifyFirstCertificate: case QSslError::UnableToDecryptCertificateSignature: case QSslError::UnableToGetIssuerCertificate: case QSslError::CertificateSignatureFailed: return KSslError::CertificateSignatureFailed; case QSslError::PathLengthExceeded: return KSslError::PathLengthExceeded; case QSslError::UnspecifiedError: case QSslError::NoSslSupport: default: return KSslError::UnknownError; } } static QString errorString(KSslError::Error e) { switch (e) { case KSslError::NoError: return i18nc("SSL error", "No error"); case KSslError::InvalidCertificateAuthorityCertificate: return i18nc("SSL error", "The certificate authority's certificate is invalid"); case KSslError::ExpiredCertificate: return i18nc("SSL error", "The certificate has expired"); case KSslError::InvalidCertificate: return i18nc("SSL error", "The certificate is invalid"); case KSslError::SelfSignedCertificate: return i18nc("SSL error", "The certificate is not signed by any trusted certificate authority"); case KSslError::RevokedCertificate: return i18nc("SSL error", "The certificate has been revoked"); case KSslError::InvalidCertificatePurpose: return i18nc("SSL error", "The certificate is unsuitable for this purpose"); case KSslError::UntrustedCertificate: return i18nc("SSL error", "The root certificate authority's certificate is not trusted for this purpose"); case KSslError::RejectedCertificate: return i18nc("SSL error", "The certificate authority's certificate is marked to reject this certificate's purpose"); case KSslError::NoPeerCertificate: return i18nc("SSL error", "The peer did not present any certificate"); case KSslError::HostNameMismatch: return i18nc("SSL error", "The certificate does not apply to the given host"); case KSslError::CertificateSignatureFailed: return i18nc("SSL error", "The certificate cannot be verified for internal reasons"); case KSslError::PathLengthExceeded: return i18nc("SSL error", "The certificate chain is too long"); case KSslError::UnknownError: default: return i18nc("SSL error", "Unknown error"); } } KSslError::Error error; QSslCertificate certificate; }; KSslError::KSslError(Error errorCode, const QSslCertificate &certificate) : d(new KSslErrorPrivate()) { d->error = errorCode; d->certificate = certificate; } KSslError::KSslError(const QSslError &other) : d(new KSslErrorPrivate()) { d->error = KSslErrorPrivate::errorFromQSslError(other.error()); d->certificate = other.certificate(); } KSslError::KSslError(const KSslError &other) : d(new KSslErrorPrivate()) { *d = *other.d; } KSslError::~KSslError() { delete d; } KSslError &KSslError::operator=(const KSslError &other) { *d = *other.d; return *this; } KSslError::Error KSslError::error() const { return d->error; } QString KSslError::errorString() const { return KSslErrorPrivate::errorString(d->error); } QSslCertificate KSslError::certificate() const { return d->certificate; } class KTcpSocketPrivate { public: explicit KTcpSocketPrivate(KTcpSocket *qq) : q(qq), certificatesLoaded(false), emittedReadyRead(false) { // create the instance, which sets Qt's static internal cert set to empty. KSslCertificateManager::self(); } KTcpSocket::State state(QAbstractSocket::SocketState s) { switch (s) { case QAbstractSocket::UnconnectedState: return KTcpSocket::UnconnectedState; case QAbstractSocket::HostLookupState: return KTcpSocket::HostLookupState; case QAbstractSocket::ConnectingState: return KTcpSocket::ConnectingState; case QAbstractSocket::ConnectedState: return KTcpSocket::ConnectedState; case QAbstractSocket::ClosingState: return KTcpSocket::ClosingState; case QAbstractSocket::BoundState: case QAbstractSocket::ListeningState: //### these two are not relevant as long as this can't be a server socket default: return KTcpSocket::UnconnectedState; //the closest to "error" } } KTcpSocket::EncryptionMode encryptionMode(QSslSocket::SslMode mode) { switch (mode) { case QSslSocket::SslClientMode: return KTcpSocket::SslClientMode; case QSslSocket::SslServerMode: return KTcpSocket::SslServerMode; default: return KTcpSocket::UnencryptedMode; } } KTcpSocket::Error errorFromAbsSocket(QAbstractSocket::SocketError e) { switch (e) { case QAbstractSocket::ConnectionRefusedError: return KTcpSocket::ConnectionRefusedError; case QAbstractSocket::RemoteHostClosedError: return KTcpSocket::RemoteHostClosedError; case QAbstractSocket::HostNotFoundError: return KTcpSocket::HostNotFoundError; case QAbstractSocket::SocketAccessError: return KTcpSocket::SocketAccessError; case QAbstractSocket::SocketResourceError: return KTcpSocket::SocketResourceError; case QAbstractSocket::SocketTimeoutError: return KTcpSocket::SocketTimeoutError; case QAbstractSocket::NetworkError: return KTcpSocket::NetworkError; case QAbstractSocket::UnsupportedSocketOperationError: return KTcpSocket::UnsupportedSocketOperationError; case QAbstractSocket::SslHandshakeFailedError: return KTcpSocket::SslHandshakeFailedError; case QAbstractSocket::DatagramTooLargeError: //we don't do UDP case QAbstractSocket::AddressInUseError: case QAbstractSocket::SocketAddressNotAvailableError: //### own values if/when we ever get server socket support case QAbstractSocket::ProxyAuthenticationRequiredError: //### maybe we need an enum value for this case QAbstractSocket::UnknownSocketError: default: return KTcpSocket::UnknownError; } } //private slots void reemitSocketError(QAbstractSocket::SocketError e) { q->setErrorString(sock.errorString()); emit q->error(errorFromAbsSocket(e)); } void reemitSslErrors(const QList &errors) { q->setErrorString(sock.errorString()); q->showSslErrors(); //H4X QList kErrors; foreach (const QSslError &e, errors) { kErrors.append(KSslError(e)); } emit q->sslErrors(kErrors); } void reemitStateChanged(QAbstractSocket::SocketState s) { emit q->stateChanged(state(s)); } void reemitModeChanged(QSslSocket::SslMode m) { emit q->encryptionModeChanged(encryptionMode(m)); } // This method is needed because we might emit readyRead() due to this QIODevice // having some data buffered, so we need to care about blocking, too. //### useless ATM as readyRead() now just calls d->sock.readyRead(). void reemitReadyRead() { if (!emittedReadyRead) { emittedReadyRead = true; emit q->readyRead(); emittedReadyRead = false; } } void maybeLoadCertificates() { if (!certificatesLoaded) { sock.setCaCertificates(KSslCertificateManager::self()->caCertificates()); certificatesLoaded = true; } } KTcpSocket *const q; bool certificatesLoaded; bool emittedReadyRead; QSslSocket sock; QList ciphers; KTcpSocket::SslVersion advertisedSslVersion; CipherCc ccc; }; KTcpSocket::KTcpSocket(QObject *parent) : QIODevice(parent), d(new KTcpSocketPrivate(this)) { d->advertisedSslVersion = SslV3; - connect(&d->sock, SIGNAL(aboutToClose()), this, SIGNAL(aboutToClose())); - connect(&d->sock, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64))); - connect(&d->sock, SIGNAL(encryptedBytesWritten(qint64)), this, SIGNAL(encryptedBytesWritten(qint64))); + connect(&d->sock, &QIODevice::aboutToClose, this, &QIODevice::aboutToClose); + connect(&d->sock, &QIODevice::bytesWritten, this, &QIODevice::bytesWritten); + connect(&d->sock, &QSslSocket::encryptedBytesWritten, this, &KTcpSocket::encryptedBytesWritten); connect(&d->sock, SIGNAL(readyRead()), this, SLOT(reemitReadyRead())); - connect(&d->sock, SIGNAL(connected()), this, SIGNAL(connected())); - connect(&d->sock, SIGNAL(encrypted()), this, SIGNAL(encrypted())); - connect(&d->sock, SIGNAL(disconnected()), this, SIGNAL(disconnected())); + connect(&d->sock, &QAbstractSocket::connected, this, &KTcpSocket::connected); + connect(&d->sock, &QSslSocket::encrypted, this, &KTcpSocket::encrypted); + connect(&d->sock, &QAbstractSocket::disconnected, this, &KTcpSocket::disconnected); #ifndef QT_NO_NETWORKPROXY - connect(&d->sock, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&d->sock, &QAbstractSocket::proxyAuthenticationRequired, + this, &KTcpSocket::proxyAuthenticationRequired); #endif connect(&d->sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(reemitSocketError(QAbstractSocket::SocketError))); connect(&d->sock, SIGNAL(sslErrors(QList)), this, SLOT(reemitSslErrors(QList))); - connect(&d->sock, SIGNAL(hostFound()), this, SIGNAL(hostFound())); + connect(&d->sock, &QAbstractSocket::hostFound, this, &KTcpSocket::hostFound); connect(&d->sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(reemitStateChanged(QAbstractSocket::SocketState))); connect(&d->sock, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(reemitModeChanged(QSslSocket::SslMode))); } KTcpSocket::~KTcpSocket() { delete d; } ////////////////////////////// (mostly) virtuals from QIODevice bool KTcpSocket::atEnd() const { return d->sock.atEnd() && QIODevice::atEnd(); } qint64 KTcpSocket::bytesAvailable() const { return d->sock.bytesAvailable() + QIODevice::bytesAvailable(); } qint64 KTcpSocket::bytesToWrite() const { return d->sock.bytesToWrite(); } bool KTcpSocket::canReadLine() const { return d->sock.canReadLine() || QIODevice::canReadLine(); } void KTcpSocket::close() { d->sock.close(); QIODevice::close(); } bool KTcpSocket::isSequential() const { return true; } bool KTcpSocket::open(QIODevice::OpenMode open) { bool ret = d->sock.open(open); setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); return ret; } bool KTcpSocket::waitForBytesWritten(int msecs) { return d->sock.waitForBytesWritten(msecs); } bool KTcpSocket::waitForReadyRead(int msecs) { return d->sock.waitForReadyRead(msecs); } qint64 KTcpSocket::readData(char *data, qint64 maxSize) { return d->sock.read(data, maxSize); } qint64 KTcpSocket::writeData(const char *data, qint64 maxSize) { return d->sock.write(data, maxSize); } ////////////////////////////// public methods from QAbstractSocket void KTcpSocket::abort() { d->sock.abort(); } void KTcpSocket::connectToHost(const QString &hostName, quint16 port, ProxyPolicy policy) { if (policy == AutoProxy) { //### } d->sock.connectToHost(hostName, port); // there are enough layers of buffers between us and the network, and there is a quirk // in QIODevice that can make it try to readData() twice per read() call if buffered and // reaData() does not deliver enough data the first time. like when the other side is // simply not sending any more data... // this can *apparently* lead to long delays sometimes which stalls applications. // do not want. setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); } void KTcpSocket::connectToHost(const QHostAddress &hostAddress, quint16 port, ProxyPolicy policy) { if (policy == AutoProxy) { //### } d->sock.connectToHost(hostAddress, port); setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); } void KTcpSocket::connectToHost(const QUrl &url, ProxyPolicy policy) { if (policy == AutoProxy) { //### } d->sock.connectToHost(url.host(), url.port()); setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); } void KTcpSocket::disconnectFromHost() { d->sock.disconnectFromHost(); setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); } KTcpSocket::Error KTcpSocket::error() const { return d->errorFromAbsSocket(d->sock.error()); } QList KTcpSocket::sslErrors() const { //### pretty slow; also consider throwing out duplicate error codes. We may get // duplicates even though there were none in the original list because KSslError // has a smallest common denominator range of SSL error codes. QList ret; foreach (const QSslError &e, d->sock.sslErrors()) { ret.append(KSslError(e)); } return ret; } bool KTcpSocket::flush() { return d->sock.flush(); } bool KTcpSocket::isValid() const { return d->sock.isValid(); } QHostAddress KTcpSocket::localAddress() const { return d->sock.localAddress(); } QHostAddress KTcpSocket::peerAddress() const { return d->sock.peerAddress(); } QString KTcpSocket::peerName() const { return d->sock.peerName(); } quint16 KTcpSocket::peerPort() const { return d->sock.peerPort(); } #ifndef QT_NO_NETWORKPROXY QNetworkProxy KTcpSocket::proxy() const { return d->sock.proxy(); } #endif qint64 KTcpSocket::readBufferSize() const { return d->sock.readBufferSize(); } #ifndef QT_NO_NETWORKPROXY void KTcpSocket::setProxy(const QNetworkProxy &proxy) { d->sock.setProxy(proxy); } #endif void KTcpSocket::setReadBufferSize(qint64 size) { d->sock.setReadBufferSize(size); } KTcpSocket::State KTcpSocket::state() const { return d->state(d->sock.state()); } bool KTcpSocket::waitForConnected(int msecs) { bool ret = d->sock.waitForConnected(msecs); if (!ret) { setErrorString(d->sock.errorString()); } setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); return ret; } bool KTcpSocket::waitForDisconnected(int msecs) { bool ret = d->sock.waitForDisconnected(msecs); if (!ret) { setErrorString(d->sock.errorString()); } setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); return ret; } ////////////////////////////// public methods from QSslSocket void KTcpSocket::addCaCertificate(const QSslCertificate &certificate) { d->maybeLoadCertificates(); d->sock.addCaCertificate(certificate); } /* bool KTcpSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format, QRegExp::PatternSyntax syntax) { d->maybeLoadCertificates(); return d->sock.addCaCertificates(path, format, syntax); } */ void KTcpSocket::addCaCertificates(const QList &certificates) { d->maybeLoadCertificates(); d->sock.addCaCertificates(certificates); } QList KTcpSocket::caCertificates() const { d->maybeLoadCertificates(); return d->sock.caCertificates(); } QList KTcpSocket::ciphers() const { return d->ciphers; } void KTcpSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode openMode) { d->maybeLoadCertificates(); d->sock.setProtocol(qSslProtocolFromK(d->advertisedSslVersion)); d->sock.connectToHostEncrypted(hostName, port, openMode); setOpenMode(d->sock.openMode() | QIODevice::Unbuffered); } QSslCertificate KTcpSocket::localCertificate() const { return d->sock.localCertificate(); } QList KTcpSocket::peerCertificateChain() const { return d->sock.peerCertificateChain(); } KSslKey KTcpSocket::privateKey() const { return KSslKey(d->sock.privateKey()); } KSslCipher KTcpSocket::sessionCipher() const { return KSslCipher(d->sock.sessionCipher()); } void KTcpSocket::setCaCertificates(const QList &certificates) { d->sock.setCaCertificates(certificates); d->certificatesLoaded = true; } void KTcpSocket::setCiphers(const QList &ciphers) { d->ciphers = ciphers; QList cl; foreach (const KSslCipher &c, d->ciphers) { cl.append(d->ccc.converted(c)); } d->sock.setCiphers(cl); } void KTcpSocket::setLocalCertificate(const QSslCertificate &certificate) { d->sock.setLocalCertificate(certificate); } void KTcpSocket::setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format) { d->sock.setLocalCertificate(fileName, format); } void KTcpSocket::setVerificationPeerName(const QString &hostName) { d->sock.setPeerVerifyName(hostName); } void KTcpSocket::setPrivateKey(const KSslKey &key) { // We cannot map KSslKey::Algorithm:Dh to anything in QSsl::KeyAlgorithm. if (key.algorithm() == KSslKey::Dh) { return; } QSslKey _key(key.toDer(), (key.algorithm() == KSslKey::Rsa) ? QSsl::Rsa : QSsl::Dsa, QSsl::Der, (key.secrecy() == KSslKey::PrivateKey) ? QSsl::PrivateKey : QSsl::PublicKey); d->sock.setPrivateKey(_key); } void KTcpSocket::setPrivateKey(const QString &fileName, KSslKey::Algorithm algorithm, QSsl::EncodingFormat format, const QByteArray &passPhrase) { // We cannot map KSslKey::Algorithm:Dh to anything in QSsl::KeyAlgorithm. if (algorithm == KSslKey::Dh) { return; } d->sock.setPrivateKey(fileName, (algorithm == KSslKey::Rsa) ? QSsl::Rsa : QSsl::Dsa, format, passPhrase); } bool KTcpSocket::waitForEncrypted(int msecs) { return d->sock.waitForEncrypted(msecs); } KTcpSocket::EncryptionMode KTcpSocket::encryptionMode() const { return d->encryptionMode(d->sock.mode()); } QVariant KTcpSocket::socketOption(QAbstractSocket::SocketOption options) const { return d->sock.socketOption(options); } void KTcpSocket::setSocketOption(QAbstractSocket::SocketOption options, const QVariant &value) { d->sock.setSocketOption(options, value); } QSslConfiguration KTcpSocket::sslConfiguration() const { return d->sock.sslConfiguration(); } void KTcpSocket::setSslConfiguration(const QSslConfiguration &configuration) { d->sock.setSslConfiguration(configuration); } //slot void KTcpSocket::ignoreSslErrors() { d->sock.ignoreSslErrors(); } //slot void KTcpSocket::startClientEncryption() { d->maybeLoadCertificates(); d->sock.setProtocol(qSslProtocolFromK(d->advertisedSslVersion)); d->sock.startClientEncryption(); } //debugging H4X void KTcpSocket::showSslErrors() { foreach (const QSslError &e, d->sock.sslErrors()) { qCDebug(KIO_CORE) << e.errorString(); } } void KTcpSocket::setAdvertisedSslVersion(KTcpSocket::SslVersion version) { d->advertisedSslVersion = version; } KTcpSocket::SslVersion KTcpSocket::advertisedSslVersion() const { return d->advertisedSslVersion; } KTcpSocket::SslVersion KTcpSocket::negotiatedSslVersion() const { if (!d->sock.isEncrypted()) { return UnknownSslVersion; } return kSslVersionFromQ(d->sock.sessionProtocol()); } QString KTcpSocket::negotiatedSslVersionName() const { if (!d->sock.isEncrypted()) { return QString(); } return protocolString(d->sock.sessionProtocol()); } ////////////////////////////// KSslKey class KSslKeyPrivate { public: KSslKey::Algorithm convertAlgorithm(QSsl::KeyAlgorithm a) { switch (a) { case QSsl::Dsa: return KSslKey::Dsa; default: return KSslKey::Rsa; } } KSslKey::Algorithm algorithm; KSslKey::KeySecrecy secrecy; bool isExportable; QByteArray der; }; KSslKey::KSslKey() : d(new KSslKeyPrivate) { d->algorithm = Rsa; d->secrecy = PublicKey; d->isExportable = true; } KSslKey::KSslKey(const KSslKey &other) : d(new KSslKeyPrivate) { *d = *other.d; } KSslKey::KSslKey(const QSslKey &qsk) : d(new KSslKeyPrivate) { d->algorithm = d->convertAlgorithm(qsk.algorithm()); d->secrecy = (qsk.type() == QSsl::PrivateKey) ? PrivateKey : PublicKey; d->isExportable = true; d->der = qsk.toDer(); } KSslKey::~KSslKey() { delete d; } KSslKey &KSslKey::operator=(const KSslKey &other) { *d = *other.d; return *this; } KSslKey::Algorithm KSslKey::algorithm() const { return d->algorithm; } bool KSslKey::isExportable() const { return d->isExportable; } KSslKey::KeySecrecy KSslKey::secrecy() const { return d->secrecy; } QByteArray KSslKey::toDer() const { return d->der; } ////////////////////////////// KSslCipher //nice-to-have: make implicitly shared class KSslCipherPrivate { public: QString authenticationMethod; QString encryptionMethod; QString keyExchangeMethod; QString name; bool isNull; int supportedBits; int usedBits; }; KSslCipher::KSslCipher() : d(new KSslCipherPrivate) { d->isNull = true; d->supportedBits = 0; d->usedBits = 0; } KSslCipher::KSslCipher(const KSslCipher &other) : d(new KSslCipherPrivate) { *d = *other.d; } KSslCipher::KSslCipher(const QSslCipher &qsc) : d(new KSslCipherPrivate) { d->authenticationMethod = qsc.authenticationMethod(); d->encryptionMethod = qsc.encryptionMethod(); //Qt likes to append the number of bits (usedBits?) to the algorithm, //for example "AES(256)". We only want the pure algorithm name, though. int parenIdx = d->encryptionMethod.indexOf(QLatin1Char('(')); if (parenIdx > 0) { d->encryptionMethod.truncate(parenIdx); } d->keyExchangeMethod = qsc.keyExchangeMethod(); d->name = qsc.name(); d->isNull = qsc.isNull(); d->supportedBits = qsc.supportedBits(); d->usedBits = qsc.usedBits(); } KSslCipher::~KSslCipher() { delete d; } KSslCipher &KSslCipher::operator=(const KSslCipher &other) { *d = *other.d; return *this; } bool KSslCipher::isNull() const { return d->isNull; } QString KSslCipher::authenticationMethod() const { return d->authenticationMethod; } QString KSslCipher::encryptionMethod() const { return d->encryptionMethod; } QString KSslCipher::keyExchangeMethod() const { return d->keyExchangeMethod; } QString KSslCipher::digestMethod() const { //### This is not really backend neutral. It works for OpenSSL and // for RFC compliant names, though. if (d->name.endsWith(QLatin1String("SHA"))) { return QStringLiteral("SHA-1"); } else if (d->name.endsWith(QLatin1String("MD5"))) { return QStringLiteral("MD5"); } else { return QString(); } } QString KSslCipher::name() const { return d->name; } int KSslCipher::supportedBits() const { return d->supportedBits; } int KSslCipher::usedBits() const { return d->usedBits; } //static QList KSslCipher::supportedCiphers() { QList ret; QList candidates = QSslSocket::supportedCiphers(); foreach (const QSslCipher &c, candidates) { ret.append(KSslCipher(c)); } return ret; } KSslErrorUiData::KSslErrorUiData() : d(new Private()) { d->usedBits = 0; d->bits = 0; } KSslErrorUiData::KSslErrorUiData(const KTcpSocket *socket) : d(new Private()) { d->certificateChain = socket->peerCertificateChain(); d->sslErrors = socket->sslErrors(); d->ip = socket->peerAddress().toString(); d->host = socket->peerName(); d->sslProtocol = socket->negotiatedSslVersionName(); d->cipher = socket->sessionCipher().name(); d->usedBits = socket->sessionCipher().usedBits(); d->bits = socket->sessionCipher().supportedBits(); } KSslErrorUiData::KSslErrorUiData(const QSslSocket *socket) : d(new Private()) { d->certificateChain = socket->peerCertificateChain(); // See KTcpSocket::sslErrors() foreach (const QSslError &e, socket->sslErrors()) { d->sslErrors.append(KSslError(e)); } d->ip = socket->peerAddress().toString(); d->host = socket->peerName(); if (socket->isEncrypted()) { d->sslProtocol = socket->sessionCipher().protocolString(); } d->cipher = socket->sessionCipher().name(); d->usedBits = socket->sessionCipher().usedBits(); d->bits = socket->sessionCipher().supportedBits(); } KSslErrorUiData::KSslErrorUiData(const KSslErrorUiData &other) : d(new Private(*other.d)) {} KSslErrorUiData::~KSslErrorUiData() { delete d; } KSslErrorUiData &KSslErrorUiData::operator=(const KSslErrorUiData &other) { *d = *other.d; return *this; } #include "moc_ktcpsocket.cpp" diff --git a/src/core/listjob.cpp b/src/core/listjob.cpp index 346c081c..25d482d0 100644 --- a/src/core/listjob.cpp +++ b/src/core/listjob.cpp @@ -1,304 +1,304 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 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. */ #include "listjob.h" #include "job_p.h" #include "scheduler.h" #include #include "slave.h" #include "../pathhelpers_p.h" #include using namespace KIO; class KIO::ListJobPrivate: public KIO::SimpleJobPrivate { public: ListJobPrivate(const QUrl &url, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden) : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()), recursive(_recursive), includeHidden(_includeHidden), m_prefix(prefix), m_displayPrefix(displayPrefix), m_processedEntries(0) {} bool recursive; bool includeHidden; QString m_prefix; QString m_displayPrefix; unsigned long m_processedEntries; QUrl m_redirectionURL; /** * @internal * Called by the scheduler when a @p slave gets to * work on this job. * @param slave the slave that starts working on this job */ void start(Slave *slave) override; void slotListEntries(const KIO::UDSEntryList &list); void slotRedirection(const QUrl &url); void gotEntries(KIO::Job *subjob, const KIO::UDSEntryList &list); void slotSubError(ListJob* job, ListJob* subJob); Q_DECLARE_PUBLIC(ListJob) static inline ListJob *newJob(const QUrl &u, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden, JobFlags flags = HideProgressInfo) { ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden)); job->setUiDelegate(KIO::createDefaultJobUiDelegate()); if (!(flags & HideProgressInfo)) { KIO::getJobTracker()->registerJob(job); } return job; } static inline ListJob *newJobNoUi(const QUrl &u, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden) { return new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden)); } }; ListJob::ListJob(ListJobPrivate &dd) : SimpleJob(dd) { Q_D(ListJob); // We couldn't set the args when calling the parent constructor, // so do it now. QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_url; } ListJob::~ListJob() { } void ListJobPrivate::slotListEntries(const KIO::UDSEntryList &list) { Q_Q(ListJob); // Emit progress info (takes care of emit processedSize and percent) m_processedEntries += list.count(); slotProcessedSize(m_processedEntries); if (recursive) { UDSEntryList::ConstIterator it = list.begin(); const UDSEntryList::ConstIterator end = list.end(); for (; it != end; ++it) { const UDSEntry &entry = *it; QUrl itemURL; const QString udsUrl = entry.stringValue(KIO::UDSEntry::UDS_URL); QString filename; if (!udsUrl.isEmpty()) { itemURL = QUrl(udsUrl); filename = itemURL.fileName(); } else { // no URL, use the name itemURL = q->url(); filename = entry.stringValue(KIO::UDSEntry::UDS_NAME); Q_ASSERT(!filename.isEmpty()); // we'll recurse forever otherwise :) itemURL.setPath(concatPaths(itemURL.path(), filename)); } if (entry.isDir() && !entry.isLink()) { Q_ASSERT(!filename.isEmpty()); QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); if (displayName.isEmpty()) { displayName = filename; } // skip hidden dirs when listing if requested if (filename != QLatin1String("..") && filename != QLatin1String(".") && (includeHidden || filename[0] != QLatin1Char('.'))) { ListJob *job = ListJobPrivate::newJobNoUi(itemURL, true /*recursive*/, m_prefix + filename + QLatin1Char('/'), m_displayPrefix + displayName + QLatin1Char('/'), includeHidden); Scheduler::setJobPriority(job, 1); QObject::connect(job, &ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) {gotEntries(job, list);} ); QObject::connect(job, &ListJob::subError, q, [this](KIO::ListJob *job, KIO::ListJob *ljob) {slotSubError(job, ljob);} ); q->addSubjob(job); } } } } // Not recursive, or top-level of recursive listing : return now (send . and .. as well) // exclusion of hidden files also requires the full sweep, but the case for full-listing // a single dir is probably common enough to justify the shortcut if (m_prefix.isNull() && includeHidden) { emit q->entries(q, list); } else { // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that UDSEntryList newlist; UDSEntryList::const_iterator it = list.begin(); const UDSEntryList::const_iterator end = list.end(); for (; it != end; ++it) { // Modify the name in the UDSEntry UDSEntry newone = *it; const QString filename = newone.stringValue(KIO::UDSEntry::UDS_NAME); QString displayName = newone.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); if (displayName.isEmpty()) { displayName = filename; } // Avoid returning entries like subdir/. and subdir/.., but include . and .. for // the toplevel dir, and skip hidden files/dirs if that was requested if ((m_prefix.isNull() || (filename != QLatin1String("..") && filename != QLatin1String("."))) && (includeHidden || (filename[0] != QLatin1Char('.')))) { // ## Didn't find a way to use the iterator instead of re-doing a key lookup newone.replace(KIO::UDSEntry::UDS_NAME, m_prefix + filename); newone.replace(KIO::UDSEntry::UDS_DISPLAY_NAME, m_displayPrefix + displayName); newlist.append(newone); } } emit q->entries(q, newlist); } } void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList &list) { // Forward entries received by subjob - faking we received them ourselves Q_Q(ListJob); emit q->entries(q, list); } void ListJobPrivate::slotSubError(KIO::ListJob* /*job*/, KIO::ListJob* subJob) { Q_Q(ListJob); emit q->subError(q, subJob); // Let the signal of subError go up } void ListJob::slotResult(KJob *job) { Q_D(ListJob); if (job->error()) { // If we can't list a subdir, the result is still ok // This is why we override KCompositeJob::slotResult - to not set // an error on parent job. // Let's emit a signal about this though emit subError(this, static_cast(job)); } removeSubjob(job); if (!hasSubjobs() && !d->m_slave) { // if the main directory listing is still running, it will emit result in SimpleJob::slotFinished() emitResult(); } } void ListJobPrivate::slotRedirection(const QUrl &url) { Q_Q(ListJob); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("redirect"), m_url, url)) { qCWarning(KIO_CORE) << "Redirection from" << m_url << "to" << url << "REJECTED!"; return; } m_redirectionURL = url; // We'll remember that when the job finishes emit q->redirection(q, m_redirectionURL); } void ListJob::slotFinished() { Q_D(ListJob); if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error()) { //qDebug() << "Redirection to " << d->m_redirectionURL; if (queryMetaData(QStringLiteral("permanent-redirect")) == QLatin1String("true")) { emit permanentRedirection(this, d->m_url, d->m_redirectionURL); } if (d->m_redirectionHandlingEnabled) { d->m_packedArgs.truncate(0); QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_redirectionURL; d->restartAfterRedirection(&d->m_redirectionURL); return; } } // Return slave to the scheduler SimpleJob::slotFinished(); } void ListJob::slotMetaData(const KIO::MetaData &_metaData) { Q_D(ListJob); SimpleJob::slotMetaData(_metaData); storeSSLSessionFromJob(d->m_redirectionURL); } ListJob *KIO::listDir(const QUrl &url, JobFlags flags, bool includeHidden) { return ListJobPrivate::newJob(url, false, QString(), QString(), includeHidden, flags); } ListJob *KIO::listRecursive(const QUrl &url, JobFlags flags, bool includeHidden) { return ListJobPrivate::newJob(url, true, QString(), QString(), includeHidden, flags); } void ListJob::setUnrestricted(bool unrestricted) { Q_D(ListJob); if (unrestricted) { d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted; } else { d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted; } } void ListJobPrivate::start(Slave *slave) { Q_Q(ListJob); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), m_url, m_url) && !(m_extraFlags & EF_ListJobUnrestricted)) { q->setError(ERR_ACCESS_DENIED); q->setErrorText(m_url.toDisplayString()); - QTimer::singleShot(0, q, SLOT(slotFinished())); + QTimer::singleShot(0, q, &ListJob::slotFinished); return; } QObject::connect(slave, &Slave::listEntries, q, [this](const KIO::UDSEntryList &list){ slotListEntries(list);} ); QObject::connect(slave, &Slave::totalSize, q, [this](KIO::filesize_t size){ slotTotalSize(size);} ); QObject::connect(slave, &Slave::redirection, q, [this](const QUrl &url){ slotRedirection(url);} ); SimpleJobPrivate::start(slave); } const QUrl &ListJob::redirectionUrl() const { return d_func()->m_redirectionURL; } #include "moc_listjob.cpp" diff --git a/src/core/listjob.h b/src/core/listjob.h index 5d809fb3..efd55f2b 100644 --- a/src/core/listjob.h +++ b/src/core/listjob.h @@ -1,142 +1,143 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 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_LISTJOB_H #define KIO_LISTJOB_H #include "simplejob.h" #include namespace KIO { class ListJobPrivate; /** * @class KIO::ListJob listjob.h * * A ListJob is allows you to get the get the content of a directory. * Don't create the job directly, but use KIO::listRecursive() or * KIO::listDir() instead. * @see KIO::listRecursive() * @see KIO::listDir() */ class KIOCORE_EXPORT ListJob : public SimpleJob { Q_OBJECT public: ~ListJob() override; /** * Returns the ListJob's redirection URL. This will be invalid if there * was no redirection. * @return the redirection url */ const QUrl &redirectionUrl() const; /** * Do not apply any KIOSK restrictions to this job. */ void setUnrestricted(bool unrestricted); Q_SIGNALS: /** * This signal emits the entry found by the job while listing. * The progress signals aren't specific to ListJob. It simply * uses SimpleJob's processedSize (number of entries listed) and * totalSize (total number of entries, if known), * as well as percent. * @param job the job that emitted this signal * @param list the list of UDSEntries */ void entries(KIO::Job *job, const KIO::UDSEntryList &list); // TODO KDE5: use KIO::ListJob* argument to avoid casting /** * This signal is emitted when a sub-directory could not be listed. * The job keeps going, thus doesn't result in an overall error. * @param job the job that emitted the signal * @param subJob the job listing a sub-directory, which failed. Use * url(), error() and errorText() on that job to find * out more. */ void subError(KIO::ListJob *job, KIO::ListJob *subJob); /** * Signals a redirection. * Use to update the URL shown to the user. * The redirection itself is handled internally. * @param job the job that is redirected * @param url the new url */ void redirection(KIO::Job *job, const QUrl &url); /** * Signals a permanent redirection. * The redirection itself is handled internally. * @param job the job that emitted this signal * @param fromUrl the original URL * @param toUrl the new URL */ void permanentRedirection(KIO::Job *job, const QUrl &fromUrl, const QUrl &toUrl); protected Q_SLOTS: void slotFinished() override; void slotMetaData(const KIO::MetaData &_metaData) override; void slotResult(KJob *job) override; protected: ListJob(ListJobPrivate &dd); Q_DECLARE_PRIVATE(ListJob) + friend class ListJobPrivate; }; /** * List the contents of @p url, which is assumed to be a directory. * * "." and ".." are returned, filter them out if you don't want them. * * * @param url the url of the directory * @param flags Can be HideProgressInfo here * @param includeHidden true for all files, false to cull out UNIX hidden * files/dirs (whose names start with dot) * @return the job handling the operation. */ KIOCORE_EXPORT ListJob *listDir(const QUrl &url, JobFlags flags = DefaultFlags, bool includeHidden = true); /** * The same as the previous method, but recurses subdirectories. * Directory links are not followed. * * "." and ".." are returned but only for the toplevel directory. * Filter them out if you don't want them. * * @param url the url of the directory * @param flags Can be HideProgressInfo here * @param includeHidden true for all files, false to cull out UNIX hidden * files/dirs (whose names start with dot) * @return the job handling the operation. */ KIOCORE_EXPORT ListJob *listRecursive(const QUrl &url, JobFlags flags = DefaultFlags, bool includeHidden = true); } #endif diff --git a/src/core/scheduler.cpp b/src/core/scheduler.cpp index 2265ca84..93e88bbf 100644 --- a/src/core/scheduler.cpp +++ b/src/core/scheduler.cpp @@ -1,1318 +1,1318 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow Waldo Bastian Copyright (C) 2009, 2010 Andreas Hartmetz 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 "scheduler.h" #include "scheduler_p.h" #include "sessiondata_p.h" #include "slaveconfig.h" #include "slave.h" #include "connection_p.h" #include "job_p.h" #include #include //#include #include #include #include #include #include #include // Slaves may be idle for a certain time (3 minutes) before they are killed. static const int s_idleSlaveLifetime = 3 * 60; using namespace KIO; static inline Slave *jobSlave(SimpleJob *job) { return SimpleJobPrivate::get(job)->m_slave; } static inline int jobCommand(SimpleJob *job) { return SimpleJobPrivate::get(job)->m_command; } static inline void startJob(SimpleJob *job, Slave *slave) { SimpleJobPrivate::get(job)->start(slave); } // here be uglies // forward declaration to break cross-dependency of SlaveKeeper and SchedulerPrivate static void setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = nullptr); // same reason as above static Scheduler *scheduler(); static Slave *heldSlaveForJob(SimpleJob *job); int SerialPicker::changedPrioritySerial(int oldSerial, int newPriority) const { Q_ASSERT(newPriority >= -10 && newPriority <= 10); newPriority = qBound(-10, newPriority, 10); int unbiasedSerial = oldSerial % m_jobsPerPriority; return unbiasedSerial + newPriority * m_jobsPerPriority; } SlaveKeeper::SlaveKeeper() { m_grimTimer.setSingleShot(true); - connect(&m_grimTimer, SIGNAL(timeout()), SLOT(grimReaper())); + connect(&m_grimTimer, &QTimer::timeout, this, &SlaveKeeper::grimReaper); } SlaveKeeper::~SlaveKeeper() { grimReaper(); } void SlaveKeeper::returnSlave(Slave *slave) { Q_ASSERT(slave); slave->setIdle(); m_idleSlaves.insert(slave->host(), slave); scheduleGrimReaper(); } Slave *SlaveKeeper::takeSlaveForJob(SimpleJob *job) { Slave *slave = heldSlaveForJob(job); if (slave) { return slave; } QUrl url = SimpleJobPrivate::get(job)->m_url; // TODO take port, username and password into account QMultiHash::Iterator it = m_idleSlaves.find(url.host()); if (it == m_idleSlaves.end()) { it = m_idleSlaves.begin(); } if (it == m_idleSlaves.end()) { return nullptr; } slave = it.value(); m_idleSlaves.erase(it); return slave; } bool SlaveKeeper::removeSlave(Slave *slave) { // ### performance not so great QMultiHash::Iterator it = m_idleSlaves.begin(); for (; it != m_idleSlaves.end(); ++it) { if (it.value() == slave) { m_idleSlaves.erase(it); return true; } } return false; } void SlaveKeeper::clear() { m_idleSlaves.clear(); } QList SlaveKeeper::allSlaves() const { return m_idleSlaves.values(); } void SlaveKeeper::scheduleGrimReaper() { if (!m_grimTimer.isActive()) { m_grimTimer.start((s_idleSlaveLifetime / 2) * 1000); } } //private slot void SlaveKeeper::grimReaper() { QMultiHash::Iterator it = m_idleSlaves.begin(); while (it != m_idleSlaves.end()) { Slave *slave = it.value(); if (slave->idleTime() >= s_idleSlaveLifetime) { it = m_idleSlaves.erase(it); if (slave->job()) { //qDebug() << "Idle slave" << slave << "still has job" << slave->job(); } slave->kill(); // avoid invoking slotSlaveDied() because its cleanup services are not needed slave->deref(); } else { ++it; } } if (!m_idleSlaves.isEmpty()) { scheduleGrimReaper(); } } int HostQueue::lowestSerial() const { QMap::ConstIterator first = m_queuedJobs.constBegin(); if (first != m_queuedJobs.constEnd()) { return first.key(); } return SerialPicker::maxSerial; } void HostQueue::queueJob(SimpleJob *job) { const int serial = SimpleJobPrivate::get(job)->m_schedSerial; Q_ASSERT(serial != 0); Q_ASSERT(!m_queuedJobs.contains(serial)); Q_ASSERT(!m_runningJobs.contains(job)); m_queuedJobs.insert(serial, job); } SimpleJob *HostQueue::takeFirstInQueue() { Q_ASSERT(!m_queuedJobs.isEmpty()); QMap::iterator first = m_queuedJobs.begin(); SimpleJob *job = first.value(); m_queuedJobs.erase(first); m_runningJobs.insert(job); return job; } bool HostQueue::removeJob(SimpleJob *job) { const int serial = SimpleJobPrivate::get(job)->m_schedSerial; if (m_runningJobs.remove(job)) { Q_ASSERT(!m_queuedJobs.contains(serial)); return true; } if (m_queuedJobs.remove(serial)) { return true; } return false; } QList HostQueue::allSlaves() const { QList ret; Q_FOREACH (SimpleJob *job, m_runningJobs) { Slave *slave = jobSlave(job); Q_ASSERT(slave); ret.append(slave); } return ret; } ConnectedSlaveQueue::ConnectedSlaveQueue() { m_startJobsTimer.setSingleShot(true); - connect(&m_startJobsTimer, SIGNAL(timeout()), SLOT(startRunnableJobs())); + connect(&m_startJobsTimer, &QTimer::timeout, this, &ConnectedSlaveQueue::startRunnableJobs); } bool ConnectedSlaveQueue::queueJob(SimpleJob *job, Slave *slave) { QHash::Iterator it = m_connectedSlaves.find(slave); if (it == m_connectedSlaves.end()) { return false; } SimpleJobPrivate::get(job)->m_slave = slave; PerSlaveQueue &jobs = it.value(); jobs.waitingList.append(job); if (!jobs.runningJob) { // idle slave now has a job to run m_runnableSlaves.insert(slave); m_startJobsTimer.start(); } return true; } bool ConnectedSlaveQueue::removeJob(SimpleJob *job) { Slave *slave = jobSlave(job); Q_ASSERT(slave); QHash::Iterator it = m_connectedSlaves.find(slave); if (it == m_connectedSlaves.end()) { return false; } PerSlaveQueue &jobs = it.value(); if (jobs.runningJob || jobs.waitingList.isEmpty()) { // a slave that was busy running a job was not runnable. // a slave that has no waiting job(s) was not runnable either. Q_ASSERT(!m_runnableSlaves.contains(slave)); } const bool removedRunning = jobs.runningJob == job; const bool removedWaiting = jobs.waitingList.removeAll(job) != 0; if (removedRunning) { jobs.runningJob = nullptr; Q_ASSERT(!removedWaiting); } const bool removedTheJob = removedRunning || removedWaiting; if (!slave->isAlive()) { removeSlave(slave); return removedTheJob; } if (removedRunning && jobs.waitingList.count()) { m_runnableSlaves.insert(slave); m_startJobsTimer.start(); } if (removedWaiting && jobs.waitingList.isEmpty()) { m_runnableSlaves.remove(slave); } return removedTheJob; } void ConnectedSlaveQueue::addSlave(Slave *slave) { Q_ASSERT(slave); if (!m_connectedSlaves.contains(slave)) { m_connectedSlaves.insert(slave, PerSlaveQueue()); } } bool ConnectedSlaveQueue::removeSlave(Slave *slave) { QHash::Iterator it = m_connectedSlaves.find(slave); if (it == m_connectedSlaves.end()) { return false; } PerSlaveQueue &jobs = it.value(); Q_FOREACH (SimpleJob *job, jobs.waitingList) { // ### for compatibility with the old scheduler we don't touch the running job, if any. // make sure that the job doesn't call back into Scheduler::cancelJob(); this would // a) crash and b) be unnecessary because we clean up just fine. SimpleJobPrivate::get(job)->m_schedSerial = 0; job->kill(); } m_connectedSlaves.erase(it); m_runnableSlaves.remove(slave); slave->kill(); return true; } // KDE5: only one caller, for doubtful reasons. remove this if possible. bool ConnectedSlaveQueue::isIdle(Slave *slave) { QHash::Iterator it = m_connectedSlaves.find(slave); if (it == m_connectedSlaves.end()) { return false; } return it.value().runningJob == nullptr; } //private slot void ConnectedSlaveQueue::startRunnableJobs() { QSet::Iterator it = m_runnableSlaves.begin(); while (it != m_runnableSlaves.end()) { Slave *slave = *it; if (!slave->isConnected()) { // this polling is somewhat inefficient... m_startJobsTimer.start(); ++it; continue; } it = m_runnableSlaves.erase(it); PerSlaveQueue &jobs = m_connectedSlaves[slave]; SimpleJob *job = jobs.waitingList.takeFirst(); Q_ASSERT(!jobs.runningJob); jobs.runningJob = job; const QUrl url = job->url(); // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that. const int port = url.port() == -1 ? 0 : url.port(); if (slave->host() == QLatin1String("")) { MetaData configData = SlaveConfig::self()->configData(url.scheme(), url.host()); slave->setConfig(configData); slave->setProtocol(url.scheme()); slave->setHost(url.host(), port, url.userName(), url.password()); } Q_ASSERT(slave->protocol() == url.scheme()); Q_ASSERT(slave->host() == url.host()); Q_ASSERT(slave->port() == port); startJob(job, slave); } } static void ensureNoDuplicates(QMap *queuesBySerial) { Q_UNUSED(queuesBySerial); #ifdef SCHEDULER_DEBUG // a host queue may *never* be in queuesBySerial twice. QSet seen; Q_FOREACH (HostQueue *hq, *queuesBySerial) { Q_ASSERT(!seen.contains(hq)); seen.insert(hq); } #endif } static void verifyRunningJobsCount(QHash *queues, int runningJobsCount) { Q_UNUSED(queues); Q_UNUSED(runningJobsCount); #ifdef SCHEDULER_DEBUG int realRunningJobsCount = 0; Q_FOREACH (const HostQueue &hq, *queues) { realRunningJobsCount += hq.runningJobsCount(); } Q_ASSERT(realRunningJobsCount == runningJobsCount); // ...and of course we may never run the same job twice! QSet seenJobs; Q_FOREACH (const HostQueue &hq, *queues) { Q_FOREACH (SimpleJob *job, hq.runningJobs()) { Q_ASSERT(!seenJobs.contains(job)); seenJobs.insert(job); } } #endif } ProtoQueue::ProtoQueue(int maxSlaves, int maxSlavesPerHost) : m_maxConnectionsPerHost(maxSlavesPerHost ? maxSlavesPerHost : maxSlaves), m_maxConnectionsTotal(qMax(maxSlaves, maxSlavesPerHost)), m_runningJobsCount(0) { /*qDebug() << "m_maxConnectionsTotal:" << m_maxConnectionsTotal << "m_maxConnectionsPerHost:" << m_maxConnectionsPerHost;*/ Q_ASSERT(m_maxConnectionsPerHost >= 1); Q_ASSERT(maxSlaves >= maxSlavesPerHost); m_startJobTimer.setSingleShot(true); - connect(&m_startJobTimer, SIGNAL(timeout()), SLOT(startAJob())); + connect(&m_startJobTimer, &QTimer::timeout, this, &ProtoQueue::startAJob); } ProtoQueue::~ProtoQueue() { // Gather list of all slaves first const QList slaves = allSlaves(); // Clear the idle slaves in the keeper to avoid dangling pointers m_slaveKeeper.clear(); for (Slave *slave : slaves) { // kill the slave process, then remove the interface in our process slave->kill(); slave->deref(); } } void ProtoQueue::queueJob(SimpleJob *job) { QString hostname = SimpleJobPrivate::get(job)->m_url.host(); HostQueue &hq = m_queuesByHostname[hostname]; const int prevLowestSerial = hq.lowestSerial(); Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost); // nevert insert a job twice Q_ASSERT(SimpleJobPrivate::get(job)->m_schedSerial == 0); SimpleJobPrivate::get(job)->m_schedSerial = m_serialPicker.next(); const bool wasQueueEmpty = hq.isQueueEmpty(); hq.queueJob(job); // note that HostQueue::queueJob() into an empty queue changes its lowestSerial() too... // the queue's lowest serial job may have changed, so update the ordered list of queues. // however, we ignore all jobs that would cause more connections to a host than allowed. if (prevLowestSerial != hq.lowestSerial()) { if (hq.runningJobsCount() < m_maxConnectionsPerHost) { // if the connection limit didn't keep the HQ unscheduled it must have been lack of jobs if (m_queuesBySerial.remove(prevLowestSerial) == 0) { Q_UNUSED(wasQueueEmpty); Q_ASSERT(wasQueueEmpty); } m_queuesBySerial.insert(hq.lowestSerial(), &hq); } else { #ifdef SCHEDULER_DEBUG // ### this assertion may fail if the limits were modified at runtime! // if the per-host connection limit is already reached the host queue's lowest serial // should not be queued. Q_ASSERT(!m_queuesBySerial.contains(prevLowestSerial)); #endif } } // just in case; startAJob() will refuse to start a job if it shouldn't. m_startJobTimer.start(); ensureNoDuplicates(&m_queuesBySerial); } void ProtoQueue::changeJobPriority(SimpleJob *job, int newPrio) { SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job); QHash::Iterator it = m_queuesByHostname.find(jobPriv->m_url.host()); if (it == m_queuesByHostname.end()) { return; } HostQueue &hq = it.value(); const int prevLowestSerial = hq.lowestSerial(); if (hq.isJobRunning(job) || !hq.removeJob(job)) { return; } jobPriv->m_schedSerial = m_serialPicker.changedPrioritySerial(jobPriv->m_schedSerial, newPrio); hq.queueJob(job); const bool needReinsert = hq.lowestSerial() != prevLowestSerial; // the host queue might be absent from m_queuesBySerial because the connections per host limit // for that host has been reached. if (needReinsert && m_queuesBySerial.remove(prevLowestSerial)) { m_queuesBySerial.insert(hq.lowestSerial(), &hq); } ensureNoDuplicates(&m_queuesBySerial); } void ProtoQueue::removeJob(SimpleJob *job) { SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job); HostQueue &hq = m_queuesByHostname[jobPriv->m_url.host()]; const int prevLowestSerial = hq.lowestSerial(); const int prevRunningJobs = hq.runningJobsCount(); Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost); if (hq.removeJob(job)) { if (hq.lowestSerial() != prevLowestSerial) { // we have dequeued the not yet running job with the lowest serial Q_ASSERT(!jobPriv->m_slave); Q_ASSERT(prevRunningJobs == hq.runningJobsCount()); if (m_queuesBySerial.remove(prevLowestSerial) == 0) { // make sure that the queue was not scheduled for a good reason Q_ASSERT(hq.runningJobsCount() == m_maxConnectionsPerHost); } } else { if (prevRunningJobs != hq.runningJobsCount()) { // we have dequeued a previously running job Q_ASSERT(prevRunningJobs - 1 == hq.runningJobsCount()); m_runningJobsCount--; Q_ASSERT(m_runningJobsCount >= 0); } } if (!hq.isQueueEmpty() && hq.runningJobsCount() < m_maxConnectionsPerHost) { // this may be a no-op, but it's faster than first checking if it's already in. m_queuesBySerial.insert(hq.lowestSerial(), &hq); } if (hq.isEmpty()) { // no queued jobs, no running jobs. this destroys hq from above. m_queuesByHostname.remove(jobPriv->m_url.host()); } if (jobPriv->m_slave && jobPriv->m_slave->isAlive()) { m_slaveKeeper.returnSlave(jobPriv->m_slave); } // just in case; startAJob() will refuse to start a job if it shouldn't. m_startJobTimer.start(); } else { // should be a connected slave // if the assertion fails the job has probably changed the host part of its URL while // running, so we can't find it by hostname. don't do this. const bool removed = m_connectedSlaveQueue.removeJob(job); Q_UNUSED(removed); Q_ASSERT(removed); } ensureNoDuplicates(&m_queuesBySerial); } Slave *ProtoQueue::createSlave(const QString &protocol, SimpleJob *job, const QUrl &url) { int error; QString errortext; Slave *slave = Slave::createSlave(protocol, url, error, errortext); if (slave) { scheduler()->connect(slave, SIGNAL(slaveDied(KIO::Slave*)), SLOT(slotSlaveDied(KIO::Slave*))); scheduler()->connect(slave, SIGNAL(slaveStatus(qint64,QByteArray,QString,bool)), SLOT(slotSlaveStatus(qint64,QByteArray,QString,bool))); } else { qCWarning(KIO_CORE) << "couldn't create slave:" << errortext; if (job) { job->slotError(error, errortext); } } return slave; } bool ProtoQueue::removeSlave(KIO::Slave *slave) { const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave); const bool removedUnconnected = m_slaveKeeper.removeSlave(slave); Q_ASSERT(!(removedConnected && removedUnconnected)); return removedConnected || removedUnconnected; } QList ProtoQueue::allSlaves() const { QList ret(m_slaveKeeper.allSlaves()); Q_FOREACH (const HostQueue &hq, m_queuesByHostname) { ret.append(hq.allSlaves()); } ret.append(m_connectedSlaveQueue.allSlaves()); return ret; } //private slot void ProtoQueue::startAJob() { ensureNoDuplicates(&m_queuesBySerial); verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount); #ifdef SCHEDULER_DEBUG //qDebug() << "m_runningJobsCount:" << m_runningJobsCount; Q_FOREACH (const HostQueue &hq, m_queuesByHostname) { Q_FOREACH (SimpleJob *job, hq.runningJobs()) { //qDebug() << SimpleJobPrivate::get(job)->m_url; } } #endif if (m_runningJobsCount >= m_maxConnectionsTotal) { #ifdef SCHEDULER_DEBUG //qDebug() << "not starting any jobs because maxConnectionsTotal has been reached."; #endif return; } QMap::iterator first = m_queuesBySerial.begin(); if (first != m_queuesBySerial.end()) { // pick a job and maintain the queue invariant: lower serials first HostQueue *hq = first.value(); const int prevLowestSerial = first.key(); Q_UNUSED(prevLowestSerial); Q_ASSERT(hq->lowestSerial() == prevLowestSerial); // the following assertions should hold due to queueJob(), takeFirstInQueue() and // removeJob() being correct Q_ASSERT(hq->runningJobsCount() < m_maxConnectionsPerHost); SimpleJob *startingJob = hq->takeFirstInQueue(); Q_ASSERT(hq->runningJobsCount() <= m_maxConnectionsPerHost); Q_ASSERT(hq->lowestSerial() != prevLowestSerial); m_queuesBySerial.erase(first); // we've increased hq's runningJobsCount() by calling nexStartingJob() // so we need to check again. if (!hq->isQueueEmpty() && hq->runningJobsCount() < m_maxConnectionsPerHost) { m_queuesBySerial.insert(hq->lowestSerial(), hq); } // always increase m_runningJobsCount because it's correct if there is a slave and if there // is no slave, removeJob() will balance the number again. removeJob() would decrease the // number too much otherwise. // Note that createSlave() can call slotError() on a job which in turn calls removeJob(), // so increase the count here already. m_runningJobsCount++; bool isNewSlave = false; Slave *slave = m_slaveKeeper.takeSlaveForJob(startingJob); SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(startingJob); if (!slave) { isNewSlave = true; slave = createSlave(jobPriv->m_protocol, startingJob, jobPriv->m_url); } if (slave) { jobPriv->m_slave = slave; setupSlave(slave, jobPriv->m_url, jobPriv->m_protocol, jobPriv->m_proxyList, isNewSlave); startJob(startingJob, slave); } else { // dispose of our records about the job and mark the job as unknown // (to prevent crashes later) // note that the job's slotError() can have called removeJob() first, so check that // it's not a ghost job with null serial already. if (jobPriv->m_schedSerial) { removeJob(startingJob); jobPriv->m_schedSerial = 0; } } } else { #ifdef SCHEDULER_DEBUG //qDebug() << "not starting any jobs because there is no queued job."; #endif } if (!m_queuesBySerial.isEmpty()) { m_startJobTimer.start(); } } class KIO::SchedulerPrivate { public: SchedulerPrivate() : q(new Scheduler()), m_slaveOnHold(nullptr), m_checkOnHold(true), // !! Always check with KLauncher for the first request m_ignoreConfigReparse(false) { } ~SchedulerPrivate() { delete q; q = nullptr; Q_FOREACH (ProtoQueue *p, m_protocols) { Q_FOREACH (Slave *slave, p->allSlaves()) { slave->kill(); } } qDeleteAll(m_protocols); } Scheduler *q; Slave *m_slaveOnHold; QUrl m_urlOnHold; bool m_checkOnHold; bool m_ignoreConfigReparse; SessionData sessionData; void doJob(SimpleJob *job); #ifndef KIOCORE_NO_DEPRECATED void scheduleJob(SimpleJob *job); #endif void setJobPriority(SimpleJob *job, int priority); void cancelJob(SimpleJob *job); void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave); void putSlaveOnHold(KIO::SimpleJob *job, const QUrl &url); void removeSlaveOnHold(); Slave *getConnectedSlave(const QUrl &url, const KIO::MetaData &metaData); bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job); bool disconnectSlave(KIO::Slave *slave); void checkSlaveOnHold(bool b); void publishSlaveOnHold(); Slave *heldSlaveForJob(KIO::SimpleJob *job); bool isSlaveOnHoldFor(const QUrl &url); void updateInternalMetaData(SimpleJob *job); MetaData metaDataFor(const QString &protocol, const QStringList &proxyList, const QUrl &url); void setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = nullptr); void slotSlaveDied(KIO::Slave *slave); void slotSlaveStatus(qint64 pid, const QByteArray &protocol, const QString &host, bool connected); void slotReparseSlaveConfiguration(const QString &, const QDBusMessage &); void slotSlaveOnHoldListChanged(); void slotSlaveConnected(); void slotSlaveError(int error, const QString &errorMsg); ProtoQueue *protoQ(const QString &protocol, const QString &host) { ProtoQueue *pq = m_protocols.value(protocol, nullptr); if (!pq) { //qDebug() << "creating ProtoQueue instance for" << protocol; const int maxSlaves = KProtocolInfo::maxSlaves(protocol); int maxSlavesPerHost = -1; if (!host.isEmpty()) { bool ok = false; const int value = SlaveConfig::self()->configData(protocol, host, QStringLiteral("MaxConnections")).toInt(&ok); if (ok) { maxSlavesPerHost = value; } } if (maxSlavesPerHost == -1) { maxSlavesPerHost = KProtocolInfo::maxSlavesPerHost(protocol); } // Never allow maxSlavesPerHost to exceed maxSlaves. pq = new ProtoQueue(maxSlaves, qMin(maxSlaves, maxSlavesPerHost)); m_protocols.insert(protocol, pq); } return pq; } private: QHash m_protocols; }; static QThreadStorage s_storage; static SchedulerPrivate *schedulerPrivate() { if (!s_storage.hasLocalData()) { s_storage.setLocalData(new SchedulerPrivate); } return s_storage.localData(); } Scheduler *Scheduler::self() { return schedulerPrivate()->q; } SchedulerPrivate *Scheduler::d_func() { return schedulerPrivate(); } //static Scheduler *scheduler() { return schedulerPrivate()->q; } //static Slave *heldSlaveForJob(SimpleJob *job) { return schedulerPrivate()->heldSlaveForJob(job); } Scheduler::Scheduler() { setObjectName(QStringLiteral("scheduler")); const QString dbusPath = QStringLiteral("/KIO/Scheduler"); const QString dbusInterface = QStringLiteral("org.kde.KIO.Scheduler"); QDBusConnection dbus = QDBusConnection::sessionBus(); // Not needed, right? We just want to emit two signals. //dbus.registerObject("/KIO/Scheduler", this, QDBusConnection::ExportScriptableSlots | // QDBusConnection::ExportScriptableSignals); dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("reparseSlaveConfiguration"), this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage))); dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("slaveOnHoldListChanged"), this, SLOT(slotSlaveOnHoldListChanged())); } Scheduler::~Scheduler() { } void Scheduler::doJob(SimpleJob *job) { schedulerPrivate()->doJob(job); } #ifndef KIOCORE_NO_DEPRECATED void Scheduler::scheduleJob(SimpleJob *job) { schedulerPrivate()->scheduleJob(job); } #endif void Scheduler::setJobPriority(SimpleJob *job, int priority) { schedulerPrivate()->setJobPriority(job, priority); } void Scheduler::cancelJob(SimpleJob *job) { schedulerPrivate()->cancelJob(job); } void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave) { schedulerPrivate()->jobFinished(job, slave); } void Scheduler::putSlaveOnHold(KIO::SimpleJob *job, const QUrl &url) { schedulerPrivate()->putSlaveOnHold(job, url); } void Scheduler::removeSlaveOnHold() { schedulerPrivate()->removeSlaveOnHold(); } void Scheduler::publishSlaveOnHold() { schedulerPrivate()->publishSlaveOnHold(); } bool Scheduler::isSlaveOnHoldFor(const QUrl &url) { return schedulerPrivate()->isSlaveOnHoldFor(url); } void Scheduler::updateInternalMetaData(SimpleJob *job) { schedulerPrivate()->updateInternalMetaData(job); } KIO::Slave *Scheduler::getConnectedSlave(const QUrl &url, const KIO::MetaData &config) { return schedulerPrivate()->getConnectedSlave(url, config); } bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job) { return schedulerPrivate()->assignJobToSlave(slave, job); } bool Scheduler::disconnectSlave(KIO::Slave *slave) { return schedulerPrivate()->disconnectSlave(slave); } bool Scheduler::connect(const char *signal, const QObject *receiver, const char *member) { return QObject::connect(self(), signal, receiver, member); } bool Scheduler::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member) { return QObject::connect(sender, signal, receiver, member); } bool Scheduler::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member) { return QObject::disconnect(sender, signal, receiver, member); } bool Scheduler::connect(const QObject *sender, const char *signal, const char *member) { return QObject::connect(sender, signal, member); } void Scheduler::checkSlaveOnHold(bool b) { schedulerPrivate()->checkSlaveOnHold(b); } void Scheduler::emitReparseSlaveConfiguration() { // Do it immediately in this process, otherwise we might send a request before reparsing // (e.g. when changing useragent in the plugin) schedulerPrivate()->slotReparseSlaveConfiguration(QString(), QDBusMessage()); schedulerPrivate()->m_ignoreConfigReparse = true; emit self()->reparseSlaveConfiguration(QString()); } void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage &) { if (m_ignoreConfigReparse) { //qDebug() << "Ignoring signal sent by myself"; m_ignoreConfigReparse = false; return; } //qDebug() << "proto=" << proto; KProtocolManager::reparseConfiguration(); SlaveConfig::self()->reset(); sessionData.reset(); NetRC::self()->reload(); QHash::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() : m_protocols.constFind(proto); // not found? if (it == m_protocols.constEnd()) { return; } QHash::ConstIterator endIt = proto.isEmpty() ? m_protocols.constEnd() : it + 1; for (; it != endIt; ++it) { Q_FOREACH (Slave *slave, (*it)->allSlaves()) { slave->send(CMD_REPARSECONFIGURATION); slave->resetHost(); } } } void SchedulerPrivate::slotSlaveOnHoldListChanged() { m_checkOnHold = true; } static bool mayReturnContent(int cmd, const QString &protocol) { if (cmd == CMD_GET) { return true; } if (cmd == CMD_MULTI_GET) { return true; } if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive)) { return true; } return false; } void SchedulerPrivate::doJob(SimpleJob *job) { //qDebug() << job; KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job); jobPriv->m_proxyList.clear(); jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxyList); if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) { jobPriv->m_checkOnHold = m_checkOnHold; m_checkOnHold = false; } ProtoQueue *proto = protoQ(jobPriv->m_protocol, job->url().host()); proto->queueJob(job); } #ifndef KIOCORE_NO_DEPRECATED void SchedulerPrivate::scheduleJob(SimpleJob *job) { //qDebug() << job; setJobPriority(job, 1); } #endif void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority) { //qDebug() << job << priority; const QString protocol = SimpleJobPrivate::get(job)->m_protocol; if (!protocol.isEmpty()) { ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol, job->url().host()); proto->changeJobPriority(job, priority); } } void SchedulerPrivate::cancelJob(SimpleJob *job) { // this method is called all over the place in job.cpp, so just do this check here to avoid // much boilerplate in job code. if (SimpleJobPrivate::get(job)->m_schedSerial == 0) { //qDebug() << "Doing nothing because I don't know job" << job; return; } Slave *slave = jobSlave(job); //qDebug() << job << slave; if (slave) { //qDebug() << "Scheduler: killing slave " << slave->slave_pid(); slave->kill(); } jobFinished(job, slave); } void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave) { //qDebug() << job << slave; KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job); // make sure that we knew about the job! Q_ASSERT(jobPriv->m_schedSerial); ProtoQueue *pq = m_protocols.value(jobPriv->m_protocol); if (pq) { pq->removeJob(job); } if (slave) { // If we have internal meta-data, tell existing ioslaves to reload // their configuration. if (jobPriv->m_internalMetaData.count()) { //qDebug() << "Updating ioslaves with new internal metadata information"; ProtoQueue *queue = m_protocols.value(slave->protocol()); if (queue) { QListIterator it(queue->allSlaves()); while (it.hasNext()) { Slave *runningSlave = it.next(); if (slave->host() == runningSlave->host()) { slave->setConfig(metaDataFor(slave->protocol(), jobPriv->m_proxyList, job->url())); /*qDebug() << "Updated configuration of" << slave->protocol() << "ioslave, pid=" << slave->slave_pid();*/ } } } } slave->setJob(nullptr); slave->disconnect(job); } jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again jobPriv->m_slave = nullptr; // Clear the values in the internal metadata container since they have // already been taken care of above... jobPriv->m_internalMetaData.clear(); } // static void setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config) { schedulerPrivate()->setupSlave(slave, url, protocol, proxyList, newSlave, config); } MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QStringList &proxyList, const QUrl &url) { const QString host = url.host(); MetaData configData = SlaveConfig::self()->configData(protocol, host); sessionData.configDataFor(configData, protocol, host); if (proxyList.isEmpty()) { configData.remove(QStringLiteral("UseProxy")); configData.remove(QStringLiteral("ProxyUrls")); } else { configData[QStringLiteral("UseProxy")] = proxyList.first(); configData[QStringLiteral("ProxyUrls")] = proxyList.join(QStringLiteral(",")); } if (configData.contains(QStringLiteral("EnableAutoLogin")) && configData.value(QStringLiteral("EnableAutoLogin")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) { NetRC::AutoLogin l; l.login = url.userName(); bool usern = (protocol == QLatin1String("ftp")); if (NetRC::self()->lookup(url, l, usern)) { configData[QStringLiteral("autoLoginUser")] = l.login; configData[QStringLiteral("autoLoginPass")] = l.password; if (usern) { QString macdef; QMap::ConstIterator it = l.macdef.constBegin(); for (; it != l.macdef.constEnd(); ++it) { macdef += it.key() + QLatin1Char('\\') + it.value().join(QStringLiteral("\\")) + QLatin1Char('\n'); } configData[QStringLiteral("autoLoginMacro")] = macdef; } } } return configData; } void SchedulerPrivate::setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config) { int port = url.port(); if (port == -1) { // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that. port = 0; } const QString host = url.host(); const QString user = url.userName(); const QString passwd = url.password(); if (newSlave || slave->host() != host || slave->port() != port || slave->user() != user || slave->passwd() != passwd) { MetaData configData = metaDataFor(protocol, proxyList, url); if (config) { configData += *config; } slave->setConfig(configData); slave->setProtocol(url.scheme()); slave->setHost(host, port, user, passwd); } } void SchedulerPrivate::slotSlaveStatus(qint64, const QByteArray &, const QString &, bool) { } void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave) { //qDebug() << slave; Q_ASSERT(slave); Q_ASSERT(!slave->isAlive()); ProtoQueue *pq = m_protocols.value(slave->protocol()); if (pq) { if (slave->job()) { pq->removeJob(slave->job()); } // in case this was a connected slave... pq->removeSlave(slave); } if (slave == m_slaveOnHold) { m_slaveOnHold = nullptr; m_urlOnHold.clear(); } // can't use slave->deref() here because we need to use deleteLater slave->aboutToDelete(); slave->deleteLater(); } void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const QUrl &url) { Slave *slave = jobSlave(job); //qDebug() << job << url << slave; slave->disconnect(job); // prevent the fake death of the slave from trying to kill the job again; // cf. Slave::hold(const QUrl &url) called in SchedulerPrivate::publishSlaveOnHold(). slave->setJob(nullptr); SimpleJobPrivate::get(job)->m_slave = nullptr; if (m_slaveOnHold) { m_slaveOnHold->kill(); } m_slaveOnHold = slave; m_urlOnHold = url; m_slaveOnHold->suspend(); } void SchedulerPrivate::publishSlaveOnHold() { //qDebug() << m_slaveOnHold; if (!m_slaveOnHold) { return; } m_slaveOnHold->hold(m_urlOnHold); emit q->slaveOnHoldListChanged(); } bool SchedulerPrivate::isSlaveOnHoldFor(const QUrl &url) { if (url.isValid() && m_urlOnHold.isValid() && url == m_urlOnHold) { return true; } return Slave::checkForHeldSlave(url); } Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job) { Slave *slave = nullptr; KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job); if (jobPriv->m_checkOnHold) { slave = Slave::holdSlave(jobPriv->m_protocol, job->url()); } if (!slave && m_slaveOnHold) { // Make sure that the job wants to do a GET or a POST, and with no offset const int cmd = jobPriv->m_command; bool canJobReuse = (cmd == CMD_GET || cmd == CMD_MULTI_GET); if (KIO::TransferJob *tJob = qobject_cast(job)) { canJobReuse = (canJobReuse || cmd == CMD_SPECIAL); if (canJobReuse) { KIO::MetaData outgoing = tJob->outgoingMetaData(); const QString resume = outgoing.value(QStringLiteral("resume")); const QString rangeStart = outgoing.value(QStringLiteral("range-start")); //qDebug() << "Resume metadata is" << resume; canJobReuse = (resume.isEmpty() || resume == QLatin1String("0")) && (rangeStart.isEmpty() || rangeStart == QLatin1String("0")); } } if (job->url() == m_urlOnHold) { if (canJobReuse) { //qDebug() << "HOLD: Reusing held slave (" << m_slaveOnHold << ")"; slave = m_slaveOnHold; } else { //qDebug() << "HOLD: Discarding held slave (" << m_slaveOnHold << ")"; m_slaveOnHold->kill(); } m_slaveOnHold = nullptr; m_urlOnHold.clear(); } } else if (slave) { //qDebug() << "HOLD: Reusing klauncher held slave (" << slave << ")"; } return slave; } void SchedulerPrivate::removeSlaveOnHold() { //qDebug() << m_slaveOnHold; if (m_slaveOnHold) { m_slaveOnHold->kill(); } m_slaveOnHold = nullptr; m_urlOnHold.clear(); } Slave *SchedulerPrivate::getConnectedSlave(const QUrl &url, const KIO::MetaData &config) { QStringList proxyList; const QString protocol = KProtocolManager::slaveProtocol(url, proxyList); ProtoQueue *pq = protoQ(protocol, url.host()); Slave *slave = pq->createSlave(protocol, /* job */nullptr, url); if (slave) { setupSlave(slave, url, protocol, proxyList, true, &config); pq->m_connectedSlaveQueue.addSlave(slave); slave->send(CMD_CONNECT); q->connect(slave, SIGNAL(connected()), SLOT(slotSlaveConnected())); q->connect(slave, SIGNAL(error(int,QString)), SLOT(slotSlaveError(int,QString))); } //qDebug() << url << slave; return slave; } void SchedulerPrivate::slotSlaveConnected() { //qDebug(); Slave *slave = static_cast(q->sender()); slave->setConnected(true); q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected())); emit q->slaveConnected(slave); } void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg) { Slave *slave = static_cast(q->sender()); //qDebug() << slave << errorNr << errorMsg; ProtoQueue *pq = protoQ(slave->protocol(), slave->host()); if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) { // Only forward to application if slave is idle or still connecting. // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError? emit q->slaveError(slave, errorNr, errorMsg); } } bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job) { //qDebug() << slave << job; // KDE5: queueing of jobs can probably be removed, it provides very little benefit ProtoQueue *pq = m_protocols.value(slave->protocol()); if (pq) { pq->removeJob(job); return pq->m_connectedSlaveQueue.queueJob(job, slave); } return false; } bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave) { //qDebug() << slave; ProtoQueue *pq = m_protocols.value(slave->protocol()); return (pq ? pq->m_connectedSlaveQueue.removeSlave(slave) : false); } void SchedulerPrivate::checkSlaveOnHold(bool b) { //qDebug() << b; m_checkOnHold = b; } void SchedulerPrivate::updateInternalMetaData(SimpleJob *job) { KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job); // Preserve all internal meta-data so they can be sent back to the // ioslaves as needed... const QUrl jobUrl = job->url(); //qDebug() << job << jobPriv->m_internalMetaData; QMapIterator it(jobPriv->m_internalMetaData); while (it.hasNext()) { it.next(); if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) { SlaveConfig::self()->setConfigData(jobUrl.scheme(), jobUrl.host(), it.key().mid(22), it.value()); } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) { SlaveConfig::self()->setConfigData(jobUrl.scheme(), QString(), it.key().mid(19), it.value()); } } } #include "moc_scheduler.cpp" #include "moc_scheduler_p.cpp" diff --git a/src/core/simplejob.cpp b/src/core/simplejob.cpp index 6f1a0ec3..b5ea51b5 100644 --- a/src/core/simplejob.cpp +++ b/src/core/simplejob.cpp @@ -1,425 +1,425 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2013 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. */ #include "simplejob.h" #include "job_p.h" #include "scheduler.h" #include "slave.h" #include "kprotocolinfo.h" #include #include #include using namespace KIO; SimpleJob::SimpleJob(SimpleJobPrivate &dd) : Job(dd) { d_func()->simpleJobInit(); } void SimpleJobPrivate::simpleJobInit() { Q_Q(SimpleJob); if (!m_url.isValid() || m_url.scheme().isEmpty()) { qCWarning(KIO_CORE) << "Invalid URL:" << m_url; q->setError(ERR_MALFORMED_URL); q->setErrorText(m_url.toString()); - QTimer::singleShot(0, q, SLOT(slotFinished())); + QTimer::singleShot(0, q, &SimpleJob::slotFinished); return; } Scheduler::doJob(q); } bool SimpleJob::doKill() { Q_D(SimpleJob); if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) { d->m_extraFlags |= JobPrivate::EF_KillCalled; Scheduler::cancelJob(this); // deletes the slave if not 0 } else { qCWarning(KIO_CORE) << this << "This is overkill."; } return Job::doKill(); } bool SimpleJob::doSuspend() { Q_D(SimpleJob); if (d->m_slave) { d->m_slave->suspend(); } return Job::doSuspend(); } bool SimpleJob::doResume() { Q_D(SimpleJob); if (d->m_slave) { d->m_slave->resume(); } return Job::doResume(); } const QUrl &SimpleJob::url() const { return d_func()->m_url; } void SimpleJob::putOnHold() { Q_D(SimpleJob); Q_ASSERT(d->m_slave); if (d->m_slave) { Scheduler::putSlaveOnHold(this, d->m_url); } // we should now be disassociated from the slave Q_ASSERT(!d->m_slave); kill(Quietly); } void SimpleJob::removeOnHold() { Scheduler::removeSlaveOnHold(); } bool SimpleJob::isRedirectionHandlingEnabled() const { return d_func()->m_redirectionHandlingEnabled; } void SimpleJob::setRedirectionHandlingEnabled(bool handle) { Q_D(SimpleJob); d->m_redirectionHandlingEnabled = handle; } SimpleJob::~SimpleJob() { Q_D(SimpleJob); // last chance to remove this job from the scheduler! if (d->m_schedSerial) { //qDebug() << "Killing job" << this << "in destructor!"/* << qBacktrace()*/; Scheduler::cancelJob(this); } } void SimpleJobPrivate::start(Slave *slave) { Q_Q(SimpleJob); m_slave = slave; // Slave::setJob can send us SSL metadata if there is a persistent connection QObject::connect(slave, &Slave::metaData, q, &SimpleJob::slotMetaData); slave->setJob(q); QObject::connect(slave, &Slave::error, q, &SimpleJob::slotError); QObject::connect(slave, &Slave::warning, q, &SimpleJob::slotWarning); QObject::connect(slave, &Slave::finished, q, &SimpleJob::slotFinished); QObject::connect(slave, &Slave::infoMessage, q, [this](const QString& message){ _k_slotSlaveInfoMessage(message);} ); QObject::connect(slave, &Slave::connected, q, [this](){ slotConnected();} ); QObject::connect(slave, &Slave::privilegeOperationRequested, q, [this](){ slotPrivilegeOperationRequested();} ); if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job QObject::connect(slave, &Slave::totalSize, q, [this](KIO::filesize_t size){ slotTotalSize(size);} ); QObject::connect(slave, &Slave::processedSize, q, [this](KIO::filesize_t size){ slotProcessedSize(size);} ); QObject::connect(slave, &Slave::speed, q, [this](ulong speed){ slotSpeed(speed);} ); } const QVariant windowIdProp = q->property("window-id"); // see KJobWidgets::setWindow if (windowIdProp.isValid()) { m_outgoingMetaData.insert(QStringLiteral("window-id"), QString::number(windowIdProp.toULongLong())); } const QVariant userTimestampProp = q->property("userTimestamp"); // see KJobWidgets::updateUserTimestamp if (userTimestampProp.isValid()) { m_outgoingMetaData.insert(QStringLiteral("user-timestamp"), QString::number(userTimestampProp.toULongLong())); } if (q->uiDelegate() == nullptr) { // not interactive m_outgoingMetaData.insert(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); } if (!m_outgoingMetaData.isEmpty()) { KIO_ARGS << m_outgoingMetaData; slave->send(CMD_META_DATA, packedArgs); } if (!m_subUrl.isEmpty()) { KIO_ARGS << m_subUrl; slave->send(CMD_SUBURL, packedArgs); } slave->send(m_command, m_packedArgs); if (q->isSuspended()) { slave->suspend(); } } void SimpleJobPrivate::slaveDone() { Q_Q(SimpleJob); if (m_slave) { if (m_command == CMD_OPEN) { m_slave->send(CMD_CLOSE); } q->disconnect(m_slave); // Remove all signals between slave and job } // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero. if (m_schedSerial) { Scheduler::jobFinished(q, m_slave); } } void SimpleJob::slotFinished() { Q_D(SimpleJob); // Return slave to the scheduler d->slaveDone(); if (!hasSubjobs()) { if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) { if (d->m_command == CMD_MKDIR) { const QUrl urlDir = url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); org::kde::KDirNotify::emitFilesAdded(urlDir); } else { /*if ( m_command == CMD_RENAME )*/ QUrl src, dst; QDataStream str(d->m_packedArgs); str >> src >> dst; if (src.adjusted(QUrl::RemoveFilename) == dst.adjusted(QUrl::RemoveFilename) // For the user, moving isn't renaming. Only renaming is. && !KProtocolInfo::slaveHandlesNotify(dst.scheme()).contains(QLatin1String("Rename"))) { org::kde::KDirNotify::emitFileRenamed(src, dst); } org::kde::KDirNotify::emitFileMoved(src, dst); if (d->m_uiDelegateExtension) { d->m_uiDelegateExtension->updateUrlInClipboard(src, dst); } } } emitResult(); } } void SimpleJob::slotError(int err, const QString &errorText) { Q_D(SimpleJob); setError(err); setErrorText(errorText); if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) { setErrorText(QString()); } // error terminates the job slotFinished(); } void SimpleJob::slotWarning(const QString &errorText) { emit warning(this, errorText); } void SimpleJobPrivate::_k_slotSlaveInfoMessage(const QString &msg) { emit q_func()->infoMessage(q_func(), msg); } void SimpleJobPrivate::slotConnected() { emit q_func()->connected(q_func()); } void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size) { Q_Q(SimpleJob); if (size != q->totalAmount(KJob::Bytes)) { q->setTotalAmount(KJob::Bytes, size); } } void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size) { Q_Q(SimpleJob); //qDebug() << KIO::number(size); q->setProcessedAmount(KJob::Bytes, size); } void SimpleJobPrivate::slotSpeed(unsigned long speed) { //qDebug() << speed; q_func()->emitSpeed(speed); } void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl) { Q_Q(SimpleJob); // Return slave to the scheduler while we still have the old URL in place; the scheduler // requires a job URL to stay invariant while the job is running. slaveDone(); m_url = *redirectionUrl; redirectionUrl->clear(); if ((m_extraFlags & EF_KillCalled) == 0) { Scheduler::doJob(q); } } int SimpleJobPrivate::requestMessageBox(int _type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &iconYes, const QString &iconNo, const QString &dontAskAgainName, const KIO::MetaData &sslMetaData) { if (m_uiDelegateExtension) { const JobUiDelegateExtension::MessageBoxType type = static_cast(_type); return m_uiDelegateExtension->requestMessageBox(type, text, caption, buttonYes, buttonNo, iconYes, iconNo, dontAskAgainName, sslMetaData); } qCWarning(KIO_CORE) << "JobUiDelegate not set! Returning -1"; return -1; } void SimpleJob::slotMetaData(const KIO::MetaData &_metaData) { Q_D(SimpleJob); QMapIterator it(_metaData); while (it.hasNext()) { it.next(); if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) { d->m_internalMetaData.insert(it.key(), it.value()); } else { d->m_incomingMetaData.insert(it.key(), it.value()); } } // Update the internal meta-data values as soon as possible. Waiting until // the ioslave is finished has unintended consequences if the client starts // a new connection without waiting for the ioslave to finish. if (!d->m_internalMetaData.isEmpty()) { Scheduler::updateInternalMetaData(this); } } void SimpleJob::storeSSLSessionFromJob(const QUrl &redirectionURL) { Q_UNUSED(redirectionURL); } void SimpleJobPrivate::slotPrivilegeOperationRequested() { m_slave->send(MSG_PRIVILEGE_EXEC, privilegeOperationData()); } ////////// SimpleJob *KIO::rmdir(const QUrl &url) { //qDebug() << "rmdir " << url; KIO_ARGS << url << qint8(false); // isFile is false return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs); } SimpleJob *KIO::chmod(const QUrl &url, int permissions) { //qDebug() << "chmod " << url; KIO_ARGS << url << permissions; return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs); } SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group) { KIO_ARGS << url << owner << group; return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs); } SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime) { //qDebug() << "setModificationTime " << url << " " << mtime; KIO_ARGS << url << mtime; return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs); } SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags) { //qDebug() << "rename " << src << " " << dest; KIO_ARGS << src << dest << (qint8)(flags & Overwrite); return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs, flags); } SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags) { //qDebug() << "symlink target=" << target << " " << dest; KIO_ARGS << target << dest << (qint8)(flags & Overwrite); return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags); } SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags) { //qDebug() << "special " << url; return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags); } SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags) { KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(fstype) << dev << point; SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags); if (!(flags & HideProgressInfo)) { KIO::JobPrivate::emitMounting(job, dev, point); } return job; } SimpleJob *KIO::unmount(const QString &point, JobFlags flags) { KIO_ARGS << int(2) << point; SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags); if (!(flags & HideProgressInfo)) { KIO::JobPrivate::emitUnmounting(job, point); } return job; } ////////// SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate) { Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")); // Send http update_cache command (2) KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000); SimpleJob *job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs); Scheduler::setJobPriority(job, 1); return job; } #include "moc_simplejob.cpp" diff --git a/src/core/slave.cpp b/src/core/slave.cpp index 99fd7025..585c5e18 100644 --- a/src/core/slave.cpp +++ b/src/core/slave.cpp @@ -1,580 +1,580 @@ /* * This file is part of the KDE libraries * Copyright (c) 2000 Waldo Bastian * 2000 Stephan Kulow * * 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 "slave.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "dataprotocol_p.h" #include "connection_p.h" #include "commands_p.h" #include "connectionserver.h" #include "kioglobal_p.h" #include #include // CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 #include "slaveinterface_p.h" #include "kiocoredebug.h" using namespace KIO; #define SLAVE_CONNECTION_TIMEOUT_MIN 2 // Without debug info we consider it an error if the slave doesn't connect // within 10 seconds. // With debug info we give the slave an hour so that developers have a chance // to debug their slave. #ifdef NDEBUG #define SLAVE_CONNECTION_TIMEOUT_MAX 10 #else #define SLAVE_CONNECTION_TIMEOUT_MAX 3600 #endif static QThreadStorage s_kslaveLauncher; static org::kde::KSlaveLauncher *klauncher() { KDEInitInterface::ensureKdeinitRunning(); if (!s_kslaveLauncher.hasLocalData()) { org::kde::KSlaveLauncher *launcher = new org::kde::KSlaveLauncher(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QDBusConnection::sessionBus()); s_kslaveLauncher.setLocalData(launcher); return launcher; } return s_kslaveLauncher.localData(); } // In such case we start the slave via QProcess. // It's possible to force this by setting the env. variable // KDE_FORK_SLAVES, Clearcase seems to require this. static QBasicAtomicInt bForkSlaves = #if KIO_FORK_SLAVES Q_BASIC_ATOMIC_INITIALIZER(1); #else Q_BASIC_ATOMIC_INITIALIZER(-1); #endif static bool forkSlaves() { // In such case we start the slave via QProcess. // It's possible to force this by setting the env. variable // KDE_FORK_SLAVES, Clearcase seems to require this. if (bForkSlaves.load() == -1) { bool fork = qEnvironmentVariableIsSet("KDE_FORK_SLAVES"); // no dbus? => fork slaves as we can't talk to klauncher if (!fork) { fork = !QDBusConnection::sessionBus().interface(); } #ifdef Q_OS_UNIX if (!fork) { // check the UID of klauncher QDBusReply reply = QDBusConnection::sessionBus().interface()->serviceUid(klauncher()->service()); // if reply is not valid, fork, most likely klauncher can not be run or is not installed // fallback: if there's an klauncher process owned by a different user: still fork if (!reply.isValid() || getuid() != reply) { fork = true; } } #endif bForkSlaves.testAndSetRelaxed(-1, fork ? 1 : 0); } return bForkSlaves.load() == 1; } namespace KIO { /** * @internal */ class SlavePrivate: public SlaveInterfacePrivate { public: explicit SlavePrivate(const QString &protocol) : m_protocol(protocol), m_slaveProtocol(protocol), slaveconnserver(new KIO::ConnectionServer), m_job(nullptr), m_pid(0), m_port(0), contacted(false), dead(false), m_refCount(1) { contact_started.start(); slaveconnserver->listenForRemote(); if (!slaveconnserver->isListening()) { qCWarning(KIO_CORE) << "KIO Connection server not listening, could not connect"; } } ~SlavePrivate() { delete slaveconnserver; } QString m_protocol; QString m_slaveProtocol; QString m_host; QString m_user; QString m_passwd; KIO::ConnectionServer *slaveconnserver; KIO::SimpleJob *m_job; qint64 m_pid; quint16 m_port; bool contacted; bool dead; QElapsedTimer contact_started; QElapsedTimer m_idleSince; int m_refCount; }; } void Slave::accept() { Q_D(Slave); d->slaveconnserver->setNextPendingConnection(d->connection); d->slaveconnserver->deleteLater(); d->slaveconnserver = nullptr; connect(d->connection, &Connection::readyRead, this, &Slave::gotInput); } void Slave::timeout() { Q_D(Slave); if (d->dead) { //already dead? then slaveDied was emitted and we are done return; } if (d->connection->isConnected()) { return; } /*qDebug() << "slave failed to connect to application pid=" << d->m_pid << " protocol=" << d->m_protocol;*/ if (d->m_pid && KIOPrivate::isProcessAlive(d->m_pid)) { int delta_t = d->contact_started.elapsed() / 1000; //qDebug() << "slave is slow... pid=" << d->m_pid << " t=" << delta_t; if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX) { - QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, this, SLOT(timeout())); + QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, this, &Slave::timeout); return; } } //qDebug() << "Houston, we lost our slave, pid=" << d->m_pid; d->connection->close(); d->dead = true; QString arg = d->m_protocol; if (!d->m_host.isEmpty()) { arg += QLatin1String("://") + d->m_host; } //qDebug() << "slave died pid = " << d->m_pid; ref(); // Tell the job about the problem. emit error(ERR_SLAVE_DIED, arg); // Tell the scheduler about the problem. emit slaveDied(this); // After the above signal we're dead!! deref(); } Slave::Slave(const QString &protocol, QObject *parent) : SlaveInterface(*new SlavePrivate(protocol), parent) { Q_D(Slave); d->slaveconnserver->setParent(this); d->connection = new Connection(this); connect(d->slaveconnserver, &ConnectionServer::newConnection, this, &Slave::accept); } Slave::~Slave() { //qDebug() << "destructing slave object pid = " << d->m_pid; //delete d; } QString Slave::protocol() { Q_D(Slave); return d->m_protocol; } void Slave::setProtocol(const QString &protocol) { Q_D(Slave); d->m_protocol = protocol; } QString Slave::slaveProtocol() { Q_D(Slave); return d->m_slaveProtocol; } QString Slave::host() { Q_D(Slave); return d->m_host; } quint16 Slave::port() { Q_D(Slave); return d->m_port; } QString Slave::user() { Q_D(Slave); return d->m_user; } QString Slave::passwd() { Q_D(Slave); return d->m_passwd; } void Slave::setIdle() { Q_D(Slave); d->m_idleSince.start(); } bool Slave::isConnected() { Q_D(Slave); return d->contacted; } void Slave::setConnected(bool c) { Q_D(Slave); d->contacted = c; } void Slave::ref() { Q_D(Slave); d->m_refCount++; } void Slave::deref() { Q_D(Slave); d->m_refCount--; if (!d->m_refCount) { aboutToDelete(); delete this; // yes it reads funny, but it's too late for a deleteLater() here, no event loop anymore } } void Slave::aboutToDelete() { Q_D(Slave); d->connection->disconnect(this); this->disconnect(); } int Slave::idleTime() { Q_D(Slave); if (!d->m_idleSince.isValid()) { return 0; } return d->m_idleSince.elapsed() / 1000; } void Slave::setPID(qint64 pid) { Q_D(Slave); d->m_pid = pid; } qint64 Slave::slave_pid() { Q_D(Slave); return d->m_pid; } void Slave::setJob(KIO::SimpleJob *job) { Q_D(Slave); if (!d->sslMetaData.isEmpty()) { emit metaData(d->sslMetaData); } d->m_job = job; } KIO::SimpleJob *Slave::job() const { Q_D(const Slave); return d->m_job; } bool Slave::isAlive() { Q_D(Slave); return !d->dead; } void Slave::hold(const QUrl &url) { Q_D(Slave); ref(); { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << url; d->connection->send(CMD_SLAVE_HOLD, data); d->connection->close(); d->dead = true; emit slaveDied(this); } deref(); // Call KSlaveLauncher::waitForSlave(pid); { klauncher()->waitForSlave(d->m_pid); } } void Slave::suspend() { Q_D(Slave); d->connection->suspend(); } void Slave::resume() { Q_D(Slave); d->connection->resume(); } bool Slave::suspended() { Q_D(Slave); return d->connection->suspended(); } void Slave::send(int cmd, const QByteArray &arr) { Q_D(Slave); d->connection->send(cmd, arr); } void Slave::gotInput() { Q_D(Slave); if (d->dead) { //already dead? then slaveDied was emitted and we are done return; } ref(); if (!dispatch()) { d->connection->close(); d->dead = true; QString arg = d->m_protocol; if (!d->m_host.isEmpty()) { arg += QLatin1String("://") + d->m_host; } //qDebug() << "slave died pid = " << d->m_pid; // Tell the job about the problem. emit error(ERR_SLAVE_DIED, arg); // Tell the scheduler about the problem. emit slaveDied(this); } deref(); // Here we might be dead!! } void Slave::kill() { Q_D(Slave); d->dead = true; // OO can be such simple. /*qDebug() << "killing slave pid" << d->m_pid << "(" << QString(d->m_protocol) + "://" + d->m_host << ")";*/ if (d->m_pid) { KIOPrivate::sendTerminateSignal(d->m_pid); d->m_pid = 0; } } void Slave::setHost(const QString &host, quint16 port, const QString &user, const QString &passwd) { Q_D(Slave); d->m_host = host; d->m_port = port; d->m_user = user; d->m_passwd = passwd; d->sslMetaData.clear(); QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << d->m_host << d->m_port << d->m_user << d->m_passwd; d->connection->send(CMD_HOST, data); } void Slave::resetHost() { Q_D(Slave); d->sslMetaData.clear(); d->m_host = QStringLiteral(""); } void Slave::setConfig(const MetaData &config) { Q_D(Slave); QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << config; d->connection->send(CMD_CONFIG, data); } Slave *Slave::createSlave(const QString &protocol, const QUrl &url, int &error, QString &error_text) { //qDebug() << "createSlave" << protocol << "for" << url; // Firstly take into account all special slaves if (protocol == QLatin1String("data")) { return new DataProtocol(); } Slave *slave = new Slave(protocol); QUrl slaveAddress = slave->d_func()->slaveconnserver->address(); if (slaveAddress.isEmpty()) { error_text = i18n("Can not create socket for launching io-slave for protocol '%1'.", protocol); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } if (forkSlaves() == 1) { QString _name = KProtocolInfo::exec(protocol); if (_name.isEmpty()) { error_text = i18n("Unknown protocol '%1'.", protocol); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } // find the kioslave using KPluginLoader; kioslave would do this // anyway, but if it doesn't exist, we want to be able to return // a useful error message immediately QString lib_path = KPluginLoader::findPlugin(_name); if (lib_path.isEmpty()) { error_text = i18n("Can not find io-slave for protocol '%1'.", protocol); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } const QStringList args = QStringList() << lib_path << protocol << QLatin1String("") << slaveAddress.toString(); //qDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress; // search paths const QStringList searchPaths = QStringList() << QCoreApplication::applicationDirPath() // then look where our application binary is located << QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath) // look where libexec path is (can be set in qt.conf) << QFile::decodeName(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5); // look at our installation location const QString kioslaveExecutable = QStandardPaths::findExecutable(QStringLiteral("kioslave"), searchPaths); if (kioslaveExecutable.isEmpty()) { error_text = i18n("Can not find 'kioslave' executable at '%1'", searchPaths.join(QStringLiteral(", "))); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } QProcess::startDetached(kioslaveExecutable, args); return slave; } QString errorStr; QDBusReply reply = klauncher()->requestSlave(protocol, url.host(), slaveAddress.toString(), errorStr); if (!reply.isValid()) { error_text = i18n("Cannot talk to klauncher: %1", klauncher()->lastError().message()); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } qint64 pid = reply; if (!pid) { error_text = i18n("klauncher said: %1", errorStr); error = KIO::ERR_CANNOT_CREATE_SLAVE; delete slave; return nullptr; } slave->setPID(pid); - QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); + QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, slave, &Slave::timeout); return slave; } Slave *Slave::holdSlave(const QString &protocol, const QUrl &url) { //qDebug() << "holdSlave" << protocol << "for" << url; // Firstly take into account all special slaves if (protocol == QLatin1String("data")) { return nullptr; } if (forkSlaves()) { return nullptr; } Slave *slave = new Slave(protocol); QUrl slaveAddress = slave->d_func()->slaveconnserver->address(); QDBusReply reply = klauncher()->requestHoldSlave(url.toString(), slaveAddress.toString()); if (!reply.isValid()) { delete slave; return nullptr; } qint64 pid = reply; if (!pid) { delete slave; return nullptr; } slave->setPID(pid); - QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); + QTimer::singleShot(1000 * SLAVE_CONNECTION_TIMEOUT_MIN, slave, &Slave::timeout); return slave; } bool Slave::checkForHeldSlave(const QUrl &url) { if (forkSlaves()) { return false; } return klauncher()->checkForHeldSlave(url.toString()); } diff --git a/src/core/slaveinterface.cpp b/src/core/slaveinterface.cpp index 0a9e6133..ada2d25f 100644 --- a/src/core/slaveinterface.cpp +++ b/src/core/slaveinterface.cpp @@ -1,441 +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())); + connect(&d_ptr->speed_timer, &QTimer::timeout, this, &SlaveInterface::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_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/statjob.cpp b/src/core/statjob.cpp index 2444c535..df982f65 100644 --- a/src/core/statjob.cpp +++ b/src/core/statjob.cpp @@ -1,216 +1,216 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 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. */ #include "statjob.h" #include "job_p.h" #include "slave.h" #include "scheduler.h" #include #include using namespace KIO; class KIO::StatJobPrivate: public SimpleJobPrivate { public: inline StatJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs) : SimpleJobPrivate(url, command, packedArgs), m_bSource(true), m_details(2) {} UDSEntry m_statResult; QUrl m_redirectionURL; bool m_bSource; short int m_details; void slotStatEntry(const KIO::UDSEntry &entry); void slotRedirection(const QUrl &url); /** * @internal * Called by the scheduler when a @p slave gets to * work on this job. * @param slave the slave that starts working on this job */ void start(Slave *slave) override; Q_DECLARE_PUBLIC(StatJob) static inline StatJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, JobFlags flags) { StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs)); job->setUiDelegate(KIO::createDefaultJobUiDelegate()); if (!(flags & HideProgressInfo)) { KIO::getJobTracker()->registerJob(job); emitStating(job, url); } return job; } }; StatJob::StatJob(StatJobPrivate &dd) : SimpleJob(dd) { } StatJob::~StatJob() { } #ifndef KIOCORE_NO_DEPRECATED void StatJob::setSide(bool source) { d_func()->m_bSource = source; } #endif void StatJob::setSide(StatSide side) { d_func()->m_bSource = side == SourceSide; } void StatJob::setDetails(short int details) { d_func()->m_details = details; } const UDSEntry &StatJob::statResult() const { return d_func()->m_statResult; } QUrl StatJob::mostLocalUrl() const { if (!url().isLocalFile()) { const UDSEntry &udsEntry = d_func()->m_statResult; const QString path = udsEntry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); if (!path.isEmpty()) { return QUrl::fromLocalFile(path); } } return url(); } void StatJobPrivate::start(Slave *slave) { Q_Q(StatJob); m_outgoingMetaData.insert(QStringLiteral("statSide"), m_bSource ? QStringLiteral("source") : QStringLiteral("dest")); m_outgoingMetaData.insert(QStringLiteral("details"), QString::number(m_details)); q->connect(slave, SIGNAL(statEntry(KIO::UDSEntry)), SLOT(slotStatEntry(KIO::UDSEntry))); q->connect(slave, SIGNAL(redirection(QUrl)), SLOT(slotRedirection(QUrl))); SimpleJobPrivate::start(slave); } void StatJobPrivate::slotStatEntry(const KIO::UDSEntry &entry) { //qCDebug(KIO_CORE); m_statResult = entry; } // Slave got a redirection request void StatJobPrivate::slotRedirection(const QUrl &url) { Q_Q(StatJob); //qCDebug(KIO_CORE) << m_url << "->" << url; if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("redirect"), m_url, url)) { qCWarning(KIO_CORE) << "Redirection from" << m_url << "to" << url << "REJECTED!"; q->setError(ERR_ACCESS_DENIED); q->setErrorText(url.toDisplayString()); return; } m_redirectionURL = url; // We'll remember that when the job finishes // Tell the user that we haven't finished yet emit q->redirection(q, m_redirectionURL); } void StatJob::slotFinished() { Q_D(StatJob); if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) { //qCDebug(KIO_CORE) << "StatJob: Redirection to " << m_redirectionURL; if (queryMetaData(QStringLiteral("permanent-redirect")) == QLatin1String("true")) { emit permanentRedirection(this, d->m_url, d->m_redirectionURL); } if (d->m_redirectionHandlingEnabled) { d->m_packedArgs.truncate(0); QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_redirectionURL; d->restartAfterRedirection(&d->m_redirectionURL); return; } } // Return slave to the scheduler SimpleJob::slotFinished(); } void StatJob::slotMetaData(const KIO::MetaData &_metaData) { Q_D(StatJob); SimpleJob::slotMetaData(_metaData); storeSSLSessionFromJob(d->m_redirectionURL); } StatJob *KIO::stat(const QUrl &url, JobFlags flags) { // Assume sideIsSource. Gets are more common than puts. return stat(url, StatJob::SourceSide, 2, flags); } StatJob *KIO::mostLocalUrl(const QUrl &url, JobFlags flags) { StatJob *job = stat(url, StatJob::SourceSide, 2, flags); if (url.isLocalFile()) { - QTimer::singleShot(0, job, SLOT(slotFinished())); + QTimer::singleShot(0, job, &StatJob::slotFinished); Scheduler::cancelJob(job); // deletes the slave if not 0 } return job; } #ifndef KIOCORE_NO_DEPRECATED StatJob *KIO::stat(const QUrl &url, bool sideIsSource, short int details, JobFlags flags) { //qCDebug(KIO_CORE) << "stat" << url; KIO_ARGS << url; StatJob *job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags); job->setSide(sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide); job->setDetails(details); return job; } #endif StatJob *KIO::stat(const QUrl &url, KIO::StatJob::StatSide side, short int details, JobFlags flags) { //qCDebug(KIO_CORE) << "stat" << url; KIO_ARGS << url; StatJob *job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags); job->setSide(side); job->setDetails(details); return job; } #include "moc_statjob.cpp" diff --git a/src/core/statjob.h b/src/core/statjob.h index df91abf7..2d32e704 100644 --- a/src/core/statjob.h +++ b/src/core/statjob.h @@ -1,230 +1,231 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2013 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_STATJOB_H #define KIO_STATJOB_H #include "simplejob.h" #include namespace KIO { class StatJobPrivate; /** * @class KIO::StatJob statjob.h * * A KIO job that retrieves information about a file or directory. * @see KIO::stat() */ class KIOCORE_EXPORT StatJob : public SimpleJob { Q_OBJECT public: enum StatSide { SourceSide, DestinationSide }; ~StatJob() override; /** * A stat() can have two meanings. Either we want to read from this URL, * or to check if we can write to it. First case is "source", second is "dest". * It is necessary to know what the StatJob is for, to tune the kioslave's behavior * (e.g. with FTP). * By default it is SourceSide. * @param side SourceSide or DestinationSide */ void setSide(StatSide side); /** * A stat() can have two meanings. Either we want to read from this URL, * or to check if we can write to it. First case is "source", second is "dest". * It is necessary to know what the StatJob is for, to tune the kioslave's behavior * (e.g. with FTP). * @param source true for "source" mode, false for "dest" mode * @deprecated use setSide(StatSide side). */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED void setSide(bool source); #endif /** * Selects the level of @p details we want. * By default this is 2 (all details wanted, including modification time, size, etc.), * setDetails(1) is used when deleting: we don't need all the information if it takes * too much time, no need to follow symlinks etc. * setDetails(0) is used for very simple probing: we'll only get the answer * "it's a file or a directory, or it doesn't exist". This is used by KRun. * @param details 2 for all details, 1 for simple, 0 for very simple */ void setDetails(short int details); /** * @brief Result of the stat operation. * Call this in the slot connected to result, * and only after making sure no error happened. * @return the result of the stat */ const UDSEntry &statResult() const; /** * @brief most local URL * Call this in the slot connected to result, * and only after making sure no error happened. * @return the most local URL for the URL we were stat'ing. * * Sample usage: * * @code * KIO::StatJob* job = KIO::mostLocalUrl("desktop:/foo"); * job->uiDelegate()->setWindow(this); * connect(job, SIGNAL(result(KJob*)), this, SLOT(slotMostLocalUrlResult(KJob*))); * [...] * // and in the slot * if (job->error()) { * [...] // doesn't exist * } else { * const QUrl localUrl = job->mostLocalUrl(); * // localUrl = file:///$HOME/Desktop/foo * [...] * } * @endcode * * \since 4.4 */ QUrl mostLocalUrl() const; Q_SIGNALS: /** * Signals a redirection. * Use to update the URL shown to the user. * The redirection itself is handled internally. * @param job the job that is redirected * @param url the new url */ void redirection(KIO::Job *job, const QUrl &url); /** * Signals a permanent redirection. * The redirection itself is handled internally. * @param job the job that is redirected * @param fromUrl the original URL * @param toUrl the new URL */ void permanentRedirection(KIO::Job *job, const QUrl &fromUrl, const QUrl &toUrl); protected Q_SLOTS: void slotFinished() override; void slotMetaData(const KIO::MetaData &_metaData) override; protected: StatJob(StatJobPrivate &dd); private: Q_PRIVATE_SLOT(d_func(), void slotStatEntry(const KIO::UDSEntry &entry)) Q_PRIVATE_SLOT(d_func(), void slotRedirection(const QUrl &url)) Q_DECLARE_PRIVATE(StatJob) + friend StatJob *mostLocalUrl(const QUrl &url, JobFlags flags); }; /** * Find all details for one file or directory. * * @param url the URL of the file * @param flags Can be HideProgressInfo here * @return the job handling the operation. */ KIOCORE_EXPORT StatJob *stat(const QUrl &url, JobFlags flags = DefaultFlags); /** * Find all details for one file or directory. * This version of the call includes two additional booleans, @p sideIsSource and @p details. * * @param url the URL of the file * @param side is SourceSide when stating a source file (we will do a get on it if * the stat works) and DestinationSide when stating a destination file (target of a copy). * The reason for this parameter is that in some cases the kioslave might not * be able to determine a file's existence (e.g. HTTP doesn't allow it, FTP * has issues with case-sensitivity on some systems). * When the slave can't reliably determine the existence of a file, it will: * @li be optimistic if SourceSide, i.e. it will assume the file exists, * and if it doesn't this will appear when actually trying to download it * @li be pessimistic if DestinationSide, i.e. it will assume the file * doesn't exist, to prevent showing "about to overwrite" errors to the user. * If you simply want to check for existence without downloading/uploading afterwards, * then you should use DestinationSide. * * @param details selects the level of details we want. * By default this is 2 (all details wanted, including modification time, size, etc.), * setDetails(1) is used when deleting: we don't need all the information if it takes * too much time, no need to follow symlinks etc. * setDetails(0) is used for very simple probing: we'll only get the answer * "it's a file or a directory or a symlink, or it doesn't exist". This is used by KRun and DeleteJob. * @param flags Can be HideProgressInfo here * @return the job handling the operation. */ KIOCORE_EXPORT StatJob *stat(const QUrl &url, KIO::StatJob::StatSide side, short int details, JobFlags flags = DefaultFlags); /** * Find all details for one file or directory. * This version of the call includes two additional booleans, @p sideIsSource and @p details. * * @param url the URL of the file * @param sideIsSource is true when stating a source file (we will do a get on it if * the stat works) and false when stating a destination file (target of a copy). * The reason for this parameter is that in some cases the kioslave might not * be able to determine a file's existence (e.g. HTTP doesn't allow it, FTP * has issues with case-sensitivity on some systems). * When the slave can't reliably determine the existence of a file, it will: * @li be optimistic if sideIsSource=true, i.e. it will assume the file exists, * and if it doesn't this will appear when actually trying to download it * @li be pessimistic if sideIsSource=false, i.e. it will assume the file * doesn't exist, to prevent showing "about to overwrite" errors to the user. * If you simply want to check for existence without downloading/uploading afterwards, * then you should use sideIsSource=false. * * @param details selects the level of details we want. * By default this is 2 (all details wanted, including modification time, size, etc.), * setDetails(1) is used when deleting: we don't need all the information if it takes * too much time, no need to follow symlinks etc. * setDetails(0) is used for very simple probing: we'll only get the answer * "it's a file or a directory, or it doesn't exist". This is used by KRun. * @param flags Can be HideProgressInfo here * @return the job handling the operation. */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED_EXPORT StatJob *stat(const QUrl &url, bool sideIsSource, short int details, JobFlags flags = DefaultFlags); #endif /** * Tries to map a local URL for the given URL, using a KIO job. * * Starts a (stat) job for determining the "most local URL" for a given URL. * Retrieve the result with StatJob::mostLocalUrl in the result slot. * @param url The URL we are testing. * \since 4.4 */ KIOCORE_EXPORT StatJob *mostLocalUrl(const QUrl &url, JobFlags flags = DefaultFlags); } #endif diff --git a/src/core/transferjob.cpp b/src/core/transferjob.cpp index e4872b64..fbed1557 100644 --- a/src/core/transferjob.cpp +++ b/src/core/transferjob.cpp @@ -1,498 +1,498 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2013 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. */ #include "transferjob.h" #include "job_p.h" #include "slave.h" #include #include using namespace KIO; static const int MAX_READ_BUF_SIZE = (64 * 1024); // 64 KB at a time seems reasonable... TransferJob::TransferJob(TransferJobPrivate &dd) : SimpleJob(dd) { Q_D(TransferJob); if (d->m_command == CMD_PUT) { d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent; } if (d->m_outgoingDataSource) { connect(d->m_outgoingDataSource, SIGNAL(readChannelFinished()), SLOT(slotIODeviceClosedBeforeStart())); } } TransferJob::~TransferJob() { } // Slave sends data void TransferJob::slotData(const QByteArray &_data) { Q_D(TransferJob); if (d->m_command == CMD_GET && !d->m_isMimetypeEmitted) { qCWarning(KIO_CORE) << "mimeType() not emitted when sending first data!; job URL =" << d->m_url << "data size =" << _data.size(); } // shut up the warning, HACK: downside is that it changes the meaning of the variable d->m_isMimetypeEmitted = true; if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) { emit data(this, _data); } } void KIO::TransferJob::setTotalSize(KIO::filesize_t bytes) { setTotalAmount(KJob::Bytes, bytes); } // Slave got a redirection request void TransferJob::slotRedirection(const QUrl &url) { Q_D(TransferJob); //qDebug() << url; if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("redirect"), d->m_url, url)) { qCWarning(KIO_CORE) << "Redirection from" << d->m_url << "to" << url << "REJECTED!"; return; } // Some websites keep redirecting to themselves where each redirection // acts as the stage in a state-machine. We define "endless redirections" // as 5 redirections to the same URL. if (d->m_redirectionList.count(url) > 5) { //qDebug() << "CYCLIC REDIRECTION!"; setError(ERR_CYCLIC_LINK); setErrorText(d->m_url.toDisplayString()); } else { d->m_redirectionURL = url; // We'll remember that when the job finishes d->m_redirectionList.append(url); QString sslInUse = queryMetaData(QStringLiteral("ssl_in_use")); if (!sslInUse.isNull()) { // the key is present addMetaData(QStringLiteral("ssl_was_in_use"), sslInUse); } else { addMetaData(QStringLiteral("ssl_was_in_use"), QStringLiteral("FALSE")); } // Tell the user that we haven't finished yet emit redirection(this, d->m_redirectionURL); } } void TransferJob::slotFinished() { Q_D(TransferJob); //qDebug() << d->m_url; if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) { //qDebug() << "Redirection to" << m_redirectionURL; if (queryMetaData(QStringLiteral("permanent-redirect")) == QLatin1String("true")) { emit permanentRedirection(this, d->m_url, d->m_redirectionURL); } if (queryMetaData(QStringLiteral("redirect-to-get")) == QLatin1String("true")) { d->m_command = CMD_GET; d->m_outgoingMetaData.remove(QStringLiteral("CustomHTTPMethod")); d->m_outgoingMetaData.remove(QStringLiteral("content-type")); } if (d->m_redirectionHandlingEnabled) { // Honour the redirection // We take the approach of "redirecting this same job" // Another solution would be to create a subjob, but the same problem // happens (unpacking+repacking) d->staticData.truncate(0); d->m_incomingMetaData.clear(); if (queryMetaData(QStringLiteral("cache")) != QLatin1String("reload")) { addMetaData(QStringLiteral("cache"), QStringLiteral("refresh")); } d->m_internalSuspended = false; // The very tricky part is the packed arguments business QUrl dummyUrl; QDataStream istream(d->m_packedArgs); switch (d->m_command) { case CMD_GET: case CMD_STAT: case CMD_DEL: { d->m_packedArgs.truncate(0); QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_redirectionURL; break; } case CMD_PUT: { int permissions; qint8 iOverwrite, iResume; istream >> dummyUrl >> iOverwrite >> iResume >> permissions; d->m_packedArgs.truncate(0); QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_redirectionURL << iOverwrite << iResume << permissions; break; } case CMD_SPECIAL: { int specialcmd; istream >> specialcmd; if (specialcmd == 1) { // HTTP POST d->m_outgoingMetaData.remove(QStringLiteral("content-type")); addMetaData(QStringLiteral("cache"), QStringLiteral("reload")); d->m_packedArgs.truncate(0); QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); stream << d->m_redirectionURL; d->m_command = CMD_GET; } break; } } d->restartAfterRedirection(&d->m_redirectionURL); return; } } SimpleJob::slotFinished(); } void TransferJob::setAsyncDataEnabled(bool enabled) { Q_D(TransferJob); if (enabled) { d->m_extraFlags |= JobPrivate::EF_TransferJobAsync; } else { d->m_extraFlags &= ~JobPrivate::EF_TransferJobAsync; } } void TransferJob::sendAsyncData(const QByteArray &dataForSlave) { Q_D(TransferJob); if (d->m_extraFlags & JobPrivate::EF_TransferJobNeedData) { if (d->m_slave) { d->m_slave->send(MSG_DATA, dataForSlave); } if (d->m_extraFlags & JobPrivate::EF_TransferJobDataSent) { // put job -> emit progress KIO::filesize_t size = processedAmount(KJob::Bytes) + dataForSlave.size(); setProcessedAmount(KJob::Bytes, size); } } d->m_extraFlags &= ~JobPrivate::EF_TransferJobNeedData; } #ifndef KIOCORE_NO_DEPRECATED void TransferJob::setReportDataSent(bool enabled) { Q_D(TransferJob); if (enabled) { d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent; } else { d->m_extraFlags &= ~JobPrivate::EF_TransferJobDataSent; } } #endif #ifndef KIOCORE_NO_DEPRECATED bool TransferJob::reportDataSent() const { return (d_func()->m_extraFlags & JobPrivate::EF_TransferJobDataSent); } #endif QString TransferJob::mimetype() const { return d_func()->m_mimetype; } QUrl TransferJob::redirectUrl() const { return d_func()->m_redirectionURL; } // Slave requests data void TransferJob::slotDataReq() { Q_D(TransferJob); QByteArray dataForSlave; d->m_extraFlags |= JobPrivate::EF_TransferJobNeedData; if (!d->staticData.isEmpty()) { dataForSlave = d->staticData; d->staticData.clear(); } else { emit dataReq(this, dataForSlave); if (d->m_extraFlags & JobPrivate::EF_TransferJobAsync) { return; } } static const int max_size = 14 * 1024 * 1024; if (dataForSlave.size() > max_size) { //qDebug() << "send" << dataForSlave.size() / 1024 / 1024 << "MB of data in TransferJob::dataReq. This needs to be split, which requires a copy. Fix the application."; d->staticData = QByteArray(dataForSlave.data() + max_size, dataForSlave.size() - max_size); dataForSlave.truncate(max_size); } sendAsyncData(dataForSlave); if (d->m_subJob) { // Bitburger protocol in action d->internalSuspend(); // Wait for more data from subJob. d->m_subJob->d_func()->internalResume(); // Ask for more! } } void TransferJob::slotMimetype(const QString &type) { Q_D(TransferJob); d->m_mimetype = type; if (d->m_command == CMD_GET && d->m_isMimetypeEmitted) { qCWarning(KIO_CORE) << "mimetype() emitted again, or after sending first data!; job URL =" << d->m_url; } d->m_isMimetypeEmitted = true; emit mimetype(this, type); } void TransferJobPrivate::internalSuspend() { m_internalSuspended = true; if (m_slave) { m_slave->suspend(); } } void TransferJobPrivate::internalResume() { m_internalSuspended = false; if (m_slave && !q_func()->isSuspended()) { m_slave->resume(); } } bool TransferJob::doResume() { Q_D(TransferJob); if (!SimpleJob::doResume()) { return false; } if (d->m_internalSuspended) { d->internalSuspend(); } return true; } bool TransferJob::isErrorPage() const { return d_func()->m_errorPage; } void TransferJobPrivate::start(Slave *slave) { Q_Q(TransferJob); Q_ASSERT(slave); JobPrivate::emitTransferring(q, m_url); - q->connect(slave, SIGNAL(data(QByteArray)), - SLOT(slotData(QByteArray))); + q->connect(slave, &SlaveInterface::data, + q, &TransferJob::slotData); if (m_outgoingDataSource) { if (m_extraFlags & JobPrivate::EF_TransferJobAsync) { q->connect(m_outgoingDataSource, SIGNAL(readyRead()), SLOT(slotDataReqFromDevice())); q->connect(m_outgoingDataSource, SIGNAL(readChannelFinished()), SLOT(slotIODeviceClosed())); // We don't really need to disconnect since we're never checking // m_closedBeforeStart again but it's the proper thing to do logically QObject::disconnect(m_outgoingDataSource, SIGNAL(readChannelFinished()), q, SLOT(slotIODeviceClosedBeforeStart())); if (m_closedBeforeStart) { QMetaObject::invokeMethod(q, "slotIODeviceClosed", Qt::QueuedConnection); } else if (m_outgoingDataSource->bytesAvailable() > 0) { QMetaObject::invokeMethod(q, "slotDataReqFromDevice", Qt::QueuedConnection); } } else { q->connect(slave, SIGNAL(dataReq()), SLOT(slotDataReqFromDevice())); } } else - q->connect(slave, SIGNAL(dataReq()), - SLOT(slotDataReq())); + q->connect(slave, &SlaveInterface::dataReq, + q, &TransferJob::slotDataReq); - q->connect(slave, SIGNAL(redirection(QUrl)), - SLOT(slotRedirection(QUrl))); + q->connect(slave, &SlaveInterface::redirection, + q, &TransferJob::slotRedirection); - q->connect(slave, SIGNAL(mimeType(QString)), - SLOT(slotMimetype(QString))); + q->connect(slave, &SlaveInterface::mimeType, + q, &TransferJob::slotMimetype); q->connect(slave, SIGNAL(errorPage()), SLOT(slotErrorPage())); q->connect(slave, SIGNAL(needSubUrlData()), SLOT(slotNeedSubUrlData())); q->connect(slave, SIGNAL(canResume(KIO::filesize_t)), SLOT(slotCanResume(KIO::filesize_t))); if (slave->suspended()) { m_mimetype = QStringLiteral("unknown"); // WABA: The slave was put on hold. Resume operation. slave->resume(); } SimpleJobPrivate::start(slave); if (m_internalSuspended) { slave->suspend(); } } void TransferJobPrivate::slotNeedSubUrlData() { Q_Q(TransferJob); // Job needs data from subURL. m_subJob = KIO::get(m_subUrl, NoReload, HideProgressInfo); internalSuspend(); // Put job on hold until we have some data. q->connect(m_subJob, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotSubUrlData(KIO::Job*,QByteArray))); q->addSubjob(m_subJob); } void TransferJobPrivate::slotSubUrlData(KIO::Job *, const QByteArray &data) { // The Alternating Bitburg protocol in action again. staticData = data; m_subJob->d_func()->internalSuspend(); // Put job on hold until we have delivered the data. internalResume(); // Activate ourselves again. } void TransferJob::slotMetaData(const KIO::MetaData &_metaData) { Q_D(TransferJob); SimpleJob::slotMetaData(_metaData); storeSSLSessionFromJob(d->m_redirectionURL); } void TransferJobPrivate::slotErrorPage() { m_errorPage = true; } void TransferJobPrivate::slotCanResume(KIO::filesize_t offset) { Q_Q(TransferJob); emit q->canResume(q, offset); } void TransferJobPrivate::slotDataReqFromDevice() { Q_Q(TransferJob); bool done = false; QByteArray dataForSlave; m_extraFlags |= JobPrivate::EF_TransferJobNeedData; if (m_outgoingDataSource) { dataForSlave.resize(MAX_READ_BUF_SIZE); //Code inspired in QNonContiguousByteDevice qint64 bytesRead = m_outgoingDataSource->read(dataForSlave.data(), MAX_READ_BUF_SIZE); if (bytesRead >= 0) { dataForSlave.resize(bytesRead); } else { dataForSlave.clear(); } done = ((bytesRead == -1) || (bytesRead == 0 && m_outgoingDataSource->atEnd() && !m_outgoingDataSource->isSequential())); } if (dataForSlave.isEmpty()) { emit q->dataReq(q, dataForSlave); if (!done && (m_extraFlags & JobPrivate::EF_TransferJobAsync)) { return; } } q->sendAsyncData(dataForSlave); if (m_subJob) { // Bitburger protocol in action internalSuspend(); // Wait for more data from subJob. m_subJob->d_func()->internalResume(); // Ask for more! } } void TransferJobPrivate::slotIODeviceClosedBeforeStart() { m_closedBeforeStart = true; } void TransferJobPrivate::slotIODeviceClosed() { Q_Q(TransferJob); const QByteArray remainder = m_outgoingDataSource->readAll(); if (!remainder.isEmpty()) { m_extraFlags |= JobPrivate::EF_TransferJobNeedData; q->sendAsyncData(remainder); } m_extraFlags |= JobPrivate::EF_TransferJobNeedData; //We send an empty data array to indicate the stream is over q->sendAsyncData(QByteArray()); if (m_subJob) { // Bitburger protocol in action internalSuspend(); // Wait for more data from subJob. m_subJob->d_func()->internalResume(); // Ask for more! } } void TransferJob::slotResult(KJob *job) { Q_D(TransferJob); // This can only be our suburl. Q_ASSERT(job == d->m_subJob); SimpleJob::slotResult(job); if (!error() && job == d->m_subJob) { d->m_subJob = nullptr; // No action required d->internalResume(); // Make sure we get the remaining data. } } void TransferJob::setModificationTime(const QDateTime &mtime) { addMetaData(QStringLiteral("modified"), mtime.toString(Qt::ISODate)); } TransferJob *KIO::get(const QUrl &url, LoadType reload, JobFlags flags) { // Send decoded path and encoded query KIO_ARGS << url; TransferJob *job = TransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags); if (reload == Reload) { job->addMetaData(QStringLiteral("cache"), QStringLiteral("reload")); } return job; } #include "moc_transferjob.cpp" diff --git a/src/core/usernotificationhandler.cpp b/src/core/usernotificationhandler.cpp index af658a02..951103c2 100644 --- a/src/core/usernotificationhandler.cpp +++ b/src/core/usernotificationhandler.cpp @@ -1,114 +1,114 @@ /* This file is part of the KDE libraries Copyright (C) 2012 Dawit Alemayehu 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 "usernotificationhandler_p.h" #include "slave.h" #include "job_p.h" #include "kiocoredebug.h" #include #include using namespace KIO; QString UserNotificationHandler::Request::key() const { QString key; if (slave) { key = slave->protocol(); key += slave->host(); key += slave->port(); key += QLatin1Char('-'); key += type; } return key; } UserNotificationHandler::UserNotificationHandler(QObject *parent) : QObject(parent) { } UserNotificationHandler::~UserNotificationHandler() { qDeleteAll(m_pendingRequests); } void UserNotificationHandler::requestMessageBox(SlaveInterface *iface, int type, const QHash &data) { Request *r = new Request; r->type = type; r->slave = qobject_cast(iface); r->data = data; m_pendingRequests.append(r); if (m_pendingRequests.count() == 1) { - QTimer::singleShot(0, this, SLOT(processRequest())); + QTimer::singleShot(0, this, &UserNotificationHandler::processRequest); } } void UserNotificationHandler::processRequest() { if (m_pendingRequests.isEmpty()) { return; } int result = -1; Request *r = m_pendingRequests.first(); if (r->slave) { const QString key = r->key(); if (m_cachedResults.contains(key)) { result = *(m_cachedResults[key]); } else { JobUiDelegateExtension *delegateExtension = nullptr; if (r->slave->job()) delegateExtension = SimpleJobPrivate::get(r->slave->job())->m_uiDelegateExtension; if (!delegateExtension) delegateExtension = KIO::defaultJobUiDelegateExtension(); if (delegateExtension) { const JobUiDelegateExtension::MessageBoxType type = static_cast(r->type); result = delegateExtension->requestMessageBox(type, r->data.value(MSG_TEXT).toString(), r->data.value(MSG_CAPTION).toString(), r->data.value(MSG_YES_BUTTON_TEXT).toString(), r->data.value(MSG_NO_BUTTON_TEXT).toString(), r->data.value(MSG_YES_BUTTON_ICON).toString(), r->data.value(MSG_NO_BUTTON_ICON).toString(), r->data.value(MSG_DONT_ASK_AGAIN).toString(), r->data.value(MSG_META_DATA).toMap()); } m_cachedResults.insert(key, new int(result)); } } else { qCWarning(KIO_CORE) << "Cannot prompt user because the requesting ioslave died!" << r->slave; } r->slave->sendMessageBoxAnswer(result); m_pendingRequests.removeFirst(); delete r; if (m_pendingRequests.isEmpty()) { m_cachedResults.clear(); } else { - QTimer::singleShot(0, this, SLOT(processRequest())); + QTimer::singleShot(0, this, &UserNotificationHandler::processRequest); } }